diff --git a/collector/processor/coldstartprocessor/README.md b/collector/processor/coldstartprocessor/README.md index 4ef1124c61..994c2edcc1 100644 --- a/collector/processor/coldstartprocessor/README.md +++ b/collector/processor/coldstartprocessor/README.md @@ -7,7 +7,7 @@ | Distributions | [extension] | This processor associates cold start information generated by the [telemetryapireceiver](../../receiver/telemetryapireceiver) with incoming span data processed by -the Collector extension. It reads the following of incoming Lambda execution spans identified by the `faas.execution` attribute: +the Collector extension. It reads the following of incoming Lambda execution spans, identified by the `faas.invocation_id` attribute (or `faas.execution`, the name it replaced in semantic conventions v1.19.0): - trace ID - parent span ID diff --git a/collector/processor/coldstartprocessor/doc.go b/collector/processor/coldstartprocessor/doc.go index 445abb573b..25f983d436 100644 --- a/collector/processor/coldstartprocessor/doc.go +++ b/collector/processor/coldstartprocessor/doc.go @@ -15,7 +15,8 @@ // Package coldstartprocessor correlates cold start information generated by the telemetryapireceiver // with incoming span data. // -// It reads the following of incoming Lambda execution spans identified by the faas.execution attribute: +// It reads the following of incoming Lambda execution spans, identified by the faas.invocation_id +// attribute (or faas.execution, the name it replaced in semantic conventions v1.19.0): // // - trace ID // - parent span ID diff --git a/collector/processor/coldstartprocessor/go.mod b/collector/processor/coldstartprocessor/go.mod index bd18192590..a6faa8760a 100644 --- a/collector/processor/coldstartprocessor/go.mod +++ b/collector/processor/coldstartprocessor/go.mod @@ -12,7 +12,7 @@ require ( go.opentelemetry.io/collector/processor v1.63.0 go.opentelemetry.io/collector/processor/processorhelper v0.157.0 go.opentelemetry.io/collector/processor/processortest v0.157.0 - go.opentelemetry.io/collector/semconv v0.128.0 + go.opentelemetry.io/otel v1.44.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.28.0 ) @@ -37,7 +37,6 @@ require ( go.opentelemetry.io/collector/pdata/testdata v0.157.0 // indirect go.opentelemetry.io/collector/pipeline v1.63.0 // indirect go.opentelemetry.io/collector/processor/xprocessor v0.157.0 // indirect - go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/sdk v1.44.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect diff --git a/collector/processor/coldstartprocessor/go.sum b/collector/processor/coldstartprocessor/go.sum index 8584eb295e..7f379bd72c 100644 --- a/collector/processor/coldstartprocessor/go.sum +++ b/collector/processor/coldstartprocessor/go.sum @@ -71,8 +71,6 @@ go.opentelemetry.io/collector/processor/processortest v0.157.0 h1:dtpyoIvbB3VLqZ go.opentelemetry.io/collector/processor/processortest v0.157.0/go.mod h1:gkRzdmNYfKJAmGCk/x8qDsIavVbNI0wTs+uhN7rY7B0= go.opentelemetry.io/collector/processor/xprocessor v0.157.0 h1:+WyLGrHwcPk3TK+qt7T8CH+oADEMY4r+U5fIMTBvrAc= go.opentelemetry.io/collector/processor/xprocessor v0.157.0/go.mod h1:DPmoWlks+CihSRjTGKvPKK6a8bl9mHk8hxthrJ+lvBI= -go.opentelemetry.io/collector/semconv v0.128.0 h1:MzYOz7Vgb3Kf5D7b49pqqgeUhEmOCuT10bIXb/Cc+k4= -go.opentelemetry.io/collector/semconv v0.128.0/go.mod h1:OPXer4l43X23cnjLXIZnRj/qQOjSuq4TgBLI76P9hns= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= diff --git a/collector/processor/coldstartprocessor/processor.go b/collector/processor/coldstartprocessor/processor.go index 805169079d..4242440ba3 100644 --- a/collector/processor/coldstartprocessor/processor.go +++ b/collector/processor/coldstartprocessor/processor.go @@ -22,10 +22,24 @@ import ( "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/processor/processorhelper" - semconv "go.opentelemetry.io/collector/semconv/v1.5.0" + semconvlegacy "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.25.0" "go.uber.org/zap" ) +// faasInvocationID reports the invocation identifier of an execution span, looking up both the +// current attribute and the one it replaced. +// +// faas.execution was renamed to faas.invocation_id in semantic conventions v1.19.0. Current +// instrumentations set only the new name, while older ones set only the old name, so both have +// to be accepted for the cold start span to be paired with its execution span. +func faasInvocationID(span ptrace.Span) (pcommon.Value, bool) { + if attr, ok := span.Attributes().Get(string(semconv.FaaSInvocationIDKey)); ok { + return attr, true + } + return span.Attributes().Get(string(semconvlegacy.FaaSExecutionKey)) +} + type faasExecution struct { span ptrace.Span scope pcommon.InstrumentationScope @@ -52,7 +66,7 @@ func (p *coldstartProcessor) processTraces(ctx context.Context, td ptrace.Traces if p.reported { return false } - if attr, ok := span.Attributes().Get(semconv.AttributeFaaSColdstart); ok && attr.Bool() { + if attr, ok := span.Attributes().Get(string(semconv.FaaSColdstartKey)); ok && attr.Bool() { if p.faasExecution == nil { sp := ptrace.NewSpan() p.coldstartSpan = &sp @@ -67,7 +81,7 @@ func (p *coldstartProcessor) processTraces(ctx context.Context, td ptrace.Traces return false } } - if _, ok := span.Attributes().Get(semconv.AttributeFaaSExecution); ok { + if _, ok := faasInvocationID(span); ok { if p.coldstartSpan == nil { p.faasExecution = &faasExecution{ span: ptrace.NewSpan(), diff --git a/collector/processor/coldstartprocessor/processor_test.go b/collector/processor/coldstartprocessor/processor_test.go index a815153242..69ad872f20 100644 --- a/collector/processor/coldstartprocessor/processor_test.go +++ b/collector/processor/coldstartprocessor/processor_test.go @@ -33,7 +33,8 @@ import ( "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/processor/processorhelper" "go.opentelemetry.io/collector/processor/processortest" - semconv "go.opentelemetry.io/collector/semconv/v1.5.0" + semconvlegacy "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.25.0" "go.uber.org/multierr" ) @@ -57,7 +58,7 @@ func TestProcessor(t *testing.T) { input: func() ptrace.Traces { td := ptrace.NewTraces() span := td.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) return td }(), expected: ptrace.NewTraces(), @@ -81,7 +82,7 @@ func TestProcessor(t *testing.T) { input: func() ptrace.Traces { td := ptrace.NewTraces() span := td.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) span.Attributes().PutBool("faas.initialization", true) addExecutionSpan(td, executionTraceID) return td @@ -105,7 +106,7 @@ func TestProcessor(t *testing.T) { td := ptrace.NewTraces() addExecutionSpan(td, executionTraceID) span := td.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) span.Attributes().PutBool("faas.initialization", true) return td }(), @@ -144,6 +145,48 @@ func TestProcessor(t *testing.T) { } } +// TestPairingByInvocationID covers the current semantic convention attribute. faas.execution was +// renamed to faas.invocation_id in v1.19.0 and current instrumentations set only the new name, so a +// cold start span must still be paired when the execution span carries it. +func TestPairingByInvocationID(t *testing.T) { + c, err := newColdstartProcessor( + nil, + nil, + processortest.NewNopSettings(Type), + ) + require.NoError(t, err) + + // The cold start span arrives first and is held back until its execution span shows up. + input := ptrace.NewTraces() + span := input.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) + output, err := c.processTraces(context.Background(), input) + require.ErrorIs(t, err, processorhelper.ErrSkipProcessingData) + require.Equal(t, 0, output.SpanCount()) + require.False(t, c.reported) + + executionTraceID := getTraceID() + input = ptrace.NewTraces() + rs := input.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("resource-attr", "faas-execution") + ss := rs.ScopeSpans().AppendEmpty() + ss.Scope().SetName("app/execution") + execSpan := ss.Spans().AppendEmpty() + execSpan.SetTraceID(executionTraceID) + execSpan.Attributes().PutStr(string(semconv.FaaSInvocationIDKey), "af9d5aa4-a685-4c5f-a22b-444f80b3cc28") + + output, err = c.processTraces(context.Background(), input) + require.NoError(t, err) + + // The held cold start span is released alongside the execution span and joins its trace. + require.Equal(t, 2, output.SpanCount()) + require.True(t, c.reported) + spans := output.ResourceSpans().At(0).ScopeSpans().At(0).Spans() + for i := 0; i < spans.Len(); i++ { + require.Equal(t, executionTraceID, spans.At(i).TraceID()) + } +} + func TestMultipleProcessTraces(t *testing.T) { c, err := newColdstartProcessor( nil, @@ -164,7 +207,7 @@ func TestMultipleProcessTraces(t *testing.T) { input = ptrace.NewTraces() expected = ptrace.NewTraces() span := input.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) span.Attributes().PutBool("faas.initialization", true) input.CopyTo(expected) output, err = c.processTraces(context.Background(), input) @@ -186,7 +229,7 @@ func TestMultipleProcessTraces(t *testing.T) { input = ptrace.NewTraces() expected = ptrace.NewTraces() span = input.ResourceSpans().AppendEmpty().ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) span.Attributes().PutBool("faas.initialization", true) input.CopyTo(expected) output, err = c.processTraces(context.Background(), input) @@ -225,17 +268,17 @@ func addExecutionSpan(td ptrace.Traces, id pcommon.TraceID) { ss.Scope().SetName("app/execution") span := ss.Spans().AppendEmpty() span.SetTraceID(id) - span.Attributes().PutStr(semconv.AttributeFaaSExecution, "af9d5aa4-a685-4c5f-a22b-444f80b3cc28") + span.Attributes().PutStr(string(semconvlegacy.FaaSExecutionKey), "af9d5aa4-a685-4c5f-a22b-444f80b3cc28") } func executionSpan(span ptrace.Span, id pcommon.TraceID) { span.SetTraceID(id) - span.Attributes().PutStr(semconv.AttributeFaaSExecution, "af9d5aa4-a685-4c5f-a22b-444f80b3cc28") + span.Attributes().PutStr(string(semconvlegacy.FaaSExecutionKey), "af9d5aa4-a685-4c5f-a22b-444f80b3cc28") } func initializationSpan(span ptrace.Span, id pcommon.TraceID) { span.SetTraceID(id) - span.Attributes().PutBool(semconv.AttributeFaaSColdstart, true) + span.Attributes().PutBool(string(semconv.FaaSColdstartKey), true) span.Attributes().PutBool("faas.initialization", true) }