Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions attribute/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,47 @@ func NewSet(kvs ...KeyValue) Set {
return s
}

// NewDistinctWithFilter returns a Distinct identifier for the filtered attribute set,
// and the sorted and de-duplicated slice of attributes. It modifies the input slice
// in-place to sort and de-duplicate the attributes.
//
// The returned Distinct represents the equivalence class of the attribute set after
// the filter is applied. The returned slice contains all unique attributes, including
// those that did not pass the filter.
func NewDistinctWithFilter(kvs []KeyValue, filter Filter) (Distinct, []KeyValue) {
if len(kvs) == 0 {
return Distinct{hash: emptyHash}, kvs
}

// Stable sort so the following de-duplication can implement
// last-value-wins semantics.
slices.SortStableFunc(kvs, func(a, b KeyValue) int {
return cmp.Compare(a.Key, b.Key)
})

position := len(kvs) - 1
offset := position - 1

// De-duplicate with last-value-wins semantics.
for ; offset >= 0; offset-- {
if kvs[offset].Key == kvs[position].Key {
continue
}
position--
kvs[offset], kvs[position] = kvs[position], kvs[offset]
}
kvs = kvs[position:]

h := xxhash.New()
for _, kv := range kvs {
if filter == nil || filter(kv) {
h = hashKV(h, kv)
}
}

return Distinct{hash: h.Sum64()}, kvs
}

// NewSetWithSortable returns a new Set. See the documentation for
// NewSetWithSortableFiltered for more details.
//
Expand Down
36 changes: 36 additions & 0 deletions attribute/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,42 @@ func TestFiltering(t *testing.T) {
}
}

func TestNewDistinctWithFilter(t *testing.T) {
kvs := []attribute.KeyValue{
attribute.String("B", "1"),
attribute.String("A", "1"),
attribute.String("C", "1"),
attribute.String("B", "2"), // Duplicate key, should win over B=1
attribute.String("D", "1"),
}
// Filter to keep A, B, and D. Drop C.
filter := func(kv attribute.KeyValue) bool {
return kv.Key != "C"
}

// Create a copy since NewDistinctWithFilter modifies in-place.
input := make([]attribute.KeyValue, len(kvs))
copy(input, kvs)

distinct, compacted := attribute.NewDistinctWithFilter(input, filter)

// Verify the returned slice is correctly sorted and de-duplicated.
// Note: the filter applies to the hash computation, NOT the physical slice returned.
require.Len(t, compacted, 4)
assert.Equal(t, "A", string(compacted[0].Key))
assert.Equal(t, "B", string(compacted[1].Key))
assert.Equal(t, "2", compacted[1].Value.AsString()) // Last value wins
assert.Equal(t, "C", string(compacted[2].Key))
assert.Equal(t, "D", string(compacted[3].Key))

// Verify the computed Distinct matches the baseline from NewSetWithFiltered.
input2 := make([]attribute.KeyValue, len(kvs))
copy(input2, kvs)
expectedSet, _ := attribute.NewSetWithFiltered(input2, filter)

assert.Equal(t, expectedSet.Equivalent(), distinct)
}

func TestUniqueness(t *testing.T) {
short := []attribute.KeyValue{
attribute.String("A", "0"),
Expand Down
2 changes: 1 addition & 1 deletion internal/tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ require (
github.com/maratori/testpackage v1.1.2 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.21 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/mgechev/revive v1.15.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions internal/tools/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q=
Expand Down
27 changes: 27 additions & 0 deletions metric/x/bound.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package x // import "go.opentelemetry.io/otel/metric/x"

import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)

// Float64Binder is an interface that can be implemented by instruments that support
// binding attributes ahead of time.
type Float64Binder interface {
// Bind returns a metric.Float64Counter for the given attributes.
// The returned counter is bound to the attributes and should be optimized
// for performance by avoiding map lookups on every Add call.
Bind(attrs ...attribute.KeyValue) metric.Float64Counter
}

// Int64Binder is an interface that can be implemented by instruments that support
// binding attributes ahead of time.
type Int64Binder interface {
// Bind returns a metric.Int64Counter for the given attributes.
// The returned counter is bound to the attributes and should be optimized
// for performance by avoiding map lookups on every Add call.
Bind(attrs ...attribute.KeyValue) metric.Int64Counter
}
Loading
Loading