diff --git a/.chloggen/ottl-introduce-temp-storage.yaml b/.chloggen/ottl-introduce-temp-storage.yaml new file mode 100755 index 000000000000..52c6cec0c864 --- /dev/null +++ b/.chloggen/ottl-introduce-temp-storage.yaml @@ -0,0 +1,16 @@ +# 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: Add new `cache` path to all contexts which can be used as temporary cache during complex transformations + +# One or more tracking issues related to the change +issues: [16994] + +# (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: diff --git a/pkg/ottl/contexts/ottldatapoint/README.md b/pkg/ottl/contexts/ottldatapoint/README.md index 407b547626a5..1523a6decea8 100644 --- a/pkg/ottl/contexts/ottldatapoint/README.md +++ b/pkg/ottl/contexts/ottldatapoint/README.md @@ -7,33 +7,35 @@ In general, the DataPoint Context supports accessing pdata using the field names The following fields are the exception. -| path | field accessed | type | -|------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the data point being processed | pcommon.Resource | -| resource.attributes | resource attributes of the data point being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the data point being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| resource.dropped_attributes_count | number of dropped attributes of the resource of the data point being processed | int64 | -| instrumentation_scope | instrumentation scope of the data point being processed | pcommon.InstrumentationScope | -| instrumentation_scope.name | name of the instrumentation scope of the data point being processed | string | -| instrumentation_scope.version | version of the instrumentation scope of the data point being processed | string | -| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the data point being processed | int64 | -| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| attributes | attributes of the data point being processed | pcommon.Map | -| attributes\[""\] | the value of the attribute of the data point being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| metric | the metric to which the data point being processed belongs | pmetric.Metric | -| metric.name | the name of the metric to which the data point being processed belongs | string | -| metric.description | the description of the metric to which the data point being processed belongs | string | -| metric.unit | the unit of the metric to which the data point being processed belongs | string | -| metric.type | the type of the metric to which the data point being processed belongs. See enums below for integer mapping. | int64 | -| metric.aggregation_temporality | the aggregation temporality of the metric to which the data point being processed belongs | int64 | -| metric.is_monotonic | the monotonicity of the metric to which the data point being processed belongs | bool | -| positive | the positive buckets of the data point being processed | pmetric.ExponentialHistogramDataPoint | -| positive.offset | the offset of the positive buckets of the data point being processed | int64 | -| positive.bucket_counts | the bucket_counts of the positive buckets of the data point being processed | uint64 | -| negative | the negative buckets of the data point being processed | pmetric.ExponentialHistogramDataPoint | -| negative.offset | the offset of the negative buckets of the data point being processed | int64 | -| negative.bucket_counts | the bucket_counts of the negative buckets of the data point being processed | uint64 | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the data point being processed | pcommon.Resource | +| resource.attributes | resource attributes of the data point being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the data point being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource.dropped_attributes_count | number of dropped attributes of the resource of the data point being processed | int64 | +| instrumentation_scope | instrumentation scope of the data point being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the data point being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the data point being processed | string | +| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the data point being processed | int64 | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| attributes | attributes of the data point being processed | pcommon.Map | +| attributes\[""\] | the value of the attribute of the data point being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| metric | the metric to which the data point being processed belongs | pmetric.Metric | +| metric.name | the name of the metric to which the data point being processed belongs | string | +| metric.description | the description of the metric to which the data point being processed belongs | string | +| metric.unit | the unit of the metric to which the data point being processed belongs | string | +| metric.type | the type of the metric to which the data point being processed belongs. See enums below for integer mapping. | int64 | +| metric.aggregation_temporality | the aggregation temporality of the metric to which the data point being processed belongs | int64 | +| metric.is_monotonic | the monotonicity of the metric to which the data point being processed belongs | bool | +| positive | the positive buckets of the data point being processed | pmetric.ExponentialHistogramDataPoint | +| positive.offset | the offset of the positive buckets of the data point being processed | int64 | +| positive.bucket_counts | the bucket_counts of the positive buckets of the data point being processed | uint64 | +| negative | the negative buckets of the data point being processed | pmetric.ExponentialHistogramDataPoint | +| negative.offset | the offset of the negative buckets of the data point being processed | int64 | +| negative.bucket_counts | the bucket_counts of the negative buckets of the data point being processed | uint64 | ## Enums diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint.go b/pkg/ottl/contexts/ottldatapoint/datapoint.go index e70d96c3a86e..271a3ded18d8 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint.go @@ -36,6 +36,7 @@ type TransformContext struct { metrics pmetric.MetricSlice instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(dataPoint interface{}, metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { @@ -45,6 +46,7 @@ func NewTransformContext(dataPoint interface{}, metric pmetric.Metric, metrics p metrics: metrics, instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -68,6 +70,10 @@ func (tCtx TransformContext) GetMetrics() pmetric.MetricSlice { return tCtx.metrics } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -102,6 +108,12 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) case "instrumentation_scope": @@ -164,6 +176,32 @@ func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], erro return nil, fmt.Errorf("invalid path expression %v", path) } +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} + func accessAttributes() ottl.StandardGetSetter[TransformContext] { return ottl.StandardGetSetter[TransformContext]{ Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go index 7b64bb78c354..cbd1b3436b03 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go @@ -27,6 +27,71 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" ) +func Test_newPathGetSetter_Cache(t *testing.T) { + newCache := pcommon.NewMap() + newCache.PutStr("temp", "value") + + tests := []struct { + name string + path []ottl.Field + orig interface{} + newVal interface{} + modified func(cache pcommon.Map) + valueType pmetric.NumberDataPointValueType + }{ + + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + accessor, err := newPathGetSetter(tt.path) + assert.NoError(t, err) + + numberDataPoint := createNumberDataPointTelemetry(tt.valueType) + + ctx := NewTransformContext(numberDataPoint, pmetric.NewMetric(), pmetric.NewMetricSlice(), pcommon.NewInstrumentationScope(), pcommon.NewResource()) + + got, err := accessor.Get(context.Background(), ctx) + assert.Nil(t, err) + assert.Equal(t, tt.orig, got) + + err = accessor.Set(context.Background(), ctx, tt.newVal) + assert.Nil(t, err) + + exCache := pcommon.NewMap() + tt.modified(exCache) + + assert.Equal(t, exCache, ctx.getCache()) + }) + } +} + func Test_newPathGetSetter_NumberDataPoint(t *testing.T) { refNumberDataPoint := createNumberDataPointTelemetry(pmetric.NumberDataPointValueTypeInt) @@ -1571,6 +1636,7 @@ func createSummaryDataPointTelemetry() pmetric.SummaryDataPoint { return summaryDataPoint } + func createAttributeTelemetry(attributes pcommon.Map) { attributes.PutStr("str", "val") attributes.PutBool("bool", true) diff --git a/pkg/ottl/contexts/ottllog/README.md b/pkg/ottl/contexts/ottllog/README.md index ab8e5e2934ee..3554a745b910 100644 --- a/pkg/ottl/contexts/ottllog/README.md +++ b/pkg/ottl/contexts/ottllog/README.md @@ -9,24 +9,26 @@ All TraceIDs and SpanIDs are returned as pdata [SpanID](https://github.com/open- The following fields are the exception. -| path | field accessed | type | -|------------------------------------------------|--------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the log being processed | pcommon.Resource | -| resource.attributes | resource attributes of the log being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the log being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| resource.dropped_attributes_count | number of dropped attributes of the resource of the log being processed | int64 | -| instrumentation_scope | instrumentation scope of the log being processed | pcommon.InstrumentationScope | -| instrumentation_scope.name | name of the instrumentation scope of the log being processed | string | -| instrumentation_scope.version | version of the instrumentation scope of the log being processed | string | -| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the log being processed | int64 | -| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| attributes | attributes of the log being processed | pcommon.Map | -| attributes\[""\] | the value of the attribute of the log being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| trace_id | a byte slice representation of the trace id | pcommon.TraceID | -| trace_id.string | a string representation of the trace id | string | -| span_id | a byte slice representation of the span id | pcommon.SpanID | -| span_id.string | a string representation of the span id | string | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the log being processed | pcommon.Resource | +| resource.attributes | resource attributes of the log being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the log being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource.dropped_attributes_count | number of dropped attributes of the resource of the log being processed | int64 | +| instrumentation_scope | instrumentation scope of the log being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the log being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the log being processed | string | +| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the log being processed | int64 | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| attributes | attributes of the log being processed | pcommon.Map | +| attributes\[""\] | the value of the attribute of the log being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| trace_id | a byte slice representation of the trace id | pcommon.TraceID | +| trace_id.string | a string representation of the trace id | string | +| span_id | a byte slice representation of the span id | pcommon.SpanID | +| span_id.string | a string representation of the span id | string | ## Enums diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index fb02607ac566..d8b2e5aacc23 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -35,6 +35,7 @@ type TransformContext struct { logRecord plog.LogRecord instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { @@ -42,6 +43,7 @@ func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon. logRecord: logRecord, instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -57,6 +59,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -108,6 +114,12 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) case "instrumentation_scope": @@ -151,6 +163,32 @@ func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], erro return nil, fmt.Errorf("invalid path expression %v", path) } +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} + func accessTimeUnixNano() ottl.StandardGetSetter[TransformContext] { return ottl.StandardGetSetter[TransformContext]{ Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { diff --git a/pkg/ottl/contexts/ottllog/log_test.go b/pkg/ottl/contexts/ottllog/log_test.go index 326344102eb8..9a03e1dd6b2e 100644 --- a/pkg/ottl/contexts/ottllog/log_test.go +++ b/pkg/ottl/contexts/ottllog/log_test.go @@ -41,6 +41,9 @@ 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") @@ -55,7 +58,7 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) + modified func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) }{ { name: "time_unix_nano", @@ -66,7 +69,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(100_000_000), newVal: int64(200_000_000), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) }, }, @@ -79,7 +82,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(500_000_000), newVal: int64(200_000_000), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetObservedTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) }, }, @@ -92,7 +95,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(plog.SeverityNumberFatal), newVal: int64(3), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetSeverityNumber(plog.SeverityNumberTrace3) }, }, @@ -105,7 +108,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "blue screen of death", newVal: "black screen of death", - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetSeverityText("black screen of death") }, }, @@ -118,7 +121,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "body", newVal: "head", - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Body().SetStr("head") }, }, @@ -131,7 +134,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(4), newVal: int64(5), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetFlags(plog.LogRecordFlags(5)) }, }, @@ -144,7 +147,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: pcommon.TraceID(traceID), newVal: pcommon.TraceID(traceID2), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetTraceID(traceID2) }, }, @@ -157,7 +160,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: pcommon.SpanID(spanID), newVal: pcommon.SpanID(spanID2), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetSpanID(spanID2) }, }, @@ -173,7 +176,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: hex.EncodeToString(traceID[:]), newVal: hex.EncodeToString(traceID2[:]), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetTraceID(traceID2) }, }, @@ -189,10 +192,37 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: hex.EncodeToString(spanID[:]), newVal: hex.EncodeToString(spanID2[:]), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetSpanID(spanID2) }, }, + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, { name: "attributes", path: []ottl.Field{ @@ -202,7 +232,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refLog.Attributes(), newVal: newAttrs, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { newAttrs.CopyTo(log.Attributes()) }, }, @@ -216,7 +246,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val", newVal: "newVal", - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutStr("str", "newVal") }, }, @@ -230,7 +260,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutBool("bool", false) }, }, @@ -244,7 +274,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutInt("int", 20) }, }, @@ -258,7 +288,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: float64(1.2), newVal: float64(2.4), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutDouble("double", 2.4) }, }, @@ -272,7 +302,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: []byte{1, 3, 2}, newVal: []byte{2, 3, 4}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptyBytes("bytes").FromRaw([]byte{2, 3, 4}) }, }, @@ -289,7 +319,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []string{"new"}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptySlice("arr_str").AppendEmpty().SetStr("new") }, }, @@ -306,7 +336,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []bool{false}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptySlice("arr_bool").AppendEmpty().SetBool(false) }, }, @@ -323,7 +353,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []int64{20}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptySlice("arr_int").AppendEmpty().SetInt(20) }, }, @@ -340,7 +370,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []float64{2.0}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptySlice("arr_float").AppendEmpty().SetDouble(2.0) }, }, @@ -357,7 +387,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: [][]byte{{9, 6, 4}}, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.Attributes().PutEmptySlice("arr_bytes").AppendEmpty().SetEmptyBytes().FromRaw([]byte{9, 6, 4}) }, }, @@ -374,7 +404,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newPMap, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := log.Attributes().PutEmptyMap("pMap") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -393,7 +423,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newMap, - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := log.Attributes().PutEmptyMap("map") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -408,7 +438,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { log.SetDroppedAttributesCount(20) }, }, @@ -421,7 +451,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS, newVal: pcommon.NewInstrumentationScope(), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewInstrumentationScope().CopyTo(il) }, }, @@ -434,7 +464,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refResource, newVal: pcommon.NewResource(), - modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewResource().CopyTo(resource) }, }, @@ -446,19 +476,23 @@ func Test_newPathGetSetter(t *testing.T) { log, il, resource := createTelemetry() - got, err := accessor.Get(context.Background(), NewTransformContext(log, il, resource)) + tCtx := NewTransformContext(log, il, resource) + got, err := accessor.Get(context.Background(), tCtx) assert.Nil(t, err) assert.Equal(t, tt.orig, got) - err = accessor.Set(context.Background(), NewTransformContext(log, il, resource), tt.newVal) + tCtx = NewTransformContext(log, il, resource) + err = accessor.Set(context.Background(), tCtx, tt.newVal) assert.Nil(t, err) exSpan, exIl, exRes := createTelemetry() - tt.modified(exSpan, exIl, exRes) + exCache := pcommon.NewMap() + tt.modified(exSpan, exIl, exRes, exCache) assert.Equal(t, exSpan, log) assert.Equal(t, exIl, il) assert.Equal(t, exRes, resource) + assert.Equal(t, exCache, tCtx.getCache()) }) } } diff --git a/pkg/ottl/contexts/ottlmetric/README.md b/pkg/ottl/contexts/ottlmetric/README.md index 4d9a691bf5bd..881374202140 100644 --- a/pkg/ottl/contexts/ottlmetric/README.md +++ b/pkg/ottl/contexts/ottlmetric/README.md @@ -7,16 +7,18 @@ In general, the Metric Context supports accessing pdata using the field names fr The following fields are the exception. -| path | field accessed | type | -|----------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the metric being processed | pcommon.Resource | -| resource.attributes | resource attributes of the metric being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the metric being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| instrumentation_scope | instrumentation scope of the metric being processed | pcommon.InstrumentationScope | -| instrumentation_scope.name | name of the instrumentation scope of the metric being processed | string | -| instrumentation_scope.version | version of the instrumentation scope of the metric being processed | string | -| instrumentation_scope.attributes | instrumentation scope attributes of the metric being processed | pcommon.Map | -| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the metric being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the metric being processed | pcommon.Resource | +| resource.attributes | resource attributes of the metric being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the metric being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| instrumentation_scope | instrumentation scope of the metric being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the metric being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the metric being processed | string | +| instrumentation_scope.attributes | instrumentation scope attributes of the metric being processed | pcommon.Map | +| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the metric being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | ## Enums diff --git a/pkg/ottl/contexts/ottlmetric/metrics.go b/pkg/ottl/contexts/ottlmetric/metrics.go index 29bce2b35622..98238ff3830a 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics.go +++ b/pkg/ottl/contexts/ottlmetric/metrics.go @@ -15,6 +15,7 @@ package ottlmetric // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric" import ( + "context" "fmt" "go.opentelemetry.io/collector/component" @@ -33,6 +34,7 @@ type TransformContext struct { metric pmetric.Metric instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(metric pmetric.Metric, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { @@ -40,6 +42,7 @@ func NewTransformContext(metric pmetric.Metric, instrumentationScope pcommon.Ins metric: metric, instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -55,6 +58,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -80,6 +87,12 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) case "instrumentation_scope": @@ -88,3 +101,29 @@ func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], erro return ottlcommon.MetricPathGetSetter[TransformContext](path) } } + +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} diff --git a/pkg/ottl/contexts/ottlmetric/metrics_test.go b/pkg/ottl/contexts/ottlmetric/metrics_test.go index bcff5369110c..71ec21de4ed0 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics_test.go +++ b/pkg/ottl/contexts/ottlmetric/metrics_test.go @@ -30,6 +30,9 @@ func Test_newPathGetSetter(t *testing.T) { refMetric := createMetricTelemetry() + newCache := pcommon.NewMap() + newCache.PutStr("temp", "value") + newMetric := pmetric.NewMetric() newMetric.SetName("new name") @@ -42,7 +45,7 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(metric pmetric.Metric) + modified func(metric pmetric.Metric, cache pcommon.Map) }{ { name: "metric name", @@ -53,7 +56,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "name", newVal: "new name", - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { metric.SetName("new name") }, }, @@ -66,7 +69,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "description", newVal: "new description", - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { metric.SetDescription("new description") }, }, @@ -79,7 +82,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "unit", newVal: "new unit", - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { metric.SetUnit("new unit") }, }, @@ -92,7 +95,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(pmetric.MetricTypeSum), newVal: int64(pmetric.MetricTypeSum), - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { }, }, { @@ -104,7 +107,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(2), newVal: int64(1), - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { metric.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityDelta) }, }, @@ -117,7 +120,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { metric.Sum().SetIsMonotonic(false) }, }, @@ -130,10 +133,37 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refMetric.Sum().DataPoints(), newVal: newDataPoints, - modified: func(metric pmetric.Metric) { + modified: func(metric pmetric.Metric, cache pcommon.Map) { newDataPoints.CopyTo(metric.Sum().DataPoints()) }, }, + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(metric pmetric.Metric, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(metric pmetric.Metric, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -152,9 +182,11 @@ func Test_newPathGetSetter(t *testing.T) { assert.Nil(t, err) exMetric := createMetricTelemetry() - tt.modified(exMetric) + exCache := pcommon.NewMap() + tt.modified(exMetric, exCache) assert.Equal(t, exMetric, metric) + assert.Equal(t, exCache, ctx.getCache()) }) } } diff --git a/pkg/ottl/contexts/ottlresource/README.md b/pkg/ottl/contexts/ottlresource/README.md index 3217926681a5..dffed68460f8 100644 --- a/pkg/ottl/contexts/ottlresource/README.md +++ b/pkg/ottl/contexts/ottlresource/README.md @@ -5,6 +5,13 @@ The Resource Context is a Context implementation for [pdata Resources](https://g ## Paths In general, the Resource Context supports accessing pdata using the field names from the [resource proto](https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/resource/v1/resource.proto). All integers are returned and set via `int64`. All doubles are returned and set via `float64`. +The following fields are the exception. + +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | + ## Enums The Resource Context does not define any Enums at this time. diff --git a/pkg/ottl/contexts/ottlresource/resource.go b/pkg/ottl/contexts/ottlresource/resource.go index 913b3342acf6..86b804b40fd9 100644 --- a/pkg/ottl/contexts/ottlresource/resource.go +++ b/pkg/ottl/contexts/ottlresource/resource.go @@ -13,7 +13,9 @@ // limitations under the License. package ottlresource // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlresource" + import ( + "context" "fmt" "go.opentelemetry.io/collector/component" @@ -27,11 +29,13 @@ var _ ottlcommon.ResourceContext = TransformContext{} type TransformContext struct { resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(resource pcommon.Resource) TransformContext { return TransformContext{ resource: resource, + cache: pcommon.NewMap(), } } @@ -39,6 +43,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -55,5 +63,40 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { } func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { - return ottlcommon.ResourcePathGetSetter[TransformContext](path) + switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil + default: + return ottlcommon.ResourcePathGetSetter[TransformContext](path) + } +} + +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } } diff --git a/pkg/ottl/contexts/ottlresource/resource_test.go b/pkg/ottl/contexts/ottlresource/resource_test.go index 633a28b961ca..46f80f1e29e7 100644 --- a/pkg/ottl/contexts/ottlresource/resource_test.go +++ b/pkg/ottl/contexts/ottlresource/resource_test.go @@ -31,6 +31,9 @@ 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") @@ -45,8 +48,35 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(resource pcommon.Resource) + modified func(resource pcommon.Resource, cache pcommon.Map) }{ + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(resource pcommon.Resource, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(resource pcommon.Resource, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, { name: "attributes", path: []ottl.Field{ @@ -56,7 +86,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refResource.Attributes(), newVal: newAttrs, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { newAttrs.CopyTo(resource.Attributes()) }, }, @@ -70,7 +100,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val", newVal: "newVal", - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutStr("str", "newVal") }, }, @@ -84,7 +114,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutBool("bool", false) }, }, @@ -98,7 +128,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutInt("int", 20) }, }, @@ -112,7 +142,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: float64(1.2), newVal: float64(2.4), - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutDouble("double", 2.4) }, }, @@ -126,7 +156,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: []byte{1, 3, 2}, newVal: []byte{2, 3, 4}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptyBytes("bytes").FromRaw([]byte{2, 3, 4}) }, }, @@ -143,7 +173,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []string{"new"}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptySlice("arr_str").AppendEmpty().SetStr("new") }, }, @@ -160,7 +190,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []bool{false}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptySlice("arr_bool").AppendEmpty().SetBool(false) }, }, @@ -177,7 +207,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []int64{20}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptySlice("arr_int").AppendEmpty().SetInt(20) }, }, @@ -194,7 +224,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []float64{2.0}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptySlice("arr_float").AppendEmpty().SetDouble(2.0) }, }, @@ -211,7 +241,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: [][]byte{{9, 6, 4}}, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.Attributes().PutEmptySlice("arr_bytes").AppendEmpty().SetEmptyBytes().FromRaw([]byte{9, 6, 4}) }, }, @@ -228,7 +258,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newPMap, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { m := resource.Attributes().PutEmptyMap("pMap") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -247,7 +277,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newMap, - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { m := resource.Attributes().PutEmptyMap("map") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -262,7 +292,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(resource pcommon.Resource) { + modified: func(resource pcommon.Resource, cache pcommon.Map) { resource.SetDroppedAttributesCount(20) }, }, @@ -274,17 +304,20 @@ func Test_newPathGetSetter(t *testing.T) { resource := createTelemetry() - got, err := accessor.Get(context.Background(), NewTransformContext(resource)) + tCtx := NewTransformContext(resource) + got, err := accessor.Get(context.Background(), tCtx) assert.Nil(t, err) assert.Equal(t, tt.orig, got) - err = accessor.Set(context.Background(), NewTransformContext(resource), tt.newVal) + err = accessor.Set(context.Background(), tCtx, tt.newVal) assert.Nil(t, err) exRes := createTelemetry() - tt.modified(exRes) + exCache := pcommon.NewMap() + tt.modified(exRes, exCache) assert.Equal(t, exRes, resource) + assert.Equal(t, exCache, tCtx.getCache()) }) } } diff --git a/pkg/ottl/contexts/ottlscope/README.md b/pkg/ottl/contexts/ottlscope/README.md index 4d89ae141ca1..f0f83167d1ce 100644 --- a/pkg/ottl/contexts/ottlscope/README.md +++ b/pkg/ottl/contexts/ottlscope/README.md @@ -7,12 +7,14 @@ In general, the Instrumentation Scope Context supports accessing pdata using the The following fields are the exception. -| path | field accessed | type | -|-----------------------------------|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the instrumentation scope being processed | pcommon.Resource | -| resource.attributes | resource attributes of the instrumentation scope being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the instrumentation scope being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| resource.dropped_attributes_count | number of dropped attributes of the resource of the instrumentation scope being processed | int64 | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the instrumentation scope being processed | pcommon.Resource | +| resource.attributes | resource attributes of the instrumentation scope being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the instrumentation scope being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource.dropped_attributes_count | number of dropped attributes of the resource of the instrumentation scope being processed | int64 | ## Enums diff --git a/pkg/ottl/contexts/ottlscope/scope.go b/pkg/ottl/contexts/ottlscope/scope.go index 0113e49ea2d7..33d9c1465adf 100644 --- a/pkg/ottl/contexts/ottlscope/scope.go +++ b/pkg/ottl/contexts/ottlscope/scope.go @@ -15,6 +15,7 @@ package ottlscope // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlscope" import ( + "context" "fmt" "go.opentelemetry.io/collector/component" @@ -30,12 +31,14 @@ var _ ottlcommon.InstrumentationScopeContext = TransformContext{} type TransformContext struct { instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { return TransformContext{ instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -47,6 +50,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -64,9 +71,41 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) default: return ottlcommon.ScopePathGetSetter[TransformContext](path) } } + +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} diff --git a/pkg/ottl/contexts/ottlscope/scope_test.go b/pkg/ottl/contexts/ottlscope/scope_test.go index 2062af9260ab..5d6b8e42c176 100644 --- a/pkg/ottl/contexts/ottlscope/scope_test.go +++ b/pkg/ottl/contexts/ottlscope/scope_test.go @@ -31,6 +31,9 @@ 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") @@ -45,8 +48,35 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(is pcommon.InstrumentationScope, resource pcommon.Resource) + modified func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) }{ + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, { name: "attributes", path: []ottl.Field{ @@ -56,7 +86,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS.Attributes(), newVal: newAttrs, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { newAttrs.CopyTo(is.Attributes()) }, }, @@ -70,7 +100,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val", newVal: "newVal", - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutStr("str", "newVal") }, }, @@ -84,7 +114,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutBool("bool", false) }, }, @@ -98,7 +128,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutInt("int", 20) }, }, @@ -112,7 +142,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: float64(1.2), newVal: float64(2.4), - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutDouble("double", 2.4) }, }, @@ -126,7 +156,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: []byte{1, 3, 2}, newVal: []byte{2, 3, 4}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptyBytes("bytes").FromRaw([]byte{2, 3, 4}) }, }, @@ -143,7 +173,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []string{"new"}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptySlice("arr_str").AppendEmpty().SetStr("new") }, }, @@ -160,7 +190,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []bool{false}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptySlice("arr_bool").AppendEmpty().SetBool(false) }, }, @@ -177,7 +207,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []int64{20}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptySlice("arr_int").AppendEmpty().SetInt(20) }, }, @@ -194,7 +224,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []float64{2.0}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptySlice("arr_float").AppendEmpty().SetDouble(2.0) }, }, @@ -211,7 +241,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: [][]byte{{9, 6, 4}}, - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.Attributes().PutEmptySlice("arr_bytes").AppendEmpty().SetEmptyBytes().FromRaw([]byte{9, 6, 4}) }, }, @@ -228,7 +258,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newPMap, - modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := il.Attributes().PutEmptyMap("pMap") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -247,7 +277,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newMap, - modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := il.Attributes().PutEmptyMap("map") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -262,7 +292,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.SetDroppedAttributesCount(20) }, }, @@ -275,7 +305,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS.Name(), newVal: "newname", - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.SetName("newname") }, }, @@ -288,7 +318,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS.Version(), newVal: "next", - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { is.SetVersion("next") }, }, @@ -301,7 +331,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refResource, newVal: pcommon.NewResource(), - modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(is pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewResource().CopyTo(resource) }, }, @@ -313,18 +343,21 @@ func Test_newPathGetSetter(t *testing.T) { il, resource := createTelemetry() - got, err := accessor.Get(context.Background(), NewTransformContext(il, resource)) + tCtx := NewTransformContext(il, resource) + got, err := accessor.Get(context.Background(), tCtx) assert.Nil(t, err) assert.Equal(t, tt.orig, got) - err = accessor.Set(context.Background(), NewTransformContext(il, resource), tt.newVal) + err = accessor.Set(context.Background(), tCtx, tt.newVal) assert.Nil(t, err) exIl, exRes := createTelemetry() - tt.modified(exIl, exRes) + exCache := pcommon.NewMap() + tt.modified(exIl, exRes, exCache) assert.Equal(t, exIl, il) assert.Equal(t, exRes, resource) + assert.Equal(t, exCache, tCtx.getCache()) }) } } diff --git a/pkg/ottl/contexts/ottlspan/README.md b/pkg/ottl/contexts/ottlspan/README.md index da9f108e6d26..e453c3c2459b 100644 --- a/pkg/ottl/contexts/ottlspan/README.md +++ b/pkg/ottl/contexts/ottlspan/README.md @@ -9,29 +9,31 @@ All TraceIDs and SpanIDs are returned as pdata [SpanID](https://github.com/open- The following fields are the exception. -| path | field accessed | type | -|------------------------------------------------|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the span being processed | pcommon.Resource | -| resource.attributes | resource attributes of the span being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| resource.dropped_attributes_count | number of dropped attributes of the resource of the span being processed | int64 | -| instrumentation_scope | instrumentation scope of the span being processed | pcommon.InstrumentationScope | -| instrumentation_scope.name | name of the instrumentation scope of the span being processed | string | -| instrumentation_scope.version | version of the instrumentation scope of the span being processed | string | -| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the span being processed | int64 | -| instrumentation_scope.attributes | instrumentation scope attributes of the span being processed | pcommon.Map | -| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| attributes | attributes of the span being processed | pcommon.Map | -| attributes\[""\] | the value of the attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| trace_id | a byte slice representation of the trace id | pcommon.TraceID | -| trace_id.string | a string representation of the trace id | string | -| span_id | a byte slice representation of the span id | pcommon.SpanID | -| span_id.string | a string representation of the span id | string | -| parent_span_id | a byte slice representation of the parent span id | pcommon.SpanID | -| parent_span_id.string | a string representation of the parent span id | string | -| trace_state\[""\] | an individual entry in the trace state | string | -| status.code | the status code of the span being processed | int64 | -| status.message | the status message of the span being processed | string | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the span being processed | pcommon.Resource | +| resource.attributes | resource attributes of the span being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource.dropped_attributes_count | number of dropped attributes of the resource of the span being processed | int64 | +| instrumentation_scope | instrumentation scope of the span being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the span being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the span being processed | string | +| instrumentation_scope.dropped_attributes_count | number of dropped attributes of the instrumentation scope of the span being processed | int64 | +| instrumentation_scope.attributes | instrumentation scope attributes of the span being processed | pcommon.Map | +| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| attributes | attributes of the span being processed | pcommon.Map | +| attributes\[""\] | the value of the attribute of the span being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| trace_id | a byte slice representation of the trace id | pcommon.TraceID | +| trace_id.string | a string representation of the trace id | string | +| span_id | a byte slice representation of the span id | pcommon.SpanID | +| span_id.string | a string representation of the span id | string | +| parent_span_id | a byte slice representation of the parent span id | pcommon.SpanID | +| parent_span_id.string | a string representation of the parent span id | string | +| trace_state\[""\] | an individual entry in the trace state | string | +| status.code | the status code of the span being processed | int64 | +| status.message | the status message of the span being processed | string | ## Enums diff --git a/pkg/ottl/contexts/ottlspan/span.go b/pkg/ottl/contexts/ottlspan/span.go index 3f30d9ff4943..61ead7a1240d 100644 --- a/pkg/ottl/contexts/ottlspan/span.go +++ b/pkg/ottl/contexts/ottlspan/span.go @@ -15,6 +15,7 @@ package ottlspan // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" import ( + "context" "fmt" "go.opentelemetry.io/collector/component" @@ -32,6 +33,7 @@ type TransformContext struct { span ptrace.Span instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { @@ -39,6 +41,7 @@ func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.Instrume span: span, instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -54,6 +57,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -77,6 +84,12 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) case "instrumentation_scope": @@ -85,3 +98,29 @@ func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], erro return ottlcommon.SpanPathGetSetter[TransformContext](path) } } + +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} diff --git a/pkg/ottl/contexts/ottlspan/span_test.go b/pkg/ottl/contexts/ottlspan/span_test.go index 63af0690e00f..236fc5d3b7e6 100644 --- a/pkg/ottl/contexts/ottlspan/span_test.go +++ b/pkg/ottl/contexts/ottlspan/span_test.go @@ -41,6 +41,9 @@ func Test_newPathGetSetter(t *testing.T) { newAttrs := pcommon.NewMap() newAttrs.PutStr("hello", "world") + newCache := pcommon.NewMap() + newCache.PutStr("temp", "value") + newEvents := ptrace.NewSpanEventSlice() newEvents.AppendEmpty().SetName("new event") @@ -64,8 +67,35 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) + modified func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) }{ + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, { name: "trace_id", path: []ottl.Field{ @@ -75,7 +105,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: pcommon.TraceID(traceID), newVal: pcommon.TraceID(traceID2), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetTraceID(traceID2) }, }, @@ -88,7 +118,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: pcommon.SpanID(spanID), newVal: pcommon.SpanID(spanID2), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetSpanID(spanID2) }, }, @@ -104,7 +134,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: hex.EncodeToString(traceID[:]), newVal: hex.EncodeToString(traceID2[:]), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetTraceID(traceID2) }, }, @@ -120,7 +150,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: hex.EncodeToString(spanID[:]), newVal: hex.EncodeToString(spanID2[:]), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetSpanID(spanID2) }, }, @@ -133,7 +163,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "key1=val1,key2=val2", newVal: "key=newVal", - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.TraceState().FromRaw("key=newVal") }, }, @@ -147,7 +177,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val1", newVal: "newVal", - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.TraceState().FromRaw("key1=newVal,key2=val2") }, }, @@ -160,7 +190,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: pcommon.SpanID(spanID2), newVal: pcommon.SpanID(spanID), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetParentSpanID(spanID) }, }, @@ -173,7 +203,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "bear", newVal: "cat", - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetName("cat") }, }, @@ -186,7 +216,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(2), newVal: int64(3), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetKind(ptrace.SpanKindClient) }, }, @@ -199,7 +229,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(100_000_000), newVal: int64(200_000_000), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) }, }, @@ -212,7 +242,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(500_000_000), newVal: int64(200_000_000), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetEndTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) }, }, @@ -225,7 +255,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpan.Attributes(), newVal: newAttrs, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { newAttrs.CopyTo(span.Attributes()) }, }, @@ -239,7 +269,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val", newVal: "newVal", - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutStr("str", "newVal") }, }, @@ -253,7 +283,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutBool("bool", false) }, }, @@ -267,7 +297,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutInt("int", 20) }, }, @@ -281,7 +311,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: float64(1.2), newVal: float64(2.4), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutDouble("double", 2.4) }, }, @@ -295,7 +325,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: []byte{1, 3, 2}, newVal: []byte{2, 3, 4}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptyBytes("bytes").FromRaw([]byte{2, 3, 4}) }, }, @@ -312,7 +342,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []string{"new"}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptySlice("arr_str").AppendEmpty().SetStr("new") }, }, @@ -329,7 +359,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []bool{false}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptySlice("arr_bool").AppendEmpty().SetBool(false) }, }, @@ -346,7 +376,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []int64{20}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptySlice("arr_int").AppendEmpty().SetInt(20) }, }, @@ -363,7 +393,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []float64{2.0}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptySlice("arr_float").AppendEmpty().SetDouble(2.0) }, }, @@ -380,7 +410,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: [][]byte{{9, 6, 4}}, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Attributes().PutEmptySlice("arr_bytes").AppendEmpty().SetEmptyBytes().FromRaw([]byte{9, 6, 4}) }, }, @@ -397,7 +427,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newPMap, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := span.Attributes().PutEmptyMap("pMap") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -416,7 +446,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newMap, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := span.Attributes().PutEmptyMap("map") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -431,7 +461,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetDroppedAttributesCount(20) }, }, @@ -444,7 +474,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpan.Events(), newVal: newEvents, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Events().RemoveIf(func(_ ptrace.SpanEvent) bool { return true }) @@ -460,7 +490,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(20), newVal: int64(30), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetDroppedEventsCount(30) }, }, @@ -473,7 +503,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpan.Links(), newVal: newLinks, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Links().RemoveIf(func(_ ptrace.SpanLink) bool { return true }) @@ -489,7 +519,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(30), newVal: int64(40), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.SetDroppedLinksCount(40) }, }, @@ -502,7 +532,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpan.Status(), newVal: newStatus, - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { newStatus.CopyTo(span.Status()) }, }, @@ -518,7 +548,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(ptrace.StatusCodeOk), newVal: int64(ptrace.StatusCodeError), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Status().SetCode(ptrace.StatusCodeError) }, }, @@ -534,7 +564,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "good span", newVal: "bad span", - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { span.Status().SetMessage("bad span") }, }, @@ -547,7 +577,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS, newVal: pcommon.NewInstrumentationScope(), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewInstrumentationScope().CopyTo(il) }, }, @@ -560,7 +590,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refResource, newVal: pcommon.NewResource(), - modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewResource().CopyTo(resource) }, }, @@ -572,19 +602,23 @@ func Test_newPathGetSetter(t *testing.T) { span, il, resource := createTelemetry() - got, err := accessor.Get(context.Background(), NewTransformContext(span, il, resource)) + tCtx := NewTransformContext(span, il, resource) + + got, err := accessor.Get(context.Background(), tCtx) assert.Nil(t, err) assert.Equal(t, tt.orig, got) - err = accessor.Set(context.Background(), NewTransformContext(span, il, resource), tt.newVal) + err = accessor.Set(context.Background(), tCtx, tt.newVal) assert.Nil(t, err) exSpan, exIl, exRes := createTelemetry() - tt.modified(exSpan, exIl, exRes) + exCache := pcommon.NewMap() + tt.modified(exSpan, exIl, exRes, exCache) assert.Equal(t, exSpan, span) assert.Equal(t, exIl, il) assert.Equal(t, exRes, resource) + assert.Equal(t, exCache, tCtx.getCache()) }) } } diff --git a/pkg/ottl/contexts/ottlspanevent/README.md b/pkg/ottl/contexts/ottlspanevent/README.md index 813dabdbfe99..48da93052bf4 100644 --- a/pkg/ottl/contexts/ottlspanevent/README.md +++ b/pkg/ottl/contexts/ottlspanevent/README.md @@ -7,20 +7,22 @@ In general, the Span Event Context supports accessing pdata using the field name The following fields are the exception. -| path | field accessed | type | -|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| resource | resource of the span event being processed | pcommon.Resource | -| resource.attributes | resource attributes of the span event being processed | pcommon.Map | -| resource.attributes\[""\] | the value of the resource attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| instrumentation_scope | instrumentation scope of the span event being processed | pcommon.InstrumentationScope | -| instrumentation_scope.name | name of the instrumentation scope of the span event being processed | string | -| instrumentation_scope.version | version of the instrumentation scope of the span event being processed | string | -| instrumentation_scope.attributes | instrumentation scope attributes of the span event being processed | pcommon.Map | -| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | -| span | span of the span event being processed | ptrace.Span | -| span.* | All fields exposed by the [ottltraces context](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottltraces) can accessed via `span.` | varies | -| attributes | attributes of the span event being processed | pcommon.Map | -| attributes\[""\] | the value of the attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| 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 | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| resource | resource of the span event being processed | pcommon.Resource | +| resource.attributes | resource attributes of the span event being processed | pcommon.Map | +| resource.attributes\[""\] | the value of the resource attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| instrumentation_scope | instrumentation scope of the span event being processed | pcommon.InstrumentationScope | +| instrumentation_scope.name | name of the instrumentation scope of the span event being processed | string | +| instrumentation_scope.version | version of the instrumentation scope of the span event being processed | string | +| instrumentation_scope.attributes | instrumentation scope attributes of the span event being processed | pcommon.Map | +| instrumentation_scope.attributes\[""\] | the value of the instrumentation scope attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | +| span | span of the span event being processed | ptrace.Span | +| span.* | All fields exposed by the [ottlspan context](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlspan) can accessed via `span.` | varies | +| attributes | attributes of the span event being processed | pcommon.Map | +| attributes\[""\] | the value of the attribute of the span event being processed | string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte or nil | ## Enums diff --git a/pkg/ottl/contexts/ottlspanevent/span_events.go b/pkg/ottl/contexts/ottlspanevent/span_events.go index 5acd079d3c48..bce1d2a81cf7 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events.go @@ -36,6 +36,7 @@ type TransformContext struct { span ptrace.Span instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource + cache pcommon.Map } func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource) TransformContext { @@ -44,6 +45,7 @@ func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumen span: span, instrumentationScope: instrumentationScope, resource: resource, + cache: pcommon.NewMap(), } } @@ -63,6 +65,10 @@ func (tCtx TransformContext) GetResource() pcommon.Resource { return tCtx.resource } +func (tCtx TransformContext) getCache() pcommon.Map { + return tCtx.cache +} + func NewParser(functions map[string]interface{}, telemetrySettings component.TelemetrySettings) ottl.Parser[TransformContext] { return ottl.NewParser[TransformContext](functions, parsePath, parseEnum, telemetrySettings) } @@ -86,6 +92,12 @@ func parsePath(val *ottl.Path) (ottl.GetSetter[TransformContext], error) { func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], error) { switch path[0].Name { + case "cache": + mapKey := path[0].MapKey + if mapKey == nil { + return accessCache(), nil + } + return accessCacheKey(mapKey), nil case "resource": return ottlcommon.ResourcePathGetSetter[TransformContext](path[1:]) case "instrumentation_scope": @@ -109,6 +121,32 @@ func newPathGetSetter(path []ottl.Field) (ottl.GetSetter[TransformContext], erro return nil, fmt.Errorf("invalid scope path expression %v", path) } +func accessCache() ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return tCtx.getCache(), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + if m, ok := val.(pcommon.Map); ok { + m.CopyTo(tCtx.getCache()) + } + return nil + }, + } +} + +func accessCacheKey(mapKey *string) ottl.StandardGetSetter[TransformContext] { + return ottl.StandardGetSetter[TransformContext]{ + Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { + return ottlcommon.GetMapValue(tCtx.getCache(), *mapKey), nil + }, + Setter: func(ctx context.Context, tCtx TransformContext, val interface{}) error { + ottlcommon.SetMapValue(tCtx.getCache(), *mapKey, val) + return nil + }, + } +} + func accessSpanEventTimeUnixNano() ottl.StandardGetSetter[TransformContext] { return ottl.StandardGetSetter[TransformContext]{ Getter: func(ctx context.Context, tCtx TransformContext) (interface{}, error) { diff --git a/pkg/ottl/contexts/ottlspanevent/span_events_test.go b/pkg/ottl/contexts/ottlspanevent/span_events_test.go index 9676cfa8f4c7..acecf0567be5 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events_test.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events_test.go @@ -40,6 +40,9 @@ func Test_newPathGetSetter(t *testing.T) { newAttrs := pcommon.NewMap() newAttrs.PutStr("hello", "world") + newCache := pcommon.NewMap() + newCache.PutStr("temp", "value") + newEvents := ptrace.NewSpanEventSlice() newEvents.AppendEmpty().SetName("new event") @@ -63,8 +66,35 @@ func Test_newPathGetSetter(t *testing.T) { path []ottl.Field orig interface{} newVal interface{} - modified func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) + modified func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) }{ + { + name: "cache", + path: []ottl.Field{ + { + Name: "cache", + }, + }, + orig: pcommon.NewMap(), + newVal: newCache, + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + newCache.CopyTo(cache) + }, + }, + { + name: "cache access", + path: []ottl.Field{ + { + Name: "cache", + MapKey: ottltest.Strp("temp"), + }, + }, + orig: nil, + newVal: "new value", + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { + cache.PutStr("temp", "new value") + }, + }, { name: "name", path: []ottl.Field{ @@ -74,7 +104,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "bear", newVal: "cat", - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.SetName("cat") }, }, @@ -87,7 +117,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(100_000_000), newVal: int64(200_000_000), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) }, }, @@ -100,7 +130,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpanEvent.Attributes(), newVal: newAttrs, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { newAttrs.CopyTo(spanEvent.Attributes()) }, }, @@ -114,7 +144,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: "val", newVal: "newVal", - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutStr("str", "newVal") }, }, @@ -128,7 +158,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: true, newVal: false, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutBool("bool", false) }, }, @@ -142,7 +172,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutInt("int", 20) }, }, @@ -156,7 +186,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: float64(1.2), newVal: float64(2.4), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutDouble("double", 2.4) }, }, @@ -170,7 +200,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: []byte{1, 3, 2}, newVal: []byte{2, 3, 4}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptyBytes("bytes").FromRaw([]byte{2, 3, 4}) }, }, @@ -187,7 +217,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []string{"new"}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptySlice("arr_str").AppendEmpty().SetStr("new") }, }, @@ -204,7 +234,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []bool{false}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptySlice("arr_bool").AppendEmpty().SetBool(false) }, }, @@ -221,7 +251,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []int64{20}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptySlice("arr_int").AppendEmpty().SetInt(20) }, }, @@ -238,7 +268,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: []float64{2.0}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptySlice("arr_float").AppendEmpty().SetDouble(2.0) }, }, @@ -255,7 +285,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Slice() }(), newVal: [][]byte{{9, 6, 4}}, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.Attributes().PutEmptySlice("arr_bytes").AppendEmpty().SetEmptyBytes().FromRaw([]byte{9, 6, 4}) }, }, @@ -272,7 +302,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newPMap, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := spanEvent.Attributes().PutEmptyMap("pMap") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -291,7 +321,7 @@ func Test_newPathGetSetter(t *testing.T) { return val.Map() }(), newVal: newMap, - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { m := spanEvent.Attributes().PutEmptyMap("map") m2 := m.PutEmptyMap("k2") m2.PutStr("k1", "string") @@ -306,7 +336,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: int64(10), newVal: int64(20), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { spanEvent.SetDroppedAttributesCount(20) }, }, @@ -319,7 +349,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refIS, newVal: pcommon.NewInstrumentationScope(), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewInstrumentationScope().CopyTo(il) }, }, @@ -332,7 +362,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refResource, newVal: pcommon.NewResource(), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { pcommon.NewResource().CopyTo(resource) }, }, @@ -345,7 +375,7 @@ func Test_newPathGetSetter(t *testing.T) { }, orig: refSpan, newVal: ptrace.NewSpan(), - modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource) { + modified: func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map) { ptrace.NewSpan().CopyTo(span) }, }, @@ -357,19 +387,23 @@ func Test_newPathGetSetter(t *testing.T) { spanEvent, span, il, resource := createTelemetry() - got, err := accessor.Get(context.Background(), NewTransformContext(spanEvent, span, il, resource)) + tCtx := NewTransformContext(spanEvent, span, il, resource) + + got, err := accessor.Get(context.Background(), tCtx) assert.NoError(t, err) assert.Equal(t, tt.orig, got) - err = accessor.Set(context.Background(), NewTransformContext(spanEvent, span, il, resource), tt.newVal) + err = accessor.Set(context.Background(), tCtx, tt.newVal) assert.NoError(t, err) exSpanEvent, exSpan, exIl, exRes := createTelemetry() - tt.modified(exSpanEvent, exSpan, exIl, exRes) + exCache := pcommon.NewMap() + tt.modified(exSpanEvent, exSpan, exIl, exRes, exCache) assert.Equal(t, exSpan, span) assert.Equal(t, exIl, il) assert.Equal(t, exRes, resource) + assert.Equal(t, exCache, tCtx.getCache()) }) } }