// TestTeeTracerSpanRefs verifies that ChildOf/FollowsFrom relations are // reflected correctly in the underlying spans. func TestTeeTracerSpanRefs(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) root := tr.StartSpan("x") child := tr.StartSpan("x", opentracing.ChildOf(root.Context())) child.Finish() root.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 := spans[0].Context.TraceID, spans[1].Context.TraceID; a != e { t.Errorf("expected %d, got %d", e, a) } if e, a := spans[1].Context.SpanID, spans[0].ParentSpanID; a != e { t.Errorf("expected %d, got %d", e, a) } } }
// testingTracer creates a Tracer that appends the events to the given slice. func testingTracer(ev *events) opentracing.Tracer { opts := basictracer.DefaultOptions() opts.ShouldSample = func(_ uint64) bool { return true } opts.NewSpanEventListener = func() func(basictracer.SpanEvent) { // The op variable is used in the returned function and is associated with // a span; it lives for as long as the span is open. var op string return func(e basictracer.SpanEvent) { switch t := e.(type) { case basictracer.EventCreate: op = t.OperationName *ev = append(*ev, fmt.Sprintf("%s:start", op)) case basictracer.EventFinish: *ev = append(*ev, fmt.Sprintf("%s:finish", op)) case basictracer.EventLogFields: *ev = append(*ev, fmt.Sprintf("%s:%s", op, t.Fields[0].Value())) case basictracer.EventLog: panic("EventLog is deprecated") } } } opts.DebugAssertUseAfterFinish = true // We don't care about the recorder but we need to set it to something. opts.Recorder = &basictracer.InMemorySpanRecorder{} return basictracer.NewWithOptions(opts) }
// JoinOrNewSnowball returns a Span which records directly via the specified // callback. If the given DelegatingCarrier is nil, a new Span is created. // otherwise, the created Span is a child. func JoinOrNewSnowball(opName string, carrier *Span, callback func(sp basictracer.RawSpan)) (opentracing.Span, error) { tr := basictracer.NewWithOptions(defaultOptions(callback)) sp, err := JoinOrNew(tr, carrier, opName) if err == nil { // We definitely want to sample a Snowball trace. // This must be set *before* SetBaggageItem, as that will otherwise be ignored. ext.SamplingPriority.Set(sp, 1) sp.SetBaggageItem(Snowball, "1") } return sp, err }
// NewTracerAndSpanFor7881 creates a new tracer and a root span. The tracer is // to be used for tracking down #7881; it runs a callback for each finished span // (and the callback used accumulates the spans in a SQL txn). func NewTracerAndSpanFor7881( callback func(sp basictracer.RawSpan), ) (opentracing.Span, opentracing.Tracer, error) { opts := basictracerOptions(callback) // Don't trim the logs in "unsampled" spans". Note that this tracer does not // use sampling; instead it uses an ad-hoc mechanism for marking spans of // interest. opts.TrimUnsampledSpans = false tr := basictracer.NewWithOptions(opts) sp, err := JoinOrNew(tr, nil, "sql txn") return sp, tr, err }
return sp, err } func defaultOptions(recorder func(basictracer.RawSpan)) basictracer.Options { opts := basictracer.DefaultOptions() opts.ShouldSample = func(traceID int64) bool { return false } opts.TrimUnsampledSpans = true opts.Recorder = CallbackRecorder(recorder) opts.NewSpanEventListener = basictracer.NetTraceIntegrator opts.DebugAssertUseAfterFinish = true // provoke crash on use-after-Finish return opts } // newTracer implements NewTracer and allows that function to be mocked out via Disable(). var newTracer = func() opentracing.Tracer { return basictracer.NewWithOptions(defaultOptions(func(_ basictracer.RawSpan) {})) } // NewTracer creates a Tracer which records to the net/trace // endpoint. func NewTracer() opentracing.Tracer { return newTracer() } // EnsureContext checks whether the given context.Context contains a Span. If // not, it creates one using the provided Tracer and wraps it in the returned // Span. The returned closure must be called after the request has been fully // processed. func EnsureContext(ctx context.Context, tracer opentracing.Tracer) (context.Context, func()) { _, _, funcName := caller.Lookup(1) if opentracing.SpanFromContext(ctx) == nil {
// By default, if a lightstep token is specified we trace to both Lightstep and // net/trace. If this flag is enabled, we will only trace to Lightstep. var lightstepOnly = envutil.EnvOrDefaultBool("COCKROACH_LIGHTSTEP_ONLY", false) // newTracer implements NewTracer and allows that function to be mocked out via Disable(). var newTracer = func() opentracing.Tracer { if lightstepToken != "" { lsTr := lightstep.NewTracer(lightstep.Options{ AccessToken: lightstepToken, MaxLogsPerSpan: maxLogsPerSpan, UseGRPC: true, }) if lightstepOnly { return lsTr } basicTr := basictracer.NewWithOptions(basictracerOptions(nil)) // The TeeTracer uses the first tracer for serialization of span contexts; // lightspan needs to be first because it correlates spans between nodes. return NewTeeTracer(lsTr, basicTr) } return basictracer.NewWithOptions(basictracerOptions(nil)) } // NewTracer creates a Tracer which records to the net/trace // endpoint. func NewTracer() opentracing.Tracer { return newTracer() } // EnsureContext checks whether the given context.Context contains a Span. If // not, it creates one using the provided Tracer and wraps it in the returned
if err == nil { sp.SetBaggageItem(Snowball, "1") // We definitely want to sample a Snowball trace. ext.SamplingPriority.Set(sp, 1) } return sp, err } // newTracer implements NewTracer and allows that function to be mocked out via Disable(). var newTracer = func() opentracing.Tracer { opts := basictracer.DefaultOptions() opts.TrimUnsampledSpans = true opts.Recorder = CallbackRecorder(func(_ basictracer.RawSpan) {}) opts.NewSpanEventListener = basictracer.NetTraceIntegrator opts.DebugAssertUseAfterFinish = true // provoke crash on use-after-Finish return basictracer.NewWithOptions(opts) } // NewTracer creates a Tracer which records to the net/trace // endpoint. func NewTracer() opentracing.Tracer { return newTracer() } // SpanFromContext returns the Span obtained from the context or, if none is // found, a new one started through the tracer. Callers should call (or defer) // the returned cleanup func as well to ensure that the span is Finish()ed, but // callers should *not* attempt to call Finish directly -- in the case where the // span was obtained from the context, it is not the caller's to Finish. func SpanFromContext(opName string, tracer opentracing.Tracer, ctx context.Context) (opentracing.Span, func()) { sp := opentracing.SpanFromContext(ctx)
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) } } }
// NewTracerWithOptions creates a new opentracing.Tracer that records spans to // the given appdash.Collector. func NewTracerWithOptions(c appdash.Collector, options Options) opentracing.Tracer { opts := basictracer.DefaultOptions() opts.ShouldSample = options.ShouldSample opts.Recorder = NewRecorder(c, options) return basictracer.NewWithOptions(opts) }