Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce pprofile.AddAttribute helper #12390

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
25 changes: 25 additions & 0 deletions .chloggen/pprofile-add-attribute.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: pdata/pprofile

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Introduce AddAttribute helper method to modify the content of attributable records

# One or more tracking issues or pull requests related to the change
issues: [12206]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]
36 changes: 36 additions & 0 deletions pdata/pprofile/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
package pprofile // import "go.opentelemetry.io/collector/pdata/pprofile"

import (
"errors"
"fmt"
"math"

"go.opentelemetry.io/collector/pdata/pcommon"
)

Expand All @@ -26,3 +30,35 @@

return m
}

// AddAttribute updates an AttributeTable and a record's AttributeIndices to
// add a new attribute.
// The record can by any struct that implements an `AttributeIndices` method.
func AddAttribute(table AttributeTableSlice, record attributable, key string, value any) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the attributable type is not an exported type (of some package)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributable is there to allow the behavior on any of the generated types that have attribute indices.
Location, Mapping, Sample.
Folks will only ever need to manipulate the generated struct. They don't need the interface.

for i := 0; i < table.Len(); i++ {
a := table.At(i)

if a.Key() == key && value == a.Value().AsRaw() {
if i >= math.MaxInt32 {
return fmt.Errorf("Attribute %s=%#v has too high an indice to be added to AttributeIndices", key, value)
}

Check warning on line 44 in pdata/pprofile/attributes.go

View check run for this annotation

Codecov / codecov/patch

pdata/pprofile/attributes.go#L43-L44

Added lines #L43 - L44 were not covered by tests

record.AttributeIndices().Append(int32(i)) //nolint:gosec // overflow checked
return nil
}
}

if table.Len() >= math.MaxInt32 {
return errors.New("AttributeTable can't take more attributes")
}

Check warning on line 53 in pdata/pprofile/attributes.go

View check run for this annotation

Codecov / codecov/patch

pdata/pprofile/attributes.go#L52-L53

Added lines #L52 - L53 were not covered by tests
table.EnsureCapacity(table.Len() + 1)
entry := table.AppendEmpty()
entry.SetKey(key)
err := entry.Value().FromRaw(value)
if err != nil {
return err
}

Check warning on line 60 in pdata/pprofile/attributes.go

View check run for this annotation

Codecov / codecov/patch

pdata/pprofile/attributes.go#L59-L60

Added lines #L59 - L60 were not covered by tests
record.AttributeIndices().Append(int32(table.Len()) - 1) //nolint:gosec // overflow checked

return nil
}
84 changes: 84 additions & 0 deletions pdata/pprofile/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/pdata/pcommon"
)
Expand Down Expand Up @@ -43,6 +44,29 @@ func TestFromAttributeIndices(t *testing.T) {
assert.Equal(t, attrs.AsRaw(), m)
}

func TestAddAttribute(t *testing.T) {
table := NewAttributeTableSlice()
att := table.AppendEmpty()
att.SetKey("hello")
att.Value().SetStr("world")

// Add a brand new attribute
loc := NewLocation()
err := AddAttribute(table, loc, "bonjour", "monde")
require.NoError(t, err)

assert.Equal(t, 2, table.Len())
assert.Equal(t, []int32{1}, loc.AttributeIndices().AsRaw())

// Add an already existing attribute
mapp := NewMapping()
err = AddAttribute(table, mapp, "hello", "world")
require.NoError(t, err)

assert.Equal(t, 2, table.Len())
assert.Equal(t, []int32{0}, mapp.AttributeIndices().AsRaw())
}

func BenchmarkFromAttributeIndices(b *testing.B) {
table := NewAttributeTableSlice()

Expand All @@ -62,3 +86,63 @@ func BenchmarkFromAttributeIndices(b *testing.B) {
_ = FromAttributeIndices(table, obj)
}
}

func BenchmarkAddAttribute(b *testing.B) {
for _, bb := range []struct {
name string
key string
value any

runBefore func(*testing.B, AttributeTableSlice)
}{
{
name: "with a new string attribute",
key: "attribute",
value: "test",
},
{
name: "with an existing attribute",
key: "attribute",
value: "test",

runBefore: func(_ *testing.B, table AttributeTableSlice) {
entry := table.AppendEmpty()
entry.SetKey("attribute")
entry.Value().SetStr("test")
},
},
{
name: "with a hundred attributes to loop through",
key: "attribute",
value: "test",

runBefore: func(_ *testing.B, table AttributeTableSlice) {
for i := range 100 {
entry := table.AppendEmpty()
entry.SetKey(fmt.Sprintf("attr_%d", i))
entry.Value().SetStr("test")
}

entry := table.AppendEmpty()
entry.SetKey("attribute")
entry.Value().SetStr("test")
},
},
} {
b.Run(bb.name, func(b *testing.B) {
table := NewAttributeTableSlice()
obj := NewLocation()

if bb.runBefore != nil {
bb.runBefore(b, table)
}

b.ResetTimer()
b.ReportAllocs()

for n := 0; n < b.N; n++ {
_ = AddAttribute(table, obj, bb.key, bb.value)
}
})
}
}
Loading