Skip to content

Commit 5863986

Browse files
edmocostasbylica-splunk
authored andcommitted
[pkg/ottl] Add OTTL context inferrer utility (open-telemetry#35721)
1 parent f182c41 commit 5863986

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

pkg/ottl/context_inferrer.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
5+
6+
import "math"
7+
8+
var (
9+
defaultContextInferPriority = []string{
10+
"log",
11+
"metric",
12+
"datapoint",
13+
"spanevent",
14+
"span",
15+
"resource",
16+
"scope",
17+
"instrumentation_scope",
18+
}
19+
)
20+
21+
// contextInferrer is an interface used to infer the OTTL context from statements paths.
22+
type contextInferrer interface {
23+
// infer returns the OTTL context inferred from the given statements paths.
24+
infer(statements []string) (string, error)
25+
}
26+
27+
type priorityContextInferrer struct {
28+
contextPriority map[string]int
29+
}
30+
31+
func (s *priorityContextInferrer) infer(statements []string) (string, error) {
32+
var inferredContext string
33+
var inferredContextPriority int
34+
35+
for _, statement := range statements {
36+
parsed, err := parseStatement(statement)
37+
if err != nil {
38+
return inferredContext, err
39+
}
40+
41+
for _, p := range getParsedStatementPaths(parsed) {
42+
pathContextPriority, ok := s.contextPriority[p.Context]
43+
if !ok {
44+
// Lowest priority
45+
pathContextPriority = math.MaxInt
46+
}
47+
48+
if inferredContext == "" || pathContextPriority < inferredContextPriority {
49+
inferredContext = p.Context
50+
inferredContextPriority = pathContextPriority
51+
}
52+
}
53+
}
54+
55+
return inferredContext, nil
56+
}
57+
58+
// defaultPriorityContextInferrer is like newPriorityContextInferrer, but using the default
59+
// context priorities and ignoring unknown/non-prioritized contexts.
60+
func defaultPriorityContextInferrer() contextInferrer {
61+
return newPriorityContextInferrer(defaultContextInferPriority)
62+
}
63+
64+
// newPriorityContextInferrer creates a new priority-based context inferrer.
65+
// To infer the context, it compares all [ottl.Path.Context] values, prioritizing them based
66+
// on the provide contextsPriority argument, the lower the context position is in the array,
67+
// the more priority it will have over other items.
68+
// If unknown/non-prioritized contexts are found on the statements, they can be either ignored
69+
// or considered when no other prioritized context is found. To skip unknown contexts, the
70+
// ignoreUnknownContext argument must be set to false.
71+
func newPriorityContextInferrer(contextsPriority []string) contextInferrer {
72+
contextPriority := make(map[string]int, len(contextsPriority))
73+
for i, ctx := range contextsPriority {
74+
contextPriority[ctx] = i
75+
}
76+
return &priorityContextInferrer{
77+
contextPriority: contextPriority,
78+
}
79+
}

pkg/ottl/context_inferrer_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package ottl
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func Test_NewPriorityContextInferrer_Infer(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
priority []string
17+
statements []string
18+
expected string
19+
}{
20+
{
21+
name: "with priority and contexts",
22+
priority: []string{"spanevent", "span", "resource"},
23+
statements: []string{"set(span.foo, resource.value) where spanevent.bar == true"},
24+
expected: "spanevent",
25+
},
26+
{
27+
name: "with multiple statements",
28+
priority: []string{"spanevent", "span", "resource"},
29+
statements: []string{
30+
"set(resource.foo, resource.value) where span.bar == true",
31+
"set(resource.foo, resource.value) where spanevent.bar == true",
32+
},
33+
expected: "spanevent",
34+
},
35+
{
36+
name: "with no context",
37+
priority: []string{"log", "resource"},
38+
statements: []string{"set(foo, true) where bar == true"},
39+
expected: "",
40+
},
41+
{
42+
name: "with empty priority",
43+
statements: []string{"set(foo.name, true) where bar.name == true"},
44+
expected: "foo",
45+
},
46+
{
47+
name: "with unknown context",
48+
priority: []string{"foo", "bar"},
49+
statements: []string{"set(span.foo, true) where span.bar == true"},
50+
expected: "span",
51+
},
52+
}
53+
54+
for _, tt := range tests {
55+
t.Run(tt.name, func(t *testing.T) {
56+
inferrer := newPriorityContextInferrer(tt.priority)
57+
inferredContext, err := inferrer.infer(tt.statements)
58+
require.NoError(t, err)
59+
assert.Equal(t, tt.expected, inferredContext)
60+
})
61+
}
62+
}
63+
64+
func Test_NewPriorityContextInferrer_InvalidStatement(t *testing.T) {
65+
inferrer := newPriorityContextInferrer([]string{"foo"})
66+
statements := []string{"set(foo.field,"}
67+
_, err := inferrer.infer(statements)
68+
require.ErrorContains(t, err, "unexpected token")
69+
}
70+
71+
func Test_DefaultPriorityContextInferrer(t *testing.T) {
72+
expectedPriority := []string{
73+
"log",
74+
"metric",
75+
"datapoint",
76+
"spanevent",
77+
"span",
78+
"resource",
79+
"scope",
80+
"instrumentation_scope",
81+
}
82+
83+
inferrer := defaultPriorityContextInferrer().(*priorityContextInferrer)
84+
require.NotNil(t, inferrer)
85+
86+
for pri, ctx := range expectedPriority {
87+
require.Equal(t, pri, inferrer.contextPriority[ctx])
88+
}
89+
}

0 commit comments

Comments
 (0)