diff --git a/.chloggen/ottl-profile.yaml b/.chloggen/ottl-profile.yaml new file mode 100644 index 0000000000000..0b303785e5386 --- /dev/null +++ b/.chloggen/ottl-profile.yaml @@ -0,0 +1,27 @@ +# 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. filelogreceiver) +component: ottlprofile + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add OTTL support for profiles. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36104] + +# (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: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# 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: [] diff --git a/pkg/ottl/contexts/internal/errors.go b/pkg/ottl/contexts/internal/errors.go index eef0e8aca99b3..60fd1b1ccae47 100644 --- a/pkg/ottl/contexts/internal/errors.go +++ b/pkg/ottl/contexts/internal/errors.go @@ -18,6 +18,7 @@ const ( MetricRef = "https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlmetric" DataPointRef = "https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottldatapoint" LogRef = "https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottllog" + ProfileRef = "https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlprofile" ) func FormatDefaultErrorMessage(pathSegment, fullPath, context, ref string) error { diff --git a/pkg/ottl/contexts/internal/profile.go b/pkg/ottl/contexts/internal/profile.go new file mode 100644 index 0000000000000..56295b6dd73f6 --- /dev/null +++ b/pkg/ottl/contexts/internal/profile.go @@ -0,0 +1,384 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" + +import ( + "context" + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" +) + +const ( + ProfileContextName = "profile" +) + +type ProfileContext interface { + GetProfile() pprofile.Profile +} + +func ProfilePathGetSetter[K ProfileContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { + if path == nil { + return nil, FormatDefaultErrorMessage(ProfileContextName, ProfileContextName, "Profile", ProfileRef) + } + switch path.Name() { + case "sample_type": + return accessSampleType[K](), nil + case "sample": + return accessSample[K](), nil + case "mapping_table": + return accessMappingTable[K](), nil + case "location_table": + return accessLocationTable[K](), nil + case "location_indices": + return accessLocationIndices[K](), nil + case "function_table": + return accessFunctionTable[K](), nil + case "attribute_table": + return accessAttributeTable[K](), nil + case "attribute_units": + return accessAttributeUnits[K](), nil + case "link_table": + return accessLinkTable[K](), nil + case "string_table": + return accessStringTable[K](), nil + case "time_unix_nano": + return accessTimeUnixNano[K](), nil + case "time": + return accessTime[K](), nil + case "duration": + return accessDuration[K](), nil + case "period_type": + return accessPeriodType[K](), nil + case "period": + return accessPeriod[K](), nil + case "comment_string_indices": + return accessCommentStringIndices[K](), nil + case "default_sample_type_string_index": + return accessDefaultSampleTypeStringIndex[K](), nil + case "profile_id": + return accessProfileID[K](), nil + case "attribute_indices": + return accessAttributeIndices[K](), nil + case "dropped_attributes_count": + return accessDroppedAttributesCount[K](), nil + case "original_payload_format": + return accessOriginalPayloadFormat[K](), nil + case "original_payload": + return accessOriginalPayload[K](), nil + default: + return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Profile", ProfileRef) + } +} + +func accessSampleType[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().SampleType(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.ValueTypeSlice); ok { + v.CopyTo(tCtx.GetProfile().SampleType()) + } + return nil + }, + } +} + +func accessSample[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().Sample(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.SampleSlice); ok { + v.CopyTo(tCtx.GetProfile().Sample()) + } + return nil + }, + } +} + +func accessMappingTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().MappingTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.MappingSlice); ok { + v.CopyTo(tCtx.GetProfile().MappingTable()) + } + return nil + }, + } +} + +func accessLocationTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().LocationTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.LocationSlice); ok { + v.CopyTo(tCtx.GetProfile().LocationTable()) + } + return nil + }, + } +} + +func accessLocationIndices[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().LocationIndices(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.Int32Slice); ok { + v.CopyTo(tCtx.GetProfile().LocationIndices()) + } + return nil + }, + } +} + +func accessFunctionTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().FunctionTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.FunctionSlice); ok { + v.CopyTo(tCtx.GetProfile().FunctionTable()) + } + return nil + }, + } +} + +func accessAttributeTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().AttributeTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.AttributeTableSlice); ok { + v.CopyTo(tCtx.GetProfile().AttributeTable()) + } + return nil + }, + } +} + +func accessAttributeUnits[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().AttributeUnits(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.AttributeUnitSlice); ok { + v.CopyTo(tCtx.GetProfile().AttributeUnits()) + } + return nil + }, + } +} + +func accessLinkTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().LinkTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.LinkSlice); ok { + v.CopyTo(tCtx.GetProfile().LinkTable()) + } + return nil + }, + } +} + +func accessStringTable[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().StringTable(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.StringSlice); ok { + v.CopyTo(tCtx.GetProfile().StringTable()) + } + return nil + }, + } +} + +func accessTimeUnixNano[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().Time().AsTime().UnixNano(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(int64); ok { + tCtx.GetProfile().SetTime(pcommon.NewTimestampFromTime(time.Unix(0, i))) + } + return nil + }, + } +} + +func accessTime[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().Time().AsTime(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(time.Time); ok { + tCtx.GetProfile().SetTime(pcommon.NewTimestampFromTime(i)) + } + return nil + }, + } +} + +func accessDuration[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().Duration(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(pcommon.Timestamp); ok { + tCtx.GetProfile().SetDuration(i) + } + return nil + }, + } +} + +func accessPeriodType[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().PeriodType(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pprofile.ValueType); ok { + v.CopyTo(tCtx.GetProfile().PeriodType()) + } + return nil + }, + } +} + +func accessPeriod[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().Period(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(int64); ok { + tCtx.GetProfile().SetPeriod(i) + } + return nil + }, + } +} + +func accessCommentStringIndices[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().CommentStrindices(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.Int32Slice); ok { + v.CopyTo(tCtx.GetProfile().CommentStrindices()) + } + return nil + }, + } +} + +func accessDefaultSampleTypeStringIndex[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().DefaultSampleTypeStrindex(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(int32); ok { + tCtx.GetProfile().SetDefaultSampleTypeStrindex(i) + } + return nil + }, + } +} + +func accessProfileID[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().ProfileID(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(pprofile.ProfileID); ok { + tCtx.GetProfile().SetProfileID(i) + } + return nil + }, + } +} + +func accessAttributeIndices[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().AttributeIndices(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.Int32Slice); ok { + v.CopyTo(tCtx.GetProfile().AttributeIndices()) + } + return nil + }, + } +} + +func accessDroppedAttributesCount[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().DroppedAttributesCount(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if i, ok := val.(uint32); ok { + tCtx.GetProfile().SetDroppedAttributesCount(i) + } + return nil + }, + } +} + +func accessOriginalPayloadFormat[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().OriginalPayloadFormat(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(string); ok { + tCtx.GetProfile().SetOriginalPayloadFormat(v) + } + return nil + }, + } +} + +func accessOriginalPayload[K ProfileContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetProfile().OriginalPayload(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.ByteSlice); ok { + v.CopyTo(tCtx.GetProfile().OriginalPayload()) + } + return nil + }, + } +} diff --git a/pkg/ottl/contexts/internal/profile_test.go b/pkg/ottl/contexts/internal/profile_test.go new file mode 100644 index 0000000000000..0b40d3b0d5d78 --- /dev/null +++ b/pkg/ottl/contexts/internal/profile_test.go @@ -0,0 +1,274 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" +import ( + "context" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" +) + +// create Test_ProfilePathGetSetter +func Test_ProfilePathGetSetter(t *testing.T) { + // create tests + tests := []struct { + path string + val any + }{ + { + path: "sample_type", + val: createValueTypeSlice(), + }, + { + path: "sample", + val: createSampleSlice(), + }, + { + path: "mapping_table", + val: createMappingSlice(), + }, + { + path: "location_table", + val: createLocationSlice(), + }, + { + path: "location_indices", + val: createInt32Slice(5), + }, + { + path: "function_table", + val: createFunctionSlice(), + }, + { + path: "attribute_table", + val: createAttributeTableSlice(), + }, + { + path: "attribute_units", + val: createAttributeUnitSlice(), + }, + { + path: "link_table", + val: createLinkSlice(), + }, + { + path: "string_table", + val: createStringSlice(), + }, + { + path: "time_unix_nano", + val: int64(123), + }, + { + path: "time", + val: time.Now().UTC(), + }, + { + path: "duration", + val: pcommon.NewTimestampFromTime(time.Now().UTC()), + }, + { + path: "period_type", + val: createValueType(), + }, + { + path: "period", + val: int64(234), + }, + { + path: "comment_string_indices", + val: createInt32Slice(345), + }, + { + path: "default_sample_type_string_index", + val: int32(456), + }, + { + path: "profile_id", + val: createProfileID(), + }, + { + path: "attribute_indices", + val: createInt32Slice(567), + }, + { + path: "dropped_attributes_count", + val: uint32(678), + }, + { + path: "original_payload_format", + val: "orgPayloadFormat", + }, + { + path: "original_payload", + val: createByteSlice(), + }, + } + + for _, tt := range tests { + t.Run(strings.ReplaceAll(tt.path, "_", " "), func(t *testing.T) { + path := &TestPath[*profileContext]{N: tt.path} + + profile := pprofile.NewProfile() + + accessor, err := ProfilePathGetSetter[*profileContext](path) + assert.NoError(t, err) + + err = accessor.Set(context.Background(), newProfileContext(profile), tt.val) + assert.NoError(t, err) + + got, err := accessor.Get(context.Background(), newProfileContext(profile)) + assert.NoError(t, err) + + assert.Equal(t, tt.val, got) + }) + } +} + +type profileContext struct { + profile pprofile.Profile +} + +func (p *profileContext) GetProfile() pprofile.Profile { + return p.profile +} + +func newProfileContext(profile pprofile.Profile) *profileContext { + return &profileContext{profile: profile} +} + +func createValueTypeSlice() pprofile.ValueTypeSlice { + sl := pprofile.NewValueTypeSlice() + vt := sl.AppendEmpty() + vt.CopyTo(createValueType()) + return sl +} + +func createValueType() pprofile.ValueType { + vt := pprofile.NewValueType() + vt.SetAggregationTemporality(1) + vt.SetTypeStrindex(2) + vt.SetUnitStrindex(3) + return vt +} + +func createSampleSlice() pprofile.SampleSlice { + sl := pprofile.NewSampleSlice() + sample := sl.AppendEmpty() + sample.CopyTo(createSample()) + return sl +} + +func createMappingSlice() pprofile.MappingSlice { + sl := pprofile.NewMappingSlice() + mapping := sl.AppendEmpty() + mapping.CopyTo(createMapping()) + return sl +} + +func createMapping() pprofile.Mapping { + mapping := pprofile.NewMapping() + mapping.SetFilenameStrindex(2) + mapping.SetFileOffset(1) + mapping.SetHasFilenames(true) + mapping.SetHasFunctions(true) + mapping.SetHasInlineFrames(true) + mapping.SetHasLineNumbers(true) + mapping.SetMemoryLimit(3) + mapping.SetMemoryStart(4) + return mapping +} + +func createLocationSlice() pprofile.LocationSlice { + sl := pprofile.NewLocationSlice() + location := sl.AppendEmpty() + location.CopyTo(createLocation()) + return sl +} + +func createLocation() pprofile.Location { + location := pprofile.NewLocation() + location.SetAddress(1) + location.SetIsFolded(true) + location.SetMappingIndex(2) + return location +} + +func createFunctionSlice() pprofile.FunctionSlice { + sl := pprofile.NewFunctionSlice() + function := sl.AppendEmpty() + function.CopyTo(createFunction()) + return sl +} + +func createFunction() pprofile.Function { + function := pprofile.NewFunction() + function.SetFilenameStrindex(1) + function.SetNameStrindex(2) + function.SetStartLine(3) + function.SetSystemNameStrindex(4) + return function +} + +func createAttributeTableSlice() pprofile.AttributeTableSlice { + sl := pprofile.NewAttributeTableSlice() + attribute := sl.AppendEmpty() + attribute.CopyTo(createAttributeTable()) + return sl +} + +func createAttributeTable() pprofile.Attribute { + attribute := pprofile.NewAttribute() + attribute.SetKey("key") + attribute.Value().SetStr("value") + return attribute +} + +func createAttributeUnitSlice() pprofile.AttributeUnitSlice { + sl := pprofile.NewAttributeUnitSlice() + attributeUnit := sl.AppendEmpty() + attributeUnit.CopyTo(createAttributeUnit()) + return sl +} + +func createAttributeUnit() pprofile.AttributeUnit { + attributeUnit := pprofile.NewAttributeUnit() + attributeUnit.SetUnitStrindex(1) + attributeUnit.SetAttributeKeyStrindex(2) + return attributeUnit +} + +func createLinkSlice() pprofile.LinkSlice { + sl := pprofile.NewLinkSlice() + link := sl.AppendEmpty() + link.CopyTo(createLink()) + return sl +} + +func createLink() pprofile.Link { + link := pprofile.NewLink() + link.SetSpanID(pcommon.SpanID([]byte{1, 2, 3, 4, 5, 6, 7, 8})) + link.SetTraceID(pcommon.TraceID([]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1})) + return link +} + +func createStringSlice() pcommon.StringSlice { + sl := pcommon.NewStringSlice() + sl.Append("string") + return sl +} + +func createProfileID() pprofile.ProfileID { + return [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} +} + +func createByteSlice() pcommon.ByteSlice { + sl := pcommon.NewByteSlice() + sl.Append(1, 2, 3) + return sl +} diff --git a/pkg/ottl/contexts/internal/sample.go b/pkg/ottl/contexts/internal/sample.go new file mode 100644 index 0000000000000..e486ee49a7fc2 --- /dev/null +++ b/pkg/ottl/contexts/internal/sample.go @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" + +import ( + "context" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" +) + +const ( + SampleContextName = "sample" +) + +type SampleContext interface { + GetSample() pprofile.Sample +} + +func SamplePathGetSetter[K SampleContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { + if path == nil { + return nil, FormatDefaultErrorMessage(SampleContextName, SampleContextName, "Sample", ProfileRef) + } + switch path.Name() { + case "locations_length": + return accessLocationsLength[K](), nil + case "locations_start_index": + return accessLocationsStartIndex[K](), nil + case "attribute_indices": + return accessSampleAttributeIndices[K](), nil + case "timestamps_unix_nano": + return accessTimestampsUnixNano[K](), nil + case "value": + return accessSampleValue[K](), nil + default: + return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Profile", ProfileRef) + } +} + +func accessLocationsLength[K SampleContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetSample().LocationsLength(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(int32); ok { + tCtx.GetSample().SetLocationsLength(v) + } + return nil + }, + } +} + +func accessLocationsStartIndex[K SampleContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetSample().LocationsStartIndex(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(int32); ok { + tCtx.GetSample().SetLocationsStartIndex(v) + } + return nil + }, + } +} + +func accessSampleAttributeIndices[K SampleContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetSample().AttributeIndices(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.Int32Slice); ok { + v.CopyTo(tCtx.GetSample().AttributeIndices()) + } + return nil + }, + } +} + +func accessTimestampsUnixNano[K SampleContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetSample().TimestampsUnixNano(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.UInt64Slice); ok { + v.CopyTo(tCtx.GetSample().TimestampsUnixNano()) + } + return nil + }, + } +} + +func accessSampleValue[K SampleContext]() ottl.StandardGetSetter[K] { + return ottl.StandardGetSetter[K]{ + Getter: func(_ context.Context, tCtx K) (any, error) { + return tCtx.GetSample().Value(), nil + }, + Setter: func(_ context.Context, tCtx K, val any) error { + if v, ok := val.(pcommon.Int64Slice); ok { + v.CopyTo(tCtx.GetSample().Value()) + } + return nil + }, + } +} diff --git a/pkg/ottl/contexts/internal/sample_test.go b/pkg/ottl/contexts/internal/sample_test.go new file mode 100644 index 0000000000000..d1f790767b203 --- /dev/null +++ b/pkg/ottl/contexts/internal/sample_test.go @@ -0,0 +1,102 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" +) + +// create Test_ProfilePathGetSetter +func Test_SamplePathGetSetter(t *testing.T) { + // create tests + tests := []struct { + path string + val any + }{ + { + path: "locations_length", + val: int32(1), + }, + { + path: "locations_start_index", + val: int32(2), + }, + { + path: "attribute_indices", + val: createInt32Slice(3), + }, + { + path: "timestamps_unix_nano", + val: createUInt64Slice(4), + }, + { + path: "value", + val: createInt64Slice(5), + }, + } + + for _, tt := range tests { + t.Run(strings.ReplaceAll(tt.path, "_", " "), func(t *testing.T) { + path := &TestPath[*sampleContext]{N: tt.path} + + sample := createSample() + + accessor, err := SamplePathGetSetter[*sampleContext](path) + assert.NoError(t, err) + + err = accessor.Set(context.Background(), newSampleContext(sample), tt.val) + assert.NoError(t, err) + + got, err := accessor.Get(context.Background(), newSampleContext(sample)) + assert.NoError(t, err) + + assert.Equal(t, tt.val, got) + }) + } +} + +func createSample() pprofile.Sample { + sample := pprofile.NewSample() + sample.AttributeIndices().Append(1) + sample.SetLocationsLength(2) + sample.SetLocationsStartIndex(3) + sample.TimestampsUnixNano().Append(4) + sample.Value().Append(5) + return sample +} + +type sampleContext struct { + sample pprofile.Sample +} + +func (p *sampleContext) GetSample() pprofile.Sample { + return p.sample +} + +func newSampleContext(sample pprofile.Sample) *sampleContext { + return &sampleContext{sample: sample} +} + +func createInt32Slice(n int32) pcommon.Int32Slice { + sl := pcommon.NewInt32Slice() + sl.Append(n) + return sl +} + +func createUInt64Slice(n uint64) pcommon.UInt64Slice { + sl := pcommon.NewUInt64Slice() + sl.Append(n) + return sl +} + +func createInt64Slice(n int64) pcommon.Int64Slice { + sl := pcommon.NewInt64Slice() + sl.Append(n) + return sl +} diff --git a/pkg/ottl/contexts/ottlprofile/README.md b/pkg/ottl/contexts/ottlprofile/README.md new file mode 100644 index 0000000000000..b60b25431aca6 --- /dev/null +++ b/pkg/ottl/contexts/ottlprofile/README.md @@ -0,0 +1,53 @@ +# Profile Context + +The Profile Context is a Context implementation for [pdata Profiles](https://github.com/open-telemetry/opentelemetry-collector/tree/main/pdata/pprofile), the collector's internal representation for OTLP profile data. This Context should be used when interacted with OTLP profiles. + +## Paths +In general, the Profile Context supports accessing pdata using the field names from the [profiles proto](https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/profiles/v1development/profiles.proto). All integers are returned and set via `int64`. All doubles are returned and set via `float64`. + +The following paths are supported. + +| path | field accessed | type | +|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| +| cache | the value of the current transform context's temporary cache. cache can be used as a temporary placeholder for data during complex transformations | pcommon.Map | +| cache\[""\] | the value of an item in cache. Supports multiple indexes to access nested fields. | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the profile being processed | pcommon.Resource | +| resource.attributes | resource attributes of the profile being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the profile being processed. Supports multiple indexes to access nested fields. | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| instrumentation_scope | instrumentation scope of the profile being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the profile being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the profile being processed | string | +| instrumentation_scope.attributes | instrumentation scope attributes of the data point being processed | pcommon.Map | +| 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 | +| sample_type | the sample types of the profile being processed | pprofile.ValueTypeSlice | +| sample | the samples of the profile being processed | pprofile.SampleSlice | +| mapping_table | the mapping table of the profile being processed | pprofile.MappingSlice | +| location_table | the location table of the profile being processed | pprofile.LocationSlice | +| location_indices | the location indices of the profile being processed | pcommon.Int32Slice | +| function_table | the function table of the profile being processed | pprofile.FunctionSlice | +| attribute_table | the attribute table of the profile being processed | pprofile.AttributeTableSlice | +| attribute_units | the attribute units of the profile being processed | pprofile.AttributeUnitSlice | +| link_table | the link table of the profile being processed | pprofile.LinkSlice | +| string_table | the string table of the profile being processed | pcommon.StringSlice | +| time_unix_nano | the time in unix nano of the profile being processed | int64 | +| time | the time of the profile being processed | time.Time | +| duration | the duration in nanoseconds of the profile being processed | pcommon.Timestamp | +| period_type | the period type of the profile being processed | pprofile.ValueType | +| period | the period of the profile being processed | int64 | +| comment_string_indices | the comment string indices of the profile being processed | pcommon.Int32Slice | +| default_sample_type_string_index | the default sample type string index of the profile being processed | int32 | +| profile_id | the profile id of the profile being processed | pprofile.ProfileID | +| attribute_indices | the attribute indices of the profile being processed | pcommon.Int32Slice | +| dropped_attributes_count | the dropped attributes count of the profile being processed | uint32 | +| original_payload_format | the original payload format of the profile being processed | string | +| original_payload | the original payload of the profile being processed | pcommon.ByteSlice | + +## Enums + +The Profile Context supports the enum names from the [profiles proto](https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/profiles/v1development/profiles.proto). + +| Enum Symbol | Value | +|-------------------------------------|-------| +| AGGREGATION_TEMPORALITY_UNSPECIFIED | 0 | +| AGGREGATION_TEMPORALITY_DELTA | 1 | +| AGGREGATION_TEMPORALITY_CUMULATIVE | 2 | diff --git a/pkg/ottl/contexts/ottlprofile/package_test.go b/pkg/ottl/contexts/ottlprofile/package_test.go new file mode 100644 index 0000000000000..b0f4327b83c55 --- /dev/null +++ b/pkg/ottl/contexts/ottlprofile/package_test.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ottlprofile + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/pkg/ottl/contexts/ottlprofile/profile.go b/pkg/ottl/contexts/ottlprofile/profile.go new file mode 100644 index 0000000000000..72c81aabfbf1c --- /dev/null +++ b/pkg/ottl/contexts/ottlprofile/profile.go @@ -0,0 +1,236 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ottlprofile // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlprofile" + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" +) + +const ( + // Experimental: *NOTE* this constant is subject to change or removal in the future. + ContextName = "profile" + contextNameDescription = "Profile" +) + +var ( + _ internal.ResourceContext = (*TransformContext)(nil) + _ internal.InstrumentationScopeContext = (*TransformContext)(nil) + _ internal.ProfileContext = TransformContext{} + + // _ zapcore.ObjectMarshaler = (*TransformContext)(nil) +) + +type TransformContext struct { + profile pprofile.Profile + instrumentationScope pcommon.InstrumentationScope + resource pcommon.Resource + cache pcommon.Map + scopeProfiles pprofile.ScopeProfiles + resourceProfiles pprofile.ResourceProfiles +} + +type Option func(*ottl.Parser[TransformContext]) + +type TransformContextOption func(*TransformContext) + +func NewTransformContext(profile pprofile.Profile, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeProfiles pprofile.ScopeProfiles, resourceProfiles pprofile.ResourceProfiles, options ...TransformContextOption) TransformContext { + tc := TransformContext{ + profile: profile, + instrumentationScope: instrumentationScope, + resource: resource, + cache: pcommon.NewMap(), + scopeProfiles: scopeProfiles, + resourceProfiles: resourceProfiles, + } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } +} + +func (tCtx TransformContext) GetProfile() pprofile.Profile { + return tCtx.profile +} + +func (tCtx TransformContext) GetInstrumentationScope() pcommon.InstrumentationScope { + return tCtx.instrumentationScope +} + +func (tCtx TransformContext) GetResource() pcommon.Resource { + return tCtx.resource +} + +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + +func (tCtx TransformContext) GetScopeSchemaURLItem() internal.SchemaURLItem { + return tCtx.scopeProfiles +} + +func (tCtx TransformContext) GetResourceSchemaURLItem() internal.SchemaURLItem { + return tCtx.resourceProfiles +} + +func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) { + pep := pathExpressionParser{telemetrySettings} + p, err := ottl.NewParser[TransformContext]( + functions, + pep.parsePath, + telemetrySettings, + ottl.WithEnumParser[TransformContext](parseEnum), + ) + if err != nil { + return ottl.Parser[TransformContext]{}, err + } + for _, opt := range options { + opt(&p) + } + return p, nil +} + +// EnablePathContextNames enables the support to path's context names on statements. +// When this option is configured, all statement's paths must have a valid context prefix, +// otherwise an error is reported. +// +// Experimental: *NOTE* this option is subject to change or removal in the future. +func EnablePathContextNames() Option { + return func(p *ottl.Parser[TransformContext]) { + ottl.WithPathContextNames[TransformContext]([]string{ + ContextName, + internal.InstrumentationScopeContextName, + internal.ResourceContextName, + })(p) + } +} + +type StatementSequenceOption func(*ottl.StatementSequence[TransformContext]) + +func WithStatementSequenceErrorMode(errorMode ottl.ErrorMode) StatementSequenceOption { + return func(s *ottl.StatementSequence[TransformContext]) { + ottl.WithStatementSequenceErrorMode[TransformContext](errorMode)(s) + } +} + +func NewStatementSequence(statements []*ottl.Statement[TransformContext], telemetrySettings component.TelemetrySettings, options ...StatementSequenceOption) ottl.StatementSequence[TransformContext] { + s := ottl.NewStatementSequence(statements, telemetrySettings) + for _, op := range options { + op(&s) + } + return s +} + +type ConditionSequenceOption func(*ottl.ConditionSequence[TransformContext]) + +func WithConditionSequenceErrorMode(errorMode ottl.ErrorMode) ConditionSequenceOption { + return func(c *ottl.ConditionSequence[TransformContext]) { + ottl.WithConditionSequenceErrorMode[TransformContext](errorMode)(c) + } +} + +func NewConditionSequence(conditions []*ottl.Condition[TransformContext], telemetrySettings component.TelemetrySettings, options ...ConditionSequenceOption) ottl.ConditionSequence[TransformContext] { + c := ottl.NewConditionSequence(conditions, telemetrySettings) + for _, op := range options { + op(&c) + } + return c +} + +// symbolTable and parseEnum are here for completeness, currently unused. +var symbolTable = map[ottl.EnumSymbol]ottl.Enum{} + +func parseEnum(val *ottl.EnumSymbol) (*ottl.Enum, error) { + if val != nil { + if enum, ok := symbolTable[*val]; ok { + return &enum, nil + } + return nil, fmt.Errorf("enum symbol, %s, not found", *val) + } + return nil, fmt.Errorf("enum symbol not provided") +} + +type pathExpressionParser struct { + telemetrySettings component.TelemetrySettings +} + +func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { + if path == nil { + return nil, fmt.Errorf("path cannot be nil") + } + // Higher contexts parsing + if path.Context() != "" && path.Context() != ContextName { + return pep.parseHigherContextPath(path.Context(), path) + } + // Backward compatibility with paths without context + if path.Context() == "" && (path.Name() == internal.ResourceContextName || path.Name() == internal.InstrumentationScopeContextName) { + return pep.parseHigherContextPath(path.Name(), path.Next()) + } + + switch path.Name() { + case "cache": + if path.Keys() == nil { + return accessCache(), nil + } + return accessCacheKey(path.Keys()), nil + default: + return internal.ProfilePathGetSetter[TransformContext](path) + } +} + +func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { + switch context { + case internal.ResourceContextName: + return internal.ResourcePathGetSetter(ContextName, path) + case internal.InstrumentationScopeContextName: + return internal.ScopePathGetSetter(ContextName, path) + default: + var fullPath string + if path != nil { + fullPath = path.String() + } + return nil, internal.FormatDefaultErrorMessage(context, fullPath, contextNameDescription, internal.ProfileRef) + } +} + +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(_ context.Context, tCtx TransformContext) (any, error) { + return tCtx.getCache(), nil + }, + Setter: func(_ context.Context, tCtx TransformContext, val any) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(key []ottl.Key[TransformContext]) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (any, error) { + return internal.GetMapValue[TransformContext](ctx, tCtx, tCtx.getCache(), key) + }, + Setter: func(ctx context.Context, tCtx TransformContext, val any) error { + return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.getCache(), key, val) + }, + } +} diff --git a/pkg/ottl/contexts/ottlprofile/profile_test.go b/pkg/ottl/contexts/ottlprofile/profile_test.go new file mode 100644 index 0000000000000..b38f5ab58fba0 --- /dev/null +++ b/pkg/ottl/contexts/ottlprofile/profile_test.go @@ -0,0 +1,246 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ottlprofile + +import ( + "context" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" +) + +var profileID = pprofile.ProfileID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + +func Test_newPathGetSetter(t *testing.T) { + newAttrs := pcommon.NewMap() + newAttrs.PutStr("hello", "world") + + newCache := pcommon.NewMap() + newCache.PutStr("temp", "value") + + newPMap := pcommon.NewMap() + pMap2 := newPMap.PutEmptyMap("k2") + pMap2.PutStr("k1", "string") + + newBodyMap := pcommon.NewMap() + newBodyMap.PutStr("new", "value") + + newBodySlice := pcommon.NewSlice() + newBodySlice.AppendEmpty().SetStr("data") + + newMap := make(map[string]any) + newMap2 := make(map[string]any) + newMap2["k1"] = "string" + newMap["k2"] = newMap2 + + tests := []struct { + name string + path ottl.Path[TransformContext] + orig any + newVal any + modified func(profile pprofile.Profile, cache pcommon.Map) + }{ + { + name: "time", + path: &internal.TestPath[TransformContext]{ + N: "time", + }, + orig: time.Date(1970, 1, 1, 0, 0, 0, 100000000, time.UTC), + newVal: time.Date(1970, 1, 1, 0, 0, 0, 200000000, time.UTC), + modified: func(profile pprofile.Profile, _ pcommon.Map) { + profile.SetTime(pcommon.NewTimestampFromTime(time.UnixMilli(200))) + }, + }, + { + name: "time_unix_nano", + path: &internal.TestPath[TransformContext]{ + N: "time_unix_nano", + }, + orig: int64(100_000_000), + newVal: int64(200_000_000), + modified: func(profile pprofile.Profile, _ pcommon.Map) { + profile.SetTime(pcommon.NewTimestampFromTime(time.UnixMilli(200))) + }, + }, + { + name: "cache", + path: &internal.TestPath[TransformContext]{ + N: "cache", + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(_ pprofile.Profile, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: &internal.TestPath[TransformContext]{ + N: "cache", + KeySlice: []ottl.Key[TransformContext]{ + &internal.TestKey[TransformContext]{ + S: ottltest.Strp("temp"), + }, + }, + }, + orig: nil, + newVal: "new value", + modified: func(_ pprofile.Profile, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, + } + // Copy all tests cases and sets the path.Context value to the generated ones. + // It ensures all exiting field access also work when the path context is set. + for _, tt := range slices.Clone(tests) { + testWithContext := tt + testWithContext.name = "with_path_context:" + tt.name + pathWithContext := *tt.path.(*internal.TestPath[TransformContext]) + pathWithContext.C = ContextName + testWithContext.path = &pathWithContext + tests = append(tests, testWithContext) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pep := pathExpressionParser{} + accessor, err := pep.parsePath(tt.path) + assert.NoError(t, err) + + profile := createProfileTelemetry() + + tCtx := NewTransformContext(profile, pcommon.NewInstrumentationScope(), pcommon.NewResource(), pprofile.NewScopeProfiles(), pprofile.NewResourceProfiles()) + got, err := accessor.Get(context.Background(), tCtx) + assert.NoError(t, err) + assert.Equal(t, tt.orig, got) + + tCtx = NewTransformContext(profile, pcommon.NewInstrumentationScope(), pcommon.NewResource(), pprofile.NewScopeProfiles(), pprofile.NewResourceProfiles()) + err = accessor.Set(context.Background(), tCtx, tt.newVal) + assert.NoError(t, err) + + exProfile := createProfileTelemetry() + exCache := pcommon.NewMap() + tt.modified(exProfile, exCache) + + assert.Equal(t, exProfile, profile) + assert.Equal(t, exCache, tCtx.getCache()) + }) + } +} + +func Test_newPathGetSetter_higherContextPath(t *testing.T) { + resource := pcommon.NewResource() + resource.Attributes().PutStr("foo", "bar") + + instrumentationScope := pcommon.NewInstrumentationScope() + instrumentationScope.SetName("instrumentation_scope") + + ctx := NewTransformContext(pprofile.NewProfile(), instrumentationScope, resource, pprofile.NewScopeProfiles(), pprofile.NewResourceProfiles()) + + tests := []struct { + name string + path ottl.Path[TransformContext] + expected any + }{ + { + name: "resource", + path: &internal.TestPath[TransformContext]{C: "", N: "resource", NextPath: &internal.TestPath[TransformContext]{ + N: "attributes", + KeySlice: []ottl.Key[TransformContext]{ + &internal.TestKey[TransformContext]{ + S: ottltest.Strp("foo"), + }, + }, + }}, + expected: "bar", + }, + { + name: "resource with context", + path: &internal.TestPath[TransformContext]{C: "resource", N: "attributes", KeySlice: []ottl.Key[TransformContext]{ + &internal.TestKey[TransformContext]{ + S: ottltest.Strp("foo"), + }, + }}, + expected: "bar", + }, + { + name: "instrumentation_scope", + path: &internal.TestPath[TransformContext]{N: "instrumentation_scope", NextPath: &internal.TestPath[TransformContext]{N: "name"}}, + expected: instrumentationScope.Name(), + }, + { + name: "instrumentation_scope with context", + path: &internal.TestPath[TransformContext]{C: "instrumentation_scope", N: "name"}, + expected: instrumentationScope.Name(), + }, + } + + pep := pathExpressionParser{} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + accessor, err := pep.parsePath(tt.path) + require.NoError(t, err) + + got, err := accessor.Get(context.Background(), ctx) + assert.NoError(t, err) + assert.Equal(t, tt.expected, got) + }) + } +} + +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pprofile.NewProfile(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pprofile.NewScopeProfiles(), + pprofile.NewResourceProfiles(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + +func createProfileTelemetry() pprofile.Profile { + profile := pprofile.NewProfile() + profile.SetProfileID(profileID) + profile.SetTime(pcommon.NewTimestampFromTime(time.UnixMilli(100))) + // profile.SetDuration(pcommon.NewTimestampFromTime(time.UnixMilli(200))) + return profile +} + +func Test_ParseEnum_False(t *testing.T) { + tests := []struct { + name string + enumSymbol *ottl.EnumSymbol + }{ + { + name: "unknown enum symbol", + enumSymbol: (*ottl.EnumSymbol)(ottltest.Strp("not an enum")), + }, + { + name: "nil enum symbol", + enumSymbol: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := parseEnum(tt.enumSymbol) + assert.Error(t, err) + assert.Nil(t, actual) + }) + } +} diff --git a/pkg/ottl/go.mod b/pkg/ottl/go.mod index e16a21dc22b34..18fa76012777c 100644 --- a/pkg/ottl/go.mod +++ b/pkg/ottl/go.mod @@ -18,6 +18,7 @@ require ( go.opentelemetry.io/collector/component v0.119.0 go.opentelemetry.io/collector/component/componenttest v0.119.0 go.opentelemetry.io/collector/pdata v1.25.0 + go.opentelemetry.io/collector/pdata/pprofile v0.119.0 go.opentelemetry.io/collector/semconv v0.119.0 go.opentelemetry.io/otel/trace v1.34.0 go.uber.org/goleak v1.3.0 diff --git a/pkg/ottl/go.sum b/pkg/ottl/go.sum index 110ead2ad27db..4030b7e0d1469 100644 --- a/pkg/ottl/go.sum +++ b/pkg/ottl/go.sum @@ -81,6 +81,8 @@ go.opentelemetry.io/collector/config/configtelemetry v0.119.0 h1:gAgMUEVXZKgpASx go.opentelemetry.io/collector/config/configtelemetry v0.119.0/go.mod h1:SlBEwQg0qly75rXZ6W1Ig8jN25KBVBkFIIAUI1GiAAE= go.opentelemetry.io/collector/pdata v1.25.0 h1:AmgBklQfbfy0lT8qsoJtRuYMZ7ZV3VZvkvhjSDentrg= go.opentelemetry.io/collector/pdata v1.25.0/go.mod h1:Zs7D4RXOGS7E2faGc/jfWdbmhoiHBxA7QbpuJOioxq8= +go.opentelemetry.io/collector/pdata/pprofile v0.119.0 h1:sVtv/MhQ3NDLkgHOWDF9BdTtThNyXdOUiz5+poRkYLQ= +go.opentelemetry.io/collector/pdata/pprofile v0.119.0/go.mod h1:ur4388PjUpmwG5HoSMzrLCPkR0gNVLT4lekcJMRPt8A= go.opentelemetry.io/collector/semconv v0.119.0 h1:xo+V3a7hnK0I6fxAWCXT8BIT1PCBYd4emolhoKSDUlI= go.opentelemetry.io/collector/semconv v0.119.0/go.mod h1:N6XE8Q0JKgBN2fAhkUQtqK9LT7rEGR6+Wu/Rtbal1iI= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=