Skip to content

Commit

Permalink
record links with empty span context
Browse files Browse the repository at this point in the history
  • Loading branch information
amanakin committed May 9, 2024
1 parent 9e7d744 commit f4cfe82
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- De-duplicate map attributes added to a `Record` in `go.opentelemetry.io/otel/sdk/log`. (#5230)
- The `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` exporter won't print `AttributeValueLengthLimit` and `AttributeCountLimit` fields now, instead it prints the `DroppedAttributes` field. (#5272)
- Improved performance in the `Stringer` implementation of `go.opentelemetry.io/otel/baggage.Member` by reducing the number of allocations. (#5286)
- Span in `go.opentelemetry.io/otel/sdk/trace` will record links without span context if either non-empty `TraceState` or attributes are provided. (#5315)

## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24

Expand Down
6 changes: 5 additions & 1 deletion sdk/trace/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,11 @@ func (s *recordingSpan) Resource() *resource.Resource {
}

func (s *recordingSpan) AddLink(link trace.Link) {
if !s.IsRecording() || !link.SpanContext.IsValid() {
if !s.IsRecording() {
return
}
if !link.SpanContext.IsValid() && len(link.Attributes) == 0 &&
link.SpanContext.TraceState().Len() == 0 {
return
}

Expand Down
88 changes: 87 additions & 1 deletion sdk/trace/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,9 @@ func TestEmptyRecordingSpanDroppedAttributes(t *testing.T) {
}

func TestSpanAddLink(t *testing.T) {
ts, err := trace.ParseTraceState("k=v")
require.NoError(t, err)

tests := []struct {
name string
attrLinkCountLimit int
Expand All @@ -1934,7 +1937,6 @@ func TestSpanAddLink(t *testing.T) {
attrLinkCountLimit: 128,
link: trace.Link{
SpanContext: trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{}), SpanID: [8]byte{}}),
Attributes: []attribute.KeyValue{{Key: "k1", Value: attribute.StringValue("v1")}},
},
want: &snapshot{
name: "span0",
Expand Down Expand Up @@ -2002,6 +2004,50 @@ func TestSpanAddLink(t *testing.T) {
instrumentationScope: instrumentation.Scope{Name: "AddLinkWithMoreAttributesThanLimit"},
},
},
{
name: "AddLinkWithAttributesEmptySpanContext",
attrLinkCountLimit: 128,
link: trace.Link{
Attributes: []attribute.KeyValue{{Key: "k1", Value: attribute.StringValue("v1")}},
},
want: &snapshot{
name: "span0",
spanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: tid,
TraceFlags: 0x1,
}),
parent: sc.WithRemote(true),
links: []Link{
{
Attributes: []attribute.KeyValue{{Key: "k1", Value: attribute.StringValue("v1")}},
},
},
spanKind: trace.SpanKindInternal,
instrumentationScope: instrumentation.Scope{Name: "AddLinkWithAttributesEmptySpanContext"},
},
},
{
name: "AddLinkWithTraceStateEmptySpanContext",
attrLinkCountLimit: 128,
link: trace.Link{
SpanContext: trace.SpanContext{}.WithTraceState(ts),
},
want: &snapshot{
name: "span0",
spanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: tid,
TraceFlags: 0x1,
}),
parent: sc.WithRemote(true),
links: []Link{
{
SpanContext: trace.SpanContext{}.WithTraceState(ts),
},
},
spanKind: trace.SpanKindInternal,
instrumentationScope: instrumentation.Scope{Name: "AddLinkWithTraceStateEmptySpanContext"},
},
},
}

for _, tc := range tests {
Expand All @@ -2026,3 +2072,43 @@ func TestSpanAddLink(t *testing.T) {
})
}
}

func TestAddLinkToNonRecordingSpan(t *testing.T) {
te := NewTestExporter()
sl := NewSpanLimits()
tp := NewTracerProvider(
WithSpanLimits(sl),
WithSyncer(te),
WithResource(resource.Empty()),
)

attrs := []attribute.KeyValue{{Key: "k", Value: attribute.StringValue("v")}}

span := startSpan(tp, "AddLinkToNonRecordingSpan")
_, err := endSpan(te, span)
require.NoError(t, err)

// Add link after ending span
span.AddLink(trace.Link{
SpanContext: sc,
Attributes: attrs,
})

require.Equal(t, 1, te.Len())
got := te.Spans()[0]
want := &snapshot{
name: "span0",
spanContext: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: tid,
TraceFlags: 0x1,
}),
parent: sc.WithRemote(true),
links: nil,
spanKind: trace.SpanKindInternal,
instrumentationScope: instrumentation.Scope{Name: "AddLinkToNonRecordingSpan"},
}

if diff := cmpDiff(got, want); diff != "" {
t.Errorf("AddLinkToNonRecordingSpan: -got +want %s", diff)
}
}

0 comments on commit f4cfe82

Please sign in to comment.