You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-17 01:12:45 +02:00
Replace span relationship with a potentially remote parent context (#451)
This PR removes the non-compliant ChildOf and FollowsFrom interfaces and the Relation type, which were inherited from OpenTracing via the initial prototype. Instead allow adding a span context to the go context as a remote span context and use a simple algorithm for figuring out an actual parent of the new span, which was proposed for the OpenTelemetry specification. Also add a way to ignore current span and remote span context in go context, so we can force the tracer to create a new root span - a span with a new trace ID. That required some moderate changes in the opentracing bridge - first reference with ChildOfRef reference type becomes a local parent, the rest become links. This also fixes links handling in the meantime. The downside of the approach proposed here is that we can only set the remote parent when creating a span through the opentracing API. Co-authored-by: Joshua MacDonald <jmacd@users.noreply.github.com>
This commit is contained in:
@ -125,35 +125,53 @@ func (h *Harness) TestTracer(subjectFactory func() trace.Tracer) {
|
|||||||
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("propagates a parent's trace ID through `ChildOf`", func(t *testing.T) {
|
t.Run("ignores parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
subject := subjectFactory()
|
subject := subjectFactory()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
ctx, parent := subject.Start(context.Background(), "parent")
|
||||||
_, child := subject.Start(context.Background(), "child", trace.ChildOf(parent.SpanContext()))
|
_, child := subject.Start(ctx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
psc := parent.SpanContext()
|
psc := parent.SpanContext()
|
||||||
csc := child.SpanContext()
|
csc := child.SpanContext()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
subject := subjectFactory()
|
||||||
|
|
||||||
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child")
|
||||||
|
|
||||||
|
psc := remoteParent.SpanContext()
|
||||||
|
csc := child.SpanContext()
|
||||||
|
|
||||||
e.Expect(csc.TraceID).ToEqual(psc.TraceID)
|
e.Expect(csc.TraceID).ToEqual(psc.TraceID)
|
||||||
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("propagates a parent's trace ID through `FollowsFrom`", func(t *testing.T) {
|
t.Run("ignores remote parent's trace ID when new root is requested", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
subject := subjectFactory()
|
subject := subjectFactory()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
_, remoteParent := subject.Start(context.Background(), "remote parent")
|
||||||
_, child := subject.Start(context.Background(), "child", trace.FollowsFrom(parent.SpanContext()))
|
parentCtx := trace.ContextWithRemoteSpanContext(context.Background(), remoteParent.SpanContext())
|
||||||
|
_, child := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
psc := parent.SpanContext()
|
psc := remoteParent.SpanContext()
|
||||||
csc := child.SpanContext()
|
csc := child.SpanContext()
|
||||||
|
|
||||||
e.Expect(csc.TraceID).ToEqual(psc.TraceID)
|
e.Expect(csc.TraceID).NotToEqual(psc.TraceID)
|
||||||
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
e.Expect(csc.SpanID).NotToEqual(psc.SpanID)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -100,26 +100,11 @@ type StartConfig struct {
|
|||||||
Attributes []core.KeyValue
|
Attributes []core.KeyValue
|
||||||
StartTime time.Time
|
StartTime time.Time
|
||||||
Links []Link
|
Links []Link
|
||||||
Relation Relation
|
|
||||||
Record bool
|
Record bool
|
||||||
|
NewRoot bool
|
||||||
SpanKind SpanKind
|
SpanKind SpanKind
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relation is used to establish relationship between newly created span and the
|
|
||||||
// other span. The other span could be related as a parent or linked or any other
|
|
||||||
// future relationship type.
|
|
||||||
type Relation struct {
|
|
||||||
core.SpanContext
|
|
||||||
RelationshipType
|
|
||||||
}
|
|
||||||
|
|
||||||
type RelationshipType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChildOfRelationship RelationshipType = iota
|
|
||||||
FollowsFromRelationship
|
|
||||||
)
|
|
||||||
|
|
||||||
// Link is used to establish relationship between two spans within the same Trace or
|
// Link is used to establish relationship between two spans within the same Trace or
|
||||||
// across different Traces. Few examples of Link usage.
|
// across different Traces. Few examples of Link usage.
|
||||||
// 1. Batch Processing: A batch of elements may contain elements associated with one
|
// 1. Batch Processing: A batch of elements may contain elements associated with one
|
||||||
@ -216,23 +201,14 @@ func WithRecord() StartOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChildOf. TODO: do we need this?.
|
// WithNewRoot specifies that the current span or remote span context
|
||||||
func ChildOf(sc core.SpanContext) StartOption {
|
// in context passed to `Start` should be ignored when deciding about
|
||||||
|
// a parent, which effectively means creating a span with new trace
|
||||||
|
// ID. The current span and the remote span context may be added as
|
||||||
|
// links to the span by the implementation.
|
||||||
|
func WithNewRoot() StartOption {
|
||||||
return func(c *StartConfig) {
|
return func(c *StartConfig) {
|
||||||
c.Relation = Relation{
|
c.NewRoot = true
|
||||||
SpanContext: sc,
|
|
||||||
RelationshipType: ChildOfRelationship,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FollowsFrom. TODO: do we need this?.
|
|
||||||
func FollowsFrom(sc core.SpanContext) StartOption {
|
|
||||||
return func(c *StartConfig) {
|
|
||||||
c.Relation = Relation{
|
|
||||||
SpanContext: sc,
|
|
||||||
RelationshipType: FollowsFromRelationship,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,19 +16,42 @@ package trace
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/api/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type currentSpanKeyType struct{}
|
type traceContextKeyType int
|
||||||
|
|
||||||
var currentSpanKey = ¤tSpanKeyType{}
|
const (
|
||||||
|
currentSpanKey traceContextKeyType = iota
|
||||||
|
remoteContextKey
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContextWithSpan creates a new context with a current span set to
|
||||||
|
// the passed span.
|
||||||
func ContextWithSpan(ctx context.Context, span Span) context.Context {
|
func ContextWithSpan(ctx context.Context, span Span) context.Context {
|
||||||
return context.WithValue(ctx, currentSpanKey, span)
|
return context.WithValue(ctx, currentSpanKey, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpanFromContext returns the current span stored in the context.
|
||||||
func SpanFromContext(ctx context.Context) Span {
|
func SpanFromContext(ctx context.Context) Span {
|
||||||
if span, has := ctx.Value(currentSpanKey).(Span); has {
|
if span, has := ctx.Value(currentSpanKey).(Span); has {
|
||||||
return span
|
return span
|
||||||
}
|
}
|
||||||
return NoopSpan{}
|
return NoopSpan{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContextWithRemoteSpanContext creates a new context with a remote
|
||||||
|
// span context set to the passed span context.
|
||||||
|
func ContextWithRemoteSpanContext(ctx context.Context, sc core.SpanContext) context.Context {
|
||||||
|
return context.WithValue(ctx, remoteContextKey, sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteSpanContextFromContext returns the remote span context stored
|
||||||
|
// in the context.
|
||||||
|
func RemoteSpanContextFromContext(ctx context.Context) core.SpanContext {
|
||||||
|
if sc, ok := ctx.Value(remoteContextKey).(core.SpanContext); ok {
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
return core.EmptySpanContext()
|
||||||
|
}
|
@ -102,10 +102,9 @@ func BenchmarkInjectB3(b *testing.B) {
|
|||||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if tt.parentSc.IsValid() {
|
if tt.parentSc.IsValid() {
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject", trace.ChildOf(tt.parentSc))
|
ctx = trace.ContextWithRemoteSpanContext(ctx, tt.parentSc)
|
||||||
} else {
|
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject")
|
|
||||||
}
|
}
|
||||||
|
ctx, _ = mockTracer.Start(ctx, "inject")
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@ -104,10 +104,9 @@ func TestInjectB3(t *testing.T) {
|
|||||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if tt.parentSc.IsValid() {
|
if tt.parentSc.IsValid() {
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject", trace.ChildOf(tt.parentSc))
|
ctx = trace.ContextWithRemoteSpanContext(ctx, tt.parentSc)
|
||||||
} else {
|
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject")
|
|
||||||
}
|
}
|
||||||
|
ctx, _ = mockTracer.Start(ctx, "inject")
|
||||||
propagator.Inject(ctx, req.Header)
|
propagator.Inject(ctx, req.Header)
|
||||||
|
|
||||||
for h, v := range tt.wantHeaders {
|
for h, v := range tt.wantHeaders {
|
||||||
|
@ -38,8 +38,8 @@ func injectSubBenchmarks(b *testing.B, fn func(context.Context, *testing.B)) {
|
|||||||
SpanID: spanID,
|
SpanID: spanID,
|
||||||
TraceFlags: core.TraceFlagsSampled,
|
TraceFlags: core.TraceFlagsSampled,
|
||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc)
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject", trace.ChildOf(sc))
|
ctx, _ = mockTracer.Start(ctx, "inject")
|
||||||
fn(ctx, b)
|
fn(ctx, b)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -273,7 +273,8 @@ func TestInjectTraceContextToHTTPReq(t *testing.T) {
|
|||||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if tt.sc.IsValid() {
|
if tt.sc.IsValid() {
|
||||||
ctx, _ = mockTracer.Start(ctx, "inject", trace.ChildOf(tt.sc))
|
ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc)
|
||||||
|
ctx, _ = mockTracer.Start(ctx, "inject")
|
||||||
}
|
}
|
||||||
propagator.Inject(ctx, req.Header)
|
propagator.Inject(ctx, req.Header)
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
"go.opentelemetry.io/otel/api/trace"
|
"go.opentelemetry.io/otel/api/trace"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal/trace/parent"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ trace.Tracer = (*Tracer)(nil)
|
var _ trace.Tracer = (*Tracer)(nil)
|
||||||
@ -52,10 +54,9 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOpti
|
|||||||
var traceID core.TraceID
|
var traceID core.TraceID
|
||||||
var parentSpanID core.SpanID
|
var parentSpanID core.SpanID
|
||||||
|
|
||||||
if parentSpanContext := c.Relation.SpanContext; parentSpanContext.IsValid() {
|
parentSpanContext, _, links := parent.GetSpanContextAndLinks(ctx, c.NewRoot)
|
||||||
traceID = parentSpanContext.TraceID
|
|
||||||
parentSpanID = parentSpanContext.SpanID
|
if parentSpanContext.IsValid() {
|
||||||
} else if parentSpanContext := trace.SpanFromContext(ctx).SpanContext(); parentSpanContext.IsValid() {
|
|
||||||
traceID = parentSpanContext.TraceID
|
traceID = parentSpanContext.TraceID
|
||||||
parentSpanID = parentSpanContext.SpanID
|
parentSpanID = parentSpanContext.SpanID
|
||||||
} else {
|
} else {
|
||||||
@ -86,6 +87,9 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.StartOpti
|
|||||||
span.SetName(name)
|
span.SetName(name)
|
||||||
span.SetAttributes(c.Attributes...)
|
span.SetAttributes(c.Attributes...)
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
span.links[link.SpanContext] = link.Attributes
|
||||||
|
}
|
||||||
for _, link := range c.Links {
|
for _, link := range c.Links {
|
||||||
span.links[link.SpanContext] = link.Attributes
|
span.links[link.SpanContext] = link.Attributes
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
"go.opentelemetry.io/otel/api/key"
|
||||||
"go.opentelemetry.io/otel/api/testharness"
|
"go.opentelemetry.io/otel/api/testharness"
|
||||||
"go.opentelemetry.io/otel/api/trace"
|
"go.opentelemetry.io/otel/api/trace"
|
||||||
"go.opentelemetry.io/otel/api/trace/testtrace"
|
"go.opentelemetry.io/otel/api/trace/testtrace"
|
||||||
@ -74,17 +75,17 @@ func TestTracer(t *testing.T) {
|
|||||||
e.Expect(attributes[attr2.Key]).ToEqual(attr2.Value)
|
e.Expect(attributes[attr2.Key]).ToEqual(attr2.Value)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uses the parent's span context from ChildOf", func(t *testing.T) {
|
t.Run("uses the current span from context as parent", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
subject := testtrace.NewTracer()
|
subject := testtrace.NewTracer()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
parent, parentSpan := subject.Start(context.Background(), "parent")
|
||||||
parentSpanContext := parent.SpanContext()
|
parentSpanContext := parentSpan.SpanContext()
|
||||||
|
|
||||||
_, span := subject.Start(context.Background(), "child", trace.ChildOf(parentSpanContext))
|
_, span := subject.Start(parent, "child")
|
||||||
|
|
||||||
testSpan, ok := span.(*testtrace.Span)
|
testSpan, ok := span.(*testtrace.Span)
|
||||||
e.Expect(ok).ToBeTrue()
|
e.Expect(ok).ToBeTrue()
|
||||||
@ -95,18 +96,19 @@ func TestTracer(t *testing.T) {
|
|||||||
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("defers to ChildOf if the provided context also contains a parent span", func(t *testing.T) {
|
t.Run("uses the current span from context as parent, even if it has remote span context", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
subject := testtrace.NewTracer()
|
subject := testtrace.NewTracer()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
parent, parentSpan := subject.Start(context.Background(), "parent")
|
||||||
parentSpanContext := parent.SpanContext()
|
_, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent")
|
||||||
|
parent = trace.ContextWithRemoteSpanContext(parent, remoteParentSpan.SpanContext())
|
||||||
|
parentSpanContext := parentSpan.SpanContext()
|
||||||
|
|
||||||
ctx, _ := subject.Start(context.Background(), "should be ignored")
|
_, span := subject.Start(parent, "child")
|
||||||
_, span := subject.Start(ctx, "child", trace.ChildOf(parentSpanContext))
|
|
||||||
|
|
||||||
testSpan, ok := span.(*testtrace.Span)
|
testSpan, ok := span.(*testtrace.Span)
|
||||||
e.Expect(ok).ToBeTrue()
|
e.Expect(ok).ToBeTrue()
|
||||||
@ -117,47 +119,101 @@ func TestTracer(t *testing.T) {
|
|||||||
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uses the parent's span context from FollowsFrom", func(t *testing.T) {
|
t.Run("uses the remote span context from context as parent, if current span is missing", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
subject := testtrace.NewTracer()
|
subject := testtrace.NewTracer()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
_, remoteParentSpan := subject.Start(context.Background(), "remote parent")
|
||||||
parentSpanContext := parent.SpanContext()
|
parent := trace.ContextWithRemoteSpanContext(context.Background(), remoteParentSpan.SpanContext())
|
||||||
|
remoteParentSpanContext := remoteParentSpan.SpanContext()
|
||||||
|
|
||||||
_, span := subject.Start(context.Background(), "child", trace.FollowsFrom(parentSpanContext))
|
_, span := subject.Start(parent, "child")
|
||||||
|
|
||||||
testSpan, ok := span.(*testtrace.Span)
|
testSpan, ok := span.(*testtrace.Span)
|
||||||
e.Expect(ok).ToBeTrue()
|
e.Expect(ok).ToBeTrue()
|
||||||
|
|
||||||
childSpanContext := testSpan.SpanContext()
|
childSpanContext := testSpan.SpanContext()
|
||||||
e.Expect(childSpanContext.TraceID).ToEqual(parentSpanContext.TraceID)
|
e.Expect(childSpanContext.TraceID).ToEqual(remoteParentSpanContext.TraceID)
|
||||||
e.Expect(childSpanContext.SpanID).NotToEqual(parentSpanContext.SpanID)
|
e.Expect(childSpanContext.SpanID).NotToEqual(remoteParentSpanContext.SpanID)
|
||||||
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
e.Expect(testSpan.ParentSpanID()).ToEqual(remoteParentSpanContext.SpanID)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("defers to FollowsFrom if the provided context also contains a parent span", func(t *testing.T) {
|
t.Run("creates new root when both current span and remote span context are missing", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
e := matchers.NewExpecter(t)
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
subject := testtrace.NewTracer()
|
subject := testtrace.NewTracer()
|
||||||
|
|
||||||
_, parent := subject.Start(context.Background(), "parent")
|
_, parentSpan := subject.Start(context.Background(), "not-a-parent")
|
||||||
parentSpanContext := parent.SpanContext()
|
_, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent")
|
||||||
|
parentSpanContext := parentSpan.SpanContext()
|
||||||
|
remoteParentSpanContext := remoteParentSpan.SpanContext()
|
||||||
|
|
||||||
ctx, _ := subject.Start(context.Background(), "should be ignored")
|
_, span := subject.Start(context.Background(), "child")
|
||||||
_, span := subject.Start(ctx, "child", trace.FollowsFrom(parentSpanContext))
|
|
||||||
|
|
||||||
testSpan, ok := span.(*testtrace.Span)
|
testSpan, ok := span.(*testtrace.Span)
|
||||||
e.Expect(ok).ToBeTrue()
|
e.Expect(ok).ToBeTrue()
|
||||||
|
|
||||||
childSpanContext := testSpan.SpanContext()
|
childSpanContext := testSpan.SpanContext()
|
||||||
e.Expect(childSpanContext.TraceID).ToEqual(parentSpanContext.TraceID)
|
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(parentSpanContext.SpanID)
|
||||||
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID)
|
e.Expect(childSpanContext.SpanID).NotToEqual(remoteParentSpanContext.SpanID)
|
||||||
|
e.Expect(testSpan.ParentSpanID().IsValid()).ToBeFalse()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("creates new root when requested, even if both current span and remote span context are in context", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
e := matchers.NewExpecter(t)
|
||||||
|
|
||||||
|
subject := testtrace.NewTracer()
|
||||||
|
|
||||||
|
parentCtx, parentSpan := subject.Start(context.Background(), "not-a-parent")
|
||||||
|
_, remoteParentSpan := subject.Start(context.Background(), "remote not-a-parent")
|
||||||
|
parentSpanContext := parentSpan.SpanContext()
|
||||||
|
remoteParentSpanContext := remoteParentSpan.SpanContext()
|
||||||
|
parentCtx = trace.ContextWithRemoteSpanContext(parentCtx, remoteParentSpanContext)
|
||||||
|
|
||||||
|
_, span := subject.Start(parentCtx, "child", trace.WithNewRoot())
|
||||||
|
|
||||||
|
testSpan, ok := span.(*testtrace.Span)
|
||||||
|
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(testSpan.ParentSpanID().IsValid()).ToBeFalse()
|
||||||
|
|
||||||
|
expectedLinks := []trace.Link{
|
||||||
|
{
|
||||||
|
SpanContext: parentSpanContext,
|
||||||
|
Attributes: []core.KeyValue{
|
||||||
|
key.String("ignored-on-demand", "current"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SpanContext: remoteParentSpanContext,
|
||||||
|
Attributes: []core.KeyValue{
|
||||||
|
key.String("ignored-on-demand", "remote"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tsLinks := testSpan.Links()
|
||||||
|
gotLinks := make([]trace.Link, 0, len(tsLinks))
|
||||||
|
for sc, attributes := range tsLinks {
|
||||||
|
gotLinks = append(gotLinks, trace.Link{
|
||||||
|
SpanContext: sc,
|
||||||
|
Attributes: attributes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
e.Expect(gotLinks).ToMatchInAnyOrder(expectedLinks)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uses the links provided through LinkedTo", func(t *testing.T) {
|
t.Run("uses the links provided through LinkedTo", func(t *testing.T) {
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
otlog "github.com/opentracing/opentracing-go/log"
|
otlog "github.com/opentracing/opentracing-go/log"
|
||||||
|
|
||||||
otelcore "go.opentelemetry.io/otel/api/core"
|
otelcore "go.opentelemetry.io/otel/api/core"
|
||||||
|
otelkey "go.opentelemetry.io/otel/api/key"
|
||||||
oteltrace "go.opentelemetry.io/otel/api/trace"
|
oteltrace "go.opentelemetry.io/otel/api/trace"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
||||||
@ -309,15 +310,18 @@ func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOptio
|
|||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt.Apply(&sso)
|
opt.Apply(&sso)
|
||||||
}
|
}
|
||||||
// TODO: handle links, needs SpanData to be in the API first?
|
parentBridgeSC, links := otSpanReferencesToParentAndLinks(sso.References)
|
||||||
bRelation, _ := otSpanReferencesToBridgeRelationAndLinks(sso.References)
|
|
||||||
attributes, kind, hadTrueErrorTag := otTagsToOtelAttributesKindAndError(sso.Tags)
|
attributes, kind, hadTrueErrorTag := otTagsToOtelAttributesKindAndError(sso.Tags)
|
||||||
checkCtx := migration.WithDeferredSetup(context.Background())
|
checkCtx := migration.WithDeferredSetup(context.Background())
|
||||||
|
if parentBridgeSC != nil {
|
||||||
|
checkCtx = oteltrace.ContextWithRemoteSpanContext(checkCtx, parentBridgeSC.otelSpanContext)
|
||||||
|
}
|
||||||
checkCtx2, otelSpan := t.setTracer.tracer().Start(checkCtx, operationName, func(opts *oteltrace.StartConfig) {
|
checkCtx2, otelSpan := t.setTracer.tracer().Start(checkCtx, operationName, func(opts *oteltrace.StartConfig) {
|
||||||
opts.Attributes = attributes
|
opts.Attributes = attributes
|
||||||
opts.StartTime = sso.StartTime
|
opts.StartTime = sso.StartTime
|
||||||
opts.Relation = bRelation.ToOtelRelation()
|
opts.Links = links
|
||||||
opts.Record = true
|
opts.Record = true
|
||||||
|
opts.NewRoot = false
|
||||||
opts.SpanKind = kind
|
opts.SpanKind = kind
|
||||||
})
|
})
|
||||||
if checkCtx != checkCtx2 {
|
if checkCtx != checkCtx2 {
|
||||||
@ -328,9 +332,15 @@ func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOptio
|
|||||||
if hadTrueErrorTag {
|
if hadTrueErrorTag {
|
||||||
otelSpan.SetStatus(codes.Unknown)
|
otelSpan.SetStatus(codes.Unknown)
|
||||||
}
|
}
|
||||||
|
// One does not simply pass a concrete pointer to function
|
||||||
|
// that takes some interface. In case of passing nil concrete
|
||||||
|
// pointer, we get an interface with non-nil type (because the
|
||||||
|
// pointer type is known) and a nil value. Which means
|
||||||
|
// interface is not nil, but calling some interface function
|
||||||
|
// on it will most likely result in nil pointer dereference.
|
||||||
var otSpanContext ot.SpanContext
|
var otSpanContext ot.SpanContext
|
||||||
if bRelation.spanContext != nil {
|
if parentBridgeSC != nil {
|
||||||
otSpanContext = bRelation.spanContext
|
otSpanContext = parentBridgeSC
|
||||||
}
|
}
|
||||||
sctx := newBridgeSpanContext(otelSpan.SpanContext(), otSpanContext)
|
sctx := newBridgeSpanContext(otelSpan.SpanContext(), otSpanContext)
|
||||||
span := &bridgeSpan{
|
span := &bridgeSpan{
|
||||||
@ -440,53 +450,59 @@ func otTagToOtelCoreKey(k string) otelcore.Key {
|
|||||||
return otelcore.Key(k)
|
return otelcore.Key(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
type bridgeRelation struct {
|
func otSpanReferencesToParentAndLinks(references []ot.SpanReference) (*bridgeSpanContext, []oteltrace.Link) {
|
||||||
spanContext *bridgeSpanContext
|
var (
|
||||||
relationshipType oteltrace.RelationshipType
|
parent *bridgeSpanContext
|
||||||
}
|
links []oteltrace.Link
|
||||||
|
)
|
||||||
func (r bridgeRelation) ToOtelRelation() oteltrace.Relation {
|
for _, reference := range references {
|
||||||
if r.spanContext == nil {
|
bridgeSC, ok := reference.ReferencedContext.(*bridgeSpanContext)
|
||||||
return oteltrace.Relation{}
|
|
||||||
}
|
|
||||||
return oteltrace.Relation{
|
|
||||||
SpanContext: r.spanContext.otelSpanContext,
|
|
||||||
RelationshipType: r.relationshipType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func otSpanReferencesToBridgeRelationAndLinks(references []ot.SpanReference) (bridgeRelation, []*bridgeSpanContext) {
|
|
||||||
if len(references) == 0 {
|
|
||||||
return bridgeRelation{}, nil
|
|
||||||
}
|
|
||||||
first := references[0]
|
|
||||||
relation := bridgeRelation{
|
|
||||||
spanContext: mustGetBridgeSpanContext(first.ReferencedContext),
|
|
||||||
relationshipType: otSpanReferenceTypeToOtelRelationshipType(first.Type),
|
|
||||||
}
|
|
||||||
var links []*bridgeSpanContext
|
|
||||||
for _, reference := range references[1:] {
|
|
||||||
links = append(links, mustGetBridgeSpanContext(reference.ReferencedContext))
|
|
||||||
}
|
|
||||||
return relation, links
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustGetBridgeSpanContext(ctx ot.SpanContext) *bridgeSpanContext {
|
|
||||||
ourCtx, ok := ctx.(*bridgeSpanContext)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("oops, some foreign span context here")
|
// We ignore foreign ot span contexts,
|
||||||
|
// sorry. We have no way of getting any
|
||||||
|
// TraceID and SpanID out of it for form a
|
||||||
|
// otelcore.SpanContext for otelcore.Link. And
|
||||||
|
// we can't make it a parent - it also needs a
|
||||||
|
// valid otelcore.SpanContext.
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return ourCtx
|
if parent != nil {
|
||||||
|
links = append(links, otSpanReferenceToOtelLink(bridgeSC, reference.Type))
|
||||||
|
} else {
|
||||||
|
if reference.Type == ot.ChildOfRef {
|
||||||
|
parent = bridgeSC
|
||||||
|
} else {
|
||||||
|
links = append(links, otSpanReferenceToOtelLink(bridgeSC, reference.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent, links
|
||||||
}
|
}
|
||||||
|
|
||||||
func otSpanReferenceTypeToOtelRelationshipType(srt ot.SpanReferenceType) oteltrace.RelationshipType {
|
func otSpanReferenceToOtelLink(bridgeSC *bridgeSpanContext, refType ot.SpanReferenceType) oteltrace.Link {
|
||||||
switch srt {
|
return oteltrace.Link{
|
||||||
|
SpanContext: bridgeSC.otelSpanContext,
|
||||||
|
Attributes: otSpanReferenceTypeToOtelLinkAttributes(refType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func otSpanReferenceTypeToOtelLinkAttributes(refType ot.SpanReferenceType) []otelcore.KeyValue {
|
||||||
|
return []otelcore.KeyValue{
|
||||||
|
otelkey.String("ot-span-reference-type", otSpanReferenceTypeToString(refType)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func otSpanReferenceTypeToString(refType ot.SpanReferenceType) string {
|
||||||
|
switch refType {
|
||||||
case ot.ChildOfRef:
|
case ot.ChildOfRef:
|
||||||
return oteltrace.ChildOfRelationship
|
// "extra", because first child-of reference is used
|
||||||
|
// as a parent, so this function isn't even called for
|
||||||
|
// it.
|
||||||
|
return "extra-child-of"
|
||||||
case ot.FollowsFromRef:
|
case ot.FollowsFromRef:
|
||||||
return oteltrace.FollowsFromRelationship
|
return "follows-from-ref"
|
||||||
default:
|
default:
|
||||||
panic("fix yer code, it uses bogus opentracing reference type")
|
return fmt.Sprintf("unknown-%d", int(refType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
otelcorrelation "go.opentelemetry.io/otel/api/correlation"
|
otelcorrelation "go.opentelemetry.io/otel/api/correlation"
|
||||||
otelkey "go.opentelemetry.io/otel/api/key"
|
otelkey "go.opentelemetry.io/otel/api/key"
|
||||||
oteltrace "go.opentelemetry.io/otel/api/trace"
|
oteltrace "go.opentelemetry.io/otel/api/trace"
|
||||||
|
otelparent "go.opentelemetry.io/otel/internal/trace/parent"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
||||||
)
|
)
|
||||||
@ -146,14 +147,8 @@ func (t *MockTracer) getParentSpanID(ctx context.Context, spanOpts *oteltrace.St
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *MockTracer) getParentSpanContext(ctx context.Context, spanOpts *oteltrace.StartConfig) otelcore.SpanContext {
|
func (t *MockTracer) getParentSpanContext(ctx context.Context, spanOpts *oteltrace.StartConfig) otelcore.SpanContext {
|
||||||
if spanOpts.Relation.RelationshipType == oteltrace.ChildOfRelationship &&
|
spanCtx, _, _ := otelparent.GetSpanContextAndLinks(ctx, spanOpts.NewRoot)
|
||||||
spanOpts.Relation.SpanContext.IsValid() {
|
return spanCtx
|
||||||
return spanOpts.Relation.SpanContext
|
|
||||||
}
|
|
||||||
if parentSpanContext := oteltrace.SpanFromContext(ctx).SpanContext(); parentSpanContext.IsValid() {
|
|
||||||
return parentSpanContext
|
|
||||||
}
|
|
||||||
return otelcore.EmptySpanContext()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MockTracer) getSpanID() otelcore.SpanID {
|
func (t *MockTracer) getSpanID() otelcore.SpanID {
|
||||||
|
@ -50,10 +50,9 @@ func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.Una
|
|||||||
|
|
||||||
tr := global.TraceProvider().Tracer("example/grpc")
|
tr := global.TraceProvider().Tracer("example/grpc")
|
||||||
ctx, span := tr.Start(
|
ctx, span := tr.Start(
|
||||||
ctx,
|
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
|
||||||
"hello-api-op",
|
"hello-api-op",
|
||||||
trace.WithAttributes(serverSpanAttrs...),
|
trace.WithAttributes(serverSpanAttrs...),
|
||||||
trace.ChildOf(spanCtx),
|
|
||||||
trace.WithSpanKind(trace.SpanKindServer),
|
trace.WithSpanKind(trace.SpanKindServer),
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -63,10 +63,9 @@ func main() {
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
ctx, span := tr.Start(
|
ctx, span := tr.Start(
|
||||||
req.Context(),
|
trace.ContextWithRemoteSpanContext(req.Context(), spanCtx),
|
||||||
"hello",
|
"hello",
|
||||||
trace.WithAttributes(attrs...),
|
trace.WithAttributes(attrs...),
|
||||||
trace.ChildOf(spanCtx),
|
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
@ -57,10 +57,9 @@ func main() {
|
|||||||
})))
|
})))
|
||||||
|
|
||||||
ctx, span := tr.Start(
|
ctx, span := tr.Start(
|
||||||
req.Context(),
|
trace.ContextWithRemoteSpanContext(req.Context(), spanCtx),
|
||||||
"hello",
|
"hello",
|
||||||
trace.WithAttributes(attrs...),
|
trace.WithAttributes(attrs...),
|
||||||
trace.ChildOf(spanCtx),
|
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
apitrace "go.opentelemetry.io/otel/api/trace"
|
apitrace "go.opentelemetry.io/otel/api/trace"
|
||||||
|
"go.opentelemetry.io/otel/internal/trace/parent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockTracer is a simple tracer used for testing purpose only.
|
// MockTracer is a simple tracer used for testing purpose only.
|
||||||
@ -45,9 +46,9 @@ func (mt *MockTracer) WithSpan(ctx context.Context, name string, body func(conte
|
|||||||
return body(ctx)
|
return body(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a MockSpan. It creates a new Span based on Relation SpanContext option.
|
// Start starts a MockSpan. It creates a new Span based on Parent SpanContext option.
|
||||||
// TracdID is used from Relation Span Context and SpanID is assigned.
|
// TracdID is used from Parent Span Context and SpanID is assigned.
|
||||||
// If Relation SpanContext option is not specified then random TraceID is used.
|
// If Parent SpanContext option is not specified then random TraceID is used.
|
||||||
// No other options are supported.
|
// No other options are supported.
|
||||||
func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.StartOption) (context.Context, apitrace.Span) {
|
func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.StartOption) (context.Context, apitrace.Span) {
|
||||||
var opts apitrace.StartConfig
|
var opts apitrace.StartConfig
|
||||||
@ -56,14 +57,17 @@ func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.Star
|
|||||||
}
|
}
|
||||||
var span *MockSpan
|
var span *MockSpan
|
||||||
var sc core.SpanContext
|
var sc core.SpanContext
|
||||||
if !opts.Relation.SpanContext.IsValid() {
|
|
||||||
|
parentSpanContext, _, _ := parent.GetSpanContextAndLinks(ctx, opts.NewRoot)
|
||||||
|
|
||||||
|
if !parentSpanContext.IsValid() {
|
||||||
sc = core.SpanContext{}
|
sc = core.SpanContext{}
|
||||||
_, _ = rand.Read(sc.TraceID[:])
|
_, _ = rand.Read(sc.TraceID[:])
|
||||||
if mt.Sampled {
|
if mt.Sampled {
|
||||||
sc.TraceFlags = core.TraceFlagsSampled
|
sc.TraceFlags = core.TraceFlagsSampled
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sc = opts.Relation.SpanContext
|
sc = parentSpanContext
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.BigEndian.PutUint64(sc.SpanID[:], atomic.AddUint64(mt.StartSpanID, 1))
|
binary.BigEndian.PutUint64(sc.SpanID[:], atomic.AddUint64(mt.StartSpanID, 1))
|
||||||
|
41
internal/trace/parent/parent.go
Normal file
41
internal/trace/parent/parent.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package parent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
"go.opentelemetry.io/otel/api/key"
|
||||||
|
"go.opentelemetry.io/otel/api/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSpanContextAndLinks(ctx context.Context, ignoreContext bool) (core.SpanContext, bool, []trace.Link) {
|
||||||
|
lsctx := trace.SpanFromContext(ctx).SpanContext()
|
||||||
|
rsctx := trace.RemoteSpanContextFromContext(ctx)
|
||||||
|
|
||||||
|
if ignoreContext {
|
||||||
|
links := addLinkIfValid(nil, lsctx, "current")
|
||||||
|
links = addLinkIfValid(links, rsctx, "remote")
|
||||||
|
|
||||||
|
return core.EmptySpanContext(), false, links
|
||||||
|
}
|
||||||
|
if lsctx.IsValid() {
|
||||||
|
links := addLinkIfValid(nil, rsctx, "remote")
|
||||||
|
return lsctx, false, links
|
||||||
|
}
|
||||||
|
if rsctx.IsValid() {
|
||||||
|
return rsctx, true, nil
|
||||||
|
}
|
||||||
|
return core.EmptySpanContext(), false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLinkIfValid(links []trace.Link, sc core.SpanContext, kind string) []trace.Link {
|
||||||
|
if !sc.IsValid() {
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
return append(links, trace.Link{
|
||||||
|
SpanContext: sc,
|
||||||
|
Attributes: []core.KeyValue{
|
||||||
|
key.String("ignored-on-demand", kind),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -146,19 +146,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// TODO: do something with the correlation context
|
// TODO: do something with the correlation context
|
||||||
sc, _ := h.prop.Extract(r.Context(), r.Header)
|
sc, _ := h.prop.Extract(r.Context(), r.Header)
|
||||||
|
ctx := r.Context()
|
||||||
if sc.IsValid() { // not a valid span context, so no link / parent relationship to establish
|
if sc.IsValid() { // not a valid span context, so no link / parent relationship to establish
|
||||||
var opt trace.StartOption
|
var opt trace.StartOption
|
||||||
if h.public {
|
if h.public {
|
||||||
// If the endpoint is a public endpoint, it should start a new trace
|
// If the endpoint is a public endpoint, it should start a new trace
|
||||||
// and incoming remote sctx should be added as a link.
|
// and incoming remote sctx should be added as a link.
|
||||||
opt = trace.LinkedTo(sc)
|
opt = trace.LinkedTo(sc)
|
||||||
} else { // not a private endpoint, so assume child relationship
|
|
||||||
opt = trace.ChildOf(sc)
|
|
||||||
}
|
|
||||||
opts = append(opts, opt)
|
opts = append(opts, opt)
|
||||||
|
} else { // not a private endpoint, so assume child relationship
|
||||||
|
ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, span := h.tracer.Start(r.Context(), h.operation, opts...)
|
ctx, span := h.tracer.Start(ctx, h.operation, opts...)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
readRecordFunc := func(int64) {}
|
readRecordFunc := func(int64) {}
|
||||||
|
@ -187,7 +187,8 @@ func generateSpan(t *testing.T, tr apitrace.Tracer, option testOption) {
|
|||||||
|
|
||||||
for i := 0; i < option.genNumSpans; i++ {
|
for i := 0; i < option.genNumSpans; i++ {
|
||||||
binary.BigEndian.PutUint64(sc.TraceID[0:8], uint64(i+1))
|
binary.BigEndian.PutUint64(sc.TraceID[0:8], uint64(i+1))
|
||||||
_, span := tr.Start(context.Background(), option.name, apitrace.ChildOf(sc))
|
ctx := apitrace.ContextWithRemoteSpanContext(context.Background(), sc)
|
||||||
|
_, span := tr.Start(ctx, option.name)
|
||||||
span.End()
|
span.End()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,8 @@ func TestSimpleSpanProcessorOnEnd(t *testing.T) {
|
|||||||
SpanID: sid,
|
SpanID: sid,
|
||||||
TraceFlags: 0x1,
|
TraceFlags: 0x1,
|
||||||
}
|
}
|
||||||
_, span := tr.Start(context.Background(), "OnEnd", apitrace.ChildOf(sc))
|
ctx := apitrace.ContextWithRemoteSpanContext(context.Background(), sc)
|
||||||
|
_, span := tr.Start(ctx, "OnEnd")
|
||||||
span.End()
|
span.End()
|
||||||
|
|
||||||
wantTraceID := tid
|
wantTraceID := tid
|
||||||
|
@ -175,7 +175,7 @@ func TestSampling(t *testing.T) {
|
|||||||
tr := p.Tracer("test")
|
tr := p.Tracer("test")
|
||||||
var sampled int
|
var sampled int
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var opts []apitrace.StartOption
|
ctx := context.Background()
|
||||||
if tc.parent {
|
if tc.parent {
|
||||||
psc := core.SpanContext{
|
psc := core.SpanContext{
|
||||||
TraceID: idg.NewTraceID(),
|
TraceID: idg.NewTraceID(),
|
||||||
@ -184,9 +184,9 @@ func TestSampling(t *testing.T) {
|
|||||||
if tc.sampledParent {
|
if tc.sampledParent {
|
||||||
psc.TraceFlags = core.TraceFlagsSampled
|
psc.TraceFlags = core.TraceFlagsSampled
|
||||||
}
|
}
|
||||||
opts = append(opts, apitrace.ChildOf(psc))
|
ctx = apitrace.ContextWithRemoteSpanContext(ctx, psc)
|
||||||
}
|
}
|
||||||
_, span := tr.Start(context.Background(), "test", opts...)
|
_, span := tr.Start(ctx, "test")
|
||||||
if span.SpanContext().IsSampled() {
|
if span.SpanContext().IsSampled() {
|
||||||
sampled++
|
sampled++
|
||||||
}
|
}
|
||||||
@ -208,21 +208,22 @@ func TestSampling(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStartSpanWithChildOf(t *testing.T) {
|
func TestStartSpanWithParent(t *testing.T) {
|
||||||
tp, _ := NewProvider()
|
tp, _ := NewProvider()
|
||||||
tr := tp.Tracer("SpanWith ChildOf")
|
tr := tp.Tracer("SpanWithParent")
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
sc1 := core.SpanContext{
|
sc1 := core.SpanContext{
|
||||||
TraceID: tid,
|
TraceID: tid,
|
||||||
SpanID: sid,
|
SpanID: sid,
|
||||||
TraceFlags: 0x0,
|
TraceFlags: 0x0,
|
||||||
}
|
}
|
||||||
_, s1 := tr.Start(context.Background(), "span1-unsampled-parent1", apitrace.ChildOf(sc1))
|
_, s1 := tr.Start(apitrace.ContextWithRemoteSpanContext(ctx, sc1), "span1-unsampled-parent1")
|
||||||
if err := checkChild(sc1, s1); err != nil {
|
if err := checkChild(sc1, s1); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, s2 := tr.Start(context.Background(), "span2-unsampled-parent1", apitrace.ChildOf(sc1))
|
_, s2 := tr.Start(apitrace.ContextWithRemoteSpanContext(ctx, sc1), "span2-unsampled-parent1")
|
||||||
if err := checkChild(sc1, s2); err != nil {
|
if err := checkChild(sc1, s2); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -233,60 +234,18 @@ func TestStartSpanWithChildOf(t *testing.T) {
|
|||||||
TraceFlags: 0x1,
|
TraceFlags: 0x1,
|
||||||
//Tracestate: testTracestate,
|
//Tracestate: testTracestate,
|
||||||
}
|
}
|
||||||
_, s3 := tr.Start(context.Background(), "span3-sampled-parent2", apitrace.ChildOf(sc2))
|
_, s3 := tr.Start(apitrace.ContextWithRemoteSpanContext(ctx, sc2), "span3-sampled-parent2")
|
||||||
if err := checkChild(sc2, s3); err != nil {
|
if err := checkChild(sc2, s3); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, s4 := tr.Start(context.Background(), "span4-sampled-parent2", apitrace.ChildOf(sc2))
|
ctx2, s4 := tr.Start(apitrace.ContextWithRemoteSpanContext(ctx, sc2), "span4-sampled-parent2")
|
||||||
if err := checkChild(sc2, s4); err != nil {
|
if err := checkChild(sc2, s4); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s4Sc := s4.SpanContext()
|
s4Sc := s4.SpanContext()
|
||||||
_, s5 := tr.Start(ctx, "span5-implicit-childof-span4")
|
_, s5 := tr.Start(ctx2, "span5-implicit-childof-span4")
|
||||||
if err := checkChild(s4Sc, s5); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStartSpanWithFollowsFrom(t *testing.T) {
|
|
||||||
tp, _ := NewProvider()
|
|
||||||
tr := tp.Tracer("SpanWith FollowsFrom")
|
|
||||||
|
|
||||||
sc1 := core.SpanContext{
|
|
||||||
TraceID: tid,
|
|
||||||
SpanID: sid,
|
|
||||||
TraceFlags: 0x0,
|
|
||||||
}
|
|
||||||
_, s1 := tr.Start(context.Background(), "span1-unsampled-parent1", apitrace.FollowsFrom(sc1))
|
|
||||||
if err := checkChild(sc1, s1); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, s2 := tr.Start(context.Background(), "span2-unsampled-parent1", apitrace.FollowsFrom(sc1))
|
|
||||||
if err := checkChild(sc1, s2); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sc2 := core.SpanContext{
|
|
||||||
TraceID: tid,
|
|
||||||
SpanID: sid,
|
|
||||||
TraceFlags: 0x1,
|
|
||||||
//Tracestate: testTracestate,
|
|
||||||
}
|
|
||||||
_, s3 := tr.Start(context.Background(), "span3-sampled-parent2", apitrace.FollowsFrom(sc2))
|
|
||||||
if err := checkChild(sc2, s3); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, s4 := tr.Start(context.Background(), "span4-sampled-parent2", apitrace.FollowsFrom(sc2))
|
|
||||||
if err := checkChild(sc2, s4); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s4Sc := s4.SpanContext()
|
|
||||||
_, s5 := tr.Start(ctx, "span5-implicit-childof-span4")
|
|
||||||
if err := checkChild(s4Sc, s5); err != nil {
|
if err := checkChild(s4Sc, s5); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -574,15 +533,15 @@ func TestLinksOverLimit(t *testing.T) {
|
|||||||
func TestSetSpanName(t *testing.T) {
|
func TestSetSpanName(t *testing.T) {
|
||||||
te := &testExporter{}
|
te := &testExporter{}
|
||||||
tp, _ := NewProvider(WithSyncer(te))
|
tp, _ := NewProvider(WithSyncer(te))
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
want := "SpanName-1"
|
want := "SpanName-1"
|
||||||
_, span := tp.Tracer("SetSpanName").Start(context.Background(), "SpanName-1",
|
ctx = apitrace.ContextWithRemoteSpanContext(ctx, core.SpanContext{
|
||||||
apitrace.ChildOf(core.SpanContext{
|
|
||||||
TraceID: tid,
|
TraceID: tid,
|
||||||
SpanID: sid,
|
SpanID: sid,
|
||||||
TraceFlags: 1,
|
TraceFlags: 1,
|
||||||
}),
|
})
|
||||||
)
|
_, span := tp.Tracer("SetSpanName").Start(ctx, "SpanName-1")
|
||||||
got, err := endSpan(te, span)
|
got, err := endSpan(te, span)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -662,13 +621,15 @@ func startSpan(tp *Provider, trName string, args ...apitrace.StartOption) apitra
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startNamed Span is a test utility func that starts a span with a
|
// startNamed Span is a test utility func that starts a span with a
|
||||||
// passed name and with ChildOf option. remote span context contains
|
// passed name and with remote span context as parent. The remote span
|
||||||
// TraceFlags with sampled bit set. This allows the span to be
|
// context contains TraceFlags with sampled bit set. This allows the
|
||||||
// automatically sampled.
|
// span to be automatically sampled.
|
||||||
func startNamedSpan(tp *Provider, trName, name string, args ...apitrace.StartOption) apitrace.Span {
|
func startNamedSpan(tp *Provider, trName, name string, args ...apitrace.StartOption) apitrace.Span {
|
||||||
args = append(args, apitrace.ChildOf(remoteSpanContext()), apitrace.WithRecord())
|
ctx := context.Background()
|
||||||
|
ctx = apitrace.ContextWithRemoteSpanContext(ctx, remoteSpanContext())
|
||||||
|
args = append(args, apitrace.WithRecord())
|
||||||
_, span := tp.Tracer(trName).Start(
|
_, span := tp.Tracer(trName).Start(
|
||||||
context.Background(),
|
ctx,
|
||||||
name,
|
name,
|
||||||
args...,
|
args...,
|
||||||
)
|
)
|
||||||
@ -678,7 +639,7 @@ func startNamedSpan(tp *Provider, trName, name string, args ...apitrace.StartOpt
|
|||||||
// endSpan is a test utility function that ends the span in the context and
|
// endSpan is a test utility function that ends the span in the context and
|
||||||
// returns the exported export.SpanData.
|
// returns the exported export.SpanData.
|
||||||
// It requires that span be sampled using one of these methods
|
// It requires that span be sampled using one of these methods
|
||||||
// 1. Passing parent span context using ChildOf option
|
// 1. Passing parent span context in context
|
||||||
// 2. Use WithSampler(AlwaysSample())
|
// 2. Use WithSampler(AlwaysSample())
|
||||||
// 3. Configuring AlwaysSample() as default sampler
|
// 3. Configuring AlwaysSample() as default sampler
|
||||||
//
|
//
|
||||||
@ -739,9 +700,10 @@ func TestEndSpanTwice(t *testing.T) {
|
|||||||
func TestStartSpanAfterEnd(t *testing.T) {
|
func TestStartSpanAfterEnd(t *testing.T) {
|
||||||
spans := make(fakeExporter)
|
spans := make(fakeExporter)
|
||||||
tp, _ := NewProvider(WithConfig(Config{DefaultSampler: AlwaysSample()}), WithSyncer(spans))
|
tp, _ := NewProvider(WithConfig(Config{DefaultSampler: AlwaysSample()}), WithSyncer(spans))
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
tr := tp.Tracer("SpanAfterEnd")
|
tr := tp.Tracer("SpanAfterEnd")
|
||||||
ctx, span0 := tr.Start(context.Background(), "parent", apitrace.ChildOf(remoteSpanContext()))
|
ctx, span0 := tr.Start(apitrace.ContextWithRemoteSpanContext(ctx, remoteSpanContext()), "parent")
|
||||||
ctx1, span1 := tr.Start(ctx, "span-1")
|
ctx1, span1 := tr.Start(ctx, "span-1")
|
||||||
span1.End()
|
span1.End()
|
||||||
// Start a new span with the context containing span-1
|
// Start a new span with the context containing span-1
|
||||||
@ -852,17 +814,18 @@ func TestExecutionTracerTaskEnd(t *testing.T) {
|
|||||||
|
|
||||||
tID, _ := core.TraceIDFromHex("0102030405060708090a0b0c0d0e0f")
|
tID, _ := core.TraceIDFromHex("0102030405060708090a0b0c0d0e0f")
|
||||||
sID, _ := core.SpanIDFromHex("0001020304050607")
|
sID, _ := core.SpanIDFromHex("0001020304050607")
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
_, apiSpan = tr.Start(
|
ctx = apitrace.ContextWithRemoteSpanContext(ctx,
|
||||||
context.Background(),
|
|
||||||
"foo",
|
|
||||||
apitrace.ChildOf(
|
|
||||||
core.SpanContext{
|
core.SpanContext{
|
||||||
TraceID: tID,
|
TraceID: tID,
|
||||||
SpanID: sID,
|
SpanID: sID,
|
||||||
TraceFlags: 0,
|
TraceFlags: 0,
|
||||||
},
|
},
|
||||||
),
|
)
|
||||||
|
_, apiSpan = tr.Start(
|
||||||
|
ctx,
|
||||||
|
"foo",
|
||||||
)
|
)
|
||||||
s = apiSpan.(*span)
|
s = apiSpan.(*span)
|
||||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||||
|
@ -17,8 +17,8 @@ package trace
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
|
||||||
apitrace "go.opentelemetry.io/otel/api/trace"
|
apitrace "go.opentelemetry.io/otel/api/trace"
|
||||||
|
"go.opentelemetry.io/otel/internal/trace/parent"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tracer struct {
|
type tracer struct {
|
||||||
@ -30,29 +30,23 @@ var _ apitrace.Tracer = &tracer{}
|
|||||||
|
|
||||||
func (tr *tracer) Start(ctx context.Context, name string, o ...apitrace.StartOption) (context.Context, apitrace.Span) {
|
func (tr *tracer) Start(ctx context.Context, name string, o ...apitrace.StartOption) (context.Context, apitrace.Span) {
|
||||||
var opts apitrace.StartConfig
|
var opts apitrace.StartConfig
|
||||||
var parent core.SpanContext
|
|
||||||
var remoteParent bool
|
|
||||||
|
|
||||||
//TODO [rghetia] : Add new option for parent. If parent is configured then use that parent.
|
|
||||||
for _, op := range o {
|
for _, op := range o {
|
||||||
op(&opts)
|
op(&opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if relation := opts.Relation; relation.SpanContext != core.EmptySpanContext() {
|
parentSpanContext, remoteParent, links := parent.GetSpanContextAndLinks(ctx, opts.NewRoot)
|
||||||
switch relation.RelationshipType {
|
|
||||||
case apitrace.ChildOfRelationship, apitrace.FollowsFromRelationship:
|
if p := apitrace.SpanFromContext(ctx); p != nil {
|
||||||
parent = relation.SpanContext
|
if sdkSpan, ok := p.(*span); ok {
|
||||||
remoteParent = true
|
sdkSpan.addChild()
|
||||||
default:
|
|
||||||
// Future relationship types may have different behavior,
|
|
||||||
// e.g., adding a `Link` instead of setting the `parent`
|
|
||||||
}
|
}
|
||||||
} else if p, ok := apitrace.SpanFromContext(ctx).(*span); ok {
|
|
||||||
p.addChild()
|
|
||||||
parent = p.spanContext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
span := startSpanInternal(tr, name, parent, remoteParent, opts)
|
span := startSpanInternal(tr, name, parentSpanContext, remoteParent, opts)
|
||||||
|
for _, l := range links {
|
||||||
|
span.addLink(l)
|
||||||
|
}
|
||||||
for _, l := range opts.Links {
|
for _, l := range opts.Links {
|
||||||
span.addLink(l)
|
span.addLink(l)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user