From 7e6b19af9457ae09721b24a4ecae17113941e774 Mon Sep 17 00:00:00 2001 From: Edmo Vamerlatti Costa <11836452+edmocosta@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:35:55 +0100 Subject: [PATCH] [pkg/ottl] Improve errors for invalid cache access and add option to set context's cache value (#37166) #### Description - Improved error message for higher context `cache` access. Now when users try to configure statements like `set(log.attributes["log_key"], resource.cache["log_key"])`, they will see an error message similar to: ```diff + access to cache must be be performed using the same statement's context, please replace "resource.cache[log_key]" with "log.cache[log_key]"` - invalid argument at position 1: segment "cache" from path "resource.cache[log_key]" is not a valid path nor a valid OTTL keyword for the Resource context - review https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlresource to see all valid paths ``` - Added the `WithCache` option to all OTTL transformation contexts. This change allows API consumer to set the initial cache value or share it among different contexts/executions. #### Link to tracking issue Split of https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/36888 #### Testing Unit tests --- .chloggen/ottl-cache-changes.yaml | 27 +++++++++++++++++++ pkg/ottl/contexts/internal/errors.go | 10 ++++++- pkg/ottl/contexts/internal/path.go | 4 +++ pkg/ottl/contexts/internal/resource.go | 4 ++- pkg/ottl/contexts/internal/resource_test.go | 20 +++++++++++++- pkg/ottl/contexts/internal/scope.go | 4 ++- pkg/ottl/contexts/internal/scope_test.go | 20 +++++++++++++- pkg/ottl/contexts/internal/span.go | 4 ++- pkg/ottl/contexts/internal/span_test.go | 20 +++++++++++++- pkg/ottl/contexts/ottldatapoint/datapoint.go | 23 +++++++++++++--- .../contexts/ottldatapoint/datapoint_test.go | 18 +++++++++++++ pkg/ottl/contexts/ottllog/log.go | 23 +++++++++++++--- pkg/ottl/contexts/ottllog/log_test.go | 16 +++++++++++ pkg/ottl/contexts/ottlmetric/metrics.go | 23 +++++++++++++--- pkg/ottl/contexts/ottlmetric/metrics_test.go | 17 ++++++++++++ pkg/ottl/contexts/ottlresource/resource.go | 21 ++++++++++++--- .../contexts/ottlresource/resource_test.go | 13 +++++++++ pkg/ottl/contexts/ottlscope/scope.go | 23 +++++++++++++--- pkg/ottl/contexts/ottlscope/scope_test.go | 15 +++++++++++ pkg/ottl/contexts/ottlspan/span.go | 25 +++++++++++++---- .../contexts/ottlspanevent/span_events.go | 25 +++++++++++++---- 21 files changed, 319 insertions(+), 36 deletions(-) create mode 100644 .chloggen/ottl-cache-changes.yaml diff --git a/.chloggen/ottl-cache-changes.yaml b/.chloggen/ottl-cache-changes.yaml new file mode 100644 index 000000000000..f8d650a358c8 --- /dev/null +++ b/.chloggen/ottl-cache-changes.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: pkg/ottl + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Enhanced error messages for invalid cache access and introduced options to configure their values within the OTTL contexts. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [29017] + +# (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: [user, api] diff --git a/pkg/ottl/contexts/internal/errors.go b/pkg/ottl/contexts/internal/errors.go index 7ccc931e2ba4..eef0e8aca99b 100644 --- a/pkg/ottl/contexts/internal/errors.go +++ b/pkg/ottl/contexts/internal/errors.go @@ -3,7 +3,10 @@ package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" -import "fmt" +import ( + "fmt" + "strings" +) const ( DefaultErrorMessage = "segment %q from path %q is not a valid path nor a valid OTTL keyword for the %v context - review %v to see all valid paths" @@ -20,3 +23,8 @@ const ( func FormatDefaultErrorMessage(pathSegment, fullPath, context, ref string) error { return fmt.Errorf(DefaultErrorMessage, pathSegment, fullPath, context, ref) } + +func FormatCacheErrorMessage(lowerContext, pathContext, fullPath string) error { + pathSuggestion := strings.Replace(fullPath, pathContext+".", lowerContext+".", 1) + return fmt.Errorf(`access to cache must be performed using the same statement's context, please replace "%s" with "%s"`, fullPath, pathSuggestion) +} diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index ebbb12a6eaae..753419354629 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -16,6 +16,7 @@ type TestPath[K any] struct { N string KeySlice []ottl.Key[K] NextPath *TestPath[K] + FullPath string } func (p *TestPath[K]) Name() string { @@ -38,6 +39,9 @@ func (p *TestPath[K]) Keys() []ottl.Key[K] { } func (p *TestPath[K]) String() string { + if p.FullPath != "" { + return p.FullPath + } return p.N } diff --git a/pkg/ottl/contexts/internal/resource.go b/pkg/ottl/contexts/internal/resource.go index a3ae9d149f3f..7b454d50ae8c 100644 --- a/pkg/ottl/contexts/internal/resource.go +++ b/pkg/ottl/contexts/internal/resource.go @@ -20,7 +20,7 @@ type ResourceContext interface { GetResourceSchemaURLItem() SchemaURLItem } -func ResourcePathGetSetter[K ResourceContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func ResourcePathGetSetter[K ResourceContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(ResourceContextName, ResourceContextName, "Resource", ResourceContextRef) } @@ -34,6 +34,8 @@ func ResourcePathGetSetter[K ResourceContext](path ottl.Path[K]) (ottl.GetSetter return accessResourceDroppedAttributesCount[K](), nil case "schema_url": return accessResourceSchemaURLItem[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Resource", ResourceContextRef) } diff --git a/pkg/ottl/contexts/internal/resource_test.go b/pkg/ottl/contexts/internal/resource_test.go index f1b251a3e22e..88cee0e0e059 100644 --- a/pkg/ottl/contexts/internal/resource_test.go +++ b/pkg/ottl/contexts/internal/resource_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" @@ -311,7 +312,7 @@ func TestResourcePathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := ResourcePathGetSetter[*resourceContext](tt.path) + accessor, err := ResourcePathGetSetter[*resourceContext](tt.path.Context(), tt.path) assert.NoError(t, err) resource := createResource() @@ -331,6 +332,23 @@ func TestResourcePathGetSetter(t *testing.T) { } } +func TestResourcePathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*resourceContext]{ + N: "cache", + C: "resource", + KeySlice: []ottl.Key[*resourceContext]{ + &TestKey[*resourceContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "resource.cache[key]", + } + + _, err := ResourcePathGetSetter[*resourceContext]("log", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "resource.cache[key]" with "log.cache[key]"`) +} + func createResource() pcommon.Resource { resource := pcommon.NewResource() resource.Attributes().PutStr("str", "val") diff --git a/pkg/ottl/contexts/internal/scope.go b/pkg/ottl/contexts/internal/scope.go index 4fabf9ee7d89..35a40a3869c3 100644 --- a/pkg/ottl/contexts/internal/scope.go +++ b/pkg/ottl/contexts/internal/scope.go @@ -21,7 +21,7 @@ type InstrumentationScopeContext interface { GetScopeSchemaURLItem() SchemaURLItem } -func ScopePathGetSetter[K InstrumentationScopeContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func ScopePathGetSetter[K InstrumentationScopeContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(InstrumentationScopeContextName, InstrumentationScopeContextName, "Instrumentation Scope", InstrumentationScopeRef) } @@ -40,6 +40,8 @@ func ScopePathGetSetter[K InstrumentationScopeContext](path ottl.Path[K]) (ottl. return accessInstrumentationScopeDroppedAttributesCount[K](), nil case "schema_url": return accessInstrumentationScopeSchemaURLItem[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Instrumentation Scope", InstrumentationScopeRef) } diff --git a/pkg/ottl/contexts/internal/scope_test.go b/pkg/ottl/contexts/internal/scope_test.go index 788924115bd5..66a4c3fa397a 100644 --- a/pkg/ottl/contexts/internal/scope_test.go +++ b/pkg/ottl/contexts/internal/scope_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" @@ -349,7 +350,7 @@ func TestScopePathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := ScopePathGetSetter[*instrumentationScopeContext](tt.path) + accessor, err := ScopePathGetSetter[*instrumentationScopeContext](tt.path.Context(), tt.path) assert.NoError(t, err) is := createInstrumentationScope() @@ -369,6 +370,23 @@ func TestScopePathGetSetter(t *testing.T) { } } +func TestScopePathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*instrumentationScopeContext]{ + N: "cache", + C: "instrumentation_scope", + KeySlice: []ottl.Key[*instrumentationScopeContext]{ + &TestKey[*instrumentationScopeContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "instrumentation_scope.cache[key]", + } + + _, err := ScopePathGetSetter[*instrumentationScopeContext]("metric", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "instrumentation_scope.cache[key]" with "metric.cache[key]"`) +} + func createInstrumentationScope() pcommon.InstrumentationScope { is := pcommon.NewInstrumentationScope() is.SetName("library") diff --git a/pkg/ottl/contexts/internal/span.go b/pkg/ottl/contexts/internal/span.go index 4669adb7b8fe..fc7ca27177ef 100644 --- a/pkg/ottl/contexts/internal/span.go +++ b/pkg/ottl/contexts/internal/span.go @@ -38,7 +38,7 @@ var SpanSymbolTable = map[ottl.EnumSymbol]ottl.Enum{ "STATUS_CODE_ERROR": ottl.Enum(ptrace.StatusCodeError), } -func SpanPathGetSetter[K SpanContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func SpanPathGetSetter[K SpanContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(SpanContextName, SpanContextName, SpanContextNameDescription, SpanRef) } @@ -128,6 +128,8 @@ func SpanPathGetSetter[K SpanContext](path ottl.Path[K]) (ottl.GetSetter[K], err } } return accessStatus[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), SpanContextNameDescription, SpanRef) } diff --git a/pkg/ottl/contexts/internal/span_test.go b/pkg/ottl/contexts/internal/span_test.go index 899d12493424..1345a3cfde5a 100644 --- a/pkg/ottl/contexts/internal/span_test.go +++ b/pkg/ottl/contexts/internal/span_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" @@ -603,7 +604,7 @@ func TestSpanPathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := SpanPathGetSetter[*spanContext](tt.path) + accessor, err := SpanPathGetSetter[*spanContext](tt.path.Context(), tt.path) assert.NoError(t, err) span := createSpan() @@ -623,6 +624,23 @@ func TestSpanPathGetSetter(t *testing.T) { } } +func TestSpanPathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*spanContext]{ + N: "cache", + C: "span", + KeySlice: []ottl.Key[*spanContext]{ + &TestKey[*spanContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "span.cache[key]", + } + + _, err := SpanPathGetSetter[*spanContext]("spanevent", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "span.cache[key]" with "spanevent.cache[key]"`) +} + func createSpan() ptrace.Span { span := ptrace.NewSpan() span.SetTraceID(traceID) diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint.go b/pkg/ottl/contexts/ottldatapoint/datapoint.go index d231c4210813..4f491c82caf7 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint.go @@ -64,8 +64,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics, options ...TransformContextOption) TransformContext { + tc := TransformContext{ dataPoint: dataPoint, metric: metric, metrics: metrics, @@ -75,6 +77,19 @@ func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.M scopeMetrics: scopeMetrics, resourceMetrics: resourceMetrics, } + 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) GetDataPoint() any { @@ -289,9 +304,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) case internal.MetricContextName: return internal.MetricPathGetSetter(path) default: diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go index 604f008865ec..1877e904d08e 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go @@ -94,6 +94,24 @@ func Test_newPathGetSetter_Cache(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pmetric.NewNumberDataPoint(), + pmetric.NewMetric(), + pmetric.NewMetricSlice(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewScopeMetrics(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func Test_newPathGetSetter_NumberDataPoint(t *testing.T) { refNumberDataPoint := createNumberDataPointTelemetry(pmetric.NumberDataPointValueTypeInt) diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 4983e60ddc6c..99970f4a093b 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -71,8 +71,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeLogs plog.ScopeLogs, resourceLogs plog.ResourceLogs) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeLogs plog.ScopeLogs, resourceLogs plog.ResourceLogs, options ...TransformContextOption) TransformContext { + tc := TransformContext{ logRecord: logRecord, instrumentationScope: instrumentationScope, resource: resource, @@ -80,6 +82,19 @@ func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon. scopeLogs: scopeLogs, resourceLogs: resourceLogs, } + 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) GetLogRecord() plog.LogRecord { @@ -290,9 +305,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottllog/log_test.go b/pkg/ottl/contexts/ottllog/log_test.go index 0e440052f1bd..1c2c141db5c4 100644 --- a/pkg/ottl/contexts/ottllog/log_test.go +++ b/pkg/ottl/contexts/ottllog/log_test.go @@ -694,6 +694,22 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + plog.NewLogRecord(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + plog.NewScopeLogs(), + plog.NewResourceLogs(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry(bodyType string) (plog.LogRecord, pcommon.InstrumentationScope, pcommon.Resource) { log := plog.NewLogRecord() log.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100))) diff --git a/pkg/ottl/contexts/ottlmetric/metrics.go b/pkg/ottl/contexts/ottlmetric/metrics.go index b8c26bac2e9d..04b7b5218bf1 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics.go +++ b/pkg/ottl/contexts/ottlmetric/metrics.go @@ -38,8 +38,10 @@ type TransformContext struct { type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics, options ...TransformContextOption) TransformContext { + tc := TransformContext{ metric: metric, metrics: metrics, instrumentationScope: instrumentationScope, @@ -48,6 +50,19 @@ func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, ins scopeMetrics: scopeMetrics, resourceMetrics: resourceMetrics, } + 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) GetMetric() pmetric.Metric { @@ -185,9 +200,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlmetric/metrics_test.go b/pkg/ottl/contexts/ottlmetric/metrics_test.go index c434ab5735e9..5eb8fb9f44ca 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics_test.go +++ b/pkg/ottl/contexts/ottlmetric/metrics_test.go @@ -239,6 +239,23 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pmetric.NewMetric(), + pmetric.NewMetricSlice(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewScopeMetrics(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createMetricTelemetry() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("name") diff --git a/pkg/ottl/contexts/ottlresource/resource.go b/pkg/ottl/contexts/ottlresource/resource.go index fef87f37b54e..6c44ec4d161f 100644 --- a/pkg/ottl/contexts/ottlresource/resource.go +++ b/pkg/ottl/contexts/ottlresource/resource.go @@ -41,12 +41,27 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(resource pcommon.Resource, schemaURLItem internal.SchemaURLItem) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(resource pcommon.Resource, schemaURLItem internal.SchemaURLItem, options ...TransformContextOption) TransformContext { + tc := TransformContext{ resource: resource, cache: pcommon.NewMap(), schemaURLItem: schemaURLItem, } + 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) GetResource() pcommon.Resource { @@ -144,7 +159,7 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) } } diff --git a/pkg/ottl/contexts/ottlresource/resource_test.go b/pkg/ottl/contexts/ottlresource/resource_test.go index f37bcdf90c80..e3d9f96c8299 100644 --- a/pkg/ottl/contexts/ottlresource/resource_test.go +++ b/pkg/ottl/contexts/ottlresource/resource_test.go @@ -398,6 +398,19 @@ func Test_newPathGetSetter(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pcommon.NewResource(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry() pcommon.Resource { resource := pcommon.NewResource() diff --git a/pkg/ottl/contexts/ottlscope/scope.go b/pkg/ottl/contexts/ottlscope/scope.go index 13f2c7a83e83..4ccc8f472d53 100644 --- a/pkg/ottl/contexts/ottlscope/scope.go +++ b/pkg/ottl/contexts/ottlscope/scope.go @@ -44,13 +44,28 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, schemaURLItem internal.SchemaURLItem) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, schemaURLItem internal.SchemaURLItem, options ...TransformContextOption) TransformContext { + tc := TransformContext{ instrumentationScope: instrumentationScope, resource: resource, cache: pcommon.NewMap(), schemaURLItem: schemaURLItem, } + 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) GetInstrumentationScope() pcommon.InstrumentationScope { @@ -164,14 +179,14 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.ScopePathGetSetter[TransformContext](path) + return internal.ScopePathGetSetter[TransformContext](ContextName, path) } } func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlscope/scope_test.go b/pkg/ottl/contexts/ottlscope/scope_test.go index 4f4d8cf7c3b5..9b1103df5fc6 100644 --- a/pkg/ottl/contexts/ottlscope/scope_test.go +++ b/pkg/ottl/contexts/ottlscope/scope_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" @@ -468,6 +469,20 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry() (pcommon.InstrumentationScope, pcommon.Resource) { is := pcommon.NewInstrumentationScope() is.SetName("library") diff --git a/pkg/ottl/contexts/ottlspan/span.go b/pkg/ottl/contexts/ottlspan/span.go index be11495e8dde..8f7b4c05f268 100644 --- a/pkg/ottl/contexts/ottlspan/span.go +++ b/pkg/ottl/contexts/ottlspan/span.go @@ -48,8 +48,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans, options ...TransformContextOption) TransformContext { + tc := TransformContext{ span: span, instrumentationScope: instrumentationScope, resource: resource, @@ -57,6 +59,19 @@ func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.Instrume scopeSpans: scopeSpans, resourceSpans: resourceSpans, } + 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) GetSpan() ptrace.Span { @@ -181,16 +196,16 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.SpanPathGetSetter[TransformContext](path) + return internal.SpanPathGetSetter[TransformContext](ContextName, path) } } func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter[TransformContext](path) + return internal.ScopePathGetSetter[TransformContext](ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlspanevent/span_events.go b/pkg/ottl/contexts/ottlspanevent/span_events.go index 6d654b058004..839147639d6c 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events.go @@ -52,8 +52,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans, options ...TransformContextOption) TransformContext { + tc := TransformContext{ spanEvent: spanEvent, span: span, instrumentationScope: instrumentationScope, @@ -62,6 +64,19 @@ func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumen scopeSpans: scopeSpans, resouceSpans: resourceSpans, } + 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) GetSpanEvent() ptrace.SpanEvent { @@ -214,11 +229,11 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) case internal.SpanContextName: - return internal.SpanPathGetSetter(path) + return internal.SpanPathGetSetter(ContextName, path) default: var fullPath string if path != nil {