Skip to content

Commit 9f50349

Browse files
bacherflevan-bradleyTylerHelmuthedmocosta
authored
[pkg/ottl] add event_index to available paths in span event context (#37092)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This PR adds the `event_index` to the available paths in the span event context <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #35778 <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests, e2e tests <!--Describe the documentation added.--> #### Documentation added an entry in the list of available paths in the readme of then span event context <!--Please delete paragraphs that you did not use before submitting.--> --------- Signed-off-by: Florian Bacher <[email protected]> Co-authored-by: Evan Bradley <[email protected]> Co-authored-by: Tyler Helmuth <[email protected]> Co-authored-by: Edmo Vamerlatti Costa <[email protected]>
1 parent e1e7396 commit 9f50349

File tree

5 files changed

+199
-10
lines changed

5 files changed

+199
-10
lines changed

.chloggen/ottl-span-event-index.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add `event_index` to the available paths of the span event context
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [35778]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

pkg/ottl/contexts/ottlspanevent/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The following paths are supported.
3030
| spanevent.time | time of the span event being processed | `time.Time` |
3131
| spanevent.name | name of the span event being processed | string |
3232
| spanevent.dropped_attributes_count | dropped_attributes_count of the span event being processed | int64 |
33+
| spanevent.event_index | index of the span event within the span | int64 |
3334

3435
## Enums
3536

pkg/ottl/contexts/ottlspanevent/span_events.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type TransformContext struct {
3939
cache pcommon.Map
4040
scopeSpans ptrace.ScopeSpans
4141
resouceSpans ptrace.ResourceSpans
42+
eventIndex *int64
4243
}
4344

4445
func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) error {
@@ -47,6 +48,9 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err
4748
err = errors.Join(err, encoder.AddObject("span", logging.Span(tCtx.span)))
4849
err = errors.Join(err, encoder.AddObject("spanevent", logging.SpanEvent(tCtx.spanEvent)))
4950
err = errors.Join(err, encoder.AddObject("cache", logging.Map(tCtx.cache)))
51+
if tCtx.eventIndex != nil {
52+
encoder.AddInt64("event_index", *tCtx.eventIndex)
53+
}
5054
return err
5155
}
5256

@@ -79,6 +83,14 @@ func WithCache(cache *pcommon.Map) TransformContextOption {
7983
}
8084
}
8185

86+
// WithEventIndex sets the index of the SpanEvent within the span, to make it accessible via the event_index property of its context.
87+
// The index must be greater than or equal to zero, otherwise the given val will not be applied.
88+
func WithEventIndex(eventIndex int64) TransformContextOption {
89+
return func(p *TransformContext) {
90+
p.eventIndex = &eventIndex
91+
}
92+
}
93+
8294
func (tCtx TransformContext) GetSpanEvent() ptrace.SpanEvent {
8395
return tCtx.spanEvent
8496
}
@@ -107,6 +119,16 @@ func (tCtx TransformContext) GetResourceSchemaURLItem() internal.SchemaURLItem {
107119
return tCtx.resouceSpans
108120
}
109121

122+
func (tCtx TransformContext) GetEventIndex() (int64, error) {
123+
if tCtx.eventIndex != nil {
124+
if *tCtx.eventIndex < 0 {
125+
return 0, errors.New("found invalid value for 'event_index'")
126+
}
127+
return *tCtx.eventIndex, nil
128+
}
129+
return 0, errors.New("no 'event_index' property has been set")
130+
}
131+
110132
func NewParser(functions map[string]ottl.Factory[TransformContext], telemetrySettings component.TelemetrySettings, options ...Option) (ottl.Parser[TransformContext], error) {
111133
pep := pathExpressionParser{telemetrySettings}
112134
p, err := ottl.NewParser[TransformContext](
@@ -221,6 +243,8 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot
221243
return accessSpanEventAttributesKey(path.Keys()), nil
222244
case "dropped_attributes_count":
223245
return accessSpanEventDroppedAttributeCount(), nil
246+
case "event_index":
247+
return accessSpanEventIndex(), nil
224248
default:
225249
return nil, internal.FormatDefaultErrorMessage(path.Name(), path.String(), contextNameDescription, internal.SpanEventRef)
226250
}
@@ -348,3 +372,14 @@ func accessSpanEventDroppedAttributeCount() ottl.StandardGetSetter[TransformCont
348372
},
349373
}
350374
}
375+
376+
func accessSpanEventIndex() ottl.StandardGetSetter[TransformContext] {
377+
return ottl.StandardGetSetter[TransformContext]{
378+
Getter: func(_ context.Context, tCtx TransformContext) (any, error) {
379+
return tCtx.GetEventIndex()
380+
},
381+
Setter: func(_ context.Context, _ TransformContext, _ any) error {
382+
return errors.New("the 'event_index' path cannot be modified")
383+
},
384+
}
385+
}

pkg/ottl/contexts/ottlspanevent/span_events_test.go

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ func Test_newPathGetSetter(t *testing.T) {
4949
newMap["k2"] = newMap2
5050

5151
tests := []struct {
52-
name string
53-
path ottl.Path[TransformContext]
54-
orig any
55-
newVal any
56-
modified func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map)
52+
name string
53+
path ottl.Path[TransformContext]
54+
orig any
55+
newVal any
56+
expectSetterError bool
57+
modified func(spanEvent ptrace.SpanEvent, span ptrace.Span, il pcommon.InstrumentationScope, resource pcommon.Resource, cache pcommon.Map)
5758
}{
5859
{
5960
name: "span event time",
@@ -407,6 +408,15 @@ func Test_newPathGetSetter(t *testing.T) {
407408
spanEvent.SetDroppedAttributesCount(20)
408409
},
409410
},
411+
{
412+
name: "event_index",
413+
path: &pathtest.Path[TransformContext]{
414+
N: "event_index",
415+
},
416+
orig: int64(1),
417+
newVal: int64(1),
418+
expectSetterError: true,
419+
},
410420
}
411421
// Copy all tests cases and sets the path.Context value to the generated ones.
412422
// It ensures all exiting field access also work when the path context is set.
@@ -426,13 +436,17 @@ func Test_newPathGetSetter(t *testing.T) {
426436

427437
spanEvent, span, il, resource := createTelemetry()
428438

429-
tCtx := NewTransformContext(spanEvent, span, il, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans())
439+
tCtx := NewTransformContext(spanEvent, span, il, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans(), WithEventIndex(1))
430440

431441
got, err := accessor.Get(context.Background(), tCtx)
432442
assert.NoError(t, err)
433443
assert.Equal(t, tt.orig, got)
434444

435445
err = accessor.Set(context.Background(), tCtx, tt.newVal)
446+
if tt.expectSetterError {
447+
assert.Error(t, err)
448+
return
449+
}
436450
assert.NoError(t, err)
437451

438452
exSpanEvent, exSpan, exIl, exRes := createTelemetry()
@@ -520,8 +534,67 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) {
520534
}
521535
}
522536

537+
func Test_setAndGetEventIndex(t *testing.T) {
538+
tests := []struct {
539+
name string
540+
setEventIndex bool
541+
eventIndexValue int64
542+
expected any
543+
expectedErrorMsg string
544+
}{
545+
{
546+
name: "event index set",
547+
setEventIndex: true,
548+
eventIndexValue: 1,
549+
expected: int64(1),
550+
},
551+
{
552+
name: "invalid value for event index",
553+
setEventIndex: true,
554+
eventIndexValue: -1,
555+
expectedErrorMsg: "found invalid value for 'event_index'",
556+
},
557+
{
558+
name: "no value for event index",
559+
setEventIndex: false,
560+
expectedErrorMsg: "no 'event_index' property has been set",
561+
},
562+
}
563+
564+
for _, tt := range tests {
565+
t.Run(tt.name, func(t *testing.T) {
566+
spanEvent, span, il, resource := createTelemetry()
567+
568+
var tCtx TransformContext
569+
if tt.setEventIndex {
570+
tCtx = NewTransformContext(spanEvent, span, il, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans(), WithEventIndex(tt.eventIndexValue))
571+
} else {
572+
tCtx = NewTransformContext(spanEvent, span, il, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans())
573+
}
574+
575+
pep := pathExpressionParser{}
576+
accessor, err := pep.parsePath(&pathtest.Path[TransformContext]{
577+
N: "event_index",
578+
})
579+
assert.NoError(t, err)
580+
581+
got, err := accessor.Get(context.Background(), tCtx)
582+
if tt.expectedErrorMsg != "" {
583+
assert.Error(t, err)
584+
assert.ErrorContains(t, err, tt.expectedErrorMsg)
585+
return
586+
}
587+
assert.NoError(t, err)
588+
assert.Equal(t, tt.expected, got)
589+
})
590+
}
591+
}
592+
523593
func createTelemetry() (ptrace.SpanEvent, ptrace.Span, pcommon.InstrumentationScope, pcommon.Resource) {
524-
spanEvent := ptrace.NewSpanEvent()
594+
span := ptrace.NewSpan()
595+
span.SetName("test")
596+
597+
spanEvent := span.Events().AppendEmpty()
525598

526599
spanEvent.SetName("bear")
527600
spanEvent.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100)))
@@ -562,9 +635,6 @@ func createTelemetry() (ptrace.SpanEvent, ptrace.Span, pcommon.InstrumentationSc
562635
s := spanEvent.Attributes().PutEmptySlice("slice")
563636
s.AppendEmpty().SetEmptyMap().PutStr("map", "pass")
564637

565-
span := ptrace.NewSpan()
566-
span.SetName("test")
567-
568638
il := pcommon.NewInstrumentationScope()
569639
il.SetName("library")
570640
il.SetVersion("version")

pkg/ottl/e2e/e2e_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
1919
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog"
2020
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan"
21+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspanevent"
2122
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs"
2223
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/plogtest"
2324
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/ptracetest"
@@ -1316,6 +1317,40 @@ func Test_ProcessTraces_TraceContext(t *testing.T) {
13161317
}
13171318
}
13181319

1320+
func Test_ProcessSpanEvents(t *testing.T) {
1321+
tests := []struct {
1322+
statement string
1323+
want func(_ ottlspanevent.TransformContext)
1324+
}{
1325+
{
1326+
statement: `set(attributes["index"], event_index)`,
1327+
want: func(tCtx ottlspanevent.TransformContext) {
1328+
tCtx.GetSpanEvent().Attributes().PutInt("index", 0)
1329+
},
1330+
},
1331+
}
1332+
1333+
for _, tt := range tests {
1334+
t.Run(tt.statement, func(t *testing.T) {
1335+
settings := componenttest.NewNopTelemetrySettings()
1336+
funcs := ottlfuncs.StandardFuncs[ottlspanevent.TransformContext]()
1337+
1338+
spanEventParser, err := ottlspanevent.NewParser(funcs, settings)
1339+
assert.NoError(t, err)
1340+
spanStatements, err := spanEventParser.ParseStatement(tt.statement)
1341+
assert.NoError(t, err)
1342+
1343+
tCtx := constructSpanEventTransformContext()
1344+
_, _, _ = spanStatements.Execute(context.Background(), tCtx)
1345+
1346+
exTCtx := constructSpanEventTransformContext()
1347+
tt.want(exTCtx)
1348+
1349+
assert.NoError(t, ptracetest.CompareSpanEvent(newSpanEvent(exTCtx), newSpanEvent(tCtx)))
1350+
})
1351+
}
1352+
}
1353+
13191354
func parseStatementWithAndWithoutPathContext(statement string) ([]*ottl.Statement[ottllog.TransformContext], error) {
13201355
settings := componenttest.NewNopTelemetrySettings()
13211356
parserWithoutPathCtx, err := ottllog.NewParser(ottlfuncs.StandardFuncs[ottllog.TransformContext](), settings)
@@ -1457,6 +1492,21 @@ func constructSpanTransformContext() ottlspan.TransformContext {
14571492
return ottlspan.NewTransformContext(td, scope, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans())
14581493
}
14591494

1495+
func constructSpanEventTransformContext() ottlspanevent.TransformContext {
1496+
resource := pcommon.NewResource()
1497+
1498+
scope := pcommon.NewInstrumentationScope()
1499+
scope.SetName("scope")
1500+
1501+
span := ptrace.NewSpan()
1502+
fillSpanOne(span)
1503+
1504+
ev1 := span.Events().AppendEmpty()
1505+
ev1.SetName("event-1")
1506+
1507+
return ottlspanevent.NewTransformContext(ev1, span, scope, resource, ptrace.NewScopeSpans(), ptrace.NewResourceSpans(), ottlspanevent.WithEventIndex(0))
1508+
}
1509+
14601510
func newResourceLogs(tCtx ottllog.TransformContext) plog.ResourceLogs {
14611511
rl := plog.NewResourceLogs()
14621512
tCtx.GetResource().CopyTo(rl.Resource())
@@ -1477,6 +1527,12 @@ func newResourceSpans(tCtx ottlspan.TransformContext) ptrace.ResourceSpans {
14771527
return rl
14781528
}
14791529

1530+
func newSpanEvent(tCtx ottlspanevent.TransformContext) ptrace.SpanEvent {
1531+
dst := ptrace.NewSpanEvent()
1532+
tCtx.GetSpanEvent().CopyTo(dst)
1533+
return dst
1534+
}
1535+
14801536
func fillSpanOne(span ptrace.Span) {
14811537
span.SetName("operationB")
14821538
span.SetSpanID(spanID)

0 commit comments

Comments
 (0)