Skip to content

Commit

Permalink
Replace use of old term label with attribute (#2790)
Browse files Browse the repository at this point in the history
* Replace use of old term label with attribute

The specification has unified on the term attribute to describe
key-value pairs. There exist still many hold-overs of the use of the
term label. This updates those uses or deprecates exported types and
functions in favor of renamed forms.

* fix infinite recursion

* Remove backticks from attribute set docs

* Remove LabelFilterSelector entirely

* Remove Metadata.Labels instead of deprecate

* Update changelog with public changes

* Revert OC err msg
  • Loading branch information
MrAlias committed Apr 18, 2022
1 parent 1884de2 commit a8ea3db
Show file tree
Hide file tree
Showing 42 changed files with 554 additions and 549 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,24 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Resolve supply-chain failure for the markdown-link-checker GitHub action by calling the CLI directly. (#2834)
- Remove import of `testing` package in non-tests builds. (#2786)

### Changed

- The `WithLabelEncoder` option from the `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` package is renamed to `WithAttributeEncoder`. (#2790)
- The `Batch.Labels` field from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package is renamed to `Batch.Attributes`. (#2790)
- The `LabelFilterSelector` interface from `go.opentelemetry.io/otel/sdk/metric/processor/reducer` is renamed to `AttributeFilterSelector`.
The method included in the renamed interface also changed from `LabelFilterFor` to `AttributeFilterFor`. (#2790)
- The `Metadata.Labels` method from the `go.opentelemetry.io/otel/sdk/metric/export` package is renamed to `Metadata.Attributes`.
Consequentially, the `Record` type from the same package also has had the embedded method renamed. (#2790)

### Deprecated

- The `Iterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `Iterator.Attribute` method instead. (#2790)
- The `Iterator.IndexedLabel` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `Iterator.IndexedAttribute` method instead. (#2790)
- The `MergeIterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated.
Use the equivalent `MergeIterator.Attribute` method instead. (#2790)

## [0.29.0] - 2022-04-11

### Added
Expand Down
82 changes: 39 additions & 43 deletions attribute/encoder.go
Expand Up @@ -21,19 +21,17 @@ import (
)

type (
// Encoder is a mechanism for serializing a label set into a
// specific string representation that supports caching, to
// avoid repeated serialization. An example could be an
// exporter encoding the label set into a wire representation.
// Encoder is a mechanism for serializing an attribute set into a specific
// string representation that supports caching, to avoid repeated
// serialization. An example could be an exporter encoding the attribute
// set into a wire representation.
Encoder interface {
// Encode returns the serialized encoding of the label
// set using its Iterator. This result may be cached
// by a attribute.Set.
// Encode returns the serialized encoding of the attribute set using
// its Iterator. This result may be cached by a attribute.Set.
Encode(iterator Iterator) string

// ID returns a value that is unique for each class of
// label encoder. Label encoders allocate these using
// `NewEncoderID`.
// ID returns a value that is unique for each class of attribute
// encoder. Attribute encoders allocate these using `NewEncoderID`.
ID() EncoderID
}

Expand All @@ -43,54 +41,53 @@ type (
value uint64
}

// defaultLabelEncoder uses a sync.Pool of buffers to reduce
// the number of allocations used in encoding labels. This
// implementation encodes a comma-separated list of key=value,
// with '/'-escaping of '=', ',', and '\'.
defaultLabelEncoder struct {
// pool is a pool of labelset builders. The buffers in this
// pool grow to a size that most label encodings will not
// allocate new memory.
// defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of
// allocations used in encoding attributes. This implementation encodes a
// comma-separated list of key=value, with '/'-escaping of '=', ',', and
// '\'.
defaultAttrEncoder struct {
// pool is a pool of attribute set builders. The buffers in this pool
// grow to a size that most attribute encodings will not allocate new
// memory.
pool sync.Pool // *bytes.Buffer
}
)

// escapeChar is used to ensure uniqueness of the label encoding where
// keys or values contain either '=' or ','. Since there is no parser
// needed for this encoding and its only requirement is to be unique,
// this choice is arbitrary. Users will see these in some exporters
// (e.g., stdout), so the backslash ('\') is used as a conventional choice.
// escapeChar is used to ensure uniqueness of the attribute encoding where
// keys or values contain either '=' or ','. Since there is no parser needed
// for this encoding and its only requirement is to be unique, this choice is
// arbitrary. Users will see these in some exporters (e.g., stdout), so the
// backslash ('\') is used as a conventional choice.
const escapeChar = '\\'

var (
_ Encoder = &defaultLabelEncoder{}
_ Encoder = &defaultAttrEncoder{}

// encoderIDCounter is for generating IDs for other label
// encoders.
// encoderIDCounter is for generating IDs for other attribute encoders.
encoderIDCounter uint64

defaultEncoderOnce sync.Once
defaultEncoderID = NewEncoderID()
defaultEncoderInstance *defaultLabelEncoder
defaultEncoderInstance *defaultAttrEncoder
)

// NewEncoderID returns a unique label encoder ID. It should be
// called once per each type of label encoder. Preferably in init() or
// in var definition.
// NewEncoderID returns a unique attribute encoder ID. It should be called
// once per each type of attribute encoder. Preferably in init() or in var
// definition.
func NewEncoderID() EncoderID {
return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
}

// DefaultEncoder returns a label encoder that encodes labels
// in such a way that each escaped label's key is followed by an equal
// sign and then by an escaped label's value. All key-value pairs are
// separated by a comma.
// DefaultEncoder returns an attribute encoder that encodes attributes in such
// a way that each escaped attribute's key is followed by an equal sign and
// then by an escaped attribute's value. All key-value pairs are separated by
// a comma.
//
// Escaping is done by prepending a backslash before either a
// backslash, equal sign or a comma.
// Escaping is done by prepending a backslash before either a backslash, equal
// sign or a comma.
func DefaultEncoder() Encoder {
defaultEncoderOnce.Do(func() {
defaultEncoderInstance = &defaultLabelEncoder{
defaultEncoderInstance = &defaultAttrEncoder{
pool: sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
Expand All @@ -101,15 +98,14 @@ func DefaultEncoder() Encoder {
return defaultEncoderInstance
}

// Encode is a part of an implementation of the LabelEncoder
// interface.
func (d *defaultLabelEncoder) Encode(iter Iterator) string {
// Encode is a part of an implementation of the AttributeEncoder interface.
func (d *defaultAttrEncoder) Encode(iter Iterator) string {
buf := d.pool.Get().(*bytes.Buffer)
defer d.pool.Put(buf)
buf.Reset()

for iter.Next() {
i, keyValue := iter.IndexedLabel()
i, keyValue := iter.IndexedAttribute()
if i > 0 {
_, _ = buf.WriteRune(',')
}
Expand All @@ -126,8 +122,8 @@ func (d *defaultLabelEncoder) Encode(iter Iterator) string {
return buf.String()
}

// ID is a part of an implementation of the LabelEncoder interface.
func (*defaultLabelEncoder) ID() EncoderID {
// ID is a part of an implementation of the AttributeEncoder interface.
func (*defaultAttrEncoder) ID() EncoderID {
return defaultEncoderID
}

Expand Down
78 changes: 48 additions & 30 deletions attribute/iterator.go
Expand Up @@ -14,61 +14,72 @@

package attribute // import "go.opentelemetry.io/otel/attribute"

// Iterator allows iterating over the set of labels in order,
// sorted by key.
// Iterator allows iterating over the set of attributes in order, sorted by
// key.
type Iterator struct {
storage *Set
idx int
}

// MergeIterator supports iterating over two sets of labels while
// eliminating duplicate values from the combined set. The first
// iterator value takes precedence.
// MergeIterator supports iterating over two sets of attributes while
// eliminating duplicate values from the combined set. The first iterator
// value takes precedence.
type MergeIterator struct {
one oneIterator
two oneIterator
current KeyValue
}

type oneIterator struct {
iter Iterator
done bool
label KeyValue
iter Iterator
done bool
attr KeyValue
}

// Next moves the iterator to the next position. Returns false if there
// are no more labels.
// Next moves the iterator to the next position. Returns false if there are no
// more attributes.
func (i *Iterator) Next() bool {
i.idx++
return i.idx < i.Len()
}

// Label returns current KeyValue. Must be called only after Next returns
// true.
//
// Deprecated: Use Attribute instead.
func (i *Iterator) Label() KeyValue {
kv, _ := i.storage.Get(i.idx)
return kv
return i.Attribute()
}

// Attribute is a synonym for Label().
// Attribute returns the current KeyValue of the Iterator. It must be called
// only after Next returns true.
func (i *Iterator) Attribute() KeyValue {
return i.Label()
kv, _ := i.storage.Get(i.idx)
return kv
}

// IndexedLabel returns current index and attribute. Must be called only
// after Next returns true.
//
// Deprecated: Use IndexedAttribute instead.
func (i *Iterator) IndexedLabel() (int, KeyValue) {
return i.idx, i.Label()
return i.idx, i.Attribute()
}

// Len returns a number of labels in the iterator's `*Set`.
// IndexedAttribute returns current index and attribute. Must be called only
// after Next returns true.
func (i *Iterator) IndexedAttribute() (int, KeyValue) {
return i.idx, i.Attribute()
}

// Len returns a number of attributes in the iterated set.
func (i *Iterator) Len() int {
return i.storage.Len()
}

// ToSlice is a convenience function that creates a slice of labels
// from the passed iterator. The iterator is set up to start from the
// beginning before creating the slice.
// ToSlice is a convenience function that creates a slice of attributes from
// the passed iterator. The iterator is set up to start from the beginning
// before creating the slice.
func (i *Iterator) ToSlice() []KeyValue {
l := i.Len()
if l == 0 {
Expand All @@ -77,12 +88,12 @@ func (i *Iterator) ToSlice() []KeyValue {
i.idx = -1
slice := make([]KeyValue, 0, l)
for i.Next() {
slice = append(slice, i.Label())
slice = append(slice, i.Attribute())
}
return slice
}

// NewMergeIterator returns a MergeIterator for merging two label sets
// NewMergeIterator returns a MergeIterator for merging two attribute sets.
// Duplicates are resolved by taking the value from the first set.
func NewMergeIterator(s1, s2 *Set) MergeIterator {
mi := MergeIterator{
Expand All @@ -102,42 +113,49 @@ func makeOne(iter Iterator) oneIterator {

func (oi *oneIterator) advance() {
if oi.done = !oi.iter.Next(); !oi.done {
oi.label = oi.iter.Label()
oi.attr = oi.iter.Attribute()
}
}

// Next returns true if there is another label available.
// Next returns true if there is another attribute available.
func (m *MergeIterator) Next() bool {
if m.one.done && m.two.done {
return false
}
if m.one.done {
m.current = m.two.label
m.current = m.two.attr
m.two.advance()
return true
}
if m.two.done {
m.current = m.one.label
m.current = m.one.attr
m.one.advance()
return true
}
if m.one.label.Key == m.two.label.Key {
m.current = m.one.label // first iterator label value wins
if m.one.attr.Key == m.two.attr.Key {
m.current = m.one.attr // first iterator attribute value wins
m.one.advance()
m.two.advance()
return true
}
if m.one.label.Key < m.two.label.Key {
m.current = m.one.label
if m.one.attr.Key < m.two.attr.Key {
m.current = m.one.attr
m.one.advance()
return true
}
m.current = m.two.label
m.current = m.two.attr
m.two.advance()
return true
}

// Label returns the current value after Next() returns true.
//
// Deprecated: Use Attribute instead.
func (m *MergeIterator) Label() KeyValue {
return m.current
}

// Attribute returns the current value after Next() returns true.
func (m *MergeIterator) Attribute() KeyValue {
return m.current
}
22 changes: 11 additions & 11 deletions attribute/iterator_test.go
Expand Up @@ -31,15 +31,15 @@ func TestIterator(t *testing.T) {
require.Equal(t, 2, iter.Len())

require.True(t, iter.Next())
require.Equal(t, one, iter.Label())
idx, attr := iter.IndexedLabel()
require.Equal(t, one, iter.Attribute())
idx, attr := iter.IndexedAttribute()
require.Equal(t, 0, idx)
require.Equal(t, one, attr)
require.Equal(t, 2, iter.Len())

require.True(t, iter.Next())
require.Equal(t, two, iter.Label())
idx, attr = iter.IndexedLabel()
require.Equal(t, two, iter.Attribute())
idx, attr = iter.IndexedAttribute()
require.Equal(t, 1, idx)
require.Equal(t, two, attr)
require.Equal(t, 2, iter.Len())
Expand All @@ -64,7 +64,7 @@ func TestMergedIterator(t *testing.T) {
expect []string
}

makeLabels := func(keys []string, num int) (result []attribute.KeyValue) {
makeAttributes := func(keys []string, num int) (result []attribute.KeyValue) {
for _, k := range keys {
result = append(result, attribute.Int(k, num))
}
Expand Down Expand Up @@ -128,19 +128,19 @@ func TestMergedIterator(t *testing.T) {
},
} {
t.Run(input.name, func(t *testing.T) {
labels1 := makeLabels(input.keys1, 1)
labels2 := makeLabels(input.keys2, 2)
attr1 := makeAttributes(input.keys1, 1)
attr2 := makeAttributes(input.keys2, 2)

set1 := attribute.NewSet(labels1...)
set2 := attribute.NewSet(labels2...)
set1 := attribute.NewSet(attr1...)
set2 := attribute.NewSet(attr2...)

merge := attribute.NewMergeIterator(&set1, &set2)

var result []string

for merge.Next() {
label := merge.Label()
result = append(result, fmt.Sprint(label.Key, "/", label.Value.Emit()))
attr := merge.Attribute()
result = append(result, fmt.Sprint(attr.Key, "/", attr.Value.Emit()))
}

require.Equal(t, input.expect, result)
Expand Down

0 comments on commit a8ea3db

Please sign in to comment.