Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[processor/transform] Add SpanID and TraceID to the grammar #10487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

### 💡 Enhancements 💡

- `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)
- `elasticsearchreceiver`: Add integration test for elasticsearch receiver (#10165)

### 🧰 Bug fixes 🧰
Expand Down
6 changes: 5 additions & 1 deletion processor/transformprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ 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, and floats can be referenced as literal values
- Literals: Strings, ints, and floats 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
- Where clause: Telemetry to modify can be filtered by appending `where a <op> b`, with `a` and `b` being any of the above.

Supported functions:
- `SpanID(bytes)` - `bytes` is a byte slice of exactly 8 bytes. The function returns a SpanID from `bytes`. e.g., `SpanID(0x0000000000000000)`

- `TraceID(bytes)` - `bytes` is a byte slice of exactly 16 bytes. The function returns a TraceID from `bytes`. e.g., `TraceID(0x00000000000000000000000000000000)`

- `set(target, value)` - `target` is a path expression to a telemetry field to set `value` into. `value` is any value type.
e.g., `set(attributes["http.path"], "/foo")`, `set(name, attributes["http.route"])`. If `value` resolves to `nil`, e.g.
it references an unset map value, there will be no action.
Expand Down
3 changes: 3 additions & 0 deletions processor/transformprocessor/internal/common/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func NewGetter(val Value, functions map[string]interface{}, pathParser PathExpre
if i := val.Int; i != nil {
return &literal{value: *i}, nil
}
if b := val.Bytes; b != nil {
return &literal{value: ([]byte)(*b)}, nil
}

if val.Path != nil {
return pathParser(val.Path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ func Test_newGetter(t *testing.T) {
},
want: int64(12),
},
{
name: "bytes literal",
val: Value{
Bytes: (*Bytes)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}),
},
want: []byte{1, 2, 3, 4, 5, 6, 7, 8},
},
{
name: "path expression",
val: Value{
Expand Down
33 changes: 33 additions & 0 deletions processor/transformprocessor/internal/common/func_span_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common"

import (
"errors"

"go.opentelemetry.io/collector/pdata/pcommon"
)

func spanID(bytes []byte) (ExprFunc, error) {
if len(bytes) != 8 {
return nil, errors.New("span ids must be 8 bytes")
}
var idArr [8]byte
copy(idArr[:8], bytes)
id := pcommon.NewSpanID(idArr)
return func(ctx TransformContext) interface{} {
return id
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common/testhelper"
)

func Test_spanID(t *testing.T) {
tests := []struct {
name string
bytes []byte
want pcommon.SpanID
}{
{
name: "create span id",
bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8},
want: pcommon.NewSpanID([8]byte{1, 2, 3, 4, 5, 6, 7, 8}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

ctx := testhelper.TestTransformContext{}

exprFunc, _ := spanID(tt.bytes)
actual := exprFunc(ctx)

assert.Equal(t, tt.want, actual)
})
}
}

func Test_spanID_validation(t *testing.T) {
tests := []struct {
name string
bytes []byte
}{
{
name: "byte slice less than 8",
bytes: []byte{1, 2, 3, 4, 5, 6, 7},
},
{
name: "byte slice longer than 8",
bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := traceID(tt.bytes)
assert.Error(t, err, "span ids must be 8 bytes")
})
}
}
33 changes: 33 additions & 0 deletions processor/transformprocessor/internal/common/func_trace_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common"

import (
"errors"

"go.opentelemetry.io/collector/pdata/pcommon"
)

func traceID(bytes []byte) (ExprFunc, error) {
if len(bytes) != 16 {
return nil, errors.New("traces ids must be 16 bytes")
}
var idArr [16]byte
copy(idArr[:16], bytes)
id := pcommon.NewTraceID(idArr)
return func(ctx TransformContext) interface{} {
return id
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common/testhelper"
)

func Test_traceID(t *testing.T) {
tests := []struct {
name string
bytes []byte
want pcommon.TraceID
}{
{
name: "create trace id",
bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
want: pcommon.NewTraceID([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

ctx := testhelper.TestTransformContext{}

exprFunc, _ := traceID(tt.bytes)
actual := exprFunc(ctx)

assert.Equal(t, tt.want, actual)
})
}
}

func Test_traceID_validation(t *testing.T) {
tests := []struct {
name string
bytes []byte
}{
{
name: "byte slice less than 16",
bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
},
{
name: "byte slice longer than 16",
bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := traceID(tt.bytes)
assert.Error(t, err, "traces ids must be 16 bytes")
})
}
}
7 changes: 7 additions & 0 deletions processor/transformprocessor/internal/common/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
)

var registry = map[string]interface{}{
"TraceID": traceID,
"SpanID": spanID,
"keep_keys": keepKeys,
"set": set,
"truncate_all": truncateAll,
Expand Down Expand Up @@ -109,6 +111,11 @@ func buildSliceArg(inv Invocation, argType reflect.Type, startingIndex int, args
arg = append(arg, *inv.Arguments[j].Int)
}
*args = append(*args, reflect.ValueOf(arg))
case reflect.Uint8:
if inv.Arguments[startingIndex].Bytes == nil {
return fmt.Errorf("invalid argument for slice parameter at position %v, must be a byte slice literal", startingIndex)
}
*args = append(*args, reflect.ValueOf(([]byte)(*inv.Arguments[startingIndex].Bytes)))
default:
return fmt.Errorf("unsupported slice type for function %v", inv.Function)
}
Expand Down
36 changes: 36 additions & 0 deletions processor/transformprocessor/internal/common/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Test_NewFunctionCall_invalid(t *testing.T) {
functions["testing_getter"] = functionWithGetter
functions["testing_multiple_args"] = functionWithMultipleArgs
functions["testing_string"] = functionWithString
functions["testing_byte_slice"] = functionWithByteSlice

tests := []struct {
name string
Expand Down Expand Up @@ -97,6 +98,23 @@ func Test_NewFunctionCall_invalid(t *testing.T) {
},
},
},
{
name: "not matching arg type when byte slice",
inv: Invocation{
Function: "testing_byte_slice",
Arguments: []Value{
{
String: testhelper.Strp("test"),
},
{
String: testhelper.Strp("test"),
},
{
String: testhelper.Strp("test"),
},
},
},
},
{
name: "function call returns error",
inv: Invocation{
Expand All @@ -117,6 +135,7 @@ func Test_NewFunctionCall(t *testing.T) {
functions["testing_string_slice"] = functionWithStringSlice
functions["testing_float_slice"] = functionWithFloatSlice
functions["testing_int_slice"] = functionWithIntSlice
functions["testing_byte_slice"] = functionWithByteSlice
functions["testing_setter"] = functionWithSetter
functions["testing_getsetter"] = functionWithGetSetter
functions["testing_getter"] = functionWithGetter
Expand Down Expand Up @@ -276,6 +295,17 @@ func Test_NewFunctionCall(t *testing.T) {
},
},
},
{
name: "bytes arg",
inv: Invocation{
Function: "testing_byte_slice",
Arguments: []Value{
{
Bytes: (*Bytes)(&[]byte{1, 2, 3, 4, 5, 6, 7, 8}),
},
},
},
},
{
name: "multiple args",
inv: Invocation{
Expand Down Expand Up @@ -335,6 +365,12 @@ func functionWithIntSlice(_ []int64) (ExprFunc, error) {
}, nil
}

func functionWithByteSlice(_ []byte) (ExprFunc, error) {
return func(ctx TransformContext) interface{} {
return "anything"
}, nil
}

func functionWithSetter(_ Setter) (ExprFunc, error) {
return func(ctx TransformContext) interface{} {
return "anything"
Expand Down
Loading