1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-08-10 22:31:50 +02:00

Make SpanContext Immutable (#1573)

* Make SpanContext Immutable

* Adds NewSpanContext() constructor and SpanContextConfig{} struct for
constructing a new SpanContext when all fields are known
* Adds With<field>() methods to SpanContext for deriving a SpanContext
with a single field changed.
* Updates all uses of SpanContext to use the new API

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>

* Update CHANGELOG.md

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>

* Add tests for new SpanContext constructor and derivation

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>

* Address PR feedback

* Fix new uses of SpanContext from main
This commit is contained in:
Anthony Mirabella
2021-03-09 11:17:29 -05:00
committed by GitHub
parent d75e268053
commit e88a091a72
37 changed files with 554 additions and 343 deletions

View File

@@ -60,11 +60,11 @@ type outOfThinAirPropagator struct {
var _ propagation.TextMapPropagator = outOfThinAirPropagator{}
func (p outOfThinAirPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
sc := trace.SpanContext{
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: 0,
}
})
require.True(p.t, sc.IsValid())
return trace.ContextWithRemoteSpanContext(ctx, sc)
}

View File

@@ -52,13 +52,13 @@ func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) {
return
}
carrier.Set(tracestateHeader, sc.TraceState.String())
carrier.Set(tracestateHeader, sc.TraceState().String())
h := fmt.Sprintf("%.2x-%s-%s-%.2x",
supportedVersion,
sc.TraceID,
sc.SpanID,
sc.TraceFlags&trace.FlagsSampled)
sc.TraceID(),
sc.SpanID(),
sc.TraceFlags()&trace.FlagsSampled)
carrier.Set(traceparentHeader, h)
}
@@ -107,9 +107,9 @@ func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
return trace.SpanContext{}
}
var sc trace.SpanContext
var scc trace.SpanContextConfig
sc.TraceID, err = trace.TraceIDFromHex(matches[2][:32])
scc.TraceID, err = trace.TraceIDFromHex(matches[2][:32])
if err != nil {
return trace.SpanContext{}
}
@@ -117,7 +117,7 @@ func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
if len(matches[3]) != 16 {
return trace.SpanContext{}
}
sc.SpanID, err = trace.SpanIDFromHex(matches[3])
scc.SpanID, err = trace.SpanIDFromHex(matches[3])
if err != nil {
return trace.SpanContext{}
}
@@ -130,10 +130,11 @@ func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
return trace.SpanContext{}
}
// Clear all flags other than the trace-context supported sampling bit.
sc.TraceFlags = opts[0] & trace.FlagsSampled
scc.TraceFlags = opts[0] & trace.FlagsSampled
sc.TraceState = parseTraceState(carrier.Get(tracestateHeader))
scc.TraceState = parseTraceState(carrier.Get(tracestateHeader))
sc := trace.NewSpanContext(scc)
if !sc.IsValid() {
return trace.SpanContext{}
}

View File

@@ -43,11 +43,11 @@ func injectSubBenchmarks(b *testing.B, fn func(context.Context, *testing.B)) {
mockTracer := oteltest.DefaultTracer()
b.ReportAllocs()
sc := trace.SpanContext{
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
}
})
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc)
ctx, _ = mockTracer.Start(ctx, "inject")
fn(ctx, b)

View File

@@ -37,72 +37,72 @@ func TestExtractValidTraceContextFromHTTPReq(t *testing.T) {
{
name: "valid w3cHeader",
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
},
}),
},
{
name: "valid w3cHeader and sampled",
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
{
name: "future version",
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
{
name: "future options with sampled bit set",
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
{
name: "future options with sampled bit cleared",
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
},
}),
},
{
name: "future additional data",
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09-XYZxsf09",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
{
name: "valid b3Header ending in dash",
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
{
name: "future valid b3Header ending in dash",
header: "01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09-",
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
},
}
@@ -114,7 +114,7 @@ func TestExtractValidTraceContextFromHTTPReq(t *testing.T) {
ctx := context.Background()
ctx = prop.Extract(ctx, propagation.HeaderCarrier(req.Header))
gotSc := trace.RemoteSpanContextFromContext(ctx)
if diff := cmp.Diff(gotSc, tt.wantSc, cmp.AllowUnexported(trace.TraceState{})); diff != "" {
if diff := cmp.Diff(gotSc, tt.wantSc, cmp.Comparer(func(sc, other trace.SpanContext) bool { return sc.Equal(other) })); diff != "" {
t.Errorf("Extract Tracecontext: %s: -got +want %s", tt.name, diff)
}
})
@@ -219,28 +219,28 @@ func TestInjectTraceContextToHTTPReq(t *testing.T) {
}{
{
name: "valid spancontext, sampled",
sc: trace.SpanContext{
sc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
}),
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01",
},
{
name: "valid spancontext, not sampled",
sc: trace.SpanContext{
sc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
},
}),
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000003-00",
},
{
name: "valid spancontext, with unsupported bit set in traceflags",
sc: trace.SpanContext{
sc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: 0xff,
},
}),
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000004-01",
},
{
@@ -298,11 +298,11 @@ func TestTraceStatePropagation(t *testing.T) {
stateHeader: "key1=value1,key2=value2",
},
valid: true,
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceState: state,
},
}),
},
{
name: "valid parent, invalid state",
@@ -311,10 +311,10 @@ func TestTraceStatePropagation(t *testing.T) {
stateHeader: "key1=value1,invalid$@#=invalid",
},
valid: false,
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
},
}),
},
{
name: "valid parent, malformed state",
@@ -323,10 +323,10 @@ func TestTraceStatePropagation(t *testing.T) {
stateHeader: "key1=value1,invalid",
},
valid: false,
wantSc: trace.SpanContext{
wantSc: trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
},
}),
},
}