1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-03-21 21:17:35 +02:00

Make SpanContext Immutable ()

* 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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 554 additions and 343 deletions

@ -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

@ -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())
}

@ -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]
}

@ -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,
}
})
}

@ -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)
}
})

@ -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{}
}

@ -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)
}
}
}

@ -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",

@ -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),
})
}

@ -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"),
},

@ -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,

@ -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,

@ -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,

@ -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",

@ -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,

@ -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",

@ -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",

@ -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)
}
}

@ -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())
})
})

@ -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
}

@ -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{

@ -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)
}

@ -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{}
}

@ -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)

@ -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,
},
}),
},
}

@ -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) {

@ -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)
}
}

@ -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(),
}
}

@ -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")

@ -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)
}

@ -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
}

@ -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:

@ -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)

@ -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},
}

@ -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
)

@ -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

@ -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))
}
}