From 02f416b884808b36ea4f6494282bcaff024dd2a8 Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Mon, 20 Jun 2022 07:21:38 -0600 Subject: [PATCH] [processor/transform] Added nil to grammar (#11150) * Added nil to grammar * Updated changelog --- CHANGELOG.md | 1 + processor/transformprocessor/README.md | 4 ++-- .../internal/common/expression.go | 4 ++++ .../internal/common/expression_test.go | 7 ++++++ .../internal/common/functions_test.go | 11 +++++++++ .../internal/common/parser.go | 8 +++++++ .../internal/common/parser_test.go | 24 +++++++++++++++++++ .../internal/traces/processor_test.go | 7 ++++++ 8 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 309c1b32a9d0..705f2b279909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - `metricstransformprocessor`: Migrate the processor from OC to pdata (#10817) - This behavior can be reverted by disabling the `processor.metricstransformprocessor.UseOTLPDataModel` feature gate. - `transformprocessor`: Add byte slice literal to the grammar. Add new SpanID and TraceID functions that take a byte slice and return a Span/Trace ID. (#10487) +- `transformprocessor`: Add nil literal to the grammar. (#11150) - `elasticsearchreceiver`: Add integration test for elasticsearch receiver (#10165) - `tailsamplingprocessor`: New sampler added that allows to sample based on minimum number of spans - `datadogexporter`: Some config validation and unmarshaling steps are now done on `Validate` and `Unmarshal` instead of `Sanitize` (#8829) diff --git a/processor/transformprocessor/README.md b/processor/transformprocessor/README.md index b8af883d7a53..30c0337834e5 100644 --- a/processor/transformprocessor/README.md +++ b/processor/transformprocessor/README.md @@ -17,8 +17,8 @@ in the OTLP protobuf definition. e.g., `status.code`, `attributes["http.method"] - Metric data types are `None`, `Gauge`, `Sum`, `Histogram`, `ExponentialHistogram`, and `Summary` - `aggregation_temporality` is converted to and from the [protobuf's numeric definition](https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/metrics/v1/metrics.proto#L291). Interact with this field using 0, 1, or 2. - Until the grammar can handle booleans, `is_monotic` is handled via strings the strings `"true"` and `"false"`. -- Literals: Strings, ints, floats, and bools can be referenced as literal values. Byte slices can be references as a literal value via a hex string prefaced with `0x`, such as `0x0001`. -- Function invocations: Functions can be invoked with arguments matching the function's expected arguments +- Literals: Strings, ints, floats, bools, and nil can be referenced as literal values. Byte slices can be references as a literal value via a hex string prefaced with `0x`, such as `0x0001`. +- Function invocations: Functions can be invoked with arguments matching the function's expected arguments. The literal nil cannot be used as a replacement for maps or slices in function calls. - Where clause: Telemetry to modify can be filtered by appending `where a b`, with `a` and `b` being any of the above. Supported functions: diff --git a/processor/transformprocessor/internal/common/expression.go b/processor/transformprocessor/internal/common/expression.go index 9e9639da07df..6e1bd890dd4f 100644 --- a/processor/transformprocessor/internal/common/expression.go +++ b/processor/transformprocessor/internal/common/expression.go @@ -58,6 +58,10 @@ func (g exprGetter) Get(ctx TransformContext) interface{} { } func NewGetter(val Value, functions map[string]interface{}, pathParser PathExpressionParser) (Getter, error) { + if val.IsNil != nil && *val.IsNil { + return &literal{value: nil}, nil + } + if s := val.String; s != nil { return &literal{value: *s}, nil } diff --git a/processor/transformprocessor/internal/common/expression_test.go b/processor/transformprocessor/internal/common/expression_test.go index b3361b7a7961..2459c9a3db20 100644 --- a/processor/transformprocessor/internal/common/expression_test.go +++ b/processor/transformprocessor/internal/common/expression_test.go @@ -62,6 +62,13 @@ func Test_newGetter(t *testing.T) { }, want: []byte{1, 2, 3, 4, 5, 6, 7, 8}, }, + { + name: "nil literal", + val: Value{ + IsNil: (*IsNil)(testhelper.Boolp(true)), + }, + want: nil, + }, { name: "bool literal", val: Value{ diff --git a/processor/transformprocessor/internal/common/functions_test.go b/processor/transformprocessor/internal/common/functions_test.go index 78fe55990749..b13cd9a4ebef 100644 --- a/processor/transformprocessor/internal/common/functions_test.go +++ b/processor/transformprocessor/internal/common/functions_test.go @@ -251,6 +251,17 @@ func Test_NewFunctionCall(t *testing.T) { }, }, }, + { + name: "getter arg with nil literal", + inv: Invocation{ + Function: "testing_getter", + Arguments: []Value{ + { + IsNil: (*IsNil)(testhelper.Boolp(true)), + }, + }, + }, + }, { name: "string arg", inv: Invocation{ diff --git a/processor/transformprocessor/internal/common/parser.go b/processor/transformprocessor/internal/common/parser.go index a162fa906a9b..5a5c910aaba1 100644 --- a/processor/transformprocessor/internal/common/parser.go +++ b/processor/transformprocessor/internal/common/parser.go @@ -56,6 +56,7 @@ type Value struct { Float *float64 `| @Float` Int *int64 `| @Int` Bool *Boolean `| @("true" | "false")` + IsNil *IsNil `| @"nil"` Path *Path `| @@ )` } @@ -101,6 +102,13 @@ func (b *Boolean) Capture(values []string) error { return nil } +type IsNil bool + +func (n *IsNil) Capture(_ []string) error { + *n = true + return nil +} + func ParseQueries(statements []string, functions map[string]interface{}, pathParser PathExpressionParser) ([]Query, error) { queries := make([]Query, 0) var errors error diff --git a/processor/transformprocessor/internal/common/parser_test.go b/processor/transformprocessor/internal/common/parser_test.go index e566afeaebe5..42ee07a0da56 100644 --- a/processor/transformprocessor/internal/common/parser_test.go +++ b/processor/transformprocessor/internal/common/parser_test.go @@ -337,6 +337,30 @@ func Test_parse(t *testing.T) { Condition: nil, }, }, + { + query: `set(attributes["test"], nil)`, + expected: &ParsedQuery{ + Invocation: Invocation{ + Function: "set", + Arguments: []Value{ + { + Path: &Path{ + Fields: []Field{ + { + Name: "attributes", + MapKey: testhelper.Strp("test"), + }, + }, + }, + }, + { + IsNil: (*IsNil)(testhelper.Boolp(true)), + }, + }, + }, + Condition: nil, + }, + }, } for _, tt := range tests { diff --git a/processor/transformprocessor/internal/traces/processor_test.go b/processor/transformprocessor/internal/traces/processor_test.go index 002fbecfd946..f85689e9fe43 100644 --- a/processor/transformprocessor/internal/traces/processor_test.go +++ b/processor/transformprocessor/internal/traces/processor_test.go @@ -107,6 +107,13 @@ func TestProcess(t *testing.T) { td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Attributes().InsertString("test", "pass") }, }, + { + query: `set(attributes["test"], "pass") where attributes["doesnt exist"] == nil`, + want: func(td ptrace.Traces) { + td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Attributes().InsertString("test", "pass") + td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).Attributes().InsertString("test", "pass") + }, + }, } for _, tt := range tests {