// ForkCtxSpan checks if ctx has a Span open; if it does, it creates a new Span // that follows from the original Span. This allows the resulting context to be // used in an async task that might outlive the original operation. // // Returns the new context and the new span (if any). The span should be // closed via FinishSpan. func ForkCtxSpan(ctx context.Context, opName string) (context.Context, opentracing.Span) { if span := opentracing.SpanFromContext(ctx); span != nil { if span.BaggageItem(Snowball) == "1" { // If we are doing snowball tracing, the span might outlive the snowball // tracer (calling the record function when it is no longer legal to do // so). Return a context with no span in this case. return opentracing.ContextWithSpan(ctx, nil), nil } tr := span.Tracer() newSpan := tr.StartSpan(opName, opentracing.FollowsFrom(span.Context())) return opentracing.ContextWithSpan(ctx, newSpan), newSpan } return ctx, nil }
// JoinOrNew creates a new Span joined to the provided DelegatingCarrier or // creates Span from the given tracer. func JoinOrNew(tr opentracing.Tracer, carrier *Span, opName string) (opentracing.Span, error) { if carrier != nil { wireContext, err := tr.Extract(basictracer.Delegator, carrier) switch err { case nil: sp := tr.StartSpan(opName, opentracing.FollowsFrom(wireContext)) sp.LogEvent(opName) return sp, nil case opentracing.ErrSpanContextNotFound: default: return nil, err } } return tr.StartSpan(opName), nil }
// JoinOrNew creates a new Span joined to the provided DelegatingCarrier or // creates Span from the given tracer. func JoinOrNew( tr opentracing.Tracer, carrier *SpanContextCarrier, opName string, ) (opentracing.Span, error) { if carrier != nil { wireContext, err := tr.Extract(basictracer.Delegator, carrier) switch err { case nil: sp := tr.StartSpan(opName, opentracing.FollowsFrom(wireContext)) // Copy baggage items to tags so they show up in the Lightstep UI. sp.Context().ForeachBaggageItem(func(k, v string) bool { sp.SetTag(k, v); return true }) sp.LogFields(otlog.String("event", opName)) return sp, nil case opentracing.ErrSpanContextNotFound: default: return nil, err } } return tr.StartSpan(opName), nil }
func TestTeeTracer(t *testing.T) { r1 := basictracer.NewInMemoryRecorder() t1 := basictracer.NewWithOptions(basictracer.Options{ Recorder: r1, ShouldSample: func(traceID uint64) bool { return true }, // always sample }) r2 := basictracer.NewInMemoryRecorder() t2 := basictracer.NewWithOptions(basictracer.Options{ Recorder: r2, ShouldSample: func(traceID uint64) bool { return true }, // always sample }) tr := NewTeeTracer(t1, t2) span := tr.StartSpan("x") span.LogKV("k1", "v1", "k2", "v2") span.SetTag("tag", "value") span.SetBaggageItem("baggage", "baggage-value") if e, a := "baggage-value", span.BaggageItem("baggage"); a != e { t.Errorf("expected %s, got %s", e, a) } spanCtx := span.Context() var ctxBuffer bytes.Buffer if err := tr.Inject(spanCtx, opentracing.Binary, &ctxBuffer); err != nil { t.Fatal(err) } decodedCtx, err := tr.Extract(opentracing.Binary, &ctxBuffer) if err != nil { t.Fatal(err) } span2 := tr.StartSpan("y", opentracing.FollowsFrom(decodedCtx)) span2.LogEvent("event2") if e, a := "baggage-value", span2.BaggageItem("baggage"); a != e { t.Errorf("expected %s, got %s", e, a) } span.Finish() span2.Finish() for _, spans := range [][]basictracer.RawSpan{r1.GetSpans(), r2.GetSpans()} { if e, a := 2, len(spans); a != e { t.Fatalf("expected %d, got %d", e, a) } if e, a := "x", spans[0].Operation; a != e { t.Errorf("expected %s, got %s", e, a) } if e, a := (opentracing.Tags{"tag": "value"}), spans[0].Tags; !reflect.DeepEqual(a, e) { t.Errorf("expected %s, got %s", e, a) } if e, a := "k1:v1", spans[0].Logs[0].Fields[0].String(); a != e { t.Errorf("expected %s, got %s", e, a) } if e, a := "k2:v2", spans[0].Logs[0].Fields[1].String(); a != e { t.Errorf("expected %s, got %s", e, a) } if e, a := 1, len(spans[0].Context.Baggage); a != e { t.Errorf("expected %d, got %d", e, a) } if e, a := "y", spans[1].Operation; a != e { t.Errorf("expected %s, got %s", e, a) } if e, a := opentracing.Tags(nil), spans[1].Tags; !reflect.DeepEqual(a, e) { t.Errorf("expected %s, got %s", e, a) } if e, a := "event:event2", spans[1].Logs[0].Fields[0].String(); a != e { t.Errorf("expected %s, got %s", e, a) } if e, a := 1, len(spans[1].Context.Baggage); a != e { t.Errorf("expected %d, got %d", e, a) } } }