-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Don't attempt to replace the current SpanContext
In general, it turns out that replacing the current SpanContext is not a good idea. Indeed, the OTel documentation [says][1]: > Please note, since `SpanContext` is immutable, it is not possible to > update `SpanContext` with a new `TraceState`. Such changes then make > sense only right before `SpanContext` propagation or telemetry data > exporting. In both cases, `Propagator`s and `SpanExporter`s may create > a modified `TraceState` copy before serializing it to the wire. So this commit updates how we set `TraceOptions` *during* a span. Namely, when we update `TraceOptions` using `WithTraceOptions`, we no longer modify the `SpanContext`. Instead, the value of `TraceOptions` is stored on the `Context`, from where it can be retrieved by `TraceOptionsPropagator` at propagation time. This means that at propagation we will respect any `TraceOptions` set in the `TraceState`, *unless* TraceOptions have been set in the `Context`, in which case those will be used instead. [1]: https://opentelemetry.io/docs/specs/otel/trace/api/#tracestate
- Loading branch information
1 parent
209e6ed
commit e251d3c
Showing
5 changed files
with
175 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package telemetry | ||
|
||
import ( | ||
"context" | ||
|
||
"go.opentelemetry.io/otel/propagation" | ||
"go.opentelemetry.io/otel/trace" | ||
) | ||
|
||
// Check TraceOptionsPropagator implements TextMapPropagator | ||
var _ propagation.TextMapPropagator = new(TraceOptionsPropagator) | ||
|
||
type TraceOptionsPropagator struct { | ||
Next propagation.TextMapPropagator | ||
} | ||
|
||
func (p *TraceOptionsPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { | ||
sc := trace.SpanContextFromContext(ctx) | ||
if !sc.IsValid() { | ||
return | ||
} | ||
|
||
// If TraceOptions has been set directly in the context, then replace the | ||
// SpanContext with one that has the appropriate TraceState. | ||
// | ||
// Note: it is generally only safe to do this in a propagator or an exporter. | ||
if to, ok := traceOptionsFromContextOnly(ctx); ok { | ||
ts := setTraceOptions(sc.TraceState(), to) | ||
ctx = trace.ContextWithSpanContext(ctx, sc.WithTraceState(ts)) | ||
} | ||
|
||
p.Next.Inject(ctx, carrier) | ||
} | ||
|
||
func (p *TraceOptionsPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { | ||
return p.Next.Extract(ctx, carrier) | ||
} | ||
|
||
func (p *TraceOptionsPropagator) Fields() []string { | ||
return p.Next.Fields() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package telemetry | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.opentelemetry.io/otel/propagation" | ||
"go.opentelemetry.io/otel/trace" | ||
) | ||
|
||
func makeValidSpanContextConfig() trace.SpanContextConfig { | ||
traceID, _ := trace.TraceIDFromHex("0123456789abcdef0123456789abcdef") | ||
spanID, _ := trace.SpanIDFromHex("0123456789abcdef") | ||
return trace.SpanContextConfig{ | ||
TraceID: traceID, | ||
SpanID: spanID, | ||
} | ||
} | ||
|
||
func makeValidSpanContext() trace.SpanContext { | ||
return trace.NewSpanContext(makeValidSpanContextConfig()) | ||
} | ||
|
||
// Check that we're correctly passing the work onto the Next propagator. | ||
func TestTraceOptionsPropagatorUsesNextPropagator(t *testing.T) { | ||
ctx := context.Background() | ||
ctx = trace.ContextWithSpanContext(ctx, makeValidSpanContext()) | ||
propagator := TraceOptionsPropagator{ | ||
Next: propagation.TraceContext{}, | ||
} | ||
carrier := propagation.MapCarrier{} | ||
|
||
propagator.Inject(ctx, carrier) | ||
|
||
require.Contains(t, carrier, "traceparent") | ||
} | ||
|
||
// Check that TraceOptions are respected both in SpanContext and | ||
// (preferentially) from the Context itself. | ||
func TestTraceOptionsPropagatorInjectsTraceOptions(t *testing.T) { | ||
ctx := context.Background() | ||
|
||
ts := trace.TraceState{} | ||
ts, _ = ts.Insert("r8/sm", "always") | ||
|
||
scc := makeValidSpanContextConfig() | ||
scc.TraceState = ts | ||
ctx = trace.ContextWithSpanContext(ctx, trace.NewSpanContext(scc)) | ||
propagator := TraceOptionsPropagator{ | ||
Next: propagation.TraceContext{}, | ||
} | ||
|
||
// First check that only the sample mode field is set | ||
{ | ||
carrier := propagation.MapCarrier{} | ||
propagator.Inject(ctx, carrier) | ||
require.Contains(t, carrier, "tracestate") | ||
assert.Equal(t, carrier["tracestate"], "r8/sm=always") | ||
} | ||
|
||
// Then update TraceOptions locally and ensure that the values override those | ||
// set in the SpanContext. | ||
{ | ||
ctx := WithTraceOptions(ctx, TraceOptions{ | ||
DetailLevel: DetailLevelFull, | ||
SampleMode: SampleModeNever, | ||
}) | ||
carrier := propagation.MapCarrier{} | ||
propagator.Inject(ctx, carrier) | ||
require.Contains(t, carrier, "tracestate") | ||
assert.Contains(t, carrier["tracestate"], "r8/sm=never") | ||
assert.Contains(t, carrier["tracestate"], "r8/dl=full") | ||
} | ||
} | ||
|
||
func TestTraceOptionsPropagatorPrefersTraceOptionsFromContext(t *testing.T) { | ||
ctx := trace.ContextWithSpanContext(context.Background(), makeValidSpanContext()) | ||
ctx = WithTraceOptions(ctx, TraceOptions{ | ||
DetailLevel: DetailLevelFull, | ||
SampleMode: SampleModeAlways, | ||
}) | ||
|
||
propagator := TraceOptionsPropagator{ | ||
Next: propagation.TraceContext{}, | ||
} | ||
carrier := propagation.MapCarrier{} | ||
|
||
propagator.Inject(ctx, carrier) | ||
|
||
require.Contains(t, carrier, "tracestate") | ||
assert.Contains(t, carrier["tracestate"], "r8/sm=always") | ||
assert.Contains(t, carrier["tracestate"], "r8/dl=full") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters