From e88a091a72e4cf4c3f3a6d6e4b440d757bd3ecac Mon Sep 17 00:00:00 2001 From: Anthony Mirabella Date: Tue, 9 Mar 2021 11:17:29 -0500 Subject: [PATCH] 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() 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 * Update CHANGELOG.md Signed-off-by: Anthony J Mirabella * Add tests for new SpanContext constructor and derivation Signed-off-by: Anthony J Mirabella * Address PR feedback * Fix new uses of SpanContext from main --- CHANGELOG.md | 6 +- bridge/opencensus/bridge.go | 2 +- bridge/opencensus/bridge_test.go | 12 +- bridge/opencensus/utils/utils.go | 10 +- bridge/opencensus/utils/utils_test.go | 28 ++- bridge/opentracing/internal/mock.go | 8 +- bridge/opentracing/mix_test.go | 18 +- exporters/otlp/internal/otlptest/data.go | 4 +- exporters/otlp/internal/transform/span.go | 16 +- .../otlp/internal/transform/span_test.go | 15 +- exporters/otlp/otlp_span_test.go | 16 +- exporters/stdout/trace_test.go | 4 +- exporters/trace/jaeger/jaeger.go | 18 +- exporters/trace/jaeger/jaeger_test.go | 16 +- exporters/trace/zipkin/model.go | 4 +- exporters/trace/zipkin/model_test.go | 36 ++-- exporters/trace/zipkin/zipkin_test.go | 8 +- oteltest/config.go | 9 +- oteltest/harness.go | 20 +-- oteltest/tracer.go | 16 +- oteltest/tracer_test.go | 34 ++-- propagation/propagators_test.go | 4 +- propagation/trace_context.go | 19 +- propagation/trace_context_benchmark_test.go | 4 +- propagation/trace_context_test.go | 58 +++---- sdk/trace/batch_span_processor_test.go | 13 +- sdk/trace/benchmark_test.go | 8 +- sdk/trace/sampling.go | 8 +- sdk/trace/sampling_test.go | 18 +- sdk/trace/simple_span_processor_test.go | 6 +- sdk/trace/span.go | 32 ++-- sdk/trace/span_processor_test.go | 16 +- sdk/trace/trace_test.go | 164 +++++++++--------- trace/config_test.go | 4 +- trace/go.mod | 1 + trace/trace.go | 114 +++++++++++- trace/trace_test.go | 128 ++++++++++++-- 37 files changed, 554 insertions(+), 343 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a047f645ac..ba983f0cf34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] -## Added +### Added - Added `Marshler` config option to `otlphttp` to enable otlp over json or protobufs. (#1586) - A `ForceFlush` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` to flush all registered `SpanProcessor`s. (#1608) - ### Changed - Update the `ForceFlush` method signature to the `"go.opentelemetry.io/otel/sdk/trace".SpanProcessor` to accept a `context.Context` and return an error. (#1608) @@ -22,6 +21,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `"go.opentelemetry.io/sdk/metric/controller.basic".WithPusher` is replaced with `WithExporter` to provide consistent naming across project. (#1656) - Added non-empty string check for trace `Attribute` keys. (#1659) - Add `description` to SpanStatus only when `StatusCode` is set to error. (#1662) +- `trace.SpanContext` is now immutable and has no exported fields. (#1573) + - `trace.NewSpanContext()` can be used in conjunction with the `trace.SpanContextConfig` struct to initialize a new `SpanContext` where all values are known. ### Removed @@ -29,7 +30,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm These are now returned as a SpanProcessor interface from their respective constructors. (#1638) - Removed setting status to `Error` while recording an error as a span event in `RecordError`. (#1663) - ### Fixed - `SamplingResult.TraceState` is correctly propagated to a newly created diff --git a/bridge/opencensus/bridge.go b/bridge/opencensus/bridge.go index b5620394294..b63fb94ec2e 100644 --- a/bridge/opencensus/bridge.go +++ b/bridge/opencensus/bridge.go @@ -186,5 +186,5 @@ func (s *span) AddLink(l octrace.Link) { } func (s *span) String() string { - return fmt.Sprintf("span %s", s.otSpan.SpanContext().SpanID.String()) + return fmt.Sprintf("span %s", s.otSpan.SpanContext().SpanID().String()) } diff --git a/bridge/opencensus/bridge_test.go b/bridge/opencensus/bridge_test.go index b06bb9e7d15..4616500dfa1 100644 --- a/bridge/opencensus/bridge_test.go +++ b/bridge/opencensus/bridge_test.go @@ -66,8 +66,8 @@ func TestMixedAPIs(t *testing.T) { // Reverse the order we look at the spans in, since they are listed in last-to-first order. i = len(spans) - i - 1 // Verify that OpenCensus spans and opentelemetry spans have each other as parents. - if spans[i].ParentSpanID() != parent.SpanContext().SpanID { - t.Errorf("Span %v had parent %v. Expected %d", spans[i].Name(), spans[i].ParentSpanID(), parent.SpanContext().SpanID) + if spans[i].ParentSpanID() != parent.SpanContext().SpanID() { + t.Errorf("Span %v had parent %v. Expected %d", spans[i].Name(), spans[i].ParentSpanID(), parent.SpanContext().SpanID()) } parent = spans[i] } @@ -111,8 +111,8 @@ func TestStartSpanWithRemoteParent(t *testing.T) { t.Fatalf("Got %d spans, exepected %d", len(spans), 1) } - if spans[0].ParentSpanID() != parent.SpanContext().SpanID { - t.Errorf("Span %v, had parent %v. Expected %d", spans[0].Name(), spans[0].ParentSpanID(), parent.SpanContext().SpanID) + if spans[0].ParentSpanID() != parent.SpanContext().SpanID() { + t.Errorf("Span %v, had parent %v. Expected %d", spans[0].Name(), spans[0].ParentSpanID(), parent.SpanContext().SpanID()) } } @@ -150,8 +150,8 @@ func TestToFromContext(t *testing.T) { // Reverse the order we look at the spans in, since they are listed in last-to-first order. i = len(spans) - i - 1 // Verify that OpenCensus spans and opentelemetry spans have each other as parents. - if spans[i].ParentSpanID() != parent.SpanContext().SpanID { - t.Errorf("Span %v had parent %v. Expected %d", spans[i].Name(), spans[i].ParentSpanID(), parent.SpanContext().SpanID) + if spans[i].ParentSpanID() != parent.SpanContext().SpanID() { + t.Errorf("Span %v had parent %v. Expected %d", spans[i].Name(), spans[i].ParentSpanID(), parent.SpanContext().SpanID()) } parent = spans[i] } diff --git a/bridge/opencensus/utils/utils.go b/bridge/opencensus/utils/utils.go index e12a5450dd5..c5abc6728cb 100644 --- a/bridge/opencensus/utils/utils.go +++ b/bridge/opencensus/utils/utils.go @@ -28,7 +28,7 @@ import ( // error handler. func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext { if sc.IsDebug() || sc.IsDeferred() { - otel.Handle(fmt.Errorf("ignoring OpenTelemetry Debug or Deferred trace flags for span %q because they are not supported by OpenCensus", sc.SpanID)) + otel.Handle(fmt.Errorf("ignoring OpenTelemetry Debug or Deferred trace flags for span %q because they are not supported by OpenCensus", sc.SpanID())) } var to octrace.TraceOptions if sc.IsSampled() { @@ -36,8 +36,8 @@ func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext { to = 0x1 } return octrace.SpanContext{ - TraceID: octrace.TraceID(sc.TraceID), - SpanID: octrace.SpanID(sc.SpanID), + TraceID: octrace.TraceID(sc.TraceID()), + SpanID: octrace.SpanID(sc.SpanID()), TraceOptions: to, } } @@ -49,9 +49,9 @@ func OCSpanContextToOTel(sc octrace.SpanContext) trace.SpanContext { if sc.IsSampled() { traceFlags = trace.FlagsSampled } - return trace.SpanContext{ + return trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID(sc.TraceID), SpanID: trace.SpanID(sc.SpanID), TraceFlags: traceFlags, - } + }) } diff --git a/bridge/opencensus/utils/utils_test.go b/bridge/opencensus/utils/utils_test.go index c324b34204c..9e7a7741b67 100644 --- a/bridge/opencensus/utils/utils_test.go +++ b/bridge/opencensus/utils/utils_test.go @@ -35,11 +35,11 @@ func TestOTelSpanContextToOC(t *testing.T) { }, { description: "sampled", - input: trace.SpanContext{ + input: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), TraceFlags: trace.FlagsSampled, - }, + }), expected: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), @@ -48,10 +48,10 @@ func TestOTelSpanContextToOC(t *testing.T) { }, { description: "not sampled", - input: trace.SpanContext{ + input: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), - }, + }), expected: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), @@ -60,11 +60,11 @@ func TestOTelSpanContextToOC(t *testing.T) { }, { description: "debug flag", - input: trace.SpanContext{ + input: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), TraceFlags: trace.FlagsDebug, - }, + }), expected: octrace.SpanContext{ TraceID: octrace.TraceID([16]byte{1}), SpanID: octrace.SpanID([8]byte{2}), @@ -97,11 +97,11 @@ func TestOCSpanContextToOTel(t *testing.T) { SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0x1), }, - expected: trace.SpanContext{ + expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), TraceFlags: trace.FlagsSampled, - }, + }), }, { description: "not sampled", @@ -110,10 +110,10 @@ func TestOCSpanContextToOTel(t *testing.T) { SpanID: octrace.SpanID([8]byte{2}), TraceOptions: octrace.TraceOptions(0), }, - expected: trace.SpanContext{ + expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), - }, + }), }, { description: "trace state is ignored", @@ -122,17 +122,15 @@ func TestOCSpanContextToOTel(t *testing.T) { SpanID: octrace.SpanID([8]byte{2}), Tracestate: &tracestate.Tracestate{}, }, - expected: trace.SpanContext{ + expected: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{1}), SpanID: trace.SpanID([8]byte{2}), - }, + }), }, } { t.Run(tc.description, func(t *testing.T) { output := OCSpanContextToOTel(tc.input) - if output.SpanID != tc.expected.SpanID || - output.TraceID != tc.expected.TraceID || - output.TraceFlags != tc.expected.TraceFlags { + if !output.Equal(tc.expected) { t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected) } }) diff --git a/bridge/opentracing/internal/mock.go b/bridge/opentracing/internal/mock.go index cc477cf8d49..7d6459c6228 100644 --- a/bridge/opentracing/internal/mock.go +++ b/bridge/opentracing/internal/mock.go @@ -76,11 +76,11 @@ func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanO if startTime.IsZero() { startTime = time.Now() } - spanContext := trace.SpanContext{ + spanContext := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: t.getTraceID(ctx, config), SpanID: t.getSpanID(), TraceFlags: 0, - } + }) span := &MockSpan{ mockTracer: t, officialTracer: t, @@ -117,7 +117,7 @@ func (t *MockTracer) addSpareContextValue(ctx context.Context) context.Context { func (t *MockTracer) getTraceID(ctx context.Context, config *trace.SpanConfig) trace.TraceID { if parent := t.getParentSpanContext(ctx, config); parent.IsValid() { - return parent.TraceID + return parent.TraceID() } if len(t.SpareTraceIDs) > 0 { traceID := t.SpareTraceIDs[0] @@ -132,7 +132,7 @@ func (t *MockTracer) getTraceID(ctx context.Context, config *trace.SpanConfig) t func (t *MockTracer) getParentSpanID(ctx context.Context, config *trace.SpanConfig) trace.SpanID { if parent := t.getParentSpanContext(ctx, config); parent.IsValid() { - return parent.SpanID + return parent.SpanID() } return trace.SpanID{} } diff --git a/bridge/opentracing/mix_test.go b/bridge/opentracing/mix_test.go index 368332a5d00..7bd65ce0303 100644 --- a/bridge/opentracing/mix_test.go +++ b/bridge/opentracing/mix_test.go @@ -229,12 +229,12 @@ func (cast *currentActiveSpanTest) runOTOtelOT(t *testing.T, ctx context.Context } func (cast *currentActiveSpanTest) recordSpans(t *testing.T, ctx context.Context) context.Context { - spanID := trace.SpanContextFromContext(ctx).SpanID + spanID := trace.SpanContextFromContext(ctx).SpanID() cast.recordedCurrentOtelSpanIDs = append(cast.recordedCurrentOtelSpanIDs, spanID) spanID = trace.SpanID{} if bridgeSpan, ok := ot.SpanFromContext(ctx).(*bridgeSpan); ok { - spanID = bridgeSpan.otelSpan.SpanContext().SpanID + spanID = bridgeSpan.otelSpan.SpanContext().SpanID() } cast.recordedActiveOTSpanIDs = append(cast.recordedActiveOTSpanIDs, spanID) return ctx @@ -637,19 +637,19 @@ func checkTraceAndSpans(t *testing.T, tracer *internal.MockTracer, expectedTrace } for idx, span := range tracer.FinishedSpans { sctx := span.SpanContext() - if sctx.TraceID != expectedTraceID { - t.Errorf("Expected trace ID %v in span %d (%d), got %v", expectedTraceID, idx, sctx.SpanID, sctx.TraceID) + if sctx.TraceID() != expectedTraceID { + t.Errorf("Expected trace ID %v in span %d (%d), got %v", expectedTraceID, idx, sctx.SpanID(), sctx.TraceID()) } expectedSpanID := spanIDs[idx] expectedParentSpanID := parentSpanIDs[idx] - if sctx.SpanID != expectedSpanID { - t.Errorf("Expected finished span %d to have span ID %d, but got %d", idx, expectedSpanID, sctx.SpanID) + if sctx.SpanID() != expectedSpanID { + t.Errorf("Expected finished span %d to have span ID %d, but got %d", idx, expectedSpanID, sctx.SpanID()) } if span.ParentSpanID != expectedParentSpanID { - t.Errorf("Expected finished span %d (span ID: %d) to have parent span ID %d, but got %d", idx, sctx.SpanID, expectedParentSpanID, span.ParentSpanID) + t.Errorf("Expected finished span %d (span ID: %d) to have parent span ID %d, but got %d", idx, sctx.SpanID(), expectedParentSpanID, span.ParentSpanID) } - if span.SpanKind != sks[span.SpanContext().SpanID] { - t.Errorf("Expected finished span %d (span ID: %d) to have span.kind to be '%v' but was '%v'", idx, sctx.SpanID, sks[span.SpanContext().SpanID], span.SpanKind) + if span.SpanKind != sks[span.SpanContext().SpanID()] { + t.Errorf("Expected finished span %d (span ID: %d) to have span.kind to be '%v' but was '%v'", idx, sctx.SpanID(), sks[span.SpanContext().SpanID()], span.SpanKind) } } } diff --git a/exporters/otlp/internal/otlptest/data.go b/exporters/otlp/internal/otlptest/data.go index da054134733..354e6c43d6c 100644 --- a/exporters/otlp/internal/otlptest/data.go +++ b/exporters/otlp/internal/otlptest/data.go @@ -82,11 +82,11 @@ func (OneRecordCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelect // may be useful for testing driver's trace export. func SingleSpanSnapshot() []*exporttrace.SpanSnapshot { sd := &exporttrace.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9}, SpanID: trace.SpanID{3, 4, 5, 6, 7, 8, 9, 0}, TraceFlags: trace.FlagsSampled, - }, + }), ParentSpanID: trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8}, SpanKind: trace.SpanKindInternal, Name: "foo", diff --git a/exporters/otlp/internal/transform/span.go b/exporters/otlp/internal/transform/span.go index 2e57f1ddd1f..1634015d0c0 100644 --- a/exporters/otlp/internal/transform/span.go +++ b/exporters/otlp/internal/transform/span.go @@ -101,10 +101,13 @@ func span(sd *export.SpanSnapshot) *tracepb.Span { return nil } + tid := sd.SpanContext.TraceID() + sid := sd.SpanContext.SpanID() + s := &tracepb.Span{ - TraceId: sd.SpanContext.TraceID[:], - SpanId: sd.SpanContext.SpanID[:], - TraceState: sd.SpanContext.TraceState.String(), + TraceId: tid[:], + SpanId: sid[:], + TraceState: sd.SpanContext.TraceState().String(), Status: status(sd.StatusCode, sd.StatusMessage), StartTimeUnixNano: uint64(sd.StartTime.UnixNano()), EndTimeUnixNano: uint64(sd.EndTime.UnixNano()), @@ -152,9 +155,12 @@ func links(links []trace.Link) []*tracepb.Span_Link { // being reused -- in short we need a new otLink per iteration. otLink := otLink + tid := otLink.TraceID() + sid := otLink.SpanID() + sl = append(sl, &tracepb.Span_Link{ - TraceId: otLink.TraceID[:], - SpanId: otLink.SpanID[:], + TraceId: tid[:], + SpanId: sid[:], Attributes: Attributes(otLink.Attributes), }) } diff --git a/exporters/otlp/internal/transform/span_test.go b/exporters/otlp/internal/transform/span_test.go index 163357a876b..c020619f302 100644 --- a/exporters/otlp/internal/transform/span_test.go +++ b/exporters/otlp/internal/transform/span_test.go @@ -148,8 +148,7 @@ func TestLinks(t *testing.T) { assert.Equal(t, expected, got[1]) // Changes to our links should not change the produced links. - l[1].TraceID[0] = byte(0x1) - l[1].SpanID[0] = byte(0x1) + l[1].SpanContext = l[1].WithTraceID(trace.TraceID{}) assert.Equal(t, expected, got[1]) } @@ -201,11 +200,11 @@ func TestSpanData(t *testing.T) { endTime := startTime.Add(10 * time.Second) traceState, _ := trace.TraceStateFromKeyValues(attribute.String("key1", "val1"), attribute.String("key2", "val2")) spanData := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, TraceState: traceState, - }, + }), SpanKind: trace.SpanKindServer, ParentSpanID: trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}, Name: "span data to span data", @@ -225,21 +224,21 @@ func TestSpanData(t *testing.T) { }, Links: []trace.Link{ { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF}, SpanID: trace.SpanID{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, TraceFlags: 0, - }, + }), Attributes: []attribute.KeyValue{ attribute.String("LinkType", "Parent"), }, }, { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}, SpanID: trace.SpanID{0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7}, TraceFlags: 0, - }, + }), Attributes: []attribute.KeyValue{ attribute.String("LinkType", "Child"), }, diff --git a/exporters/otlp/otlp_span_test.go b/exporters/otlp/otlp_span_test.go index 77a06e8c655..c8524c5675e 100644 --- a/exporters/otlp/otlp_span_test.go +++ b/exporters/otlp/otlp_span_test.go @@ -55,11 +55,11 @@ func TestExportSpans(t *testing.T) { { []*tracesdk.SpanSnapshot{ { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), TraceFlags: byte(1), - }, + }), SpanKind: trace.SpanKindServer, Name: "parent process", StartTime: startTime, @@ -77,11 +77,11 @@ func TestExportSpans(t *testing.T) { }, }, { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), TraceFlags: byte(1), - }, + }), SpanKind: trace.SpanKindServer, Name: "secondary parent process", StartTime: startTime, @@ -99,11 +99,11 @@ func TestExportSpans(t *testing.T) { }, }, { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), TraceFlags: byte(1), - }, + }), ParentSpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), SpanKind: trace.SpanKindInternal, Name: "internal process", @@ -122,11 +122,11 @@ func TestExportSpans(t *testing.T) { }, }, { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), TraceFlags: byte(1), - }, + }), SpanKind: trace.SpanKindServer, Name: "parent process", StartTime: startTime, diff --git a/exporters/stdout/trace_test.go b/exporters/stdout/trace_test.go index af61302df2f..166b436fe9d 100644 --- a/exporters/stdout/trace_test.go +++ b/exporters/stdout/trace_test.go @@ -48,11 +48,11 @@ func TestExporter_ExportSpan(t *testing.T) { resource := resource.NewWithAttributes(attribute.String("rk1", "rv11")) testSpan := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceState: traceState, - }, + }), Name: "/foo", StartTime: now, EndTime: now, diff --git a/exporters/trace/jaeger/jaeger.go b/exporters/trace/jaeger/jaeger.go index 76932edadf1..eff694412e1 100644 --- a/exporters/trace/jaeger/jaeger.go +++ b/exporters/trace/jaeger/jaeger.go @@ -328,21 +328,25 @@ func spanSnapshotToThrift(ss *export.SpanSnapshot) *gen.Span { var refs []*gen.SpanRef for _, link := range ss.Links { + tid := link.TraceID() + sid := link.SpanID() refs = append(refs, &gen.SpanRef{ - TraceIdHigh: int64(binary.BigEndian.Uint64(link.TraceID[0:8])), - TraceIdLow: int64(binary.BigEndian.Uint64(link.TraceID[8:16])), - SpanId: int64(binary.BigEndian.Uint64(link.SpanID[:])), + TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])), + TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])), + SpanId: int64(binary.BigEndian.Uint64(sid[:])), RefType: gen.SpanRefType_FOLLOWS_FROM, }) } + tid := ss.SpanContext.TraceID() + sid := ss.SpanContext.SpanID() return &gen.Span{ - TraceIdHigh: int64(binary.BigEndian.Uint64(ss.SpanContext.TraceID[0:8])), - TraceIdLow: int64(binary.BigEndian.Uint64(ss.SpanContext.TraceID[8:16])), - SpanId: int64(binary.BigEndian.Uint64(ss.SpanContext.SpanID[:])), + TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])), + TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])), + SpanId: int64(binary.BigEndian.Uint64(sid[:])), ParentSpanId: int64(binary.BigEndian.Uint64(ss.ParentSpanID[:])), OperationName: ss.Name, // TODO: if span kind is added then add prefix "Sent"/"Recv" - Flags: int32(ss.SpanContext.TraceFlags), + Flags: int32(ss.SpanContext.TraceFlags()), StartTime: ss.StartTime.UnixNano() / 1000, Duration: ss.EndTime.Sub(ss.StartTime).Nanoseconds() / 1000, Tags: tags, diff --git a/exporters/trace/jaeger/jaeger_test.go b/exporters/trace/jaeger/jaeger_test.go index fed27d44d8e..32190225db9 100644 --- a/exporters/trace/jaeger/jaeger_test.go +++ b/exporters/trace/jaeger/jaeger_test.go @@ -383,19 +383,19 @@ func Test_spanSnapshotToThrift(t *testing.T) { { name: "no parent", data: &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, - }, + }), Name: "/foo", StartTime: now, EndTime: now, Links: []trace.Link{ { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: linkTraceID, SpanID: linkSpanID, - }, + }), }, }, Attributes: []attribute.KeyValue{ @@ -466,16 +466,16 @@ func Test_spanSnapshotToThrift(t *testing.T) { name: "with parent", data: &export.SpanSnapshot{ ParentSpanID: parentSpanID, - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, - }, + }), Links: []trace.Link{ { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: linkTraceID, SpanID: linkSpanID, - }, + }), }, }, Name: "/foo", diff --git a/exporters/trace/zipkin/model.go b/exporters/trace/zipkin/model.go index a670926aea1..38813cad97e 100644 --- a/exporters/trace/zipkin/model.go +++ b/exporters/trace/zipkin/model.go @@ -58,8 +58,8 @@ func toZipkinSpanModel(data *export.SpanSnapshot, serviceName string) zkmodel.Sp func toZipkinSpanContext(data *export.SpanSnapshot) zkmodel.SpanContext { return zkmodel.SpanContext{ - TraceID: toZipkinTraceID(data.SpanContext.TraceID), - ID: toZipkinID(data.SpanContext.SpanID), + TraceID: toZipkinTraceID(data.SpanContext.TraceID()), + ID: toZipkinID(data.SpanContext.SpanID()), ParentID: toZipkinParentID(data.ParentSpanID), Debug: false, Sampled: nil, diff --git a/exporters/trace/zipkin/model_test.go b/exporters/trace/zipkin/model_test.go index 82b2c333b65..f41826f0608 100644 --- a/exporters/trace/zipkin/model_test.go +++ b/exporters/trace/zipkin/model_test.go @@ -35,10 +35,10 @@ func TestModelConversion(t *testing.T) { inputBatch := []*export.SpanSnapshot{ // typical span data { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindServer, Name: "foo", @@ -68,10 +68,10 @@ func TestModelConversion(t *testing.T) { // span data with no parent (same as typical, but has // invalid parent) { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{}, SpanKind: trace.SpanKindServer, Name: "foo", @@ -100,10 +100,10 @@ func TestModelConversion(t *testing.T) { }, // span data of unspecified kind { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindUnspecified, Name: "foo", @@ -132,10 +132,10 @@ func TestModelConversion(t *testing.T) { }, // span data of internal kind { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindInternal, Name: "foo", @@ -164,10 +164,10 @@ func TestModelConversion(t *testing.T) { }, // span data of client kind { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindClient, Name: "foo", @@ -196,10 +196,10 @@ func TestModelConversion(t *testing.T) { }, // span data of producer kind { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindProducer, Name: "foo", @@ -228,10 +228,10 @@ func TestModelConversion(t *testing.T) { }, // span data of consumer kind { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindConsumer, Name: "foo", @@ -260,10 +260,10 @@ func TestModelConversion(t *testing.T) { }, // span data with no events { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindServer, Name: "foo", @@ -279,10 +279,10 @@ func TestModelConversion(t *testing.T) { }, // span data with an "error" attribute set to "false" { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, SpanKind: trace.SpanKindServer, Name: "foo", diff --git a/exporters/trace/zipkin/zipkin_test.go b/exporters/trace/zipkin/zipkin_test.go index cf04112a5aa..30ba5469342 100644 --- a/exporters/trace/zipkin/zipkin_test.go +++ b/exporters/trace/zipkin/zipkin_test.go @@ -242,10 +242,10 @@ func TestExportSpans(t *testing.T) { spans := []*export.SpanSnapshot{ // parent { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - }, + }), ParentSpanID: trace.SpanID{}, SpanKind: trace.SpanKindServer, Name: "foo", @@ -258,10 +258,10 @@ func TestExportSpans(t *testing.T) { }, // child { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, SpanID: trace.SpanID{0xDF, 0xDE, 0xDD, 0xDC, 0xDB, 0xDA, 0xD9, 0xD8}, - }, + }), ParentSpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, SpanKind: trace.SpanKindServer, Name: "bar", diff --git a/oteltest/config.go b/oteltest/config.go index 30a8798c4a9..9ede2124c44 100644 --- a/oteltest/config.go +++ b/oteltest/config.go @@ -33,10 +33,13 @@ func defaultSpanContextFunc() func(context.Context) trace.SpanContext { } else if rsc := trace.RemoteSpanContextFromContext(ctx); rsc.IsValid() { sc = rsc } else { - binary.BigEndian.PutUint64(sc.TraceID[:], atomic.AddUint64(&traceID, 1)) + var tid trace.TraceID + binary.BigEndian.PutUint64(tid[:], atomic.AddUint64(&traceID, 1)) + sc = sc.WithTraceID(tid) } - binary.BigEndian.PutUint64(sc.SpanID[:], atomic.AddUint64(&spanID, 1)) - return sc + var sid trace.SpanID + binary.BigEndian.PutUint64(sid[:], atomic.AddUint64(&spanID, 1)) + return sc.WithSpanID(sid) } } diff --git a/oteltest/harness.go b/oteltest/harness.go index e5f7c6494bd..f6809e0273c 100644 --- a/oteltest/harness.go +++ b/oteltest/harness.go @@ -148,8 +148,8 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { sc1 := span1.SpanContext() sc2 := span2.SpanContext() - e.Expect(sc1.TraceID).NotToEqual(sc2.TraceID) - e.Expect(sc1.SpanID).NotToEqual(sc2.SpanID) + e.Expect(sc1.TraceID()).NotToEqual(sc2.TraceID()) + e.Expect(sc1.SpanID()).NotToEqual(sc2.SpanID()) }) t.Run("records the span if specified", func(t *testing.T) { @@ -175,8 +175,8 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { psc := parent.SpanContext() csc := child.SpanContext() - e.Expect(csc.TraceID).ToEqual(psc.TraceID) - e.Expect(csc.SpanID).NotToEqual(psc.SpanID) + e.Expect(csc.TraceID()).ToEqual(psc.TraceID()) + e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) { @@ -191,8 +191,8 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { psc := parent.SpanContext() csc := child.SpanContext() - e.Expect(csc.TraceID).NotToEqual(psc.TraceID) - e.Expect(csc.SpanID).NotToEqual(psc.SpanID) + e.Expect(csc.TraceID()).NotToEqual(psc.TraceID()) + e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("propagates remote parent's trace ID through the context", func(t *testing.T) { @@ -208,8 +208,8 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { psc := remoteParent.SpanContext() csc := child.SpanContext() - e.Expect(csc.TraceID).ToEqual(psc.TraceID) - e.Expect(csc.SpanID).NotToEqual(psc.SpanID) + e.Expect(csc.TraceID()).ToEqual(psc.TraceID()) + e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) { @@ -225,8 +225,8 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) { psc := remoteParent.SpanContext() csc := child.SpanContext() - e.Expect(csc.TraceID).NotToEqual(psc.TraceID) - e.Expect(csc.SpanID).NotToEqual(psc.SpanID) + e.Expect(csc.TraceID()).NotToEqual(psc.TraceID()) + e.Expect(csc.SpanID()).NotToEqual(psc.SpanID()) }) }) diff --git a/oteltest/tracer.go b/oteltest/tracer.go index 555e71a8fe1..f9302650e13 100644 --- a/oteltest/tracer.go +++ b/oteltest/tracer.go @@ -70,20 +70,20 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio } else { span.spanContext = t.config.SpanContextFunc(ctx) if lsc := trace.SpanContextFromContext(ctx); lsc.IsValid() { - span.spanContext.TraceID = lsc.TraceID - span.parentSpanID = lsc.SpanID + span.spanContext = span.spanContext.WithTraceID(lsc.TraceID()) + span.parentSpanID = lsc.SpanID() } else if rsc := trace.RemoteSpanContextFromContext(ctx); rsc.IsValid() { - span.spanContext.TraceID = rsc.TraceID - span.parentSpanID = rsc.SpanID + span.spanContext = span.spanContext.WithTraceID(rsc.TraceID()) + span.parentSpanID = rsc.SpanID() } } for _, link := range c.Links { for i, sl := range span.links { - if sl.SpanContext.SpanID == link.SpanContext.SpanID && - sl.SpanContext.TraceID == link.SpanContext.TraceID && - sl.SpanContext.TraceFlags == link.SpanContext.TraceFlags && - sl.SpanContext.TraceState.String() == link.SpanContext.TraceState.String() { + if sl.SpanContext.SpanID() == link.SpanContext.SpanID() && + sl.SpanContext.TraceID() == link.SpanContext.TraceID() && + sl.SpanContext.TraceFlags() == link.SpanContext.TraceFlags() && + sl.SpanContext.TraceState().String() == link.SpanContext.TraceState().String() { span.links[i].Attributes = link.Attributes break } diff --git a/oteltest/tracer_test.go b/oteltest/tracer_test.go index 5cca521e9bc..e1ab877f136 100644 --- a/oteltest/tracer_test.go +++ b/oteltest/tracer_test.go @@ -97,9 +97,9 @@ func TestTracer(t *testing.T) { e.Expect(ok).ToBeTrue() childSpanContext := testSpan.SpanContext() - e.Expect(childSpanContext.TraceID).ToEqual(parentSpanContext.TraceID) - e.Expect(childSpanContext.SpanID).NotToEqual(parentSpanContext.SpanID) - e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID) + e.Expect(childSpanContext.TraceID()).ToEqual(parentSpanContext.TraceID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID()) + e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID()) }) t.Run("uses the current span from context as parent, even if it has remote span context", func(t *testing.T) { @@ -120,9 +120,9 @@ func TestTracer(t *testing.T) { e.Expect(ok).ToBeTrue() childSpanContext := testSpan.SpanContext() - e.Expect(childSpanContext.TraceID).ToEqual(parentSpanContext.TraceID) - e.Expect(childSpanContext.SpanID).NotToEqual(parentSpanContext.SpanID) - e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID) + e.Expect(childSpanContext.TraceID()).ToEqual(parentSpanContext.TraceID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID()) + e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID()) }) t.Run("uses the remote span context from context as parent, if current span is missing", func(t *testing.T) { @@ -142,9 +142,9 @@ func TestTracer(t *testing.T) { e.Expect(ok).ToBeTrue() childSpanContext := testSpan.SpanContext() - e.Expect(childSpanContext.TraceID).ToEqual(remoteParentSpanContext.TraceID) - e.Expect(childSpanContext.SpanID).NotToEqual(remoteParentSpanContext.SpanID) - e.Expect(testSpan.ParentSpanID()).ToEqual(remoteParentSpanContext.SpanID) + e.Expect(childSpanContext.TraceID()).ToEqual(remoteParentSpanContext.TraceID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(remoteParentSpanContext.SpanID()) + e.Expect(testSpan.ParentSpanID()).ToEqual(remoteParentSpanContext.SpanID()) }) t.Run("creates new root when both current span and remote span context are missing", func(t *testing.T) { @@ -165,10 +165,10 @@ func TestTracer(t *testing.T) { e.Expect(ok).ToBeTrue() childSpanContext := testSpan.SpanContext() - e.Expect(childSpanContext.TraceID).NotToEqual(parentSpanContext.TraceID) - e.Expect(childSpanContext.TraceID).NotToEqual(remoteParentSpanContext.TraceID) - e.Expect(childSpanContext.SpanID).NotToEqual(parentSpanContext.SpanID) - e.Expect(childSpanContext.SpanID).NotToEqual(remoteParentSpanContext.SpanID) + e.Expect(childSpanContext.TraceID()).NotToEqual(parentSpanContext.TraceID()) + e.Expect(childSpanContext.TraceID()).NotToEqual(remoteParentSpanContext.TraceID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(remoteParentSpanContext.SpanID()) e.Expect(testSpan.ParentSpanID().IsValid()).ToBeFalse() }) @@ -191,10 +191,10 @@ func TestTracer(t *testing.T) { e.Expect(ok).ToBeTrue() childSpanContext := testSpan.SpanContext() - e.Expect(childSpanContext.TraceID).NotToEqual(parentSpanContext.TraceID) - e.Expect(childSpanContext.TraceID).NotToEqual(remoteParentSpanContext.TraceID) - e.Expect(childSpanContext.SpanID).NotToEqual(parentSpanContext.SpanID) - e.Expect(childSpanContext.SpanID).NotToEqual(remoteParentSpanContext.SpanID) + e.Expect(childSpanContext.TraceID()).NotToEqual(parentSpanContext.TraceID()) + e.Expect(childSpanContext.TraceID()).NotToEqual(remoteParentSpanContext.TraceID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID()) + e.Expect(childSpanContext.SpanID()).NotToEqual(remoteParentSpanContext.SpanID()) e.Expect(testSpan.ParentSpanID().IsValid()).ToBeFalse() expectedLinks := []trace.Link{ diff --git a/propagation/propagators_test.go b/propagation/propagators_test.go index 4cf72e67955..cf2ee533981 100644 --- a/propagation/propagators_test.go +++ b/propagation/propagators_test.go @@ -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) } diff --git a/propagation/trace_context.go b/propagation/trace_context.go index fc52975cfcb..18c8c385a65 100644 --- a/propagation/trace_context.go +++ b/propagation/trace_context.go @@ -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{} } diff --git a/propagation/trace_context_benchmark_test.go b/propagation/trace_context_benchmark_test.go index 9bb66f2fb3f..095b343c54e 100644 --- a/propagation/trace_context_benchmark_test.go +++ b/propagation/trace_context_benchmark_test.go @@ -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) diff --git a/propagation/trace_context_test.go b/propagation/trace_context_test.go index 7601cf440fa..ebbbd580e36 100644 --- a/propagation/trace_context_test.go +++ b/propagation/trace_context_test.go @@ -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, - }, + }), }, } diff --git a/sdk/trace/batch_span_processor_test.go b/sdk/trace/batch_span_processor_test.go index 21349079580..6b721ffe322 100644 --- a/sdk/trace/batch_span_processor_test.go +++ b/sdk/trace/batch_span_processor_test.go @@ -210,7 +210,10 @@ func generateSpan(t *testing.T, parallel bool, tr trace.Tracer, option testOptio wg := &sync.WaitGroup{} for i := 0; i < option.genNumSpans; i++ { - binary.BigEndian.PutUint64(sc.TraceID[0:8], uint64(i+1)) + tid := sc.TraceID() + binary.BigEndian.PutUint64(tid[0:8], uint64(i+1)) + newSc := sc.WithTraceID(tid) + wg.Add(1) f := func(sc trace.SpanContext) { ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) @@ -219,9 +222,9 @@ func generateSpan(t *testing.T, parallel bool, tr trace.Tracer, option testOptio wg.Done() } if parallel { - go f(sc) + go f(newSc) } else { - f(sc) + f(newSc) } } wg.Wait() @@ -230,11 +233,11 @@ func generateSpan(t *testing.T, parallel bool, tr trace.Tracer, option testOptio func getSpanContext() trace.SpanContext { tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ := trace.SpanIDFromHex("0102040810203040") - return trace.SpanContext{ + return trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, - } + }) } func TestBatchSpanProcessorShutdown(t *testing.T) { diff --git a/sdk/trace/benchmark_test.go b/sdk/trace/benchmark_test.go index f6d5357d92f..3243f5a24d0 100644 --- a/sdk/trace/benchmark_test.go +++ b/sdk/trace/benchmark_test.go @@ -117,21 +117,21 @@ func BenchmarkSpanWithAttributes_all_2x(b *testing.B) { func BenchmarkTraceID_DotString(b *testing.B) { t, _ := trace.TraceIDFromHex("0000000000000001000000000000002a") - sc := trace.SpanContext{TraceID: t} + sc := trace.NewSpanContext(trace.SpanContextConfig{TraceID: t}) want := "0000000000000001000000000000002a" for i := 0; i < b.N; i++ { - if got := sc.TraceID.String(); got != want { + if got := sc.TraceID().String(); got != want { b.Fatalf("got = %q want = %q", got, want) } } } func BenchmarkSpanID_DotString(b *testing.B) { - sc := trace.SpanContext{SpanID: trace.SpanID{1}} + sc := trace.NewSpanContext(trace.SpanContextConfig{SpanID: trace.SpanID{1}}) want := "0100000000000000" for i := 0; i < b.N; i++ { - if got := sc.SpanID.String(); got != want { + if got := sc.SpanID().String(); got != want { b.Fatalf("got = %q want = %q", got, want) } } diff --git a/sdk/trace/sampling.go b/sdk/trace/sampling.go index 59c08369de1..30063886964 100644 --- a/sdk/trace/sampling.go +++ b/sdk/trace/sampling.go @@ -73,12 +73,12 @@ func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult if x < ts.traceIDUpperBound { return SamplingResult{ Decision: RecordAndSample, - Tracestate: p.ParentContext.TraceState, + Tracestate: p.ParentContext.TraceState(), } } return SamplingResult{ Decision: Drop, - Tracestate: p.ParentContext.TraceState, + Tracestate: p.ParentContext.TraceState(), } } @@ -111,7 +111,7 @@ type alwaysOnSampler struct{} func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult { return SamplingResult{ Decision: RecordAndSample, - Tracestate: p.ParentContext.TraceState, + Tracestate: p.ParentContext.TraceState(), } } @@ -132,7 +132,7 @@ type alwaysOffSampler struct{} func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult { return SamplingResult{ Decision: Drop, - Tracestate: p.ParentContext.TraceState, + Tracestate: p.ParentContext.TraceState(), } } diff --git a/sdk/trace/sampling_test.go b/sdk/trace/sampling_test.go index 0d6f4f24e79..61b38eb78f3 100644 --- a/sdk/trace/sampling_test.go +++ b/sdk/trace/sampling_test.go @@ -30,11 +30,11 @@ func TestParentBasedDefaultLocalParentSampled(t *testing.T) { sampler := ParentBased(AlwaysSample()) traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") - parentCtx := trace.SpanContext{ + parentCtx := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, - } + }) if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != RecordAndSample { t.Error("Sampling decision should be RecordAndSample") } @@ -44,10 +44,10 @@ func TestParentBasedDefaultLocalParentNotSampled(t *testing.T) { sampler := ParentBased(AlwaysSample()) traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") - parentCtx := trace.SpanContext{ + parentCtx := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, - } + }) if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != Drop { t.Error("Sampling decision should be Drop") } @@ -108,13 +108,13 @@ func TestParentBasedWithSamplerOptions(t *testing.T) { t.Run(tc.name, func(t *testing.T) { traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736") spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7") - parentCtx := trace.SpanContext{ + parentCtx := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, - } + }) if tc.isParentSampled { - parentCtx.TraceFlags = trace.FlagsSampled + parentCtx = parentCtx.WithTraceFlags(trace.FlagsSampled) } params := SamplingParameters{ParentContext: parentCtx} @@ -225,9 +225,9 @@ func TestTracestateIsPassed(t *testing.T) { t.Error(err) } - parentCtx := trace.SpanContext{ + parentCtx := trace.NewSpanContext(trace.SpanContextConfig{ TraceState: traceState, - } + }) params := SamplingParameters{ParentContext: parentCtx} require.Equal(t, traceState, tc.sampler.ShouldSample(params).Tracestate, "TraceState is not equal") diff --git a/sdk/trace/simple_span_processor_test.go b/sdk/trace/simple_span_processor_test.go index a5c8ff0531d..95f237528c6 100644 --- a/sdk/trace/simple_span_processor_test.go +++ b/sdk/trace/simple_span_processor_test.go @@ -60,11 +60,11 @@ func TestNewSimpleSpanProcessorWithNilExporter(t *testing.T) { func startSpan(tp trace.TracerProvider) trace.Span { tr := tp.Tracer("SimpleSpanProcessor") - sc := trace.SpanContext{ + sc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, - } + }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) _, span := tr.Start(ctx, "OnEnd") return span @@ -79,7 +79,7 @@ func TestSimpleSpanProcessorOnEnd(t *testing.T) { startSpan(tp).End() wantTraceID := tid - gotTraceID := te.spans[0].SpanContext.TraceID + gotTraceID := te.spans[0].SpanContext.TraceID() if wantTraceID != gotTraceID { t.Errorf("SimplerSpanProcessor OnEnd() check: got %+v, want %+v\n", gotTraceID, wantTraceID) } diff --git a/sdk/trace/span.go b/sdk/trace/span.go index a747c82be83..99b236727f4 100644 --- a/sdk/trace/span.go +++ b/sdk/trace/span.go @@ -451,7 +451,7 @@ func (s *span) Snapshot() *export.SpanSnapshot { sd.HasRemoteParent = s.hasRemoteParent sd.InstrumentationLibrary = s.instrumentationLibrary sd.Name = s.name - sd.ParentSpanID = s.parent.SpanID + sd.ParentSpanID = s.parent.SpanID() sd.Resource = s.resource sd.SpanContext = s.spanContext sd.SpanKind = s.spanKind @@ -520,18 +520,28 @@ func (*span) private() {} func startSpanInternal(ctx context.Context, tr *tracer, name string, parent trace.SpanContext, remoteParent bool, o *trace.SpanConfig) *span { span := &span{} - span.spanContext = parent cfg := tr.provider.config.Load().(*Config) + var tid trace.TraceID + var sid trace.SpanID + if hasEmptySpanContext(parent) { // Generate both TraceID and SpanID - span.spanContext.TraceID, span.spanContext.SpanID = cfg.IDGenerator.NewIDs(ctx) + tid, sid = cfg.IDGenerator.NewIDs(ctx) } else { // TraceID already exists, just generate a SpanID - span.spanContext.SpanID = cfg.IDGenerator.NewSpanID(ctx, parent.TraceID) + tid = parent.TraceID() + sid = cfg.IDGenerator.NewSpanID(ctx, tid) } + span.spanContext = trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: tid, + SpanID: sid, + TraceFlags: parent.TraceFlags(), + TraceState: parent.TraceState(), + }) + span.attributes = newAttributesMap(cfg.SpanLimits.AttributeCountLimit) span.messageEvents = newEvictedQueue(cfg.SpanLimits.EventCountLimit) span.links = newEvictedQueue(cfg.SpanLimits.LinkCountLimit) @@ -549,7 +559,7 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, parent trac kind: o.SpanKind, } sampled := makeSamplingDecision(data) - span.spanContext.TraceState = sampled.Tracestate + span.spanContext = span.spanContext.WithTraceState(sampled.Tracestate) if !span.spanContext.IsSampled() && !o.Record { return span @@ -575,10 +585,7 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, parent trac } func hasEmptySpanContext(parent trace.SpanContext) bool { - return parent.SpanID == emptySpanContext.SpanID && - parent.TraceID == emptySpanContext.TraceID && - parent.TraceFlags == emptySpanContext.TraceFlags && - parent.TraceState.IsEmpty() + return parent.Equal(emptySpanContext) } type samplingData struct { @@ -595,10 +602,9 @@ type samplingData struct { func makeSamplingDecision(data samplingData) SamplingResult { sampler := data.cfg.DefaultSampler - spanContext := &data.span.spanContext sampled := sampler.ShouldSample(SamplingParameters{ ParentContext: data.parent, - TraceID: spanContext.TraceID, + TraceID: data.span.spanContext.TraceID(), Name: data.name, HasRemoteParent: data.remoteParent, Kind: data.kind, @@ -606,9 +612,9 @@ func makeSamplingDecision(data samplingData) SamplingResult { Links: data.links, }) if sampled.Decision == RecordAndSample { - spanContext.TraceFlags |= trace.FlagsSampled + data.span.spanContext = data.span.spanContext.WithTraceFlags(data.span.spanContext.TraceFlags() | trace.FlagsSampled) } else { - spanContext.TraceFlags &^= trace.FlagsSampled + data.span.spanContext = data.span.spanContext.WithTraceFlags(data.span.spanContext.TraceFlags() &^ trace.FlagsSampled) } return sampled } diff --git a/sdk/trace/span_processor_test.go b/sdk/trace/span_processor_test.go index 4e3061ef435..b9029aba22e 100644 --- a/sdk/trace/span_processor_test.go +++ b/sdk/trace/span_processor_test.go @@ -43,11 +43,11 @@ func (t *testSpanProcessor) OnStart(parent context.Context, s sdktrace.ReadWrite // a more meaningful way. { Key: "ParentTraceID", - Value: attribute.StringValue(psc.TraceID.String()), + Value: attribute.StringValue(psc.TraceID().String()), }, { Key: "ParentSpanID", - Value: attribute.StringValue(psc.SpanID.String()), + Value: attribute.StringValue(psc.SpanID().String()), }, } s.AddEvent("OnStart", trace.WithAttributes(kv...)) @@ -79,10 +79,10 @@ func TestRegisterSpanProcessor(t *testing.T) { tid, _ := trace.TraceIDFromHex("01020304050607080102040810203040") sid, _ := trace.SpanIDFromHex("0102040810203040") - parent := trace.SpanContext{ + parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, - } + }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) tr := tp.Tracer("SpanProcessor") @@ -114,14 +114,14 @@ func TestRegisterSpanProcessor(t *testing.T) { c++ case "ParentTraceID": gotValue := kv.Value.AsString() - if gotValue != parent.TraceID.String() { - t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.TraceID) + if gotValue != parent.TraceID().String() { + t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.TraceID()) } tidOK = true case "ParentSpanID": gotValue := kv.Value.AsString() - if gotValue != parent.SpanID.String() { - t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.SpanID) + if gotValue != parent.SpanID().String() { + t.Errorf("%s: attributes: got %s, want %s\n", name, gotValue, parent.SpanID()) } sidOK = true default: diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go index fb46f393efc..2b8d2f184dd 100644 --- a/sdk/trace/trace_test.go +++ b/sdk/trace/trace_test.go @@ -277,12 +277,12 @@ func TestSampling(t *testing.T) { ctx := context.Background() if tc.parent { tid, sid := idg.NewIDs(ctx) - psc := trace.SpanContext{ + psc := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, - } + }) if tc.sampledParent { - psc.TraceFlags = trace.FlagsSampled + psc = psc.WithTraceFlags(trace.FlagsSampled) } ctx = trace.ContextWithRemoteSpanContext(ctx, psc) } @@ -313,11 +313,11 @@ func TestStartSpanWithParent(t *testing.T) { tr := tp.Tracer("SpanWithParent") ctx := context.Background() - sc1 := trace.SpanContext{ + sc1 := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, - } + }) _, s1 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc1), "span1-unsampled-parent1") if err := checkChild(t, sc1, s1); err != nil { t.Error(err) @@ -332,12 +332,12 @@ func TestStartSpanWithParent(t *testing.T) { if err != nil { t.Error(err) } - sc2 := trace.SpanContext{ + sc2 := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 0x1, TraceState: ts, - } + }) _, s3 := tr.Start(trace.ContextWithRemoteSpanContext(ctx, sc2), "span3-sampled-parent2") if err := checkChild(t, sc2, s3); err != nil { t.Error(err) @@ -369,10 +369,10 @@ func TestSetSpanAttributesOnStart(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: []attribute.KeyValue{ @@ -399,10 +399,10 @@ func TestSetSpanAttributes(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: []attribute.KeyValue{ @@ -435,27 +435,27 @@ func TestSamplerAttributesLocalChildSpan(t *testing.T) { // endSpan expects only a single span in the test exporter, so manually clear the // fields that can't be tested for easily (times, span and trace ids). - pid := got[0].SpanContext.SpanID - got[0].SpanContext.TraceID = tid + pid := got[0].SpanContext.SpanID() + got[0].SpanContext = got[0].SpanContext.WithTraceID(tid) got[0].ParentSpanID = sid checkTime(&got[0].StartTime) checkTime(&got[0].EndTime) - got[1].SpanContext.SpanID = trace.SpanID{} - got[1].SpanContext.TraceID = tid + got[1].SpanContext = got[1].SpanContext.WithSpanID(trace.SpanID{}) + got[1].SpanContext = got[1].SpanContext.WithTraceID(tid) got[1].ParentSpanID = pid - got[0].SpanContext.SpanID = trace.SpanID{} + got[0].SpanContext = got[0].SpanContext.WithSpanID(trace.SpanID{}) checkTime(&got[1].StartTime) checkTime(&got[1].EndTime) want := []*export.SpanSnapshot{ { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span1", Attributes: []attribute.KeyValue{attribute.Int("callCount", 2)}, @@ -464,10 +464,10 @@ func TestSamplerAttributesLocalChildSpan(t *testing.T) { InstrumentationLibrary: instrumentation.Library{Name: "SpanTwo"}, }, { - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: pid, Name: "span0", Attributes: []attribute.KeyValue{attribute.Int("callCount", 1)}, @@ -500,10 +500,10 @@ func TestSetSpanAttributesOverLimit(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: []attribute.KeyValue{ @@ -535,10 +535,10 @@ func TestSetSpanAttributesWithInvalidKey(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: []attribute.KeyValue{ @@ -580,10 +580,10 @@ func TestEvents(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", HasRemoteParent: true, @@ -630,10 +630,10 @@ func TestEventsOverLimit(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", MessageEvents: []trace.Event{ @@ -658,8 +658,8 @@ func TestLinks(t *testing.T) { k2v2 := attribute.String("key2", "value2") k3v3 := attribute.String("key3", "value3") - sc1 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} - sc2 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} + sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) + sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) links := []trace.Link{ {SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1}}, @@ -673,10 +673,10 @@ func TestLinks(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", HasRemoteParent: true, @@ -692,9 +692,9 @@ func TestLinks(t *testing.T) { func TestLinksOverLimit(t *testing.T) { te := NewTestExporter() - sc1 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} - sc2 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} - sc3 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} + sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) + sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) + sc3 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) tp := NewTracerProvider(WithSpanLimits(SpanLimits{LinkCountLimit: 2}), WithSyncer(te), WithResource(resource.Empty())) @@ -715,10 +715,10 @@ func TestLinksOverLimit(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Links: []trace.Link{ @@ -741,11 +741,11 @@ func TestSetSpanName(t *testing.T) { ctx := context.Background() want := "SpanName-1" - ctx = trace.ContextWithRemoteSpanContext(ctx, trace.SpanContext{ + ctx = trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 1, - }) + })) _, span := tp.Tracer("SetSpanName").Start(ctx, "SpanName-1") got, err := endSpan(te, span) if err != nil { @@ -769,10 +769,10 @@ func TestSetSpanStatus(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", SpanKind: trace.SpanKindInternal, @@ -798,10 +798,10 @@ func TestSetSpanStatusWithoutMessageWhenStatusIsNotError(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", SpanKind: trace.SpanKindInternal, @@ -823,11 +823,11 @@ func cmpDiff(x, y interface{}) string { } func remoteSpanContext() trace.SpanContext { - return trace.SpanContext{ + return trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: 1, - } + }) } // checkChild is test utility function that tests that c has fields set appropriately, @@ -837,16 +837,16 @@ func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error { if s == nil { return fmt.Errorf("got nil child span, want non-nil") } - if got, want := s.spanContext.TraceID.String(), p.TraceID.String(); got != want { + if got, want := s.spanContext.TraceID().String(), p.TraceID().String(); got != want { return fmt.Errorf("got child trace ID %s, want %s", got, want) } - if childID, parentID := s.spanContext.SpanID.String(), p.SpanID.String(); childID == parentID { + if childID, parentID := s.spanContext.SpanID().String(), p.SpanID().String(); childID == parentID { return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID) } - if got, want := s.spanContext.TraceFlags, p.TraceFlags; got != want { + if got, want := s.spanContext.TraceFlags(), p.TraceFlags(); got != want { return fmt.Errorf("got child trace options %d, want %d", got, want) } - got, want := s.spanContext.TraceState, p.TraceState + got, want := s.spanContext.TraceState(), p.TraceState() assert.Equal(t, want, got) return nil } @@ -909,10 +909,10 @@ func endSpan(te *testExporter, span trace.Span) (*export.SpanSnapshot, error) { return nil, fmt.Errorf("got %d exported spans, want one span", te.Len()) } got := te.Spans()[0] - if !got.SpanContext.SpanID.IsValid() { + if !got.SpanContext.SpanID().IsValid() { return nil, fmt.Errorf("exporting span: expected nonzero SpanID") } - got.SpanContext.SpanID = trace.SpanID{} + got.SpanContext = got.SpanContext.WithSpanID(trace.SpanID{}) if !checkTime(&got.StartTime) { return nil, fmt.Errorf("exporting span: expected nonzero StartTime") } @@ -984,16 +984,16 @@ func TestStartSpanAfterEnd(t *testing.T) { t.Fatal("span-2 not recorded") } - if got, want := gotSpan1.SpanContext.TraceID, gotParent.SpanContext.TraceID; got != want { + if got, want := gotSpan1.SpanContext.TraceID(), gotParent.SpanContext.TraceID(); got != want { t.Errorf("span-1.TraceID=%q; want %q", got, want) } - if got, want := gotSpan2.SpanContext.TraceID, gotParent.SpanContext.TraceID; got != want { + if got, want := gotSpan2.SpanContext.TraceID(), gotParent.SpanContext.TraceID(); got != want { t.Errorf("span-2.TraceID=%q; want %q", got, want) } - if got, want := gotSpan1.ParentSpanID, gotParent.SpanContext.SpanID; got != want { + if got, want := gotSpan1.ParentSpanID, gotParent.SpanContext.SpanID(); got != want { t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want) } - if got, want := gotSpan2.ParentSpanID, gotSpan1.SpanContext.SpanID; got != want { + if got, want := gotSpan2.ParentSpanID, gotSpan1.SpanContext.SpanID(); got != want { t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want) } } @@ -1073,11 +1073,11 @@ func TestExecutionTracerTaskEnd(t *testing.T) { ctx := context.Background() ctx = trace.ContextWithRemoteSpanContext(ctx, - trace.SpanContext{ + trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0, - }, + }), ) _, apiSpan = tr.Start( ctx, @@ -1158,10 +1158,10 @@ func TestRecordError(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", StatusCode: codes.Unset, @@ -1198,10 +1198,10 @@ func TestRecordErrorNil(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", SpanKind: trace.SpanKindInternal, @@ -1296,10 +1296,10 @@ func TestWithResource(t *testing.T) { t.Error(err.Error()) } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: []attribute.KeyValue{ @@ -1333,10 +1333,10 @@ func TestWithInstrumentationVersion(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", SpanKind: trace.SpanKindInternal, @@ -1384,20 +1384,20 @@ func TestReadOnlySpan(t *testing.T) { // Initialize parent context. tID, sID := cfg.IDGenerator.NewIDs(context.Background()) - parent := trace.SpanContext{ + parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, - } + }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) // Initialize linked context. tID, sID = cfg.IDGenerator.NewIDs(context.Background()) - linked := trace.SpanContext{ + linked := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, - } + }) st := time.Now() ctx, span := tr.Start(ctx, "foo", trace.WithTimestamp(st), @@ -1464,11 +1464,11 @@ func TestReadWriteSpan(t *testing.T) { // Initialize parent context. tID, sID := cfg.IDGenerator.NewIDs(context.Background()) - parent := trace.SpanContext{ + parent := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tID, SpanID: sID, TraceFlags: 0x1, - } + }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), parent) _, span := tr.Start(ctx, "foo") @@ -1524,10 +1524,10 @@ func TestAddEventsWithMoreAttributesThanLimit(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", Attributes: nil, @@ -1570,8 +1570,8 @@ func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) { k3v3 := attribute.String("key3", "value3") k4v4 := attribute.String("key4", "value4") - sc1 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} - sc2 := trace.SpanContext{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}} + sc1 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) + sc2 := trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID([16]byte{1, 1}), SpanID: trace.SpanID{3}}) span := startSpan(tp, "Links", trace.WithLinks([]trace.Link{ {SpanContext: sc1, Attributes: []attribute.KeyValue{k1v1, k2v2}}, @@ -1584,10 +1584,10 @@ func TestAddLinksWithMoreAttributesThanLimit(t *testing.T) { } want := &export.SpanSnapshot{ - SpanContext: trace.SpanContext{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, TraceFlags: 0x1, - }, + }), ParentSpanID: sid, Name: "span0", HasRemoteParent: true, @@ -1614,7 +1614,7 @@ func (s *stateSampler) ShouldSample(p SamplingParameters) SamplingResult { if strings.HasPrefix(p.Name, s.prefix) { decision = RecordAndSample } - return SamplingResult{Decision: decision, Tracestate: s.f(p.ParentContext.TraceState)} + return SamplingResult{Decision: decision, Tracestate: s.f(p.ParentContext.TraceState())} } func (s stateSampler) Description() string { @@ -1714,17 +1714,17 @@ func TestSamplerTraceState(t *testing.T) { tp := NewTracerProvider(WithDefaultSampler(ts.sampler), WithSyncer(te), WithResource(resource.Empty())) tr := tp.Tracer("TraceState") - sc1 := trace.SpanContext{ + sc1 := trace.NewSpanContext(trace.SpanContextConfig{ TraceID: tid, SpanID: sid, TraceFlags: trace.FlagsSampled, TraceState: ts.input, - } + }) ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc1) _, span := tr.Start(ctx, ts.spanName) // span's TraceState should be set regardless of Sampled/NonSampled state. - require.Equal(t, ts.want, span.SpanContext().TraceState) + require.Equal(t, ts.want, span.SpanContext().TraceState()) span.End() @@ -1736,7 +1736,7 @@ func TestSamplerTraceState(t *testing.T) { return } - receivedState := got[0].SpanContext.TraceState + receivedState := got[0].SpanContext.TraceState() if diff := cmpDiff(receivedState, ts.want); diff != "" { t.Errorf("TraceState not propagated: -got +want %s", diff) diff --git a/trace/config_test.go b/trace/config_test.go index b1e79d04f7b..82b6b35c37c 100644 --- a/trace/config_test.go +++ b/trace/config_test.go @@ -32,11 +32,11 @@ func TestNewSpanConfig(t *testing.T) { timestamp1 := time.Unix(0, 0) link1 := Link{ - SpanContext: SpanContext{TraceID: TraceID([16]byte{1, 1}), SpanID: SpanID{3}}, + SpanContext: SpanContext{traceID: TraceID([16]byte{1, 1}), spanID: SpanID{3}}, Attributes: []attribute.KeyValue{k1v1}, } link2 := Link{ - SpanContext: SpanContext{TraceID: TraceID([16]byte{1, 1}), SpanID: SpanID{3}}, + SpanContext: SpanContext{traceID: TraceID([16]byte{1, 1}), spanID: SpanID{3}}, Attributes: []attribute.KeyValue{k1v2, k2v2}, } diff --git a/trace/go.mod b/trace/go.mod index f598527207a..d9e55d023d1 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -47,6 +47,7 @@ replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric replace go.opentelemetry.io/otel/trace => ./ require ( + github.com/google/go-cmp v0.5.5 github.com/stretchr/testify v1.7.0 go.opentelemetry.io/otel v0.18.0 ) diff --git a/trace/trace.go b/trace/trace.go index 27f43e6a1da..a98c1a7b1aa 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -189,6 +189,8 @@ type TraceState struct { //nolint:golint } var _ json.Marshaler = TraceState{} +var _ json.Marshaler = SpanContext{} + var keyFormatRegExp = regexp.MustCompile( `^((` + traceStateKeyFormat + `)|(` + traceStateKeyFormatWithMultiTenantVendor + `))$`, ) @@ -317,43 +319,141 @@ func isTraceStateKeyValueValid(kv attribute.KeyValue) bool { valueFormatRegExp.MatchString(kv.Value.Emit()) } -// SpanContext contains identifying trace information about a Span. -type SpanContext struct { +// SpanContextConfig contains mutable fields usable for constructing +// an immutable SpanContext. +type SpanContextConfig struct { TraceID TraceID SpanID SpanID TraceFlags byte TraceState TraceState } +// NewSpanContext constructs a SpanContext using values from the provided +// SpanContextConfig. +func NewSpanContext(config SpanContextConfig) SpanContext { + return SpanContext{ + traceID: config.TraceID, + spanID: config.SpanID, + traceFlags: config.TraceFlags, + traceState: config.TraceState, + } +} + +// SpanContext contains identifying trace information about a Span. +type SpanContext struct { + traceID TraceID + spanID SpanID + traceFlags byte + traceState TraceState +} + // IsValid returns if the SpanContext is valid. A valid span context has a // valid TraceID and SpanID. func (sc SpanContext) IsValid() bool { return sc.HasTraceID() && sc.HasSpanID() } +// TraceID returns the TraceID from the SpanContext. +func (sc SpanContext) TraceID() TraceID { + return sc.traceID +} + // HasTraceID checks if the SpanContext has a valid TraceID. func (sc SpanContext) HasTraceID() bool { - return sc.TraceID.IsValid() + return sc.traceID.IsValid() +} + +// WithTraceID returns a new SpanContext with the TraceID replaced. +func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext { + return SpanContext{ + traceID: traceID, + spanID: sc.spanID, + traceFlags: sc.traceFlags, + traceState: sc.traceState, + } +} + +// SpanID returns the SpanID from the SpanContext. +func (sc SpanContext) SpanID() SpanID { + return sc.spanID } // HasSpanID checks if the SpanContext has a valid SpanID. func (sc SpanContext) HasSpanID() bool { - return sc.SpanID.IsValid() + return sc.spanID.IsValid() +} + +// WithSpanID returns a new SpanContext with the SpanID replaced. +func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: spanID, + traceFlags: sc.traceFlags, + traceState: sc.traceState, + } +} + +// TraceFlags returns the flags from the SpanContext. +func (sc SpanContext) TraceFlags() byte { + return sc.traceFlags +} + +// WithTraceFlags returns a new SpanContext with the TraceFlags replaced. +func (sc SpanContext) WithTraceFlags(flags byte) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: sc.spanID, + traceFlags: flags, + traceState: sc.traceState, + } } // IsDeferred returns if the deferred bit is set in the trace flags. func (sc SpanContext) IsDeferred() bool { - return sc.TraceFlags&FlagsDeferred == FlagsDeferred + return sc.traceFlags&FlagsDeferred == FlagsDeferred } // IsDebug returns if the debug bit is set in the trace flags. func (sc SpanContext) IsDebug() bool { - return sc.TraceFlags&FlagsDebug == FlagsDebug + return sc.traceFlags&FlagsDebug == FlagsDebug } // IsSampled returns if the sampling bit is set in the trace flags. func (sc SpanContext) IsSampled() bool { - return sc.TraceFlags&FlagsSampled == FlagsSampled + return sc.traceFlags&FlagsSampled == FlagsSampled +} + +// TraceState returns the TraceState from the SpanContext. +func (sc SpanContext) TraceState() TraceState { + return sc.traceState +} + +// WithTraceState returns a new SpanContext with the TraceState replaced. +func (sc SpanContext) WithTraceState(state TraceState) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: sc.spanID, + traceFlags: sc.traceFlags, + traceState: state, + } +} + +// Equal is a predicate that determines whether two SpanContext values are equal. +func (sc SpanContext) Equal(other SpanContext) bool { + return sc.traceID == other.traceID && + sc.spanID == other.spanID && + sc.traceFlags == other.traceFlags && + sc.traceState.String() == other.traceState.String() +} + +// MarshalJSON implements a custom marshal function to encode a SpanContext. +func (sc SpanContext) MarshalJSON() ([]byte, error) { + return json.Marshal(SpanContextConfig{ + TraceID: sc.traceID, + SpanID: sc.spanID, + TraceFlags: sc.traceFlags, + TraceState: sc.traceState, + }) } type traceContextKeyType int diff --git a/trace/trace_test.go b/trace/trace_test.go index 34919cd55bd..a5dc019d94c 100644 --- a/trace/trace_test.go +++ b/trace/trace_test.go @@ -19,6 +19,7 @@ import ( "fmt" "testing" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,7 +32,7 @@ type testSpan struct { ID byte } -func (s testSpan) SpanContext() SpanContext { return SpanContext{SpanID: [8]byte{s.ID}} } +func (s testSpan) SpanContext() SpanContext { return SpanContext{spanID: [8]byte{s.ID}} } func TestContextSpan(t *testing.T) { testCases := []struct { @@ -77,7 +78,7 @@ func TestContextRemoteSpanContext(t *testing.T) { t.Errorf("RemoteSpanContextFromContext returned %v from an empty context, want %v", got, empty) } - want := SpanContext{TraceID: [16]byte{1}, SpanID: [8]byte{42}} + want := SpanContext{traceID: [16]byte{1}, spanID: [8]byte{42}} ctx = ContextWithRemoteSpanContext(ctx, want) if got, ok := ctx.Value(remoteContextKey).(SpanContext); !ok { t.Errorf("failed to set SpanContext with %#v", want) @@ -89,7 +90,7 @@ func TestContextRemoteSpanContext(t *testing.T) { t.Errorf("RemoteSpanContextFromContext returned %v from a set context, want %v", got, want) } - want = SpanContext{TraceID: [16]byte{1}, SpanID: [8]byte{43}} + want = SpanContext{traceID: [16]byte{1}, spanID: [8]byte{43}} ctx = ContextWithRemoteSpanContext(ctx, want) if got, ok := ctx.Value(remoteContextKey).(SpanContext); !ok { t.Errorf("failed to set SpanContext with %#v", want) @@ -134,8 +135,8 @@ func TestIsValid(t *testing.T) { } { t.Run(testcase.name, func(t *testing.T) { sc := SpanContext{ - TraceID: testcase.tid, - SpanID: testcase.sid, + traceID: testcase.tid, + spanID: testcase.sid, } have := sc.IsValid() if have != testcase.want { @@ -207,7 +208,7 @@ func TestHasTraceID(t *testing.T) { } { t.Run(testcase.name, func(t *testing.T) { //proto: func (sc SpanContext) HasTraceID() bool{} - sc := SpanContext{TraceID: testcase.tid} + sc := SpanContext{traceID: testcase.tid} have := sc.HasTraceID() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) @@ -224,7 +225,7 @@ func TestHasSpanID(t *testing.T) { }{ { name: "SpanContext.HasSpanID() returns true if self.SpanID != 0", - sc: SpanContext{SpanID: [8]byte{42}}, + sc: SpanContext{spanID: [8]byte{42}}, want: true, }, { name: "SpanContext.HasSpanID() returns false if self.SpanID == 0", @@ -251,27 +252,27 @@ func TestSpanContextIsSampled(t *testing.T) { { name: "sampled", sc: SpanContext{ - TraceID: TraceID([16]byte{1}), - TraceFlags: FlagsSampled, + traceID: TraceID([16]byte{1}), + traceFlags: FlagsSampled, }, want: true, }, { name: "unused bits are ignored, still not sampled", sc: SpanContext{ - TraceID: TraceID([16]byte{1}), - TraceFlags: ^FlagsSampled, + traceID: TraceID([16]byte{1}), + traceFlags: ^FlagsSampled, }, want: false, }, { name: "unused bits are ignored, still sampled", sc: SpanContext{ - TraceID: TraceID([16]byte{1}), - TraceFlags: FlagsSampled | ^FlagsSampled, + traceID: TraceID([16]byte{1}), + traceFlags: FlagsSampled | ^FlagsSampled, }, want: true, }, { name: "not sampled/default", - sc: SpanContext{TraceID: TraceID{}}, + sc: SpanContext{traceID: TraceID{}}, want: false, }, } { @@ -431,7 +432,7 @@ func TestSpanContextFromContext(t *testing.T) { { name: "span 1", context: ContextWithSpan(context.Background(), testSpan{ID: 1}), - expectedSpanContext: SpanContext{SpanID: [8]byte{1}}, + expectedSpanContext: SpanContext{spanID: [8]byte{1}}, }, } @@ -978,10 +979,10 @@ func TestTraceStateFromKeyValues(t *testing.T) { } func assertSpanContextEqual(got SpanContext, want SpanContext) bool { - return got.SpanID == want.SpanID && - got.TraceID == want.TraceID && - got.TraceFlags == want.TraceFlags && - assertTraceStateEqual(got.TraceState, want.TraceState) + return got.spanID == want.spanID && + got.traceID == want.traceID && + got.traceFlags == want.traceFlags && + assertTraceStateEqual(got.traceState, want.traceState) } func assertTraceStateEqual(got TraceState, want TraceState) bool { @@ -1006,3 +1007,92 @@ var kvsWithMaxMembers = func() []attribute.KeyValue { } return kvs }() + +func TestNewSpanContext(t *testing.T) { + testCases := []struct { + name string + config SpanContextConfig + expectedSpanContext SpanContext + }{ + { + name: "Complete SpanContext", + config: SpanContextConfig{ + TraceID: TraceID([16]byte{1}), + SpanID: SpanID([8]byte{42}), + TraceFlags: 0x1, + TraceState: TraceState{kvs: []attribute.KeyValue{ + attribute.String("foo", "bar"), + }}, + }, + expectedSpanContext: SpanContext{ + traceID: TraceID([16]byte{1}), + spanID: SpanID([8]byte{42}), + traceFlags: 0x1, + traceState: TraceState{kvs: []attribute.KeyValue{ + attribute.String("foo", "bar"), + }}, + }, + }, + { + name: "Empty SpanContext", + config: SpanContextConfig{}, + expectedSpanContext: SpanContext{}, + }, + { + name: "Partial SpanContext", + config: SpanContextConfig{ + TraceID: TraceID([16]byte{1}), + SpanID: SpanID([8]byte{42}), + }, + expectedSpanContext: SpanContext{ + traceID: TraceID([16]byte{1}), + spanID: SpanID([8]byte{42}), + traceFlags: 0x0, + traceState: TraceState{}, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sctx := NewSpanContext(tc.config) + if !assertSpanContextEqual(sctx, tc.expectedSpanContext) { + t.Fatalf("%s: Unexpected context created: %s", tc.name, cmp.Diff(sctx, tc.expectedSpanContext)) + } + }) + } +} + +func TestSpanContextDerivation(t *testing.T) { + from := SpanContext{} + to := SpanContext{traceID: TraceID([16]byte{1})} + + modified := from.WithTraceID(to.TraceID()) + if !assertSpanContextEqual(modified, to) { + t.Fatalf("WithTraceID: Unexpected context created: %s", cmp.Diff(modified, to)) + } + + from = to + to.spanID = SpanID([8]byte{42}) + + modified = from.WithSpanID(to.SpanID()) + if !assertSpanContextEqual(modified, to) { + t.Fatalf("WithSpanID: Unexpected context created: %s", cmp.Diff(modified, to)) + } + + from = to + to.traceFlags = 0x13 + + modified = from.WithTraceFlags(to.TraceFlags()) + if !assertSpanContextEqual(modified, to) { + t.Fatalf("WithTraceFlags: Unexpected context created: %s", cmp.Diff(modified, to)) + } + + from = to + to.traceState = TraceState{kvs: []attribute.KeyValue{attribute.String("foo", "bar")}} + + modified = from.WithTraceState(to.TraceState()) + if !assertSpanContextEqual(modified, to) { + t.Fatalf("WithTraceState: Unexpected context created: %s", cmp.Diff(modified, to)) + } +}