From d96551909094ed64935637b01ece08e40f87aa22 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 12 Dec 2024 12:56:03 +0100 Subject: [PATCH 01/20] dollar version Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/resource.go | 1 + pkg/ottl/contexts/internal/scope.go | 1 + pkg/ottl/contexts/internal/span.go | 1 + pkg/ottl/contexts/ottldatapoint/datapoint.go | 1 + pkg/ottl/contexts/ottllog/log.go | 1 + .../contexts/ottlspanevent/span_events.go | 1 + pkg/ottl/e2e/e2e_test.go | 1246 ++++++++--------- pkg/ottl/functions.go | 31 +- pkg/ottl/grammar.go | 5 + pkg/ottl/lexer_test.go | 9 + pkg/ottl/ottlfuncs/func_set.go | 4 +- 11 files changed, 666 insertions(+), 635 deletions(-) diff --git a/pkg/ottl/contexts/internal/resource.go b/pkg/ottl/contexts/internal/resource.go index 101bbf178244..ae1efbffe96c 100644 --- a/pkg/ottl/contexts/internal/resource.go +++ b/pkg/ottl/contexts/internal/resource.go @@ -57,6 +57,7 @@ func accessResourceAttributesKey[K ResourceContext](keys []ottl.Key[K]) ottl.Sta Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetResource().Attributes(), keys, val) }, + // TODO } } diff --git a/pkg/ottl/contexts/internal/scope.go b/pkg/ottl/contexts/internal/scope.go index 7a698ebc533f..f6da0b6fdd32 100644 --- a/pkg/ottl/contexts/internal/scope.go +++ b/pkg/ottl/contexts/internal/scope.go @@ -62,6 +62,7 @@ func accessInstrumentationScopeAttributesKey[K InstrumentationScopeContext](keys Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetInstrumentationScope().Attributes(), keys, val) }, + // TODO } } diff --git a/pkg/ottl/contexts/internal/span.go b/pkg/ottl/contexts/internal/span.go index c1ebf5a9b109..329ebeebc14c 100644 --- a/pkg/ottl/contexts/internal/span.go +++ b/pkg/ottl/contexts/internal/span.go @@ -451,6 +451,7 @@ func accessAttributesKey[K SpanContext](keys []ottl.Key[K]) ottl.StandardGetSett Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetSpan().Attributes(), keys, val) }, + // TODO } } diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint.go b/pkg/ottl/contexts/ottldatapoint/datapoint.go index 9c50d85cd723..fc5e8170a557 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint.go @@ -353,6 +353,7 @@ func accessAttributesKey(key []ottl.Key[TransformContext]) ottl.StandardGetSette case pmetric.SummaryDataPoint: return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetDataPoint().(pmetric.SummaryDataPoint).Attributes(), key, val) } + // TODO return nil }, } diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 7ca056730cc7..3dde1bb851e6 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -448,6 +448,7 @@ func accessAttributesKey(key []ottl.Key[TransformContext]) ottl.StandardGetSette Setter: func(ctx context.Context, tCtx TransformContext, val any) error { return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetLogRecord().Attributes(), key, val) }, + // TODO } } diff --git a/pkg/ottl/contexts/ottlspanevent/span_events.go b/pkg/ottl/contexts/ottlspanevent/span_events.go index b3826f690d2d..7e8851ec1033 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events.go @@ -272,6 +272,7 @@ func accessSpanEventAttributesKey(key []ottl.Key[TransformContext]) ottl.Standar Setter: func(ctx context.Context, tCtx TransformContext, val any) error { return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetSpanEvent().Attributes(), key, val) }, + // TODO } } diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index bb1dd43b5282..77c948211784 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -5,11 +5,11 @@ package e2e import ( "context" - "net/http" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" @@ -330,635 +330,635 @@ func Test_e2e_converters(t *testing.T) { want func(tCtx ottllog.TransformContext) }{ { - statement: `set(attributes["test"], Base64Decode("cGFzcw=="))`, + statement: `set(${attributes["test"]}, Base64Decode("cGFzcw=="))`, want: func(tCtx ottllog.TransformContext) { tCtx.GetLogRecord().Attributes().PutStr("test", "pass") }, }, - { - statement: `set(attributes["test"], Decode("cGFzcw==", "base64"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], Concat(["A","B"], ":"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "A:B") - }, - }, - { - statement: `set(attributes["test"], ConvertCase(attributes["http.method"], "upper"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", http.MethodGet) - }, - }, - { - statement: `set(attributes["test"], ConvertCase("PASS", "lower"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], ConvertCase("fooBar", "snake"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "foo_bar") - }, - }, - { - statement: `set(attributes["test"], ConvertCase("foo_bar", "camel"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "FooBar") - }, - }, - { - statement: `set(attributes["test"], ConvertAttributesToElementsXML("This is a log message!"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", `This is a log message!1`) - }, - }, - { - statement: `set(body, ConvertTextToElementsXML("foo"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Body().SetStr("foo") - }, - }, - { - statement: `set(body, ConvertTextToElementsXML("foobar", "/a", "custom"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Body().SetStr("foobar") - }, - }, - { - statement: `set(attributes["test"], Double(1.0))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - }, - }, - { - statement: `set(attributes["test"], Double("1"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - }, - }, - { - statement: `set(attributes["test"], Double(true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - }, - }, - { - statement: `set(attributes["test"], Double(1))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - }, - }, - { - statement: `set(attributes["test"], "pass") where Time("10", "%M") - Time("01", "%M") < Duration("10m")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], ExtractPatterns("aa123bb", "(?P\\d+)"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("numbers", "123") - }, - }, - { - statement: `set(attributes["test"], ExtractGrokPatterns("http://user:password@example.com:80/path?query=string", "%{ELB_URI}", true))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("url.scheme", "http") - m.PutStr("url.username", "user") - m.PutStr("url.domain", "example.com") - m.PutInt("url.port", 80) - m.PutStr("url.path", "/path") - m.PutStr("url.query", "query=string") - }, - }, - { - statement: `set(attributes["test"], FNV("pass"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 266877920130663416) - }, - }, - { - statement: `set(attributes["test"], Format("%03d-%s", [7, "test"]))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "007-test") - }, - }, - { - statement: `set(attributes["test"], Hour(Time("12", "%H")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 12) - }, - }, - { - statement: `set(attributes["test"], Hours(Duration("90m")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 1.5) - }, - }, - { - statement: `set(attributes["test"], InsertXML("", "/a", ""))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "") - }, - }, - { - statement: `set(attributes["test"], Int(1.0))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1) - }, - }, - { - statement: `set(attributes["test"], Int("1"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1) - }, - }, - { - statement: `set(attributes["test"], Int(true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1) - }, - }, - { - statement: `set(attributes["test"], Int(1))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1) - }, - }, - { - statement: `set(attributes["test"], GetXML("12", "/a//b"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "12") - }, - }, - { - statement: `set(attributes["test"], Hex(1.0))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "3ff0000000000000") - }, - }, - { - statement: `set(attributes["test"], Hex(true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "01") - }, - }, - { - statement: `set(attributes["test"], Hex(12))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "000000000000000c") - }, - }, - { - statement: `set(attributes["test"], Hex("12"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "3132") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsBool(false)`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsDouble(1.0)`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsMap(attributes["foo"])`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsList(attributes["foo"]["slice"])`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsMatch("aa123bb", "\\d{3}")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsString("")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], Len(attributes["foo"]))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 4) - }, - }, - { - statement: `set(attributes["test"], Log(1))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 0) - }, - }, - { - statement: `set(attributes["test"], MD5("pass"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "1a1dc91c907325c69271ddf0c944bc72") - }, - }, - { - statement: `set(attributes["test"], Microseconds(Duration("1ms")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1000) - }, - }, - { - statement: `set(attributes["test"], Milliseconds(Duration("1s")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1000) - }, - }, - { - statement: `set(attributes["test"], Minutes(Duration("1h")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 60) - }, - }, - { - statement: `set(attributes["test"], Nanoseconds(Duration("1ms")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutInt("test", 1000000) - }, - }, - { - statement: `set(attributes["test"], "pass") where Now() - Now() < Duration("1h")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], ParseCSV("val1;val2;val3","header1|header2|header3",";","|","strict"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("header1", "val1") - m.PutStr("header2", "val2") - m.PutStr("header3", "val3") - }, - }, - { - statement: `set(attributes["test"], ParseCSV("val1,val2,val3","header1|header2|header3",headerDelimiter="|",mode="strict"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("header1", "val1") - m.PutStr("header2", "val2") - m.PutStr("header3", "val3") - }, - }, - { - statement: `set(attributes["test"], ParseJSON("{\"id\":1}"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutDouble("id", 1) - }, - }, - { - statement: `set(attributes["test"], ParseJSON("[\"value1\",\"value2\"]"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - m.AppendEmpty().SetStr("value1") - m.AppendEmpty().SetStr("value2") - }, - }, - { - statement: `set(attributes["test"], ParseKeyValue("k1=v1 k2=v2"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("k1", "v1") - m.PutStr("k2", "v2") - }, - }, - { - statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!v2", "!", "_"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("k1", "v1") - m.PutStr("k2", "v2") - }, - }, - { - statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!\"v2__!__v2\"", "!", "_"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("k1", "v1") - m.PutStr("k2", "v2__!__v2") - }, - }, - { - statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "=", " ", true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=v2") - }, - }, - { - statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1:v1,k2:v2", ":" , ","), ":", ",", true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "k1:v1,k2:v2") - }, - }, - { - statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "!", "+", true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "k1!v1+k2!v2") - }, - }, - { - statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2=v3"), "=", " ", true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=\"v2=v3\"") - }, - }, - { - statement: `set(attributes["test"], ParseSimplifiedXML("1This is a log message!"))`, - want: func(tCtx ottllog.TransformContext) { - attr := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - log := attr.PutEmptyMap("Log") - log.PutStr("id", "1") - log.PutStr("Message", "This is a log message!") - }, - }, - { - statement: `set(attributes["test"], ParseXML("This is a log message!"))`, - want: func(tCtx ottllog.TransformContext) { - log := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - log.PutStr("tag", "Log") + // { + // statement: `set(attributes["test"], Decode("cGFzcw==", "base64"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], Concat(["A","B"], ":"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "A:B") + // }, + // }, + // { + // statement: `set(attributes["test"], ConvertCase(attributes["http.method"], "upper"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", http.MethodGet) + // }, + // }, + // { + // statement: `set(attributes["test"], ConvertCase("PASS", "lower"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], ConvertCase("fooBar", "snake"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "foo_bar") + // }, + // }, + // { + // statement: `set(attributes["test"], ConvertCase("foo_bar", "camel"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "FooBar") + // }, + // }, + // { + // statement: `set(attributes["test"], ConvertAttributesToElementsXML("This is a log message!"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", `This is a log message!1`) + // }, + // }, + // { + // statement: `set(body, ConvertTextToElementsXML("foo"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Body().SetStr("foo") + // }, + // }, + // { + // statement: `set(body, ConvertTextToElementsXML("foobar", "/a", "custom"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Body().SetStr("foobar") + // }, + // }, + // { + // statement: `set(attributes["test"], Double(1.0))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + // }, + // }, + // { + // statement: `set(attributes["test"], Double("1"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + // }, + // }, + // { + // statement: `set(attributes["test"], Double(true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + // }, + // }, + // { + // statement: `set(attributes["test"], Double(1))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where Time("10", "%M") - Time("01", "%M") < Duration("10m")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], ExtractPatterns("aa123bb", "(?P\\d+)"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("numbers", "123") + // }, + // }, + // { + // statement: `set(attributes["test"], ExtractGrokPatterns("http://user:password@example.com:80/path?query=string", "%{ELB_URI}", true))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("url.scheme", "http") + // m.PutStr("url.username", "user") + // m.PutStr("url.domain", "example.com") + // m.PutInt("url.port", 80) + // m.PutStr("url.path", "/path") + // m.PutStr("url.query", "query=string") + // }, + // }, + // { + // statement: `set(attributes["test"], FNV("pass"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 266877920130663416) + // }, + // }, + // { + // statement: `set(attributes["test"], Format("%03d-%s", [7, "test"]))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "007-test") + // }, + // }, + // { + // statement: `set(attributes["test"], Hour(Time("12", "%H")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 12) + // }, + // }, + // { + // statement: `set(attributes["test"], Hours(Duration("90m")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.5) + // }, + // }, + // { + // statement: `set(attributes["test"], InsertXML("", "/a", ""))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "") + // }, + // }, + // { + // statement: `set(attributes["test"], Int(1.0))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1) + // }, + // }, + // { + // statement: `set(attributes["test"], Int("1"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1) + // }, + // }, + // { + // statement: `set(attributes["test"], Int(true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1) + // }, + // }, + // { + // statement: `set(attributes["test"], Int(1))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1) + // }, + // }, + // { + // statement: `set(attributes["test"], GetXML("12", "/a//b"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "12") + // }, + // }, + // { + // statement: `set(attributes["test"], Hex(1.0))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "3ff0000000000000") + // }, + // }, + // { + // statement: `set(attributes["test"], Hex(true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "01") + // }, + // }, + // { + // statement: `set(attributes["test"], Hex(12))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "000000000000000c") + // }, + // }, + // { + // statement: `set(attributes["test"], Hex("12"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "3132") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsBool(false)`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsDouble(1.0)`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsMap(attributes["foo"])`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsList(attributes["foo"]["slice"])`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsMatch("aa123bb", "\\d{3}")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsString("")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], Len(attributes["foo"]))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 4) + // }, + // }, + // { + // statement: `set(attributes["test"], Log(1))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 0) + // }, + // }, + // { + // statement: `set(attributes["test"], MD5("pass"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "1a1dc91c907325c69271ddf0c944bc72") + // }, + // }, + // { + // statement: `set(attributes["test"], Microseconds(Duration("1ms")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1000) + // }, + // }, + // { + // statement: `set(attributes["test"], Milliseconds(Duration("1s")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1000) + // }, + // }, + // { + // statement: `set(attributes["test"], Minutes(Duration("1h")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 60) + // }, + // }, + // { + // statement: `set(attributes["test"], Nanoseconds(Duration("1ms")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutInt("test", 1000000) + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where Now() - Now() < Duration("1h")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseCSV("val1;val2;val3","header1|header2|header3",";","|","strict"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("header1", "val1") + // m.PutStr("header2", "val2") + // m.PutStr("header3", "val3") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseCSV("val1,val2,val3","header1|header2|header3",headerDelimiter="|",mode="strict"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("header1", "val1") + // m.PutStr("header2", "val2") + // m.PutStr("header3", "val3") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseJSON("{\"id\":1}"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutDouble("id", 1) + // }, + // }, + // { + // statement: `set(attributes["test"], ParseJSON("[\"value1\",\"value2\"]"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // m.AppendEmpty().SetStr("value1") + // m.AppendEmpty().SetStr("value2") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseKeyValue("k1=v1 k2=v2"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("k1", "v1") + // m.PutStr("k2", "v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!v2", "!", "_"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("k1", "v1") + // m.PutStr("k2", "v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!\"v2__!__v2\"", "!", "_"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("k1", "v1") + // m.PutStr("k2", "v2__!__v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "=", " ", true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1:v1,k2:v2", ":" , ","), ":", ",", true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "k1:v1,k2:v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "!", "+", true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "k1!v1+k2!v2") + // }, + // }, + // { + // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2=v3"), "=", " ", true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=\"v2=v3\"") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseSimplifiedXML("1This is a log message!"))`, + // want: func(tCtx ottllog.TransformContext) { + // attr := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // log := attr.PutEmptyMap("Log") + // log.PutStr("id", "1") + // log.PutStr("Message", "This is a log message!") + // }, + // }, + // { + // statement: `set(attributes["test"], ParseXML("This is a log message!"))`, + // want: func(tCtx ottllog.TransformContext) { + // log := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // log.PutStr("tag", "Log") - attrs := log.PutEmptyMap("attributes") - attrs.PutStr("id", "1") + // attrs := log.PutEmptyMap("attributes") + // attrs.PutStr("id", "1") - logChildren := log.PutEmptySlice("children") + // logChildren := log.PutEmptySlice("children") - message := logChildren.AppendEmpty().SetEmptyMap() - message.PutStr("tag", "Message") - message.PutStr("content", "This is a log message!") - }, - }, - { - statement: `set(attributes["test"], RemoveXML("This is a log message!", "/Log/Message"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", ``) - }, - }, - { - statement: `set(attributes["test"], Seconds(Duration("1m")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutDouble("test", 60) - }, - }, - { - statement: `set(attributes["test"], SHA1("pass"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684") - }, - }, - { - statement: `set(attributes["test"], SHA256("pass"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "d74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1") - }, - }, - { - statement: `set(attributes["test"], SHA512("pass"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "5b722b307fce6c944905d132691d5e4a2214b7fe92b738920eb3fce3a90420a19511c3010a0e7712b054daef5b57bad59ecbd93b3280f210578f547f4aed4d25") - }, - }, - { - statement: `set(attributes["test"], Sort(Split(attributes["flags"], "|"), "desc"))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetStr("C") - s.AppendEmpty().SetStr("B") - s.AppendEmpty().SetStr("A") - }, - }, - { - statement: `set(attributes["test"], Sort([true, false, false]))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetBool(false) - s.AppendEmpty().SetBool(false) - s.AppendEmpty().SetBool(true) - }, - }, - { - statement: `set(attributes["test"], Sort([3, 6, 9], "desc"))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetInt(9) - s.AppendEmpty().SetInt(6) - s.AppendEmpty().SetInt(3) - }, - }, - { - statement: `set(attributes["test"], Sort([Double(1.5), Double(10.2), Double(2.3), Double(0.5)]))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetDouble(0.5) - s.AppendEmpty().SetDouble(1.5) - s.AppendEmpty().SetDouble(2.3) - s.AppendEmpty().SetDouble(10.2) - }, - }, - { - statement: `set(attributes["test"], Sort([Int(11), Double(2.2), Double(-1)]))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetDouble(-1) - s.AppendEmpty().SetDouble(2.2) - s.AppendEmpty().SetInt(11) - }, - }, - { - statement: `set(attributes["test"], Sort([false, Int(11), Double(2.2), "three"]))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetInt(11) - s.AppendEmpty().SetDouble(2.2) - s.AppendEmpty().SetBool(false) - s.AppendEmpty().SetStr("three") - }, - }, - { - statement: `set(span_id, SpanID(0x0000000000000000))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().SetSpanID(pcommon.NewSpanIDEmpty()) - }, - }, - { - statement: `set(attributes["test"], Split(attributes["flags"], "|"))`, - want: func(tCtx ottllog.TransformContext) { - s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - s.AppendEmpty().SetStr("A") - s.AppendEmpty().SetStr("B") - s.AppendEmpty().SetStr("C") - }, - }, - { - statement: `set(attributes["test"], String("test"))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "test") - }, - }, - { - statement: `set(attributes["test"], String(attributes["http.method"]))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "get") - }, - }, - { - statement: `set(attributes["test"], String(span_id))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3,4,5,6,7,8]") - }, - }, - { - statement: `set(attributes["test"], String([1,2,3]))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3]") - }, - }, - { - statement: `set(attributes["test"], String(true))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "true") - }, - }, - { - statement: `set(attributes["test"], Substring("pass", 0, 2))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pa") - }, - }, - { - statement: `set(trace_id, TraceID(0x00000000000000000000000000000000))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().SetTraceID(pcommon.NewTraceIDEmpty()) - }, - }, - { - statement: `set(time, TruncateTime(time, Duration("1s")))`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().SetTimestamp(pcommon.NewTimestampFromTime(TestLogTimestamp.AsTime().Truncate(time.Second))) - }, - }, - { - statement: `set(attributes["test"], "pass") where UnixMicro(time) > 0`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where UnixMilli(time) > 0`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where UnixNano(time) > 0`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where UnixSeconds(time) > 0`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "pass") where IsString(UUID())`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - }, - }, - { - statement: `set(attributes["test"], "\\")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "\\") - }, - }, - { - statement: `set(attributes["test"], "\\\\")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\") - }, - }, - { - statement: `set(attributes["test"], "\\\\\\")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\") - }, - }, - { - statement: `set(attributes["test"], "\\\\\\\\")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\\\") - }, - }, - { - statement: `set(attributes["test"], "\"")`, - want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", `"`) - }, - }, - { - statement: `keep_keys(attributes["foo"], ["\\", "bar"])`, - want: func(tCtx ottllog.TransformContext) { - // keep_keys should see two arguments - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("foo") - m.PutStr("bar", "pass") - }, - }, - { - statement: `set(attributes["test"], UserAgent("curl/7.81.0"))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutStr("user_agent.original", "curl/7.81.0") - m.PutStr("user_agent.name", "curl") - m.PutStr("user_agent.version", "7.81.0") - }, - }, - { - statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"]))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - thing1 := m.PutEmptyMap("foo") - thing1.PutStr("name", "foo") - thing1.PutInt("value", 2) + // message := logChildren.AppendEmpty().SetEmptyMap() + // message.PutStr("tag", "Message") + // message.PutStr("content", "This is a log message!") + // }, + // }, + // { + // statement: `set(attributes["test"], RemoveXML("This is a log message!", "/Log/Message"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", ``) + // }, + // }, + // { + // statement: `set(attributes["test"], Seconds(Duration("1m")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutDouble("test", 60) + // }, + // }, + // { + // statement: `set(attributes["test"], SHA1("pass"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684") + // }, + // }, + // { + // statement: `set(attributes["test"], SHA256("pass"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "d74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1") + // }, + // }, + // { + // statement: `set(attributes["test"], SHA512("pass"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "5b722b307fce6c944905d132691d5e4a2214b7fe92b738920eb3fce3a90420a19511c3010a0e7712b054daef5b57bad59ecbd93b3280f210578f547f4aed4d25") + // }, + // }, + // { + // statement: `set(attributes["test"], Sort(Split(attributes["flags"], "|"), "desc"))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetStr("C") + // s.AppendEmpty().SetStr("B") + // s.AppendEmpty().SetStr("A") + // }, + // }, + // { + // statement: `set(attributes["test"], Sort([true, false, false]))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetBool(false) + // s.AppendEmpty().SetBool(false) + // s.AppendEmpty().SetBool(true) + // }, + // }, + // { + // statement: `set(attributes["test"], Sort([3, 6, 9], "desc"))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetInt(9) + // s.AppendEmpty().SetInt(6) + // s.AppendEmpty().SetInt(3) + // }, + // }, + // { + // statement: `set(attributes["test"], Sort([Double(1.5), Double(10.2), Double(2.3), Double(0.5)]))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetDouble(0.5) + // s.AppendEmpty().SetDouble(1.5) + // s.AppendEmpty().SetDouble(2.3) + // s.AppendEmpty().SetDouble(10.2) + // }, + // }, + // { + // statement: `set(attributes["test"], Sort([Int(11), Double(2.2), Double(-1)]))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetDouble(-1) + // s.AppendEmpty().SetDouble(2.2) + // s.AppendEmpty().SetInt(11) + // }, + // }, + // { + // statement: `set(attributes["test"], Sort([false, Int(11), Double(2.2), "three"]))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetInt(11) + // s.AppendEmpty().SetDouble(2.2) + // s.AppendEmpty().SetBool(false) + // s.AppendEmpty().SetStr("three") + // }, + // }, + // { + // statement: `set(span_id, SpanID(0x0000000000000000))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().SetSpanID(pcommon.NewSpanIDEmpty()) + // }, + // }, + // { + // statement: `set(attributes["test"], Split(attributes["flags"], "|"))`, + // want: func(tCtx ottllog.TransformContext) { + // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + // s.AppendEmpty().SetStr("A") + // s.AppendEmpty().SetStr("B") + // s.AppendEmpty().SetStr("C") + // }, + // }, + // { + // statement: `set(attributes["test"], String("test"))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "test") + // }, + // }, + // { + // statement: `set(attributes["test"], String(attributes["http.method"]))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "get") + // }, + // }, + // { + // statement: `set(attributes["test"], String(span_id))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3,4,5,6,7,8]") + // }, + // }, + // { + // statement: `set(attributes["test"], String([1,2,3]))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3]") + // }, + // }, + // { + // statement: `set(attributes["test"], String(true))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "true") + // }, + // }, + // { + // statement: `set(attributes["test"], Substring("pass", 0, 2))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pa") + // }, + // }, + // { + // statement: `set(trace_id, TraceID(0x00000000000000000000000000000000))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().SetTraceID(pcommon.NewTraceIDEmpty()) + // }, + // }, + // { + // statement: `set(time, TruncateTime(time, Duration("1s")))`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().SetTimestamp(pcommon.NewTimestampFromTime(TestLogTimestamp.AsTime().Truncate(time.Second))) + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where UnixMicro(time) > 0`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where UnixMilli(time) > 0`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where UnixNano(time) > 0`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where UnixSeconds(time) > 0`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "pass") where IsString(UUID())`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], "\\")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "\\") + // }, + // }, + // { + // statement: `set(attributes["test"], "\\\\")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\") + // }, + // }, + // { + // statement: `set(attributes["test"], "\\\\\\")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\") + // }, + // }, + // { + // statement: `set(attributes["test"], "\\\\\\\\")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\\\") + // }, + // }, + // { + // statement: `set(attributes["test"], "\"")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("test", `"`) + // }, + // }, + // { + // statement: `keep_keys(attributes["foo"], ["\\", "bar"])`, + // want: func(tCtx ottllog.TransformContext) { + // // keep_keys should see two arguments + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("foo") + // m.PutStr("bar", "pass") + // }, + // }, + // { + // statement: `set(attributes["test"], UserAgent("curl/7.81.0"))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutStr("user_agent.original", "curl/7.81.0") + // m.PutStr("user_agent.name", "curl") + // m.PutStr("user_agent.version", "7.81.0") + // }, + // }, + // { + // statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"]))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // thing1 := m.PutEmptyMap("foo") + // thing1.PutStr("name", "foo") + // thing1.PutInt("value", 2) - thing2 := m.PutEmptyMap("bar") - thing2.PutStr("name", "bar") - thing2.PutInt("value", 5) - }, - }, - { - statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"], ["value"]))`, - want: func(tCtx ottllog.TransformContext) { - m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - m.PutInt("foo", 2) - m.PutInt("bar", 5) - }, - }, + // thing2 := m.PutEmptyMap("bar") + // thing2.PutStr("name", "bar") + // thing2.PutInt("value", 5) + // }, + // }, + // { + // statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"], ["value"]))`, + // want: func(tCtx ottllog.TransformContext) { + // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + // m.PutInt("foo", 2) + // m.PutInt("bar", 5) + // }, + // }, } for _, tt := range tests { @@ -967,7 +967,7 @@ func Test_e2e_converters(t *testing.T) { logParser, err := ottllog.NewParser(ottlfuncs.StandardFuncs[ottllog.TransformContext](), settings) assert.NoError(t, err) logStatements, err := logParser.ParseStatement(tt.statement) - assert.NoError(t, err) + require.Nil(t, err) tCtx := constructLogTransformContext() _, _, _ = logStatements.Execute(context.Background(), tCtx) diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 4ff92123c7e6..1199423c81f3 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -481,18 +481,29 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { case strings.HasPrefix(name, "Setter"): fallthrough case strings.HasPrefix(name, "GetSetter"): - if argVal.Literal == nil || argVal.Literal.Path == nil { - return nil, fmt.Errorf("must be a path") - } - np, err := p.newPath(argVal.Literal.Path) - if err != nil { - return nil, err + if argVal.Literal != nil && argVal.Literal.Path != nil { + np, err := p.newPath(argVal.Literal.Path) + if err != nil { + return nil, err + } + arg, err := p.parsePath(np) + if err != nil { + return nil, err + } + return arg, nil } - arg, err := p.parsePath(np) - if err != nil { - return nil, err + if argVal.ExpressionPath != nil { + np, err := p.newPath(argVal.ExpressionPath) + if err != nil { + return nil, err + } + arg, err := p.parsePath(np) + if err != nil { + return nil, err + } + return arg, nil } - return arg, nil + return nil, fmt.Errorf("must be a path") case strings.HasPrefix(name, "Getter"): arg, err := p.newGetter(argVal) if err != nil { diff --git a/pkg/ottl/grammar.go b/pkg/ottl/grammar.go index a1e5eb53a81d..393ca2a100f8 100644 --- a/pkg/ottl/grammar.go +++ b/pkg/ottl/grammar.go @@ -234,6 +234,7 @@ func (a *argument) accept(v grammarVisitor) { // mathExpression, function call, or literal. type value struct { IsNil *isNil `parser:"( @'nil'"` + ExpressionPath *path `parser:"| '$''{' @@ '}'"` Literal *mathExprLiteral `parser:"| @@ (?! OpAddSub | OpMultDiv)"` MathExpression *mathExpression `parser:"| @@"` Bytes *byteSlice `parser:"| @Bytes"` @@ -260,6 +261,9 @@ func (v *value) accept(vis grammarVisitor) { i.accept(vis) } } + if v.ExpressionPath != nil { + vis.visitPath(v.ExpressionPath) + } } // path represents a telemetry path mathExpression. @@ -486,6 +490,7 @@ func buildLexer() *lexer.StatefulDefinition { {Name: `LBrace`, Pattern: `\{`}, {Name: `RBrace`, Pattern: `\}`}, {Name: `Colon`, Pattern: `\:`}, + {Name: `Dollar`, Pattern: `\$`}, {Name: `Punct`, Pattern: `[,.\[\]]`}, {Name: `Uppercase`, Pattern: `[A-Z][A-Z0-9_]*`}, {Name: `Lowercase`, Pattern: `[a-z][a-z0-9_]*`}, diff --git a/pkg/ottl/lexer_test.go b/pkg/ottl/lexer_test.go index b73d71d80e8f..927dab112e84 100644 --- a/pkg/ottl/lexer_test.go +++ b/pkg/ottl/lexer_test.go @@ -130,6 +130,15 @@ func Test_lexer(t *testing.T) { {"String", `"bar"`}, {"RBrace", "}"}, }}, + {"Expression path", `${foo["foo"]}`, false, []result{ + {"Dollar", "$"}, + {"LBrace", "{"}, + {"Lowercase", "foo"}, + {"Punct", "["}, + {"String", `"foo"`}, + {"Punct", "]"}, + {"RBrace", "}"}, + }}, } for _, tt := range tests { diff --git a/pkg/ottl/ottlfuncs/func_set.go b/pkg/ottl/ottlfuncs/func_set.go index a0ce390a4788..4341fca9e034 100644 --- a/pkg/ottl/ottlfuncs/func_set.go +++ b/pkg/ottl/ottlfuncs/func_set.go @@ -11,7 +11,7 @@ import ( ) type SetArguments[K any] struct { - Target ottl.Setter[K] + Target ottl.GetSetter[K] Value ottl.Getter[K] } @@ -29,7 +29,7 @@ func createSetFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (ott return set(args.Target, args.Value), nil } -func set[K any](target ottl.Setter[K], value ottl.Getter[K]) ottl.ExprFunc[K] { +func set[K any](target ottl.GetSetter[K], value ottl.Getter[K]) ottl.ExprFunc[K] { return func(ctx context.Context, tCtx K) (any, error) { val, err := value.Get(ctx, tCtx) if err != nil { From 02b72ecd427cf8a80d9206ca27dfc8e104d1bb95 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Fri, 13 Dec 2024 06:43:51 +0100 Subject: [PATCH 02/20] different approach in parser Signed-off-by: odubajDT --- pkg/ottl/e2e/e2e_test.go | 628 +-------------------------------------- pkg/ottl/expression.go | 5 + pkg/ottl/functions.go | 22 +- pkg/ottl/grammar.go | 8 +- pkg/ottl/lexer_test.go | 10 +- 5 files changed, 25 insertions(+), 648 deletions(-) diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 77c948211784..618aa8f4cb43 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -330,635 +330,11 @@ func Test_e2e_converters(t *testing.T) { want func(tCtx ottllog.TransformContext) }{ { - statement: `set(${attributes["test"]}, Base64Decode("cGFzcw=="))`, + statement: `set(attributes[attributes["flags"]], attributes["total.string"])`, want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "123456789") }, }, - // { - // statement: `set(attributes["test"], Decode("cGFzcw==", "base64"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], Concat(["A","B"], ":"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "A:B") - // }, - // }, - // { - // statement: `set(attributes["test"], ConvertCase(attributes["http.method"], "upper"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", http.MethodGet) - // }, - // }, - // { - // statement: `set(attributes["test"], ConvertCase("PASS", "lower"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], ConvertCase("fooBar", "snake"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "foo_bar") - // }, - // }, - // { - // statement: `set(attributes["test"], ConvertCase("foo_bar", "camel"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "FooBar") - // }, - // }, - // { - // statement: `set(attributes["test"], ConvertAttributesToElementsXML("This is a log message!"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", `This is a log message!1`) - // }, - // }, - // { - // statement: `set(body, ConvertTextToElementsXML("foo"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Body().SetStr("foo") - // }, - // }, - // { - // statement: `set(body, ConvertTextToElementsXML("foobar", "/a", "custom"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Body().SetStr("foobar") - // }, - // }, - // { - // statement: `set(attributes["test"], Double(1.0))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - // }, - // }, - // { - // statement: `set(attributes["test"], Double("1"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - // }, - // }, - // { - // statement: `set(attributes["test"], Double(true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - // }, - // }, - // { - // statement: `set(attributes["test"], Double(1))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where Time("10", "%M") - Time("01", "%M") < Duration("10m")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], ExtractPatterns("aa123bb", "(?P\\d+)"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("numbers", "123") - // }, - // }, - // { - // statement: `set(attributes["test"], ExtractGrokPatterns("http://user:password@example.com:80/path?query=string", "%{ELB_URI}", true))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("url.scheme", "http") - // m.PutStr("url.username", "user") - // m.PutStr("url.domain", "example.com") - // m.PutInt("url.port", 80) - // m.PutStr("url.path", "/path") - // m.PutStr("url.query", "query=string") - // }, - // }, - // { - // statement: `set(attributes["test"], FNV("pass"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 266877920130663416) - // }, - // }, - // { - // statement: `set(attributes["test"], Format("%03d-%s", [7, "test"]))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "007-test") - // }, - // }, - // { - // statement: `set(attributes["test"], Hour(Time("12", "%H")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 12) - // }, - // }, - // { - // statement: `set(attributes["test"], Hours(Duration("90m")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 1.5) - // }, - // }, - // { - // statement: `set(attributes["test"], InsertXML("", "/a", ""))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "") - // }, - // }, - // { - // statement: `set(attributes["test"], Int(1.0))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1) - // }, - // }, - // { - // statement: `set(attributes["test"], Int("1"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1) - // }, - // }, - // { - // statement: `set(attributes["test"], Int(true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1) - // }, - // }, - // { - // statement: `set(attributes["test"], Int(1))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1) - // }, - // }, - // { - // statement: `set(attributes["test"], GetXML("12", "/a//b"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "12") - // }, - // }, - // { - // statement: `set(attributes["test"], Hex(1.0))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "3ff0000000000000") - // }, - // }, - // { - // statement: `set(attributes["test"], Hex(true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "01") - // }, - // }, - // { - // statement: `set(attributes["test"], Hex(12))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "000000000000000c") - // }, - // }, - // { - // statement: `set(attributes["test"], Hex("12"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "3132") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsBool(false)`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsDouble(1.0)`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsMap(attributes["foo"])`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsList(attributes["foo"]["slice"])`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsMatch("aa123bb", "\\d{3}")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsString("")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], Len(attributes["foo"]))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 4) - // }, - // }, - // { - // statement: `set(attributes["test"], Log(1))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 0) - // }, - // }, - // { - // statement: `set(attributes["test"], MD5("pass"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "1a1dc91c907325c69271ddf0c944bc72") - // }, - // }, - // { - // statement: `set(attributes["test"], Microseconds(Duration("1ms")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1000) - // }, - // }, - // { - // statement: `set(attributes["test"], Milliseconds(Duration("1s")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1000) - // }, - // }, - // { - // statement: `set(attributes["test"], Minutes(Duration("1h")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 60) - // }, - // }, - // { - // statement: `set(attributes["test"], Nanoseconds(Duration("1ms")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutInt("test", 1000000) - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where Now() - Now() < Duration("1h")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseCSV("val1;val2;val3","header1|header2|header3",";","|","strict"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("header1", "val1") - // m.PutStr("header2", "val2") - // m.PutStr("header3", "val3") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseCSV("val1,val2,val3","header1|header2|header3",headerDelimiter="|",mode="strict"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("header1", "val1") - // m.PutStr("header2", "val2") - // m.PutStr("header3", "val3") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseJSON("{\"id\":1}"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutDouble("id", 1) - // }, - // }, - // { - // statement: `set(attributes["test"], ParseJSON("[\"value1\",\"value2\"]"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // m.AppendEmpty().SetStr("value1") - // m.AppendEmpty().SetStr("value2") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseKeyValue("k1=v1 k2=v2"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("k1", "v1") - // m.PutStr("k2", "v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!v2", "!", "_"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("k1", "v1") - // m.PutStr("k2", "v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!\"v2__!__v2\"", "!", "_"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("k1", "v1") - // m.PutStr("k2", "v2__!__v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "=", " ", true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1:v1,k2:v2", ":" , ","), ":", ",", true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "k1:v1,k2:v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "!", "+", true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "k1!v1+k2!v2") - // }, - // }, - // { - // statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2=v3"), "=", " ", true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=\"v2=v3\"") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseSimplifiedXML("1This is a log message!"))`, - // want: func(tCtx ottllog.TransformContext) { - // attr := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // log := attr.PutEmptyMap("Log") - // log.PutStr("id", "1") - // log.PutStr("Message", "This is a log message!") - // }, - // }, - // { - // statement: `set(attributes["test"], ParseXML("This is a log message!"))`, - // want: func(tCtx ottllog.TransformContext) { - // log := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // log.PutStr("tag", "Log") - - // attrs := log.PutEmptyMap("attributes") - // attrs.PutStr("id", "1") - - // logChildren := log.PutEmptySlice("children") - - // message := logChildren.AppendEmpty().SetEmptyMap() - // message.PutStr("tag", "Message") - // message.PutStr("content", "This is a log message!") - // }, - // }, - // { - // statement: `set(attributes["test"], RemoveXML("This is a log message!", "/Log/Message"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", ``) - // }, - // }, - // { - // statement: `set(attributes["test"], Seconds(Duration("1m")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutDouble("test", 60) - // }, - // }, - // { - // statement: `set(attributes["test"], SHA1("pass"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684") - // }, - // }, - // { - // statement: `set(attributes["test"], SHA256("pass"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "d74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1") - // }, - // }, - // { - // statement: `set(attributes["test"], SHA512("pass"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "5b722b307fce6c944905d132691d5e4a2214b7fe92b738920eb3fce3a90420a19511c3010a0e7712b054daef5b57bad59ecbd93b3280f210578f547f4aed4d25") - // }, - // }, - // { - // statement: `set(attributes["test"], Sort(Split(attributes["flags"], "|"), "desc"))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetStr("C") - // s.AppendEmpty().SetStr("B") - // s.AppendEmpty().SetStr("A") - // }, - // }, - // { - // statement: `set(attributes["test"], Sort([true, false, false]))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetBool(false) - // s.AppendEmpty().SetBool(false) - // s.AppendEmpty().SetBool(true) - // }, - // }, - // { - // statement: `set(attributes["test"], Sort([3, 6, 9], "desc"))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetInt(9) - // s.AppendEmpty().SetInt(6) - // s.AppendEmpty().SetInt(3) - // }, - // }, - // { - // statement: `set(attributes["test"], Sort([Double(1.5), Double(10.2), Double(2.3), Double(0.5)]))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetDouble(0.5) - // s.AppendEmpty().SetDouble(1.5) - // s.AppendEmpty().SetDouble(2.3) - // s.AppendEmpty().SetDouble(10.2) - // }, - // }, - // { - // statement: `set(attributes["test"], Sort([Int(11), Double(2.2), Double(-1)]))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetDouble(-1) - // s.AppendEmpty().SetDouble(2.2) - // s.AppendEmpty().SetInt(11) - // }, - // }, - // { - // statement: `set(attributes["test"], Sort([false, Int(11), Double(2.2), "three"]))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetInt(11) - // s.AppendEmpty().SetDouble(2.2) - // s.AppendEmpty().SetBool(false) - // s.AppendEmpty().SetStr("three") - // }, - // }, - // { - // statement: `set(span_id, SpanID(0x0000000000000000))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().SetSpanID(pcommon.NewSpanIDEmpty()) - // }, - // }, - // { - // statement: `set(attributes["test"], Split(attributes["flags"], "|"))`, - // want: func(tCtx ottllog.TransformContext) { - // s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") - // s.AppendEmpty().SetStr("A") - // s.AppendEmpty().SetStr("B") - // s.AppendEmpty().SetStr("C") - // }, - // }, - // { - // statement: `set(attributes["test"], String("test"))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "test") - // }, - // }, - // { - // statement: `set(attributes["test"], String(attributes["http.method"]))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "get") - // }, - // }, - // { - // statement: `set(attributes["test"], String(span_id))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3,4,5,6,7,8]") - // }, - // }, - // { - // statement: `set(attributes["test"], String([1,2,3]))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3]") - // }, - // }, - // { - // statement: `set(attributes["test"], String(true))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "true") - // }, - // }, - // { - // statement: `set(attributes["test"], Substring("pass", 0, 2))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pa") - // }, - // }, - // { - // statement: `set(trace_id, TraceID(0x00000000000000000000000000000000))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().SetTraceID(pcommon.NewTraceIDEmpty()) - // }, - // }, - // { - // statement: `set(time, TruncateTime(time, Duration("1s")))`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().SetTimestamp(pcommon.NewTimestampFromTime(TestLogTimestamp.AsTime().Truncate(time.Second))) - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where UnixMicro(time) > 0`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where UnixMilli(time) > 0`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where UnixNano(time) > 0`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where UnixSeconds(time) > 0`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "pass") where IsString(UUID())`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], "\\")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "\\") - // }, - // }, - // { - // statement: `set(attributes["test"], "\\\\")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\") - // }, - // }, - // { - // statement: `set(attributes["test"], "\\\\\\")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\") - // }, - // }, - // { - // statement: `set(attributes["test"], "\\\\\\\\")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\\\") - // }, - // }, - // { - // statement: `set(attributes["test"], "\"")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("test", `"`) - // }, - // }, - // { - // statement: `keep_keys(attributes["foo"], ["\\", "bar"])`, - // want: func(tCtx ottllog.TransformContext) { - // // keep_keys should see two arguments - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("foo") - // m.PutStr("bar", "pass") - // }, - // }, - // { - // statement: `set(attributes["test"], UserAgent("curl/7.81.0"))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutStr("user_agent.original", "curl/7.81.0") - // m.PutStr("user_agent.name", "curl") - // m.PutStr("user_agent.version", "7.81.0") - // }, - // }, - // { - // statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"]))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // thing1 := m.PutEmptyMap("foo") - // thing1.PutStr("name", "foo") - // thing1.PutInt("value", 2) - - // thing2 := m.PutEmptyMap("bar") - // thing2.PutStr("name", "bar") - // thing2.PutInt("value", 5) - // }, - // }, - // { - // statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"], ["value"]))`, - // want: func(tCtx ottllog.TransformContext) { - // m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") - // m.PutInt("foo", 2) - // m.PutInt("bar", 5) - // }, - // }, } for _, tt := range tests { diff --git a/pkg/ottl/expression.go b/pkg/ottl/expression.go index a2e8d29e63c4..91d7cb7bdc32 100644 --- a/pkg/ottl/expression.go +++ b/pkg/ottl/expression.go @@ -143,6 +143,11 @@ func (g exprGetter[K]) Get(ctx context.Context, tCtx K) (any, error) { default: return nil, fmt.Errorf("type, %T, does not support int indexing", result) } + case k.Path != nil: + // getter, err := p.newGetter(k.Path) + // if err != nil { + // return nil, err + // } default: return nil, fmt.Errorf("neither map nor slice index were set; this is an error in OTTL") } diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 1199423c81f3..184077053bd0 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -492,17 +492,17 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { } return arg, nil } - if argVal.ExpressionPath != nil { - np, err := p.newPath(argVal.ExpressionPath) - if err != nil { - return nil, err - } - arg, err := p.parsePath(np) - if err != nil { - return nil, err - } - return arg, nil - } + // if argVal.ExpressionPath != nil { + // np, err := p.newPath(argVal.ExpressionPath) + // if err != nil { + // return nil, err + // } + // arg, err := p.parsePath(np) + // if err != nil { + // return nil, err + // } + // return arg, nil + // } return nil, fmt.Errorf("must be a path") case strings.HasPrefix(name, "Getter"): arg, err := p.newGetter(argVal) diff --git a/pkg/ottl/grammar.go b/pkg/ottl/grammar.go index 393ca2a100f8..6e810195c7f6 100644 --- a/pkg/ottl/grammar.go +++ b/pkg/ottl/grammar.go @@ -234,7 +234,6 @@ func (a *argument) accept(v grammarVisitor) { // mathExpression, function call, or literal. type value struct { IsNil *isNil `parser:"( @'nil'"` - ExpressionPath *path `parser:"| '$''{' @@ '}'"` Literal *mathExprLiteral `parser:"| @@ (?! OpAddSub | OpMultDiv)"` MathExpression *mathExpression `parser:"| @@"` Bytes *byteSlice `parser:"| @Bytes"` @@ -261,9 +260,6 @@ func (v *value) accept(vis grammarVisitor) { i.accept(vis) } } - if v.ExpressionPath != nil { - vis.visitPath(v.ExpressionPath) - } } // path represents a telemetry path mathExpression. @@ -281,7 +277,8 @@ type field struct { type key struct { String *string `parser:"'[' (@String "` - Int *int64 `parser:"| @Int) ']'"` + Int *int64 `parser:"| @Int"` + Path *path `parser:"| @@ ) ']'"` } type list struct { @@ -490,7 +487,6 @@ func buildLexer() *lexer.StatefulDefinition { {Name: `LBrace`, Pattern: `\{`}, {Name: `RBrace`, Pattern: `\}`}, {Name: `Colon`, Pattern: `\:`}, - {Name: `Dollar`, Pattern: `\$`}, {Name: `Punct`, Pattern: `[,.\[\]]`}, {Name: `Uppercase`, Pattern: `[A-Z][A-Z0-9_]*`}, {Name: `Lowercase`, Pattern: `[a-z][a-z0-9_]*`}, diff --git a/pkg/ottl/lexer_test.go b/pkg/ottl/lexer_test.go index 927dab112e84..87aac86da80c 100644 --- a/pkg/ottl/lexer_test.go +++ b/pkg/ottl/lexer_test.go @@ -130,14 +130,14 @@ func Test_lexer(t *testing.T) { {"String", `"bar"`}, {"RBrace", "}"}, }}, - {"Expression path", `${foo["foo"]}`, false, []result{ - {"Dollar", "$"}, - {"LBrace", "{"}, - {"Lowercase", "foo"}, + {"Expression path", `attributes[attributes["foo"]]`, false, []result{ + {"Lowercase", "attributes"}, + {"Punct", "["}, + {"Lowercase", "attributes"}, {"Punct", "["}, {"String", `"foo"`}, {"Punct", "]"}, - {"RBrace", "}"}, + {"Punct", "]"}, }}, } From fda69bcf2c2cc08ad1e7af572153e78c7c1341d4 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Mon, 16 Dec 2024 15:35:31 +0100 Subject: [PATCH 03/20] working version Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 36 ++++++++++++++++- pkg/ottl/contexts/internal/path.go | 5 +++ pkg/ottl/contexts/ottllog/log.go | 12 ++++++ pkg/ottl/e2e/e2e_test.go | 4 +- pkg/ottl/functions.go | 61 ++++++++++++++++++++++------- pkg/ottl/functions_test.go | 44 ++++++++++----------- pkg/ottl/ottlfuncs/func_set_test.go | 2 +- 7 files changed, 122 insertions(+), 42 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index 34e891c032c1..783089203423 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -22,7 +22,23 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return nil, err } if s == nil { - return nil, fmt.Errorf("non-string indexing is not supported") + p, err := keys[0].Path() + if err != nil { + return nil, err + } + if p != nil { + res, err := GetMapValue[K](ctx, tCtx, m, p.Keys()) + if err != nil { + return nil, err + } + resString, ok := res.(string) + if !ok { + return nil, fmt.Errorf("err") + } + s = &resString + } else { + return nil, fmt.Errorf("non-string indexing is not supported") + } } val, ok := m.Get(*s) @@ -43,7 +59,23 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return err } if s == nil { - return fmt.Errorf("non-string indexing is not supported") + p, err := keys[0].Path() + if err != nil { + return err + } + if p != nil { + res, err := GetMapValue[K](ctx, tCtx, m, p.Keys()) + if err != nil { + return err + } + resString, ok := res.(string) + if !ok { + return fmt.Errorf("err") + } + s = &resString + } else { + return fmt.Errorf("non-string indexing is not supported") + } } currentValue, ok := m.Get(*s) diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index 954d14329646..08b25886b8f1 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -46,6 +46,7 @@ var _ ottl.Key[any] = &TestKey[any]{} type TestKey[K any] struct { S *string I *int64 + P *ottl.Path[K] } func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -55,3 +56,7 @@ func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) { func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.I, nil } + +func (k *TestKey[K]) Path() (ottl.Path[K], error) { + return *k.P, nil +} diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 3dde1bb851e6..27469229dfee 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -237,6 +237,18 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot if path.Keys() == nil { return accessAttributes(), nil } + // p, err := path.Keys()[0].Path() + // if err != nil { + // return nil, err + // } + // var subGetter ottl.GetSetter[TransformContext] + // if p.Keys() != nil { + // s, err := pep.parsePath(p) + // if err != nil { + // return nil, err + // } + // subGetter = s + // } return accessAttributesKey(path.Keys()), nil case "dropped_attributes_count": return accessDroppedAttributesCount(), nil diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 618aa8f4cb43..cab539369016 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -330,9 +330,9 @@ func Test_e2e_converters(t *testing.T) { want func(tCtx ottllog.TransformContext) }{ { - statement: `set(attributes[attributes["flags"]], attributes["total.string"])`, + statement: `set(attributes[attributes["flags"]], "something")`, want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "123456789") + tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "something") }, }, } diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 184077053bd0..7db9d1967f49 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -53,6 +53,9 @@ func buildOriginalKeysText(keys []key) string { if k.String != nil { builder.WriteString(*k.String) } + if k.Path != nil { + builder.WriteString(buildOriginalText(k.Path)) + } builder.WriteString("]") } } @@ -72,10 +75,14 @@ func (p *Parser[K]) newPath(path *path) (*basePath[K], error) { originalText := buildOriginalText(path) var current *basePath[K] for i := len(fields) - 1; i >= 0; i-- { + keys, err := p.newKeys(fields[i].Keys) + if err != nil { + return nil, err + } current = &basePath[K]{ context: pathContext, name: fields[i].Name, - keys: newKeys[K](fields[i].Keys), + keys: keys, nextPath: current, originalText: originalText, } @@ -203,18 +210,32 @@ func (p *basePath[K]) isComplete() error { return p.nextPath.isComplete() } -func newKeys[K any](keys []key) []Key[K] { +func (p *Parser[K]) newKeys(keys []key) ([]Key[K], error) { if len(keys) == 0 { - return nil + return nil, nil } ks := make([]Key[K], len(keys)) for i := range keys { + var par *basePath[K] + if keys[i].Path != nil { + pp, err := p.newPath(keys[i].Path) // newGetter here + if err != nil { + return nil, err + } + // arg, err := p.parsePath() + // if err != nil { + // return nil, err + // } + par = pp + } ks[i] = &baseKey[K]{ s: keys[i].String, i: keys[i].Int, + p: par, } + } - return ks + return ks, nil } // Key represents a chain of keys in an OTTL statement, such as `attributes["foo"]["bar"]`. @@ -230,6 +251,8 @@ type Key[K any] interface { // If the Key does not have a int value the returned value is nil. // If Key experiences an error retrieving the value it is returned. Int(context.Context, K) (*int64, error) + + Path() (Path[K], error) } var _ Key[any] = &baseKey[any]{} @@ -237,6 +260,7 @@ var _ Key[any] = &baseKey[any]{} type baseKey[K any] struct { s *string i *int64 + p *basePath[K] } func (k *baseKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -247,7 +271,24 @@ func (k *baseKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.i, nil } +func (k *baseKey[K]) Path() (Path[K], error) { + var path Path[K] + path = k.p + return path, nil +} + func (p *Parser[K]) parsePath(ip *basePath[K]) (GetSetter[K], error) { + // path, err := ip.Keys()[0].Path() + // if err != nil { + // return nil, err + // } + // if p != nil { + // s, err := p.parsePath(path.(*basePath[K])) + // if err != nil { + // return nil, err + // } + + // } g, err := p.pathParser(ip) if err != nil { return nil, err @@ -490,19 +531,9 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { if err != nil { return nil, err } + //tu return arg, nil } - // if argVal.ExpressionPath != nil { - // np, err := p.newPath(argVal.ExpressionPath) - // if err != nil { - // return nil, err - // } - // arg, err := p.parsePath(np) - // if err != nil { - // return nil, err - // } - // return arg, nil - // } return nil, fmt.Errorf("must be a path") case strings.HasPrefix(name, "Getter"): arg, err := p.newGetter(argVal) diff --git a/pkg/ottl/functions_test.go b/pkg/ottl/functions_test.go index a7bd4aef87c7..cbe95b45b22d 100644 --- a/pkg/ottl/functions_test.go +++ b/pkg/ottl/functions_test.go @@ -2524,25 +2524,25 @@ func Test_baseKey_Int(t *testing.T) { assert.Equal(t, int64(1), *i) } -func Test_newKey(t *testing.T) { - keys := []key{ - { - String: ottltest.Strp("foo"), - }, - { - String: ottltest.Strp("bar"), - }, - } - ks := newKeys[any](keys) - - assert.Len(t, ks, 2) - - s, err := ks[0].String(context.Background(), nil) - assert.NoError(t, err) - assert.NotNil(t, s) - assert.Equal(t, "foo", *s) - s, err = ks[1].String(context.Background(), nil) - assert.NoError(t, err) - assert.NotNil(t, s) - assert.Equal(t, "bar", *s) -} +// func Test_newKey(t *testing.T) { +// keys := []key{ +// { +// String: ottltest.Strp("foo"), +// }, +// { +// String: ottltest.Strp("bar"), +// }, +// } +// ks, _ := newKeys[any](keys) + +// assert.Len(t, ks, 2) + +// s, err := ks[0].String(context.Background(), nil) +// assert.NoError(t, err) +// assert.NotNil(t, s) +// assert.Equal(t, "foo", *s) +// s, err = ks[1].String(context.Background(), nil) +// assert.NoError(t, err) +// assert.NotNil(t, s) +// assert.Equal(t, "bar", *s) +// } diff --git a/pkg/ottl/ottlfuncs/func_set_test.go b/pkg/ottl/ottlfuncs/func_set_test.go index 7fbf3b2fc765..5bd910b551fe 100644 --- a/pkg/ottl/ottlfuncs/func_set_test.go +++ b/pkg/ottl/ottlfuncs/func_set_test.go @@ -25,7 +25,7 @@ func Test_set(t *testing.T) { tests := []struct { name string - setter ottl.Setter[pcommon.Value] + setter ottl.GetSetter[pcommon.Value] getter ottl.Getter[pcommon.Value] want func(pcommon.Value) }{ From 97b369c2a08c8d8878f83fefaf8e1535337b7f05 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Mon, 16 Dec 2024 16:03:58 +0100 Subject: [PATCH 04/20] polish Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 8 +++--- pkg/ottl/contexts/internal/path.go | 4 +-- pkg/ottl/functions.go | 43 ++++++++++++++---------------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index 783089203423..47683fdce4c0 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -22,12 +22,12 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return nil, err } if s == nil { - p, err := keys[0].Path() + p, err := keys[0].PathGetter() if err != nil { return nil, err } if p != nil { - res, err := GetMapValue[K](ctx, tCtx, m, p.Keys()) + res, err := p.Get(ctx, tCtx) if err != nil { return nil, err } @@ -59,12 +59,12 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return err } if s == nil { - p, err := keys[0].Path() + p, err := keys[0].PathGetter() if err != nil { return err } if p != nil { - res, err := GetMapValue[K](ctx, tCtx, m, p.Keys()) + res, err := p.Get(ctx, tCtx) if err != nil { return err } diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index 08b25886b8f1..eaa8532a7b7d 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -46,7 +46,7 @@ var _ ottl.Key[any] = &TestKey[any]{} type TestKey[K any] struct { S *string I *int64 - P *ottl.Path[K] + P *ottl.GetSetter[K] } func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -57,6 +57,6 @@ func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.I, nil } -func (k *TestKey[K]) Path() (ottl.Path[K], error) { +func (k *TestKey[K]) PathGetter() (ottl.GetSetter[K], error) { return *k.P, nil } diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 7db9d1967f49..adb103ae59f3 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -216,17 +216,13 @@ func (p *Parser[K]) newKeys(keys []key) ([]Key[K], error) { } ks := make([]Key[K], len(keys)) for i := range keys { - var par *basePath[K] + var par GetSetter[K] if keys[i].Path != nil { - pp, err := p.newPath(keys[i].Path) // newGetter here + arg, err := p.buildGetSetterFromPath(keys[i].Path) if err != nil { return nil, err } - // arg, err := p.parsePath() - // if err != nil { - // return nil, err - // } - par = pp + par = arg } ks[i] = &baseKey[K]{ s: keys[i].String, @@ -252,7 +248,7 @@ type Key[K any] interface { // If Key experiences an error retrieving the value it is returned. Int(context.Context, K) (*int64, error) - Path() (Path[K], error) + PathGetter() (GetSetter[K], error) } var _ Key[any] = &baseKey[any]{} @@ -260,7 +256,7 @@ var _ Key[any] = &baseKey[any]{} type baseKey[K any] struct { s *string i *int64 - p *basePath[K] + p GetSetter[K] } func (k *baseKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -271,10 +267,8 @@ func (k *baseKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.i, nil } -func (k *baseKey[K]) Path() (Path[K], error) { - var path Path[K] - path = k.p - return path, nil +func (k *baseKey[K]) PathGetter() (GetSetter[K], error) { + return k.p, nil } func (p *Parser[K]) parsePath(ip *basePath[K]) (GetSetter[K], error) { @@ -515,6 +509,18 @@ func (p *Parser[K]) buildSliceArg(argVal value, argType reflect.Type) (any, erro } } +func (p *Parser[K]) buildGetSetterFromPath(path *path) (GetSetter[K], error) { + np, err := p.newPath(path) + if err != nil { + return nil, err + } + arg, err := p.parsePath(np) + if err != nil { + return nil, err + } + return arg, nil +} + // Handle interfaces that can be passed as arguments to OTTL functions. func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { name := argType.Name() @@ -523,16 +529,7 @@ func (p *Parser[K]) buildArg(argVal value, argType reflect.Type) (any, error) { fallthrough case strings.HasPrefix(name, "GetSetter"): if argVal.Literal != nil && argVal.Literal.Path != nil { - np, err := p.newPath(argVal.Literal.Path) - if err != nil { - return nil, err - } - arg, err := p.parsePath(np) - if err != nil { - return nil, err - } - //tu - return arg, nil + return p.buildGetSetterFromPath(argVal.Literal.Path) } return nil, fmt.Errorf("must be a path") case strings.HasPrefix(name, "Getter"): From bc1dc8e328bf571c759b6e4051f3bbeda6217446 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Mon, 16 Dec 2024 16:06:47 +0100 Subject: [PATCH 05/20] remove dead code Signed-off-by: odubajDT --- pkg/ottl/contexts/ottllog/log.go | 12 ------------ pkg/ottl/expression.go | 5 ----- pkg/ottl/functions.go | 11 ----------- 3 files changed, 28 deletions(-) diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 27469229dfee..3dde1bb851e6 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -237,18 +237,6 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot if path.Keys() == nil { return accessAttributes(), nil } - // p, err := path.Keys()[0].Path() - // if err != nil { - // return nil, err - // } - // var subGetter ottl.GetSetter[TransformContext] - // if p.Keys() != nil { - // s, err := pep.parsePath(p) - // if err != nil { - // return nil, err - // } - // subGetter = s - // } return accessAttributesKey(path.Keys()), nil case "dropped_attributes_count": return accessDroppedAttributesCount(), nil diff --git a/pkg/ottl/expression.go b/pkg/ottl/expression.go index 91d7cb7bdc32..a2e8d29e63c4 100644 --- a/pkg/ottl/expression.go +++ b/pkg/ottl/expression.go @@ -143,11 +143,6 @@ func (g exprGetter[K]) Get(ctx context.Context, tCtx K) (any, error) { default: return nil, fmt.Errorf("type, %T, does not support int indexing", result) } - case k.Path != nil: - // getter, err := p.newGetter(k.Path) - // if err != nil { - // return nil, err - // } default: return nil, fmt.Errorf("neither map nor slice index were set; this is an error in OTTL") } diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index adb103ae59f3..3ba4678e0b70 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -272,17 +272,6 @@ func (k *baseKey[K]) PathGetter() (GetSetter[K], error) { } func (p *Parser[K]) parsePath(ip *basePath[K]) (GetSetter[K], error) { - // path, err := ip.Keys()[0].Path() - // if err != nil { - // return nil, err - // } - // if p != nil { - // s, err := p.parsePath(path.(*basePath[K])) - // if err != nil { - // return nil, err - // } - - // } g, err := p.pathParser(ip) if err != nil { return nil, err From 28ffc31d4f64353631fe28d735b890b9d92a39f2 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Mon, 16 Dec 2024 16:08:23 +0100 Subject: [PATCH 06/20] revert tests Signed-off-by: odubajDT --- pkg/ottl/e2e/e2e_test.go | 623 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 623 insertions(+) diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index cab539369016..d0c1d1e2ca29 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -5,6 +5,7 @@ package e2e import ( "context" + "net/http" "testing" "time" @@ -335,6 +336,628 @@ func Test_e2e_converters(t *testing.T) { tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "something") }, }, + { + statement: `set(attributes["test"], Base64Decode("cGFzcw=="))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], Decode("cGFzcw==", "base64"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], Concat(["A","B"], ":"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "A:B") + }, + }, + { + statement: `set(attributes["test"], ConvertCase(attributes["http.method"], "upper"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", http.MethodGet) + }, + }, + { + statement: `set(attributes["test"], ConvertCase("PASS", "lower"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], ConvertCase("fooBar", "snake"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "foo_bar") + }, + }, + { + statement: `set(attributes["test"], ConvertCase("foo_bar", "camel"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "FooBar") + }, + }, + { + statement: `set(attributes["test"], ConvertAttributesToElementsXML("This is a log message!"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", `This is a log message!1`) + }, + }, + { + statement: `set(body, ConvertTextToElementsXML("foo"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Body().SetStr("foo") + }, + }, + { + statement: `set(body, ConvertTextToElementsXML("foobar", "/a", "custom"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Body().SetStr("foobar") + }, + }, + { + statement: `set(attributes["test"], Double(1.0))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + }, + }, + { + statement: `set(attributes["test"], Double("1"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + }, + }, + { + statement: `set(attributes["test"], Double(true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + }, + }, + { + statement: `set(attributes["test"], Double(1))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 1.0) + }, + }, + { + statement: `set(attributes["test"], "pass") where Time("10", "%M") - Time("01", "%M") < Duration("10m")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], ExtractPatterns("aa123bb", "(?P\\d+)"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("numbers", "123") + }, + }, + { + statement: `set(attributes["test"], ExtractGrokPatterns("http://user:password@example.com:80/path?query=string", "%{ELB_URI}", true))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("url.scheme", "http") + m.PutStr("url.username", "user") + m.PutStr("url.domain", "example.com") + m.PutInt("url.port", 80) + m.PutStr("url.path", "/path") + m.PutStr("url.query", "query=string") + }, + }, + { + statement: `set(attributes["test"], FNV("pass"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 266877920130663416) + }, + }, + { + statement: `set(attributes["test"], Format("%03d-%s", [7, "test"]))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "007-test") + }, + }, + { + statement: `set(attributes["test"], Hour(Time("12", "%H")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 12) + }, + }, + { + statement: `set(attributes["test"], Hours(Duration("90m")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 1.5) + }, + }, + { + statement: `set(attributes["test"], InsertXML("", "/a", ""))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "") + }, + }, + { + statement: `set(attributes["test"], Int(1.0))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1) + }, + }, + { + statement: `set(attributes["test"], Int("1"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1) + }, + }, + { + statement: `set(attributes["test"], Int(true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1) + }, + }, + { + statement: `set(attributes["test"], Int(1))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1) + }, + }, + { + statement: `set(attributes["test"], GetXML("12", "/a//b"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "12") + }, + }, + { + statement: `set(attributes["test"], Hex(1.0))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "3ff0000000000000") + }, + }, + { + statement: `set(attributes["test"], Hex(true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "01") + }, + }, + { + statement: `set(attributes["test"], Hex(12))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "000000000000000c") + }, + }, + { + statement: `set(attributes["test"], Hex("12"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "3132") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsBool(false)`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsDouble(1.0)`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsMap(attributes["foo"])`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsList(attributes["foo"]["slice"])`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsMatch("aa123bb", "\\d{3}")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsString("")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], Len(attributes["foo"]))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 4) + }, + }, + { + statement: `set(attributes["test"], Log(1))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 0) + }, + }, + { + statement: `set(attributes["test"], MD5("pass"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "1a1dc91c907325c69271ddf0c944bc72") + }, + }, + { + statement: `set(attributes["test"], Microseconds(Duration("1ms")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1000) + }, + }, + { + statement: `set(attributes["test"], Milliseconds(Duration("1s")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1000) + }, + }, + { + statement: `set(attributes["test"], Minutes(Duration("1h")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 60) + }, + }, + { + statement: `set(attributes["test"], Nanoseconds(Duration("1ms")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutInt("test", 1000000) + }, + }, + { + statement: `set(attributes["test"], "pass") where Now() - Now() < Duration("1h")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], ParseCSV("val1;val2;val3","header1|header2|header3",";","|","strict"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("header1", "val1") + m.PutStr("header2", "val2") + m.PutStr("header3", "val3") + }, + }, + { + statement: `set(attributes["test"], ParseCSV("val1,val2,val3","header1|header2|header3",headerDelimiter="|",mode="strict"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("header1", "val1") + m.PutStr("header2", "val2") + m.PutStr("header3", "val3") + }, + }, + { + statement: `set(attributes["test"], ParseJSON("{\"id\":1}"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutDouble("id", 1) + }, + }, + { + statement: `set(attributes["test"], ParseJSON("[\"value1\",\"value2\"]"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + m.AppendEmpty().SetStr("value1") + m.AppendEmpty().SetStr("value2") + }, + }, + { + statement: `set(attributes["test"], ParseKeyValue("k1=v1 k2=v2"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("k1", "v1") + m.PutStr("k2", "v2") + }, + }, + { + statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!v2", "!", "_"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("k1", "v1") + m.PutStr("k2", "v2") + }, + }, + { + statement: `set(attributes["test"], ParseKeyValue("k1!v1_k2!\"v2__!__v2\"", "!", "_"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("k1", "v1") + m.PutStr("k2", "v2__!__v2") + }, + }, + { + statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "=", " ", true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=v2") + }, + }, + { + statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1:v1,k2:v2", ":" , ","), ":", ",", true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "k1:v1,k2:v2") + }, + }, + { + statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2"), "!", "+", true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "k1!v1+k2!v2") + }, + }, + { + statement: `set(attributes["test"], ToKeyValueString(ParseKeyValue("k1=v1 k2=v2=v3"), "=", " ", true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "k1=v1 k2=\"v2=v3\"") + }, + }, + { + statement: `set(attributes["test"], ParseSimplifiedXML("1This is a log message!"))`, + want: func(tCtx ottllog.TransformContext) { + attr := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + log := attr.PutEmptyMap("Log") + log.PutStr("id", "1") + log.PutStr("Message", "This is a log message!") + }, + }, + { + statement: `set(attributes["test"], ParseXML("This is a log message!"))`, + want: func(tCtx ottllog.TransformContext) { + log := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + log.PutStr("tag", "Log") + + attrs := log.PutEmptyMap("attributes") + attrs.PutStr("id", "1") + + logChildren := log.PutEmptySlice("children") + + message := logChildren.AppendEmpty().SetEmptyMap() + message.PutStr("tag", "Message") + message.PutStr("content", "This is a log message!") + }, + }, + { + statement: `set(attributes["test"], RemoveXML("This is a log message!", "/Log/Message"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", ``) + }, + }, + { + statement: `set(attributes["test"], Seconds(Duration("1m")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutDouble("test", 60) + }, + }, + { + statement: `set(attributes["test"], SHA1("pass"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684") + }, + }, + { + statement: `set(attributes["test"], SHA256("pass"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "d74ff0ee8da3b9806b18c877dbf29bbde50b5bd8e4dad7a3a725000feb82e8f1") + }, + }, + { + statement: `set(attributes["test"], SHA512("pass"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "5b722b307fce6c944905d132691d5e4a2214b7fe92b738920eb3fce3a90420a19511c3010a0e7712b054daef5b57bad59ecbd93b3280f210578f547f4aed4d25") + }, + }, + { + statement: `set(attributes["test"], Sort(Split(attributes["flags"], "|"), "desc"))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetStr("C") + s.AppendEmpty().SetStr("B") + s.AppendEmpty().SetStr("A") + }, + }, + { + statement: `set(attributes["test"], Sort([true, false, false]))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetBool(false) + s.AppendEmpty().SetBool(false) + s.AppendEmpty().SetBool(true) + }, + }, + { + statement: `set(attributes["test"], Sort([3, 6, 9], "desc"))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetInt(9) + s.AppendEmpty().SetInt(6) + s.AppendEmpty().SetInt(3) + }, + }, + { + statement: `set(attributes["test"], Sort([Double(1.5), Double(10.2), Double(2.3), Double(0.5)]))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetDouble(0.5) + s.AppendEmpty().SetDouble(1.5) + s.AppendEmpty().SetDouble(2.3) + s.AppendEmpty().SetDouble(10.2) + }, + }, + { + statement: `set(attributes["test"], Sort([Int(11), Double(2.2), Double(-1)]))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetDouble(-1) + s.AppendEmpty().SetDouble(2.2) + s.AppendEmpty().SetInt(11) + }, + }, + { + statement: `set(attributes["test"], Sort([false, Int(11), Double(2.2), "three"]))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetInt(11) + s.AppendEmpty().SetDouble(2.2) + s.AppendEmpty().SetBool(false) + s.AppendEmpty().SetStr("three") + }, + }, + { + statement: `set(span_id, SpanID(0x0000000000000000))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().SetSpanID(pcommon.NewSpanIDEmpty()) + }, + }, + { + statement: `set(attributes["test"], Split(attributes["flags"], "|"))`, + want: func(tCtx ottllog.TransformContext) { + s := tCtx.GetLogRecord().Attributes().PutEmptySlice("test") + s.AppendEmpty().SetStr("A") + s.AppendEmpty().SetStr("B") + s.AppendEmpty().SetStr("C") + }, + }, + { + statement: `set(attributes["test"], String("test"))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "test") + }, + }, + { + statement: `set(attributes["test"], String(attributes["http.method"]))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "get") + }, + }, + { + statement: `set(attributes["test"], String(span_id))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3,4,5,6,7,8]") + }, + }, + { + statement: `set(attributes["test"], String([1,2,3]))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3]") + }, + }, + { + statement: `set(attributes["test"], String(true))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "true") + }, + }, + { + statement: `set(attributes["test"], Substring("pass", 0, 2))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pa") + }, + }, + { + statement: `set(trace_id, TraceID(0x00000000000000000000000000000000))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().SetTraceID(pcommon.NewTraceIDEmpty()) + }, + }, + { + statement: `set(time, TruncateTime(time, Duration("1s")))`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().SetTimestamp(pcommon.NewTimestampFromTime(TestLogTimestamp.AsTime().Truncate(time.Second))) + }, + }, + { + statement: `set(attributes["test"], "pass") where UnixMicro(time) > 0`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where UnixMilli(time) > 0`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where UnixNano(time) > 0`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where UnixSeconds(time) > 0`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "pass") where IsString(UUID())`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "pass") + }, + }, + { + statement: `set(attributes["test"], "\\")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "\\") + }, + }, + { + statement: `set(attributes["test"], "\\\\")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\") + }, + }, + { + statement: `set(attributes["test"], "\\\\\\")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\") + }, + }, + { + statement: `set(attributes["test"], "\\\\\\\\")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", "\\\\\\\\") + }, + }, + { + statement: `set(attributes["test"], "\"")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("test", `"`) + }, + }, + { + statement: `keep_keys(attributes["foo"], ["\\", "bar"])`, + want: func(tCtx ottllog.TransformContext) { + // keep_keys should see two arguments + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("foo") + m.PutStr("bar", "pass") + }, + }, + { + statement: `set(attributes["test"], UserAgent("curl/7.81.0"))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutStr("user_agent.original", "curl/7.81.0") + m.PutStr("user_agent.name", "curl") + m.PutStr("user_agent.version", "7.81.0") + }, + }, + { + statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"]))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + thing1 := m.PutEmptyMap("foo") + thing1.PutStr("name", "foo") + thing1.PutInt("value", 2) + + thing2 := m.PutEmptyMap("bar") + thing2.PutStr("name", "bar") + thing2.PutInt("value", 5) + }, + }, } for _, tt := range tests { From ee9878d9175c5cc175a3037d364861203e92d369 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Mon, 16 Dec 2024 16:09:16 +0100 Subject: [PATCH 07/20] revert tests Signed-off-by: odubajDT --- pkg/ottl/e2e/e2e_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index d0c1d1e2ca29..66db6828559a 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -958,6 +958,14 @@ func Test_e2e_converters(t *testing.T) { thing2.PutInt("value", 5) }, }, + { + statement: `set(attributes["test"], SliceToMap(attributes["things"], ["name"], ["value"]))`, + want: func(tCtx ottllog.TransformContext) { + m := tCtx.GetLogRecord().Attributes().PutEmptyMap("test") + m.PutInt("foo", 2) + m.PutInt("bar", 5) + }, + }, } for _, tt := range tests { From db9ec8b13fd7e2a0e5f27044c27616cbf7008c8b Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 17 Dec 2024 07:27:19 +0100 Subject: [PATCH 08/20] polish Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 4 +- pkg/ottl/contexts/internal/map_test.go | 353 ++++++++++++------------- pkg/ottl/contexts/internal/path.go | 2 +- pkg/ottl/e2e/e2e_test.go | 3 +- pkg/ottl/functions.go | 5 +- 5 files changed, 182 insertions(+), 185 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index 47683fdce4c0..b412432a7ccc 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -22,7 +22,7 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return nil, err } if s == nil { - p, err := keys[0].PathGetter() + p, err := keys[0].PathGetter(ctx, tCtx) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return err } if s == nil { - p, err := keys[0].PathGetter() + p, err := keys[0].PathGetter(ctx, tCtx) if err != nil { return err } diff --git a/pkg/ottl/contexts/internal/map_test.go b/pkg/ottl/contexts/internal/map_test.go index 18d41462413e..e13a8f2982c9 100644 --- a/pkg/ottl/contexts/internal/map_test.go +++ b/pkg/ottl/contexts/internal/map_test.go @@ -5,7 +5,6 @@ package internal import ( "context" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -15,97 +14,97 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" ) -func Test_GetMapValue_Invalid(t *testing.T) { - tests := []struct { - name string - keys []ottl.Key[any] - err error - }{ - { - name: "first key not a string", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(0), - }, - }, - err: fmt.Errorf("non-string indexing is not supported"), - }, - { - name: "index map with int", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("map"), - }, - &TestKey[any]{ - I: ottltest.Intp(0), - }, - }, - err: fmt.Errorf("map must be indexed by a string"), - }, - { - name: "index slice with string", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - S: ottltest.Strp("invalid"), - }, - }, - err: fmt.Errorf("slice must be indexed by an int"), - }, - { - name: "index too large", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - I: ottltest.Intp(1), - }, - }, - err: fmt.Errorf("index 1 out of bounds"), - }, - { - name: "index too small", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - I: ottltest.Intp(-1), - }, - }, - err: fmt.Errorf("index -1 out of bounds"), - }, - { - name: "invalid type", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - }, - err: fmt.Errorf("type Str does not support string indexing"), - }, - } +// func Test_GetMapValue_Invalid(t *testing.T) { +// tests := []struct { +// name string +// keys []ottl.Key[any] +// err error +// }{ +// { +// name: "first key not a string", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// }, +// err: fmt.Errorf("non-string indexing is not supported"), +// }, +// { +// name: "index map with int", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("map"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// }, +// err: fmt.Errorf("map must be indexed by a string"), +// }, +// { +// name: "index slice with string", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("invalid"), +// }, +// }, +// err: fmt.Errorf("slice must be indexed by an int"), +// }, +// { +// name: "index too large", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(1), +// }, +// }, +// err: fmt.Errorf("index 1 out of bounds"), +// }, +// { +// name: "index too small", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(-1), +// }, +// }, +// err: fmt.Errorf("index -1 out of bounds"), +// }, +// { +// name: "invalid type", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// }, +// err: fmt.Errorf("type Str does not support string indexing"), +// }, +// } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := pcommon.NewMap() - m.PutStr("string", "invalid") - m.PutEmptyMap("map").PutStr("foo", "bar") +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// m := pcommon.NewMap() +// m.PutStr("string", "invalid") +// m.PutEmptyMap("map").PutStr("foo", "bar") - s := m.PutEmptySlice("slice") - s.AppendEmpty() +// s := m.PutEmptySlice("slice") +// s.AppendEmpty() - _, err := GetMapValue[any](context.Background(), nil, m, tt.keys) - assert.Equal(t, tt.err, err) - }) - } -} +// _, err := GetMapValue[any](context.Background(), nil, m, tt.keys) +// assert.Equal(t, tt.err, err) +// }) +// } +// } func Test_GetMapValue_MissingKey(t *testing.T) { m := pcommon.NewMap() @@ -128,97 +127,97 @@ func Test_GetMapValue_NilKey(t *testing.T) { assert.Error(t, err) } -func Test_SetMapValue_Invalid(t *testing.T) { - tests := []struct { - name string - keys []ottl.Key[any] - err error - }{ - { - name: "first key not a string", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(0), - }, - }, - err: fmt.Errorf("non-string indexing is not supported"), - }, - { - name: "index map with int", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("map"), - }, - &TestKey[any]{ - I: ottltest.Intp(0), - }, - }, - err: fmt.Errorf("map must be indexed by a string"), - }, - { - name: "index slice with string", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - S: ottltest.Strp("map"), - }, - }, - err: fmt.Errorf("slice must be indexed by an int"), - }, - { - name: "slice index too large", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - I: ottltest.Intp(1), - }, - }, - err: fmt.Errorf("index 1 out of bounds"), - }, - { - name: "slice index too small", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("slice"), - }, - &TestKey[any]{ - I: ottltest.Intp(-1), - }, - }, - err: fmt.Errorf("index -1 out of bounds"), - }, - { - name: "slice index too small", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - }, - err: fmt.Errorf("type Str does not support string indexing"), - }, - } +// func Test_SetMapValue_Invalid(t *testing.T) { +// tests := []struct { +// name string +// keys []ottl.Key[any] +// err error +// }{ +// { +// name: "first key not a string", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// }, +// err: fmt.Errorf("non-string indexing is not supported"), +// }, +// { +// name: "index map with int", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("map"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// }, +// err: fmt.Errorf("map must be indexed by a string"), +// }, +// { +// name: "index slice with string", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("map"), +// }, +// }, +// err: fmt.Errorf("slice must be indexed by an int"), +// }, +// { +// name: "slice index too large", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(1), +// }, +// }, +// err: fmt.Errorf("index 1 out of bounds"), +// }, +// { +// name: "slice index too small", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("slice"), +// }, +// &TestKey[any]{ +// I: ottltest.Intp(-1), +// }, +// }, +// err: fmt.Errorf("index -1 out of bounds"), +// }, +// { +// name: "slice index too small", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// }, +// err: fmt.Errorf("type Str does not support string indexing"), +// }, +// } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := pcommon.NewMap() - m.PutStr("string", "invalid") - m.PutEmptyMap("map") +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// m := pcommon.NewMap() +// m.PutStr("string", "invalid") +// m.PutEmptyMap("map") - s := m.PutEmptySlice("slice") - s.AppendEmpty() +// s := m.PutEmptySlice("slice") +// s.AppendEmpty() - err := SetMapValue[any](context.Background(), nil, m, tt.keys, "value") - assert.Equal(t, tt.err, err) - }) - } -} +// err := SetMapValue[any](context.Background(), nil, m, tt.keys, "value") +// assert.Equal(t, tt.err, err) +// }) +// } +// } func Test_SetMapValue_AddingNewSubMap(t *testing.T) { m := pcommon.NewMap() diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index eaa8532a7b7d..e5d3c9c0db3e 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -57,6 +57,6 @@ func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.I, nil } -func (k *TestKey[K]) PathGetter() (ottl.GetSetter[K], error) { +func (k *TestKey[K]) PathGetter(_ context.Context, _ K) (ottl.GetSetter[K], error) { return *k.P, nil } diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 66db6828559a..b106c8bc8d98 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" @@ -974,7 +973,7 @@ func Test_e2e_converters(t *testing.T) { logParser, err := ottllog.NewParser(ottlfuncs.StandardFuncs[ottllog.TransformContext](), settings) assert.NoError(t, err) logStatements, err := logParser.ParseStatement(tt.statement) - require.Nil(t, err) + assert.NoError(t, err) tCtx := constructLogTransformContext() _, _, _ = logStatements.Execute(context.Background(), tCtx) diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 3ba4678e0b70..5941a5441333 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -229,7 +229,6 @@ func (p *Parser[K]) newKeys(keys []key) ([]Key[K], error) { i: keys[i].Int, p: par, } - } return ks, nil } @@ -248,7 +247,7 @@ type Key[K any] interface { // If Key experiences an error retrieving the value it is returned. Int(context.Context, K) (*int64, error) - PathGetter() (GetSetter[K], error) + PathGetter(context.Context, K) (GetSetter[K], error) } var _ Key[any] = &baseKey[any]{} @@ -267,7 +266,7 @@ func (k *baseKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.i, nil } -func (k *baseKey[K]) PathGetter() (GetSetter[K], error) { +func (k *baseKey[K]) PathGetter(_ context.Context, _ K) (GetSetter[K], error) { return k.p, nil } From 09a6ab21d37aa533ecb3f8051546dd81e34cf262 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 17 Dec 2024 11:42:19 +0100 Subject: [PATCH 09/20] support more use cases Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/resource.go | 1 - pkg/ottl/contexts/internal/scope.go | 1 - pkg/ottl/contexts/internal/slice.go | 36 ++- pkg/ottl/contexts/internal/slice_test.go | 224 +++++++++--------- pkg/ottl/contexts/internal/span.go | 1 - pkg/ottl/contexts/internal/value.go | 109 ++++++++- pkg/ottl/contexts/internal/value_test.go | 24 +- pkg/ottl/contexts/ottldatapoint/datapoint.go | 1 - pkg/ottl/contexts/ottllog/log.go | 1 - .../contexts/ottlspanevent/span_events.go | 1 - pkg/ottl/e2e/e2e_test.go | 99 +++++++- pkg/ottl/expression.go | 2 +- pkg/ottl/lexer_test.go | 16 +- 13 files changed, 358 insertions(+), 158 deletions(-) diff --git a/pkg/ottl/contexts/internal/resource.go b/pkg/ottl/contexts/internal/resource.go index ae1efbffe96c..101bbf178244 100644 --- a/pkg/ottl/contexts/internal/resource.go +++ b/pkg/ottl/contexts/internal/resource.go @@ -57,7 +57,6 @@ func accessResourceAttributesKey[K ResourceContext](keys []ottl.Key[K]) ottl.Sta Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetResource().Attributes(), keys, val) }, - // TODO } } diff --git a/pkg/ottl/contexts/internal/scope.go b/pkg/ottl/contexts/internal/scope.go index f6da0b6fdd32..7a698ebc533f 100644 --- a/pkg/ottl/contexts/internal/scope.go +++ b/pkg/ottl/contexts/internal/scope.go @@ -62,7 +62,6 @@ func accessInstrumentationScopeAttributesKey[K InstrumentationScopeContext](keys Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetInstrumentationScope().Attributes(), keys, val) }, - // TODO } } diff --git a/pkg/ottl/contexts/internal/slice.go b/pkg/ottl/contexts/internal/slice.go index 5a90e281a902..664a1f9c8804 100644 --- a/pkg/ottl/contexts/internal/slice.go +++ b/pkg/ottl/contexts/internal/slice.go @@ -22,7 +22,23 @@ func GetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return nil, err } if i == nil { - return nil, fmt.Errorf("non-integer indexing is not supported") + p, err := keys[0].PathGetter(ctx, tCtx) + if err != nil { + return nil, err + } + if p != nil { + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resInt, ok := res.(int64) + if !ok { + return nil, fmt.Errorf("err") + } + i = &resInt + } else { + return nil, fmt.Errorf("non-integer indexing is not supported") + } } idx := int(*i) @@ -44,7 +60,23 @@ func SetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return err } if i == nil { - return fmt.Errorf("non-integer indexing is not supported") + p, err := keys[0].PathGetter(ctx, tCtx) + if err != nil { + return err + } + if p != nil { + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resInt, ok := res.(int64) + if !ok { + return fmt.Errorf("err") + } + i = &resInt + } else { + return fmt.Errorf("non-integer indexing is not supported") + } } idx := int(*i) diff --git a/pkg/ottl/contexts/internal/slice_test.go b/pkg/ottl/contexts/internal/slice_test.go index 0f238225251a..6b65f4a80746 100644 --- a/pkg/ottl/contexts/internal/slice_test.go +++ b/pkg/ottl/contexts/internal/slice_test.go @@ -5,136 +5,132 @@ package internal import ( "context" - "fmt" "testing" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" - - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" ) -func Test_GetSliceValue_Invalid(t *testing.T) { - tests := []struct { - name string - keys []ottl.Key[any] - err error - }{ - { - name: "first key not an integer", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("key"), - }, - }, - err: fmt.Errorf("non-integer indexing is not supported"), - }, - { - name: "index too large", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(1), - }, - }, - err: fmt.Errorf("index 1 out of bounds"), - }, - { - name: "index too small", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(-1), - }, - }, - err: fmt.Errorf("index -1 out of bounds"), - }, - { - name: "invalid type", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(0), - }, - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - }, - err: fmt.Errorf("type Str does not support string indexing"), - }, - } +// func Test_GetSliceValue_Invalid(t *testing.T) { +// tests := []struct { +// name string +// keys []ottl.Key[any] +// err error +// }{ +// { +// name: "first key not an integer", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("key"), +// }, +// }, +// err: fmt.Errorf("non-integer indexing is not supported"), +// }, +// { +// name: "index too large", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(1), +// }, +// }, +// err: fmt.Errorf("index 1 out of bounds"), +// }, +// { +// name: "index too small", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(-1), +// }, +// }, +// err: fmt.Errorf("index -1 out of bounds"), +// }, +// { +// name: "invalid type", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// }, +// err: fmt.Errorf("type Str does not support string indexing"), +// }, +// } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := pcommon.NewSlice() - s.AppendEmpty().SetStr("val") +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// s := pcommon.NewSlice() +// s.AppendEmpty().SetStr("val") - _, err := GetSliceValue[any](context.Background(), nil, s, tt.keys) - assert.Equal(t, tt.err, err) - }) - } -} +// _, err := GetSliceValue[any](context.Background(), nil, s, tt.keys) +// assert.Equal(t, tt.err, err) +// }) +// } +// } func Test_GetSliceValue_NilKey(t *testing.T) { _, err := GetSliceValue[any](context.Background(), nil, pcommon.NewSlice(), nil) assert.Error(t, err) } -func Test_SetSliceValue_Invalid(t *testing.T) { - tests := []struct { - name string - keys []ottl.Key[any] - err error - }{ - { - name: "first key not an integer", - keys: []ottl.Key[any]{ - &TestKey[any]{ - S: ottltest.Strp("key"), - }, - }, - err: fmt.Errorf("non-integer indexing is not supported"), - }, - { - name: "index too large", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(1), - }, - }, - err: fmt.Errorf("index 1 out of bounds"), - }, - { - name: "index too small", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(-1), - }, - }, - err: fmt.Errorf("index -1 out of bounds"), - }, - { - name: "invalid type", - keys: []ottl.Key[any]{ - &TestKey[any]{ - I: ottltest.Intp(0), - }, - &TestKey[any]{ - S: ottltest.Strp("string"), - }, - }, - err: fmt.Errorf("type Str does not support string indexing"), - }, - } +// func Test_SetSliceValue_Invalid(t *testing.T) { +// tests := []struct { +// name string +// keys []ottl.Key[any] +// err error +// }{ +// { +// name: "first key not an integer", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// S: ottltest.Strp("key"), +// }, +// }, +// err: fmt.Errorf("non-integer indexing is not supported"), +// }, +// { +// name: "index too large", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(1), +// }, +// }, +// err: fmt.Errorf("index 1 out of bounds"), +// }, +// { +// name: "index too small", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(-1), +// }, +// }, +// err: fmt.Errorf("index -1 out of bounds"), +// }, +// { +// name: "invalid type", +// keys: []ottl.Key[any]{ +// &TestKey[any]{ +// I: ottltest.Intp(0), +// }, +// &TestKey[any]{ +// S: ottltest.Strp("string"), +// }, +// }, +// err: fmt.Errorf("type Str does not support string indexing"), +// }, +// } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := pcommon.NewSlice() - s.AppendEmpty().SetStr("val") +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// s := pcommon.NewSlice() +// s.AppendEmpty().SetStr("val") - err := SetSliceValue[any](context.Background(), nil, s, tt.keys, "value") - assert.Equal(t, tt.err, err) - }) - } -} +// err := SetSliceValue[any](context.Background(), nil, s, tt.keys, "value") +// assert.Equal(t, tt.err, err) +// }) +// } +// } func Test_SetSliceValue_NilKey(t *testing.T) { err := SetSliceValue[any](context.Background(), nil, pcommon.NewSlice(), nil, "value") diff --git a/pkg/ottl/contexts/internal/span.go b/pkg/ottl/contexts/internal/span.go index 329ebeebc14c..c1ebf5a9b109 100644 --- a/pkg/ottl/contexts/internal/span.go +++ b/pkg/ottl/contexts/internal/span.go @@ -451,7 +451,6 @@ func accessAttributesKey[K SpanContext](keys []ottl.Key[K]) ottl.StandardGetSett Setter: func(ctx context.Context, tCtx K, val any) error { return SetMapValue[K](ctx, tCtx, tCtx.GetSpan().Attributes(), keys, val) }, - // TODO } } diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index ce335854eb3a..83cf3847c2dc 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -71,27 +71,57 @@ func SetValue(value pcommon.Value, val any) error { func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, keys []ottl.Key[K]) (any, error) { val := value var ok bool - for i := 0; i < len(keys); i++ { + for count := 0; count < len(keys); count++ { switch val.Type() { case pcommon.ValueTypeMap: - s, err := keys[i].String(ctx, tCtx) + s, err := keys[count].String(ctx, tCtx) if err != nil { return nil, err } if s == nil { - return nil, fmt.Errorf("map must be indexed by a string") + p, err := keys[count].PathGetter(ctx, tCtx) + if err != nil { + return nil, err + } + if p == nil { + return nil, errors.New("map must be indexed by a string") + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resString, ok := res.(string) + if !ok { + return nil, fmt.Errorf("err") + } + s = &resString } val, ok = val.Map().Get(*s) if !ok { return nil, nil } case pcommon.ValueTypeSlice: - i, err := keys[i].Int(ctx, tCtx) + i, err := keys[count].Int(ctx, tCtx) if err != nil { return nil, err } if i == nil { - return nil, fmt.Errorf("slice must be indexed by an int") + p, err := keys[count].PathGetter(ctx, tCtx) + if err != nil { + return nil, err + } + if p == nil { + return nil, errors.New("slice must be indexed by an int") + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resInt, ok := res.(int64) + if !ok { + return nil, fmt.Errorf("err") + } + i = &resInt } if int(*i) >= val.Slice().Len() || int(*i) < 0 { return nil, fmt.Errorf("index %v out of bounds", *i) @@ -117,15 +147,30 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. return err } - for i := 0; i < len(keys); i++ { + for count := 0; count < len(keys); count++ { switch currentValue.Type() { case pcommon.ValueTypeMap: - s, err := keys[i].String(ctx, tCtx) + s, err := keys[count].String(ctx, tCtx) if err != nil { return err } if s == nil { - return errors.New("map must be indexed by a string") + p, err := keys[count].PathGetter(ctx, tCtx) + if err != nil { + return err + } + if p == nil { + return errors.New("map must be indexed by a string") + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resString, ok := res.(string) + if !ok { + return fmt.Errorf("err") + } + s = &resString } potentialValue, ok := currentValue.Map().Get(*s) if !ok { @@ -134,23 +179,38 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. currentValue = potentialValue } case pcommon.ValueTypeSlice: - i, err := keys[i].Int(ctx, tCtx) + i, err := keys[count].Int(ctx, tCtx) if err != nil { return err } if i == nil { - return errors.New("slice must be indexed by an int") + p, err := keys[count].PathGetter(ctx, tCtx) + if err != nil { + return err + } + if p == nil { + return errors.New("slice must be indexed by an int") + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resInt, ok := res.(int64) + if !ok { + return fmt.Errorf("err") + } + i = &resInt } if int(*i) >= currentValue.Slice().Len() || int(*i) < 0 { return fmt.Errorf("index %v out of bounds", *i) } currentValue = currentValue.Slice().At(int(*i)) case pcommon.ValueTypeEmpty: - s, err := keys[i].String(ctx, tCtx) + s, err := keys[count].String(ctx, tCtx) if err != nil { return err } - i, err := keys[i].Int(ctx, tCtx) + i, err := keys[count].Int(ctx, tCtx) if err != nil { return err } @@ -164,7 +224,30 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. } currentValue = currentValue.Slice().AppendEmpty() default: - return errors.New("neither a string nor an int index was given, this is an error in the OTTL") + p, err := keys[count].PathGetter(ctx, tCtx) + if err != nil { + return err + } + if p == nil { + return errors.New("neither a string nor an int index was given, this is an error in the OTTL") + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resInt, ok := res.(int64) + if !ok { + resString, ok := res.(string) + if !ok { + return fmt.Errorf("err") + } + currentValue = currentValue.SetEmptyMap().PutEmpty(resString) + } + currentValue.SetEmptySlice() + for k := 0; k < int(*&resInt); k++ { + currentValue.Slice().AppendEmpty() + } + currentValue = currentValue.Slice().AppendEmpty() } default: return fmt.Errorf("type %v does not support string indexing", currentValue.Type()) diff --git a/pkg/ottl/contexts/internal/value_test.go b/pkg/ottl/contexts/internal/value_test.go index 5be89f72ea46..59b4e6d31c5b 100644 --- a/pkg/ottl/contexts/internal/value_test.go +++ b/pkg/ottl/contexts/internal/value_test.go @@ -3,20 +3,10 @@ package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/pdata/pcommon" - - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" -) - -func Test_SetIndexableValue_EmptyValueNoIndex(t *testing.T) { - keys := []ottl.Key[any]{ - &TestKey[any]{}, - } - err := setIndexableValue[any](context.Background(), nil, pcommon.NewValueEmpty(), nil, keys) - assert.Error(t, err) -} +// func Test_SetIndexableValue_EmptyValueNoIndex(t *testing.T) { +// keys := []ottl.Key[any]{ +// &TestKey[any]{}, +// } +// err := setIndexableValue[any](context.Background(), nil, pcommon.NewValueEmpty(), nil, keys) +// assert.Error(t, err) +// } diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint.go b/pkg/ottl/contexts/ottldatapoint/datapoint.go index fc5e8170a557..9c50d85cd723 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint.go @@ -353,7 +353,6 @@ func accessAttributesKey(key []ottl.Key[TransformContext]) ottl.StandardGetSette case pmetric.SummaryDataPoint: return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetDataPoint().(pmetric.SummaryDataPoint).Attributes(), key, val) } - // TODO return nil }, } diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 3dde1bb851e6..7ca056730cc7 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -448,7 +448,6 @@ func accessAttributesKey(key []ottl.Key[TransformContext]) ottl.StandardGetSette Setter: func(ctx context.Context, tCtx TransformContext, val any) error { return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetLogRecord().Attributes(), key, val) }, - // TODO } } diff --git a/pkg/ottl/contexts/ottlspanevent/span_events.go b/pkg/ottl/contexts/ottlspanevent/span_events.go index 7e8851ec1033..b3826f690d2d 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events.go @@ -272,7 +272,6 @@ func accessSpanEventAttributesKey(key []ottl.Key[TransformContext]) ottl.Standar Setter: func(ctx context.Context, tCtx TransformContext, val any) error { return internal.SetMapValue[TransformContext](ctx, tCtx, tCtx.GetSpanEvent().Attributes(), key, val) }, - // TODO } } diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index b106c8bc8d98..06ead5ea8159 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -313,10 +313,10 @@ func Test_e2e_editors(t *testing.T) { logStatements, err := logParser.ParseStatement(tt.statement) assert.NoError(t, err) - tCtx := constructLogTransformContext() + tCtx := constructLogTransformContextEditors() _, _, _ = logStatements.Execute(context.Background(), tCtx) - exTCtx := constructLogTransformContext() + exTCtx := constructLogTransformContextEditors() tt.want(exTCtx) assert.NoError(t, plogtest.CompareResourceLogs(newResourceLogs(exTCtx), newResourceLogs(tCtx))) @@ -330,9 +330,43 @@ func Test_e2e_converters(t *testing.T) { want func(tCtx ottllog.TransformContext) }{ { - statement: `set(attributes[attributes["flags"]], "something")`, + statement: `set(attributes[attributes["flags"]], "something33")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "something33") + }, + }, + { + statement: `set(attributes[attributes[attributes["flags"]]], "something2")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("something", "something2") + }, + }, + // { + // statement: `set(attributes[ConvertCase(attributes["A|B|C"], "upper")], "myvalue")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") + // }, + // }, + { + statement: `set(body, attributes[attributes["foo"][attributes["slice"]][attributes["int_value"]]])`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Body().SetStr("val2") + }, + }, + { + statement: `set(body, attributes["array"])`, + want: func(tCtx ottllog.TransformContext) { + arr := tCtx.GetLogRecord().Body().SetEmptySlice() + arr0 := arr.AppendEmpty() + arr0.SetStr("looong") + }, + }, + { + statement: `set(attributes["array"][attributes["int_value"]], 3)`, want: func(tCtx ottllog.TransformContext) { - tCtx.GetLogRecord().Attributes().PutStr("A|B|C", "something") + arr := tCtx.GetLogRecord().Attributes().PutEmptySlice("array") + arr0 := arr.AppendEmpty() + arr0.SetInt(3) }, }, { @@ -1004,6 +1038,13 @@ func Test_e2e_ottl_features(t *testing.T) { tCtx.GetLogRecord().Attributes().PutStr("test", "pass") }, }, + { + name: "where clause with dynamic indexing", + statement: `set(attributes["foo"], "bar") where attributes[attributes["flags"]] != nil`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("foo", "bar") + }, + }, { name: "Using enums", statement: `set(severity_number, SEVERITY_NUMBER_TRACE2) where severity_number == SEVERITY_NUMBER_TRACE`, @@ -1140,6 +1181,56 @@ func constructLogTransformContext() ottllog.TransformContext { scope := pcommon.NewInstrumentationScope() scope.SetName("scope") + logRecord := plog.NewLogRecord() + logRecord.Body().SetStr("operationA") + logRecord.SetTimestamp(TestLogTimestamp) + logRecord.SetObservedTimestamp(TestObservedTimestamp) + logRecord.SetDroppedAttributesCount(1) + logRecord.SetFlags(plog.DefaultLogRecordFlags.WithIsSampled(true)) + logRecord.SetSeverityNumber(1) + logRecord.SetTraceID(traceID) + logRecord.SetSpanID(spanID) + logRecord.Attributes().PutStr("http.method", "get") + logRecord.Attributes().PutStr("http.path", "/health") + logRecord.Attributes().PutStr("http.url", "http://localhost/health") + logRecord.Attributes().PutStr("flags", "A|B|C") + logRecord.Attributes().PutStr("total.string", "123456789") + logRecord.Attributes().PutStr("A|B|C", "something") + logRecord.Attributes().PutStr("foo", "foo") + logRecord.Attributes().PutStr("slice", "slice") + logRecord.Attributes().PutStr("val", "val2") + logRecord.Attributes().PutInt("int_value", 0) + arr := logRecord.Attributes().PutEmptySlice("array") + arr0 := arr.AppendEmpty() + arr0.SetStr("looong") + m := logRecord.Attributes().PutEmptyMap("foo") + m.PutStr("bar", "pass") + m.PutStr("flags", "pass") + s := m.PutEmptySlice("slice") + v := s.AppendEmpty() + v.SetStr("val") + m2 := m.PutEmptyMap("nested") + m2.PutStr("test", "pass") + + s2 := logRecord.Attributes().PutEmptySlice("things") + thing1 := s2.AppendEmpty().SetEmptyMap() + thing1.PutStr("name", "foo") + thing1.PutInt("value", 2) + + thing2 := s2.AppendEmpty().SetEmptyMap() + thing2.PutStr("name", "bar") + thing2.PutInt("value", 5) + + return ottllog.NewTransformContext(logRecord, scope, resource, plog.NewScopeLogs(), plog.NewResourceLogs()) +} + +func constructLogTransformContextEditors() ottllog.TransformContext { + resource := pcommon.NewResource() + resource.Attributes().PutStr("host.name", "localhost") + + scope := pcommon.NewInstrumentationScope() + scope.SetName("scope") + logRecord := plog.NewLogRecord() logRecord.Body().SetStr("operationA") logRecord.SetTimestamp(TestLogTimestamp) diff --git a/pkg/ottl/expression.go b/pkg/ottl/expression.go index a2e8d29e63c4..397967bb605d 100644 --- a/pkg/ottl/expression.go +++ b/pkg/ottl/expression.go @@ -8,12 +8,12 @@ import ( "context" "encoding/binary" "encoding/hex" + "encoding/json" "fmt" "reflect" "strconv" "time" - "github.com/goccy/go-json" "go.opentelemetry.io/collector/pdata/pcommon" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/internal/ottlcommon" diff --git a/pkg/ottl/lexer_test.go b/pkg/ottl/lexer_test.go index 87aac86da80c..4e9f7b5013ed 100644 --- a/pkg/ottl/lexer_test.go +++ b/pkg/ottl/lexer_test.go @@ -130,7 +130,7 @@ func Test_lexer(t *testing.T) { {"String", `"bar"`}, {"RBrace", "}"}, }}, - {"Expression path", `attributes[attributes["foo"]]`, false, []result{ + {"Dynamic path", `attributes[attributes["foo"]]`, false, []result{ {"Lowercase", "attributes"}, {"Punct", "["}, {"Lowercase", "attributes"}, @@ -139,6 +139,20 @@ func Test_lexer(t *testing.T) { {"Punct", "]"}, {"Punct", "]"}, }}, + // {"Dynamic path", `attributes[ConvertCase(attributes["foo"], "upper")]`, false, []result{ + // {"Lowercase", "attributes"}, + // {"Punct", "["}, + // {"Uppercase", "ConvertCase"}, + // {"LParen", "("}, + // {"Lowercase", "attributes"}, + // {"Punct", "["}, + // {"String", `"foo"`}, + // {"Punct", "]"}, + // {"Punct", ","}, + // {"String", "upper"}, + // {"RParen", ")"}, + // {"Punct", "]"}, + // }}, } for _, tt := range tests { From b3bc495e121642b68c450fb1d59c510f841bef4a Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 17 Dec 2024 12:55:04 +0100 Subject: [PATCH 10/20] fix lint Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index 83cf3847c2dc..f89db47b19b7 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -244,7 +244,7 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. currentValue = currentValue.SetEmptyMap().PutEmpty(resString) } currentValue.SetEmptySlice() - for k := 0; k < int(*&resInt); k++ { + for k := 0; k < int(resInt); k++ { currentValue.Slice().AppendEmpty() } currentValue = currentValue.Slice().AppendEmpty() From 2a2e8c4a4b19aa6f90254d8eb6bf7cbddfd282e6 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 17 Dec 2024 12:59:24 +0100 Subject: [PATCH 11/20] fix lint Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/value.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index f89db47b19b7..90e2697ad5e6 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -90,8 +90,8 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, if err != nil { return nil, err } - resString, ok := res.(string) - if !ok { + resString, okk := res.(string) + if !okk { return nil, fmt.Errorf("err") } s = &resString From 31c45d64ab45a73bd7670229c3e05d00574e3a74 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 18 Dec 2024 09:13:06 +0100 Subject: [PATCH 12/20] working tests Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 36 +-- pkg/ottl/contexts/internal/map_test.go | 391 +++++++++++++---------- pkg/ottl/contexts/internal/path.go | 4 +- pkg/ottl/contexts/internal/slice.go | 36 +-- pkg/ottl/contexts/internal/slice_test.go | 249 ++++++++------- pkg/ottl/contexts/internal/value.go | 25 +- pkg/ottl/contexts/internal/value_test.go | 23 +- pkg/ottl/functions_test.go | 51 +-- 8 files changed, 434 insertions(+), 381 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index b412432a7ccc..be407155be73 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -26,19 +26,15 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. if err != nil { return nil, err } - if p != nil { - res, err := p.Get(ctx, tCtx) - if err != nil { - return nil, err - } - resString, ok := res.(string) - if !ok { - return nil, fmt.Errorf("err") - } - s = &resString - } else { + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resString, ok := res.(string) + if !ok { return nil, fmt.Errorf("non-string indexing is not supported") } + s = &resString } val, ok := m.Get(*s) @@ -63,19 +59,15 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. if err != nil { return err } - if p != nil { - res, err := p.Get(ctx, tCtx) - if err != nil { - return err - } - resString, ok := res.(string) - if !ok { - return fmt.Errorf("err") - } - s = &resString - } else { + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resString, ok := res.(string) + if !ok { return fmt.Errorf("non-string indexing is not supported") } + s = &resString } currentValue, ok := m.Get(*s) diff --git a/pkg/ottl/contexts/internal/map_test.go b/pkg/ottl/contexts/internal/map_test.go index e13a8f2982c9..227bd2b41ed0 100644 --- a/pkg/ottl/contexts/internal/map_test.go +++ b/pkg/ottl/contexts/internal/map_test.go @@ -5,6 +5,7 @@ package internal import ( "context" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -14,97 +15,116 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" ) -// func Test_GetMapValue_Invalid(t *testing.T) { -// tests := []struct { -// name string -// keys []ottl.Key[any] -// err error -// }{ -// { -// name: "first key not a string", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// }, -// err: fmt.Errorf("non-string indexing is not supported"), -// }, -// { -// name: "index map with int", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("map"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// }, -// err: fmt.Errorf("map must be indexed by a string"), -// }, -// { -// name: "index slice with string", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("invalid"), -// }, -// }, -// err: fmt.Errorf("slice must be indexed by an int"), -// }, -// { -// name: "index too large", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(1), -// }, -// }, -// err: fmt.Errorf("index 1 out of bounds"), -// }, -// { -// name: "index too small", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(-1), -// }, -// }, -// err: fmt.Errorf("index -1 out of bounds"), -// }, -// { -// name: "invalid type", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// }, -// err: fmt.Errorf("type Str does not support string indexing"), -// }, -// } +func Test_GetMapValue_Invalid(t *testing.T) { + getSetter := &ottl.StandardGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (any, error) { + return nil, nil + }, + Setter: func(_ context.Context, tCtx any, val any) error { + return nil + }, + } + tests := []struct { + name string + keys []ottl.Key[any] + err error + }{ + { + name: "first key not a string", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + }, + err: fmt.Errorf("non-string indexing is not supported"), + }, + { + name: "index map with int", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("map"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + }, + err: fmt.Errorf("map must be indexed by a string"), + }, + { + name: "index slice with string", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("invalid"), + P: getSetter, + }, + }, + err: fmt.Errorf("slice must be indexed by an int"), + }, + { + name: "index too large", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(1), + P: getSetter, + }, + }, + err: fmt.Errorf("index 1 out of bounds"), + }, + { + name: "index too small", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(-1), + P: getSetter, + }, + }, + err: fmt.Errorf("index -1 out of bounds"), + }, + { + name: "invalid type", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + }, + err: fmt.Errorf("type Str does not support string indexing"), + }, + } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// m := pcommon.NewMap() -// m.PutStr("string", "invalid") -// m.PutEmptyMap("map").PutStr("foo", "bar") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := pcommon.NewMap() + m.PutStr("string", "invalid") + m.PutEmptyMap("map").PutStr("foo", "bar") -// s := m.PutEmptySlice("slice") -// s.AppendEmpty() + s := m.PutEmptySlice("slice") + s.AppendEmpty() -// _, err := GetMapValue[any](context.Background(), nil, m, tt.keys) -// assert.Equal(t, tt.err, err) -// }) -// } -// } + _, err := GetMapValue[any](context.Background(), nil, m, tt.keys) + assert.Equal(t, tt.err, err) + }) + } +} func Test_GetMapValue_MissingKey(t *testing.T) { m := pcommon.NewMap() @@ -127,97 +147,116 @@ func Test_GetMapValue_NilKey(t *testing.T) { assert.Error(t, err) } -// func Test_SetMapValue_Invalid(t *testing.T) { -// tests := []struct { -// name string -// keys []ottl.Key[any] -// err error -// }{ -// { -// name: "first key not a string", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// }, -// err: fmt.Errorf("non-string indexing is not supported"), -// }, -// { -// name: "index map with int", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("map"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// }, -// err: fmt.Errorf("map must be indexed by a string"), -// }, -// { -// name: "index slice with string", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("map"), -// }, -// }, -// err: fmt.Errorf("slice must be indexed by an int"), -// }, -// { -// name: "slice index too large", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(1), -// }, -// }, -// err: fmt.Errorf("index 1 out of bounds"), -// }, -// { -// name: "slice index too small", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("slice"), -// }, -// &TestKey[any]{ -// I: ottltest.Intp(-1), -// }, -// }, -// err: fmt.Errorf("index -1 out of bounds"), -// }, -// { -// name: "slice index too small", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// }, -// err: fmt.Errorf("type Str does not support string indexing"), -// }, -// } +func Test_SetMapValue_Invalid(t *testing.T) { + getSetter := &ottl.StandardGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (any, error) { + return nil, nil + }, + Setter: func(_ context.Context, tCtx any, val any) error { + return nil + }, + } + tests := []struct { + name string + keys []ottl.Key[any] + err error + }{ + { + name: "first key not a string", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + }, + err: fmt.Errorf("non-string indexing is not supported"), + }, + { + name: "index map with int", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("map"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + }, + err: fmt.Errorf("map must be indexed by a string"), + }, + { + name: "index slice with string", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("map"), + P: getSetter, + }, + }, + err: fmt.Errorf("slice must be indexed by an int"), + }, + { + name: "slice index too large", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(1), + P: getSetter, + }, + }, + err: fmt.Errorf("index 1 out of bounds"), + }, + { + name: "slice index too small", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("slice"), + P: getSetter, + }, + &TestKey[any]{ + I: ottltest.Intp(-1), + P: getSetter, + }, + }, + err: fmt.Errorf("index -1 out of bounds"), + }, + { + name: "slice index too small", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + }, + err: fmt.Errorf("type Str does not support string indexing"), + }, + } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// m := pcommon.NewMap() -// m.PutStr("string", "invalid") -// m.PutEmptyMap("map") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := pcommon.NewMap() + m.PutStr("string", "invalid") + m.PutEmptyMap("map") -// s := m.PutEmptySlice("slice") -// s.AppendEmpty() + s := m.PutEmptySlice("slice") + s.AppendEmpty() -// err := SetMapValue[any](context.Background(), nil, m, tt.keys, "value") -// assert.Equal(t, tt.err, err) -// }) -// } -// } + err := SetMapValue[any](context.Background(), nil, m, tt.keys, "value") + assert.Equal(t, tt.err, err) + }) + } +} func Test_SetMapValue_AddingNewSubMap(t *testing.T) { m := pcommon.NewMap() diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index e5d3c9c0db3e..11f0ce558e1f 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -46,7 +46,7 @@ var _ ottl.Key[any] = &TestKey[any]{} type TestKey[K any] struct { S *string I *int64 - P *ottl.GetSetter[K] + P ottl.GetSetter[K] } func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -58,5 +58,5 @@ func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { } func (k *TestKey[K]) PathGetter(_ context.Context, _ K) (ottl.GetSetter[K], error) { - return *k.P, nil + return k.P, nil } diff --git a/pkg/ottl/contexts/internal/slice.go b/pkg/ottl/contexts/internal/slice.go index 664a1f9c8804..80cb04509564 100644 --- a/pkg/ottl/contexts/internal/slice.go +++ b/pkg/ottl/contexts/internal/slice.go @@ -26,19 +26,15 @@ func GetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o if err != nil { return nil, err } - if p != nil { - res, err := p.Get(ctx, tCtx) - if err != nil { - return nil, err - } - resInt, ok := res.(int64) - if !ok { - return nil, fmt.Errorf("err") - } - i = &resInt - } else { + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resInt, ok := res.(int64) + if !ok { return nil, fmt.Errorf("non-integer indexing is not supported") } + i = &resInt } idx := int(*i) @@ -64,19 +60,15 @@ func SetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o if err != nil { return err } - if p != nil { - res, err := p.Get(ctx, tCtx) - if err != nil { - return err - } - resInt, ok := res.(int64) - if !ok { - return fmt.Errorf("err") - } - i = &resInt - } else { + res, err := p.Get(ctx, tCtx) + if err != nil { + return err + } + resInt, ok := res.(int64) + if !ok { return fmt.Errorf("non-integer indexing is not supported") } + i = &resInt } idx := int(*i) diff --git a/pkg/ottl/contexts/internal/slice_test.go b/pkg/ottl/contexts/internal/slice_test.go index 6b65f4a80746..4ab5190f67f3 100644 --- a/pkg/ottl/contexts/internal/slice_test.go +++ b/pkg/ottl/contexts/internal/slice_test.go @@ -5,132 +5,161 @@ package internal import ( "context" + "fmt" "testing" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" ) -// func Test_GetSliceValue_Invalid(t *testing.T) { -// tests := []struct { -// name string -// keys []ottl.Key[any] -// err error -// }{ -// { -// name: "first key not an integer", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("key"), -// }, -// }, -// err: fmt.Errorf("non-integer indexing is not supported"), -// }, -// { -// name: "index too large", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(1), -// }, -// }, -// err: fmt.Errorf("index 1 out of bounds"), -// }, -// { -// name: "index too small", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(-1), -// }, -// }, -// err: fmt.Errorf("index -1 out of bounds"), -// }, -// { -// name: "invalid type", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// }, -// err: fmt.Errorf("type Str does not support string indexing"), -// }, -// } +func Test_GetSliceValue_Invalid(t *testing.T) { + getSetter := &ottl.StandardGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (any, error) { + return nil, nil + }, + Setter: func(_ context.Context, tCtx any, val any) error { + return nil + }, + } + tests := []struct { + name string + keys []ottl.Key[any] + err error + }{ + { + name: "first key not an integer", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("key"), + P: getSetter, + }, + }, + err: fmt.Errorf("non-integer indexing is not supported"), + }, + { + name: "index too large", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(1), + P: getSetter, + }, + }, + err: fmt.Errorf("index 1 out of bounds"), + }, + { + name: "index too small", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(-1), + P: getSetter, + }, + }, + err: fmt.Errorf("index -1 out of bounds"), + }, + { + name: "invalid type", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + }, + err: fmt.Errorf("type Str does not support string indexing"), + }, + } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// s := pcommon.NewSlice() -// s.AppendEmpty().SetStr("val") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := pcommon.NewSlice() + s.AppendEmpty().SetStr("val") -// _, err := GetSliceValue[any](context.Background(), nil, s, tt.keys) -// assert.Equal(t, tt.err, err) -// }) -// } -// } + _, err := GetSliceValue[any](context.Background(), nil, s, tt.keys) + assert.Equal(t, tt.err, err) + }) + } +} func Test_GetSliceValue_NilKey(t *testing.T) { _, err := GetSliceValue[any](context.Background(), nil, pcommon.NewSlice(), nil) assert.Error(t, err) } -// func Test_SetSliceValue_Invalid(t *testing.T) { -// tests := []struct { -// name string -// keys []ottl.Key[any] -// err error -// }{ -// { -// name: "first key not an integer", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// S: ottltest.Strp("key"), -// }, -// }, -// err: fmt.Errorf("non-integer indexing is not supported"), -// }, -// { -// name: "index too large", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(1), -// }, -// }, -// err: fmt.Errorf("index 1 out of bounds"), -// }, -// { -// name: "index too small", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(-1), -// }, -// }, -// err: fmt.Errorf("index -1 out of bounds"), -// }, -// { -// name: "invalid type", -// keys: []ottl.Key[any]{ -// &TestKey[any]{ -// I: ottltest.Intp(0), -// }, -// &TestKey[any]{ -// S: ottltest.Strp("string"), -// }, -// }, -// err: fmt.Errorf("type Str does not support string indexing"), -// }, -// } +func Test_SetSliceValue_Invalid(t *testing.T) { + getSetter := &ottl.StandardGetSetter[any]{ + Getter: func(_ context.Context, tCtx any) (any, error) { + return nil, nil + }, + Setter: func(_ context.Context, tCtx any, val any) error { + return nil + }, + } + tests := []struct { + name string + keys []ottl.Key[any] + err error + }{ + { + name: "first key not an integer", + keys: []ottl.Key[any]{ + &TestKey[any]{ + S: ottltest.Strp("key"), + P: getSetter, + }, + }, + err: fmt.Errorf("non-integer indexing is not supported"), + }, + { + name: "index too large", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(1), + P: getSetter, + }, + }, + err: fmt.Errorf("index 1 out of bounds"), + }, + { + name: "index too small", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(-1), + P: getSetter, + }, + }, + err: fmt.Errorf("index -1 out of bounds"), + }, + { + name: "invalid type", + keys: []ottl.Key[any]{ + &TestKey[any]{ + I: ottltest.Intp(0), + P: getSetter, + }, + &TestKey[any]{ + S: ottltest.Strp("string"), + P: getSetter, + }, + }, + err: fmt.Errorf("type Str does not support string indexing"), + }, + } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// s := pcommon.NewSlice() -// s.AppendEmpty().SetStr("val") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := pcommon.NewSlice() + s.AppendEmpty().SetStr("val") -// err := SetSliceValue[any](context.Background(), nil, s, tt.keys, "value") -// assert.Equal(t, tt.err, err) -// }) -// } -// } + err := SetSliceValue[any](context.Background(), nil, s, tt.keys, "value") + assert.Equal(t, tt.err, err) + }) + } +} func Test_SetSliceValue_NilKey(t *testing.T) { err := SetSliceValue[any](context.Background(), nil, pcommon.NewSlice(), nil, "value") diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index 90e2697ad5e6..d6d14d6c3a4c 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -83,16 +83,13 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, if err != nil { return nil, err } - if p == nil { - return nil, errors.New("map must be indexed by a string") - } res, err := p.Get(ctx, tCtx) if err != nil { return nil, err } resString, okk := res.(string) if !okk { - return nil, fmt.Errorf("err") + return nil, errors.New("map must be indexed by a string") } s = &resString } @@ -110,16 +107,13 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, if err != nil { return nil, err } - if p == nil { - return nil, errors.New("slice must be indexed by an int") - } res, err := p.Get(ctx, tCtx) if err != nil { return nil, err } resInt, ok := res.(int64) if !ok { - return nil, fmt.Errorf("err") + return nil, errors.New("slice must be indexed by an int") } i = &resInt } @@ -159,16 +153,13 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. if err != nil { return err } - if p == nil { - return errors.New("map must be indexed by a string") - } res, err := p.Get(ctx, tCtx) if err != nil { return err } resString, ok := res.(string) if !ok { - return fmt.Errorf("err") + return errors.New("map must be indexed by a string") } s = &resString } @@ -188,16 +179,13 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. if err != nil { return err } - if p == nil { - return errors.New("slice must be indexed by an int") - } res, err := p.Get(ctx, tCtx) if err != nil { return err } resInt, ok := res.(int64) if !ok { - return fmt.Errorf("err") + return errors.New("slice must be indexed by an int") } i = &resInt } @@ -228,9 +216,6 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. if err != nil { return err } - if p == nil { - return errors.New("neither a string nor an int index was given, this is an error in the OTTL") - } res, err := p.Get(ctx, tCtx) if err != nil { return err @@ -239,7 +224,7 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. if !ok { resString, ok := res.(string) if !ok { - return fmt.Errorf("err") + return errors.New("neither a string nor an int index was given, this is an error in the OTTL") } currentValue = currentValue.SetEmptyMap().PutEmpty(resString) } diff --git a/pkg/ottl/contexts/internal/value_test.go b/pkg/ottl/contexts/internal/value_test.go index 59b4e6d31c5b..69aa7049206b 100644 --- a/pkg/ottl/contexts/internal/value_test.go +++ b/pkg/ottl/contexts/internal/value_test.go @@ -3,10 +3,19 @@ package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" -// func Test_SetIndexableValue_EmptyValueNoIndex(t *testing.T) { -// keys := []ottl.Key[any]{ -// &TestKey[any]{}, -// } -// err := setIndexableValue[any](context.Background(), nil, pcommon.NewValueEmpty(), nil, keys) -// assert.Error(t, err) -// } +import ( + "context" + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" +) + +func Test_SetIndexableValue_InvalidValue(t *testing.T) { + keys := []ottl.Key[any]{ + &TestKey[any]{}, + } + err := setIndexableValue[any](context.Background(), nil, pcommon.NewValueStr("str"), nil, keys) + assert.Error(t, err) +} diff --git a/pkg/ottl/functions_test.go b/pkg/ottl/functions_test.go index cbe95b45b22d..bb81c316dd27 100644 --- a/pkg/ottl/functions_test.go +++ b/pkg/ottl/functions_test.go @@ -2524,25 +2524,32 @@ func Test_baseKey_Int(t *testing.T) { assert.Equal(t, int64(1), *i) } -// func Test_newKey(t *testing.T) { -// keys := []key{ -// { -// String: ottltest.Strp("foo"), -// }, -// { -// String: ottltest.Strp("bar"), -// }, -// } -// ks, _ := newKeys[any](keys) - -// assert.Len(t, ks, 2) - -// s, err := ks[0].String(context.Background(), nil) -// assert.NoError(t, err) -// assert.NotNil(t, s) -// assert.Equal(t, "foo", *s) -// s, err = ks[1].String(context.Background(), nil) -// assert.NoError(t, err) -// assert.NotNil(t, s) -// assert.Equal(t, "bar", *s) -// } +func Test_newKey(t *testing.T) { + ps, _ := NewParser[any]( + defaultFunctionsForTests(), + testParsePath[any], + componenttest.NewNopTelemetrySettings(), + WithEnumParser[any](testParseEnum), + WithPathContextNames[any]([]string{"log"}), + ) + keys := []key{ + { + String: ottltest.Strp("foo"), + }, + { + String: ottltest.Strp("bar"), + }, + } + ks, _ := ps.newKeys(keys) + + assert.Len(t, ks, 2) + + s, err := ks[0].String(context.Background(), nil) + assert.NoError(t, err) + assert.NotNil(t, s) + assert.Equal(t, "foo", *s) + s, err = ks[1].String(context.Background(), nil) + assert.NoError(t, err) + assert.NotNil(t, s) + assert.Equal(t, "bar", *s) +} From 63b2fe71514308f5acdf047ce4d48e6888daa894 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 18 Dec 2024 10:18:26 +0100 Subject: [PATCH 13/20] refactor Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 40 +++++++-------- pkg/ottl/contexts/internal/slice.go | 24 ++------- pkg/ottl/contexts/internal/value.go | 79 ++++++++--------------------- 3 files changed, 44 insertions(+), 99 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index be407155be73..0340190308be 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -22,19 +22,11 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return nil, err } if s == nil { - p, err := keys[0].PathGetter(ctx, tCtx) + resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[0]) if err != nil { - return nil, err - } - res, err := p.Get(ctx, tCtx) - if err != nil { - return nil, err - } - resString, ok := res.(string) - if !ok { return nil, fmt.Errorf("non-string indexing is not supported") } - s = &resString + s = resString } val, ok := m.Get(*s) @@ -55,19 +47,11 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return err } if s == nil { - p, err := keys[0].PathGetter(ctx, tCtx) - if err != nil { - return err - } - res, err := p.Get(ctx, tCtx) + resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[0]) if err != nil { - return err - } - resString, ok := res.(string) - if !ok { return fmt.Errorf("non-string indexing is not supported") } - s = &resString + s = resString } currentValue, ok := m.Get(*s) @@ -76,3 +60,19 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. } return setIndexableValue[K](ctx, tCtx, currentValue, val, keys[1:]) } + +func FetchValueFromPath[K any, T int64 | string](ctx context.Context, tCtx K, key ottl.Key[K]) (*T, error) { + p, err := key.PathGetter(ctx, tCtx) + if err != nil { + return nil, err + } + res, err := p.Get(ctx, tCtx) + if err != nil { + return nil, err + } + resVal, ok := res.(T) + if !ok { + return nil, fmt.Errorf("casting not successfull") + } + return &resVal, nil +} diff --git a/pkg/ottl/contexts/internal/slice.go b/pkg/ottl/contexts/internal/slice.go index 80cb04509564..d1d0285ffdd0 100644 --- a/pkg/ottl/contexts/internal/slice.go +++ b/pkg/ottl/contexts/internal/slice.go @@ -22,19 +22,11 @@ func GetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return nil, err } if i == nil { - p, err := keys[0].PathGetter(ctx, tCtx) + resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[0]) if err != nil { - return nil, err - } - res, err := p.Get(ctx, tCtx) - if err != nil { - return nil, err - } - resInt, ok := res.(int64) - if !ok { return nil, fmt.Errorf("non-integer indexing is not supported") } - i = &resInt + i = resInt } idx := int(*i) @@ -56,19 +48,11 @@ func SetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return err } if i == nil { - p, err := keys[0].PathGetter(ctx, tCtx) + resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[0]) if err != nil { - return err - } - res, err := p.Get(ctx, tCtx) - if err != nil { - return err - } - resInt, ok := res.(int64) - if !ok { return fmt.Errorf("non-integer indexing is not supported") } - i = &resInt + i = resInt } idx := int(*i) diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index d6d14d6c3a4c..2b62f6d5066e 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -79,19 +79,11 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, return nil, err } if s == nil { - p, err := keys[count].PathGetter(ctx, tCtx) + resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) if err != nil { - return nil, err - } - res, err := p.Get(ctx, tCtx) - if err != nil { - return nil, err - } - resString, okk := res.(string) - if !okk { return nil, errors.New("map must be indexed by a string") } - s = &resString + s = resString } val, ok = val.Map().Get(*s) if !ok { @@ -103,19 +95,11 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, return nil, err } if i == nil { - p, err := keys[count].PathGetter(ctx, tCtx) - if err != nil { - return nil, err - } - res, err := p.Get(ctx, tCtx) + resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) if err != nil { - return nil, err - } - resInt, ok := res.(int64) - if !ok { return nil, errors.New("slice must be indexed by an int") } - i = &resInt + i = resInt } if int(*i) >= val.Slice().Len() || int(*i) < 0 { return nil, fmt.Errorf("index %v out of bounds", *i) @@ -149,19 +133,11 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. return err } if s == nil { - p, err := keys[count].PathGetter(ctx, tCtx) - if err != nil { - return err - } - res, err := p.Get(ctx, tCtx) + resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) if err != nil { - return err - } - resString, ok := res.(string) - if !ok { return errors.New("map must be indexed by a string") } - s = &resString + s = resString } potentialValue, ok := currentValue.Map().Get(*s) if !ok { @@ -175,19 +151,11 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. return err } if i == nil { - p, err := keys[count].PathGetter(ctx, tCtx) - if err != nil { - return err - } - res, err := p.Get(ctx, tCtx) + resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) if err != nil { - return err - } - resInt, ok := res.(int64) - if !ok { return errors.New("slice must be indexed by an int") } - i = &resInt + i = resInt } if int(*i) >= currentValue.Slice().Len() || int(*i) < 0 { return fmt.Errorf("index %v out of bounds", *i) @@ -212,27 +180,20 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. } currentValue = currentValue.Slice().AppendEmpty() default: - p, err := keys[count].PathGetter(ctx, tCtx) - if err != nil { - return err - } - res, err := p.Get(ctx, tCtx) - if err != nil { - return err - } - resInt, ok := res.(int64) - if !ok { - resString, ok := res.(string) - if !ok { - return errors.New("neither a string nor an int index was given, this is an error in the OTTL") + resString, errString := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) + resInt, errInt := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) + switch { + case errInt == nil: + currentValue.SetEmptySlice() + for k := 0; k < int(*resInt); k++ { + currentValue.Slice().AppendEmpty() } - currentValue = currentValue.SetEmptyMap().PutEmpty(resString) + currentValue = currentValue.Slice().AppendEmpty() + case errString == nil: + currentValue = currentValue.SetEmptyMap().PutEmpty(*resString) + default: + return errors.New("neither a string nor an int index was given, this is an error in the OTTL") } - currentValue.SetEmptySlice() - for k := 0; k < int(resInt); k++ { - currentValue.Slice().AppendEmpty() - } - currentValue = currentValue.Slice().AppendEmpty() } default: return fmt.Errorf("type %v does not support string indexing", currentValue.Type()) From 0cb42daa865343a0950b5e30740c590ebd9362ca Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 18 Dec 2024 10:54:14 +0100 Subject: [PATCH 14/20] fix typo Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index 0340190308be..439f9f71c701 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -72,7 +72,7 @@ func FetchValueFromPath[K any, T int64 | string](ctx context.Context, tCtx K, ke } resVal, ok := res.(T) if !ok { - return nil, fmt.Errorf("casting not successfull") + return nil, fmt.Errorf("casting not successful") } return &resVal, nil } From d8af6b12358ea73f2d2694fa83e5cc211b2d4800 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 18 Dec 2024 11:06:30 +0100 Subject: [PATCH 15/20] make generate Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/slice_test.go | 5 +++-- pkg/ottl/contexts/internal/value_test.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/ottl/contexts/internal/slice_test.go b/pkg/ottl/contexts/internal/slice_test.go index 4ab5190f67f3..1a98d695559b 100644 --- a/pkg/ottl/contexts/internal/slice_test.go +++ b/pkg/ottl/contexts/internal/slice_test.go @@ -8,10 +8,11 @@ import ( "fmt" "testing" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest" ) func Test_GetSliceValue_Invalid(t *testing.T) { diff --git a/pkg/ottl/contexts/internal/value_test.go b/pkg/ottl/contexts/internal/value_test.go index 69aa7049206b..726fd6de5eaa 100644 --- a/pkg/ottl/contexts/internal/value_test.go +++ b/pkg/ottl/contexts/internal/value_test.go @@ -7,9 +7,10 @@ import ( "context" "testing" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" ) func Test_SetIndexableValue_InvalidValue(t *testing.T) { From dde89356623b7cba3434d85007f8f71753fd1ce7 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 18 Dec 2024 14:14:36 +0100 Subject: [PATCH 16/20] support expression as attribute key in parser Signed-off-by: odubajDT --- pkg/ottl/e2e/e2e_test.go | 12 ++++++------ pkg/ottl/functions.go | 8 ++++---- pkg/ottl/grammar.go | 6 +++--- pkg/ottl/lexer_test.go | 14 -------------- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 06ead5ea8159..0babf19bc525 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -329,6 +329,12 @@ func Test_e2e_converters(t *testing.T) { statement string want func(tCtx ottllog.TransformContext) }{ + // { + // statement: `set(attributes[ConvertCase(attributes["A|B|C"], "upper")], "myvalue")`, + // want: func(tCtx ottllog.TransformContext) { + // tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") + // }, + // }, { statement: `set(attributes[attributes["flags"]], "something33")`, want: func(tCtx ottllog.TransformContext) { @@ -341,12 +347,6 @@ func Test_e2e_converters(t *testing.T) { tCtx.GetLogRecord().Attributes().PutStr("something", "something2") }, }, - // { - // statement: `set(attributes[ConvertCase(attributes["A|B|C"], "upper")], "myvalue")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") - // }, - // }, { statement: `set(body, attributes[attributes["foo"][attributes["slice"]][attributes["int_value"]]])`, want: func(tCtx ottllog.TransformContext) { diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 5941a5441333..76fb106294a8 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -53,8 +53,8 @@ func buildOriginalKeysText(keys []key) string { if k.String != nil { builder.WriteString(*k.String) } - if k.Path != nil { - builder.WriteString(buildOriginalText(k.Path)) + if k.Expression != nil && k.Expression.Path != nil { + builder.WriteString(buildOriginalText(k.Expression.Path)) } builder.WriteString("]") } @@ -217,8 +217,8 @@ func (p *Parser[K]) newKeys(keys []key) ([]Key[K], error) { ks := make([]Key[K], len(keys)) for i := range keys { var par GetSetter[K] - if keys[i].Path != nil { - arg, err := p.buildGetSetterFromPath(keys[i].Path) + if keys[i].Expression != nil && keys[i].Expression.Path != nil { + arg, err := p.buildGetSetterFromPath(keys[i].Expression.Path) if err != nil { return nil, err } diff --git a/pkg/ottl/grammar.go b/pkg/ottl/grammar.go index 6e810195c7f6..ee371dee4020 100644 --- a/pkg/ottl/grammar.go +++ b/pkg/ottl/grammar.go @@ -276,9 +276,9 @@ type field struct { } type key struct { - String *string `parser:"'[' (@String "` - Int *int64 `parser:"| @Int"` - Path *path `parser:"| @@ ) ']'"` + String *string `parser:"'[' (@String "` + Int *int64 `parser:"| @Int"` + Expression *mathExprLiteral `parser:"| @@ ) ']'"` } type list struct { diff --git a/pkg/ottl/lexer_test.go b/pkg/ottl/lexer_test.go index 4e9f7b5013ed..4cd54f3fd9b8 100644 --- a/pkg/ottl/lexer_test.go +++ b/pkg/ottl/lexer_test.go @@ -139,20 +139,6 @@ func Test_lexer(t *testing.T) { {"Punct", "]"}, {"Punct", "]"}, }}, - // {"Dynamic path", `attributes[ConvertCase(attributes["foo"], "upper")]`, false, []result{ - // {"Lowercase", "attributes"}, - // {"Punct", "["}, - // {"Uppercase", "ConvertCase"}, - // {"LParen", "("}, - // {"Lowercase", "attributes"}, - // {"Punct", "["}, - // {"String", `"foo"`}, - // {"Punct", "]"}, - // {"Punct", ","}, - // {"String", "upper"}, - // {"RParen", ")"}, - // {"Punct", "]"}, - // }}, } for _, tt := range tests { From 51c0d1febfc94b7d982b6a40f1e24feeb52d89b1 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 19 Dec 2024 09:04:14 +0100 Subject: [PATCH 17/20] support converters as attribute keys Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/path.go | 4 ++-- pkg/ottl/e2e/e2e_test.go | 12 +++++------ pkg/ottl/functions.go | 33 ++++++++++++++++++++++-------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index 11f0ce558e1f..b83dfefd1299 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -46,7 +46,7 @@ var _ ottl.Key[any] = &TestKey[any]{} type TestKey[K any] struct { S *string I *int64 - P ottl.GetSetter[K] + P ottl.Getter[K] } func (k *TestKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -57,6 +57,6 @@ func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.I, nil } -func (k *TestKey[K]) PathGetter(_ context.Context, _ K) (ottl.GetSetter[K], error) { +func (k *TestKey[K]) PathGetter(_ context.Context, _ K) (ottl.Getter[K], error) { return k.P, nil } diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 0babf19bc525..2f31e6c434fa 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -329,12 +329,12 @@ func Test_e2e_converters(t *testing.T) { statement string want func(tCtx ottllog.TransformContext) }{ - // { - // statement: `set(attributes[ConvertCase(attributes["A|B|C"], "upper")], "myvalue")`, - // want: func(tCtx ottllog.TransformContext) { - // tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") - // }, - // }, + { + statement: `set(attributes[ConvertCase(attributes["A|B|C"], "upper")], "myvalue")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") + }, + }, { statement: `set(attributes[attributes["flags"]], "something33")`, want: func(tCtx ottllog.TransformContext) { diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index 76fb106294a8..bbc7ee630225 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -216,13 +216,28 @@ func (p *Parser[K]) newKeys(keys []key) ([]Key[K], error) { } ks := make([]Key[K], len(keys)) for i := range keys { - var par GetSetter[K] - if keys[i].Expression != nil && keys[i].Expression.Path != nil { - arg, err := p.buildGetSetterFromPath(keys[i].Expression.Path) - if err != nil { - return nil, err + var par Getter[K] + if keys[i].Expression != nil { + if keys[i].Expression.Path != nil { + arg, err := p.buildGetSetterFromPath(keys[i].Expression.Path) + if err != nil { + return nil, err + } + par = arg + } + if f := keys[i].Expression.Float; f != nil { + par = literal[K]{value: *f} + } + if i := keys[i].Expression.Int; i != nil { + par = literal[K]{value: *i} + } + if keys[i].Expression.Converter != nil { + g, err := p.newGetterFromConverter(*keys[i].Expression.Converter) + if err != nil { + return nil, err + } + par = g } - par = arg } ks[i] = &baseKey[K]{ s: keys[i].String, @@ -247,7 +262,7 @@ type Key[K any] interface { // If Key experiences an error retrieving the value it is returned. Int(context.Context, K) (*int64, error) - PathGetter(context.Context, K) (GetSetter[K], error) + PathGetter(context.Context, K) (Getter[K], error) } var _ Key[any] = &baseKey[any]{} @@ -255,7 +270,7 @@ var _ Key[any] = &baseKey[any]{} type baseKey[K any] struct { s *string i *int64 - p GetSetter[K] + p Getter[K] } func (k *baseKey[K]) String(_ context.Context, _ K) (*string, error) { @@ -266,7 +281,7 @@ func (k *baseKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.i, nil } -func (k *baseKey[K]) PathGetter(_ context.Context, _ K) (GetSetter[K], error) { +func (k *baseKey[K]) PathGetter(_ context.Context, _ K) (Getter[K], error) { return k.p, nil } From b8c4622082928058f2fb90956935c11cf14d3fb2 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 19 Dec 2024 09:09:01 +0100 Subject: [PATCH 18/20] add chlog Signed-off-by: odubajDT --- .chloggen/indexing-pkg-ottl.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .chloggen/indexing-pkg-ottl.yaml diff --git a/.chloggen/indexing-pkg-ottl.yaml b/.chloggen/indexing-pkg-ottl.yaml new file mode 100644 index 000000000000..6ec875b3be5a --- /dev/null +++ b/.chloggen/indexing-pkg-ottl.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/ottl + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Support dynamic indexing of maps and slices." + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36644] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] From b62391be4d7470f964b358c88f28fdcf7ad55c76 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 19 Dec 2024 09:57:12 +0100 Subject: [PATCH 19/20] polishing of implementation Signed-off-by: odubajDT --- pkg/ottl/contexts/internal/map.go | 8 ++++---- pkg/ottl/contexts/internal/path.go | 2 +- pkg/ottl/contexts/internal/slice.go | 4 ++-- pkg/ottl/contexts/internal/value.go | 12 ++++++------ pkg/ottl/functions.go | 20 ++++++++++++++++---- pkg/ottl/ottlfuncs/func_set.go | 4 ++-- pkg/ottl/ottlfuncs/func_set_test.go | 2 +- 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/pkg/ottl/contexts/internal/map.go b/pkg/ottl/contexts/internal/map.go index 439f9f71c701..52f57e95559b 100644 --- a/pkg/ottl/contexts/internal/map.go +++ b/pkg/ottl/contexts/internal/map.go @@ -22,7 +22,7 @@ func GetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return nil, err } if s == nil { - resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[0]) + resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0]) if err != nil { return nil, fmt.Errorf("non-string indexing is not supported") } @@ -47,7 +47,7 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return err } if s == nil { - resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[0]) + resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[0]) if err != nil { return fmt.Errorf("non-string indexing is not supported") } @@ -61,8 +61,8 @@ func SetMapValue[K any](ctx context.Context, tCtx K, m pcommon.Map, keys []ottl. return setIndexableValue[K](ctx, tCtx, currentValue, val, keys[1:]) } -func FetchValueFromPath[K any, T int64 | string](ctx context.Context, tCtx K, key ottl.Key[K]) (*T, error) { - p, err := key.PathGetter(ctx, tCtx) +func FetchValueFromExpression[K any, T int64 | string](ctx context.Context, tCtx K, key ottl.Key[K]) (*T, error) { + p, err := key.ExpressionGetter(ctx, tCtx) if err != nil { return nil, err } diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index b83dfefd1299..cfba45aaf291 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -57,6 +57,6 @@ func (k *TestKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.I, nil } -func (k *TestKey[K]) PathGetter(_ context.Context, _ K) (ottl.Getter[K], error) { +func (k *TestKey[K]) ExpressionGetter(_ context.Context, _ K) (ottl.Getter[K], error) { return k.P, nil } diff --git a/pkg/ottl/contexts/internal/slice.go b/pkg/ottl/contexts/internal/slice.go index d1d0285ffdd0..a4ba242a40bf 100644 --- a/pkg/ottl/contexts/internal/slice.go +++ b/pkg/ottl/contexts/internal/slice.go @@ -22,7 +22,7 @@ func GetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return nil, err } if i == nil { - resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[0]) + resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[0]) if err != nil { return nil, fmt.Errorf("non-integer indexing is not supported") } @@ -48,7 +48,7 @@ func SetSliceValue[K any](ctx context.Context, tCtx K, s pcommon.Slice, keys []o return err } if i == nil { - resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[0]) + resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[0]) if err != nil { return fmt.Errorf("non-integer indexing is not supported") } diff --git a/pkg/ottl/contexts/internal/value.go b/pkg/ottl/contexts/internal/value.go index 2b62f6d5066e..2136a3fbc995 100644 --- a/pkg/ottl/contexts/internal/value.go +++ b/pkg/ottl/contexts/internal/value.go @@ -79,7 +79,7 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, return nil, err } if s == nil { - resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) + resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[count]) if err != nil { return nil, errors.New("map must be indexed by a string") } @@ -95,7 +95,7 @@ func getIndexableValue[K any](ctx context.Context, tCtx K, value pcommon.Value, return nil, err } if i == nil { - resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) + resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[count]) if err != nil { return nil, errors.New("slice must be indexed by an int") } @@ -133,7 +133,7 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. return err } if s == nil { - resString, err := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) + resString, err := FetchValueFromExpression[K, string](ctx, tCtx, keys[count]) if err != nil { return errors.New("map must be indexed by a string") } @@ -151,7 +151,7 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. return err } if i == nil { - resInt, err := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) + resInt, err := FetchValueFromExpression[K, int64](ctx, tCtx, keys[count]) if err != nil { return errors.New("slice must be indexed by an int") } @@ -180,8 +180,8 @@ func setIndexableValue[K any](ctx context.Context, tCtx K, currentValue pcommon. } currentValue = currentValue.Slice().AppendEmpty() default: - resString, errString := FetchValueFromPath[K, string](ctx, tCtx, keys[count]) - resInt, errInt := FetchValueFromPath[K, int64](ctx, tCtx, keys[count]) + resString, errString := FetchValueFromExpression[K, string](ctx, tCtx, keys[count]) + resInt, errInt := FetchValueFromExpression[K, int64](ctx, tCtx, keys[count]) switch { case errInt == nil: currentValue.SetEmptySlice() diff --git a/pkg/ottl/functions.go b/pkg/ottl/functions.go index bbc7ee630225..816a2dd34302 100644 --- a/pkg/ottl/functions.go +++ b/pkg/ottl/functions.go @@ -53,8 +53,16 @@ func buildOriginalKeysText(keys []key) string { if k.String != nil { builder.WriteString(*k.String) } - if k.Expression != nil && k.Expression.Path != nil { - builder.WriteString(buildOriginalText(k.Expression.Path)) + if k.Expression != nil { + if k.Expression.Path != nil { + builder.WriteString(buildOriginalText(k.Expression.Path)) + } + if k.Expression.Float != nil { + builder.WriteString(strconv.FormatFloat(*k.Expression.Float, 'f', 10, 64)) + } + if k.Expression.Int != nil { + builder.WriteString(strconv.FormatInt(*k.Expression.Int, 10)) + } } builder.WriteString("]") } @@ -262,7 +270,11 @@ type Key[K any] interface { // If Key experiences an error retrieving the value it is returned. Int(context.Context, K) (*int64, error) - PathGetter(context.Context, K) (Getter[K], error) + // ExpressionGetter returns a Getter to the expression, that can be + // part of the path. + // If the Key does not have an expression the returned value is nil. + // If Key experiences an error retrieving the value it is returned. + ExpressionGetter(context.Context, K) (Getter[K], error) } var _ Key[any] = &baseKey[any]{} @@ -281,7 +293,7 @@ func (k *baseKey[K]) Int(_ context.Context, _ K) (*int64, error) { return k.i, nil } -func (k *baseKey[K]) PathGetter(_ context.Context, _ K) (Getter[K], error) { +func (k *baseKey[K]) ExpressionGetter(_ context.Context, _ K) (Getter[K], error) { return k.p, nil } diff --git a/pkg/ottl/ottlfuncs/func_set.go b/pkg/ottl/ottlfuncs/func_set.go index 4341fca9e034..a0ce390a4788 100644 --- a/pkg/ottl/ottlfuncs/func_set.go +++ b/pkg/ottl/ottlfuncs/func_set.go @@ -11,7 +11,7 @@ import ( ) type SetArguments[K any] struct { - Target ottl.GetSetter[K] + Target ottl.Setter[K] Value ottl.Getter[K] } @@ -29,7 +29,7 @@ func createSetFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (ott return set(args.Target, args.Value), nil } -func set[K any](target ottl.GetSetter[K], value ottl.Getter[K]) ottl.ExprFunc[K] { +func set[K any](target ottl.Setter[K], value ottl.Getter[K]) ottl.ExprFunc[K] { return func(ctx context.Context, tCtx K) (any, error) { val, err := value.Get(ctx, tCtx) if err != nil { diff --git a/pkg/ottl/ottlfuncs/func_set_test.go b/pkg/ottl/ottlfuncs/func_set_test.go index 5bd910b551fe..7fbf3b2fc765 100644 --- a/pkg/ottl/ottlfuncs/func_set_test.go +++ b/pkg/ottl/ottlfuncs/func_set_test.go @@ -25,7 +25,7 @@ func Test_set(t *testing.T) { tests := []struct { name string - setter ottl.GetSetter[pcommon.Value] + setter ottl.Setter[pcommon.Value] getter ottl.Getter[pcommon.Value] want func(pcommon.Value) }{ From 5e92cc6c6af6dc8ddc03fed995b9941681fb4d72 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 19 Dec 2024 10:20:13 +0100 Subject: [PATCH 20/20] add more e2e tests Signed-off-by: odubajDT --- pkg/ottl/e2e/e2e_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/ottl/e2e/e2e_test.go b/pkg/ottl/e2e/e2e_test.go index 2f31e6c434fa..6384dd185bbf 100644 --- a/pkg/ottl/e2e/e2e_test.go +++ b/pkg/ottl/e2e/e2e_test.go @@ -335,6 +335,12 @@ func Test_e2e_converters(t *testing.T) { tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") }, }, + { + statement: `set(attributes[ConvertCase(attributes[attributes["flags"]], "upper")], "myvalue")`, + want: func(tCtx ottllog.TransformContext) { + tCtx.GetLogRecord().Attributes().PutStr("SOMETHING", "myvalue") + }, + }, { statement: `set(attributes[attributes["flags"]], "something33")`, want: func(tCtx ottllog.TransformContext) {