Skip to content

Commit 44979f5

Browse files
rockdabootevan-bradley
authored andcommitted
[pkg/ottl] Accessors for profile attributes (open-telemetry#39681)
Co-authored-by: Evan Bradley <[email protected]>
1 parent 534cadc commit 44979f5

File tree

6 files changed

+145
-6
lines changed

6 files changed

+145
-6
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add accessors for profile attributes
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [39681]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

pkg/ottl/contexts/internal/ctxprofile/context.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ const (
1313

1414
type Context interface {
1515
GetProfile() pprofile.Profile
16+
GetProfilesDictionary() pprofile.ProfilesDictionary
1617
}

pkg/ottl/contexts/internal/ctxprofile/profile.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
type ProfileContext interface {
2222
GetProfile() pprofile.Profile
23+
GetProfilesDictionary() pprofile.ProfilesDictionary
2324
}
2425

2526
func PathGetSetter[K ProfileContext](path ottl.Path[K]) (ottl.GetSetter[K], error) {
@@ -66,6 +67,11 @@ func PathGetSetter[K ProfileContext](path ottl.Path[K]) (ottl.GetSetter[K], erro
6667
return accessOriginalPayloadFormat[K](), nil
6768
case "original_payload":
6869
return accessOriginalPayload[K](), nil
70+
case "attributes":
71+
if path.Keys() == nil {
72+
return accessAttributes[K](), nil
73+
}
74+
return accessAttributesKey(path.Keys()), nil
6975
default:
7076
return nil, ctxerror.New(path.Name(), path.String(), Name, DocRef)
7177
}
@@ -310,3 +316,43 @@ func accessOriginalPayload[K ProfileContext]() ottl.StandardGetSetter[K] {
310316
},
311317
}
312318
}
319+
320+
func accessAttributes[K ProfileContext]() ottl.StandardGetSetter[K] {
321+
return ottl.StandardGetSetter[K]{
322+
Getter: func(_ context.Context, tCtx K) (any, error) {
323+
return pprofile.FromAttributeIndices(tCtx.GetProfilesDictionary().AttributeTable(), tCtx.GetProfile()), nil
324+
},
325+
Setter: func(_ context.Context, tCtx K, val any) error {
326+
m, err := ctxutil.GetMap(val)
327+
if err != nil {
328+
return err
329+
}
330+
tCtx.GetProfile().AttributeIndices().FromRaw([]int32{})
331+
for k, v := range m.All() {
332+
if err := pprofile.PutAttribute(tCtx.GetProfilesDictionary().AttributeTable(), tCtx.GetProfile(), k, v); err != nil {
333+
return err
334+
}
335+
}
336+
return nil
337+
},
338+
}
339+
}
340+
341+
func accessAttributesKey[K Context](key []ottl.Key[K]) ottl.StandardGetSetter[K] {
342+
return ottl.StandardGetSetter[K]{
343+
Getter: func(ctx context.Context, tCtx K) (any, error) {
344+
return ctxutil.GetMapValue[K](ctx, tCtx, pprofile.FromAttributeIndices(tCtx.GetProfilesDictionary().AttributeTable(), tCtx.GetProfile()), key)
345+
},
346+
Setter: func(ctx context.Context, tCtx K, val any) error {
347+
newKey, err := ctxutil.GetMapKeyName(ctx, tCtx, key[0])
348+
if err != nil {
349+
return err
350+
}
351+
v := pcommon.NewValueEmpty()
352+
if err = ctxutil.SetIndexableValue[K](ctx, tCtx, v, val, key[1:]); err != nil {
353+
return err
354+
}
355+
return pprofile.PutAttribute(tCtx.GetProfilesDictionary().AttributeTable(), tCtx.GetProfile(), *newKey, v)
356+
},
357+
}
358+
}

pkg/ottl/contexts/internal/ctxprofile/profile_test.go

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ import (
1111

1212
"github.com/stretchr/testify/assert"
1313
"github.com/stretchr/testify/require"
14+
"go.opentelemetry.io/collector/pdata/pcommon"
1415
"go.opentelemetry.io/collector/pdata/pprofile"
1516

17+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
1618
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/pathtest"
19+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest"
1720
)
1821

1922
func TestPathGetSetter(t *testing.T) {
2023
// create tests
2124
tests := []struct {
2225
path string
2326
val any
27+
keys []ottl.Key[*profileContext]
2428
setFails bool
2529
}{
2630
{
@@ -109,29 +113,78 @@ func TestPathGetSetter(t *testing.T) {
109113
path: "original_payload",
110114
val: []byte{1, 2, 3},
111115
},
116+
{
117+
path: "attributes",
118+
val: func() pcommon.Map {
119+
m := pcommon.NewMap()
120+
m.PutStr("akey", "val")
121+
return m
122+
}(),
123+
},
124+
{
125+
path: "attributes",
126+
keys: []ottl.Key[*profileContext]{
127+
&pathtest.Key[*profileContext]{
128+
S: ottltest.Strp("akey"),
129+
},
130+
},
131+
val: "val",
132+
},
133+
{
134+
path: "attributes",
135+
keys: []ottl.Key[*profileContext]{
136+
&pathtest.Key[*profileContext]{
137+
S: ottltest.Strp("akey"),
138+
},
139+
&pathtest.Key[*profileContext]{
140+
S: ottltest.Strp("bkey"),
141+
},
142+
},
143+
val: "val",
144+
},
145+
{
146+
path: "attributes",
147+
keys: []ottl.Key[*profileContext]{
148+
&pathtest.Key[*profileContext]{
149+
G: &ottl.StandardGetSetter[*profileContext]{
150+
Getter: func(context.Context, *profileContext) (any, error) {
151+
return "", nil
152+
},
153+
Setter: func(context.Context, *profileContext, any) error {
154+
return nil
155+
},
156+
},
157+
},
158+
},
159+
val: "val",
160+
},
112161
}
113162

114163
for _, tt := range tests {
115164
t.Run(tt.path, func(t *testing.T) {
116165
pathParts := strings.Split(tt.path, " ")
117166
path := &pathtest.Path[*profileContext]{N: pathParts[0]}
167+
if tt.keys != nil {
168+
path.KeySlice = tt.keys
169+
}
118170
if len(pathParts) > 1 {
119171
path.NextPath = &pathtest.Path[*profileContext]{N: pathParts[1]}
120172
}
121173

122174
profile := pprofile.NewProfile()
175+
dictionary := pprofile.NewProfilesDictionary()
123176

124-
accessor, err := PathGetSetter[*profileContext](path)
177+
accessor, err := PathGetSetter(path)
125178
require.NoError(t, err)
126179

127-
err = accessor.Set(context.Background(), newProfileContext(profile), tt.val)
180+
err = accessor.Set(context.Background(), newProfileContext(profile, dictionary), tt.val)
128181
if tt.setFails {
129182
require.Error(t, err)
130183
return
131184
}
132185
require.NoError(t, err)
133186

134-
got, err := accessor.Get(context.Background(), newProfileContext(profile))
187+
got, err := accessor.Get(context.Background(), newProfileContext(profile, dictionary))
135188
require.NoError(t, err)
136189

137190
assert.Equal(t, tt.val, got)
@@ -140,15 +193,20 @@ func TestPathGetSetter(t *testing.T) {
140193
}
141194

142195
type profileContext struct {
143-
profile pprofile.Profile
196+
profile pprofile.Profile
197+
dictionary pprofile.ProfilesDictionary
198+
}
199+
200+
func (p *profileContext) GetProfilesDictionary() pprofile.ProfilesDictionary {
201+
return p.dictionary
144202
}
145203

146204
func (p *profileContext) GetProfile() pprofile.Profile {
147205
return p.profile
148206
}
149207

150-
func newProfileContext(profile pprofile.Profile) *profileContext {
151-
return &profileContext{profile: profile}
208+
func newProfileContext(profile pprofile.Profile, dictionary pprofile.ProfilesDictionary) *profileContext {
209+
return &profileContext{profile: profile, dictionary: dictionary}
152210
}
153211

154212
func createValueTypeSlice() pprofile.ValueTypeSlice {

pkg/ottl/contexts/ottlprofile/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ The following paths are supported.
2222
| instrumentation_scope.version | version of the instrumentation scope of the profile being processed | string |
2323
| instrumentation_scope.attributes | instrumentation scope attributes of the data point being processed | pcommon.Map |
2424
| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the data point being processed. Supports multiple indexes to access nested fields. | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil |
25+
| profile.attributes | attributes of the profile being processed | pcommon.Map |
26+
| profile.attributes\[""\] | the value of the attribute of the profile being processed. Supports multiple indexes to access nested fields. | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil |
2527
| profile.sample_type | the sample types of the profile being processed | pprofile.ValueTypeSlice |
2628
| profile.sample | the samples of the profile being processed | pprofile.SampleSlice |
2729
| profile.location_indices | the location indices of the profile being processed | []int64 |

pkg/ottl/contexts/ottlprofile/profile.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ func (tCtx TransformContext) GetProfile() pprofile.Profile {
8888
return tCtx.profile
8989
}
9090

91+
// GetProfilesDictionary returns the profiles dictionary from the TransformContext.
92+
func (tCtx TransformContext) GetProfilesDictionary() pprofile.ProfilesDictionary {
93+
return tCtx.dictionary
94+
}
95+
9196
// GetInstrumentationScope returns the instrumentation scope from the TransformContext.
9297
func (tCtx TransformContext) GetInstrumentationScope() pcommon.InstrumentationScope {
9398
return tCtx.instrumentationScope

0 commit comments

Comments
 (0)