From fc167d96bcf1441b665fcbb17558cf8825eaeea1 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Wed, 18 Jan 2023 15:18:52 -0700 Subject: [PATCH] [pkg/ottl] Add tmp path (#16994) We've had discussion recently around how to handle functions and converters (previously factory functions) that share the same functionality (see #16571) This PR suggests a different approach that should reduce the need to chain converters/functions. Before we chained converters together because OTTL had no place to store data except for the telemetry payload itself. While attributes could be used, it results in the need for cleanup and potentially unwanted transformations based on condition resolution. This PR introduces the concept of tmp to the logs contexts (and future contexts if we like the solution) that statements can use as a "staging" location for complex transformations. Before to handle situations like the one in #14938 we would have to chain together functions. Pretending we had KeepKeys converter the statement would look like merge_maps(attributes, KeepKeys(ParseJSON(body), ["log_type"]), "upsert"). These types of statements are tricky to write and can difficult to comprehend, especially for new users. Each Converter we add on increased the burden. Adding a single extra function, like Flatten, really makes a difference: merge_maps(attributes, Flatten(KeepKeys(ParseJSON(body), ["log_type"]), "."), "upsert") Co-authored-by: Evan Bradley --- .chloggen/ottl-introduce-temp-storage.yaml | 16 +++ pkg/ottl/contexts/ottldatapoint/README.md | 56 ++++----- pkg/ottl/contexts/ottldatapoint/datapoint.go | 38 ++++++ .../contexts/ottldatapoint/datapoint_test.go | 66 +++++++++++ pkg/ottl/contexts/ottllog/README.md | 38 +++--- pkg/ottl/contexts/ottllog/log.go | 38 ++++++ pkg/ottl/contexts/ottllog/log_test.go | 94 ++++++++++----- pkg/ottl/contexts/ottlmetric/README.md | 22 ++-- pkg/ottl/contexts/ottlmetric/metrics.go | 39 +++++++ pkg/ottl/contexts/ottlmetric/metrics_test.go | 50 ++++++-- pkg/ottl/contexts/ottlresource/README.md | 7 ++ pkg/ottl/contexts/ottlresource/resource.go | 45 ++++++- .../contexts/ottlresource/resource_test.go | 69 ++++++++--- pkg/ottl/contexts/ottlscope/README.md | 14 ++- pkg/ottl/contexts/ottlscope/scope.go | 39 +++++++ pkg/ottl/contexts/ottlscope/scope_test.go | 75 ++++++++---- pkg/ottl/contexts/ottlspan/README.md | 48 ++++---- pkg/ottl/contexts/ottlspan/span.go | 39 +++++++ pkg/ottl/contexts/ottlspan/span_test.go | 110 ++++++++++++------ pkg/ottl/contexts/ottlspanevent/README.md | 30 ++--- .../contexts/ottlspanevent/span_events.go | 38 ++++++ .../ottlspanevent/span_events_test.go | 80 +++++++++---- 22 files changed, 813 insertions(+), 238 deletions(-) create mode 100755 .chloggen/ottl-introduce-temp-storage.yaml 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()) }) } }