Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion collector/processor/coldstartprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion collector/processor/coldstartprocessor/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions collector/processor/coldstartprocessor/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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
Expand Down
2 changes: 0 additions & 2 deletions collector/processor/coldstartprocessor/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
20 changes: 17 additions & 3 deletions collector/processor/coldstartprocessor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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(),
Expand Down
61 changes: 52 additions & 9 deletions collector/processor/coldstartprocessor/processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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(),
Expand All @@ -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
Expand All @@ -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
}(),
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down
Loading