package brave; import brave.internal.Internal; import brave.internal.Platform; import brave.propagation.CurrentTraceContext; import brave.propagation.Propagation; import brave.propagation.TraceContext; import brave.sampler.Sampler; import java.io.Closeable; import javax.annotation.Nullable; import zipkin.Endpoint; import zipkin.reporter.AsyncReporter; import zipkin.reporter.Reporter; import zipkin.reporter.Sender; /** * This provides utilities needed for trace instrumentation. For example, a {@link Tracer}. * * <p>Instances built via {@link #newBuilder()} are registered automatically such that statically * configured instrumentation like JDBC drivers can use {@link #current()}. * * <p>This type can be extended so that the object graph can be built differently or overridden, for * example via spring or when mocking. */ public abstract class Tracing implements Closeable { public static Builder newBuilder() { return new Builder(); } /** All tracing commands start with a {@link Span}. Use a tracer to create spans. */ abstract public Tracer tracer(); /** * When a trace leaves the process, it needs to be propagated, usually via headers. This utility * is used to inject or extract a trace context from remote requests. */ // Implementations should override and cache this as a field. public Propagation<String> propagation() { return propagationFactory().create(Propagation.KeyFactory.STRING); } /** This supports edge cases like GRPC Metadata propagation which doesn't use String keys. */ abstract public Propagation.Factory propagationFactory(); /** * This supports in-process propagation, typically across thread boundaries. This includes * utilities for concurrent types like {@linkplain java.util.concurrent.ExecutorService}. */ abstract public CurrentTraceContext currentTraceContext(); /** * This exposes the microsecond clock used by operations such as {@link Span#finish()}. This is * helpful when you want to time things manually. */ abstract public Clock clock(); // volatile for visibility on get. writes guarded by Tracing.class static volatile Tracing current = null; /** * Returns the most recently created tracer iff its component hasn't been closed. null otherwise. * * <p>This object should not be cached. */ @Nullable public static Tracer currentTracer() { Tracing tracing = current; return tracing != null ? tracing.tracer() : null; } /** * Returns the most recently created tracing component iff it hasn't been closed. null otherwise. * * <p>This object should not be cached. */ @Nullable public static Tracing current() { return current; } /** Ensures this component can be garbage collected, by making it not {@link #current()} */ @Override abstract public void close(); public static final class Builder { String localServiceName; Endpoint localEndpoint; Reporter<zipkin.Span> reporter; Clock clock; Sampler sampler = Sampler.ALWAYS_SAMPLE; CurrentTraceContext currentTraceContext = new CurrentTraceContext.Default(); boolean traceId128Bit = false; Propagation.Factory propagationFactory = Propagation.Factory.B3; /** * Controls the name of the service being traced, while still using a default site-local IP. * This is an alternative to {@link #localEndpoint(Endpoint)}. * * @param localServiceName name of the service being traced. Defaults to "unknown". */ public Builder localServiceName(String localServiceName) { if (localServiceName == null) throw new NullPointerException("localServiceName == null"); this.localServiceName = localServiceName; return this; } /** * @param localEndpoint Endpoint of the local service being traced. Defaults to site local. */ public Builder localEndpoint(Endpoint localEndpoint) { if (localEndpoint == null) throw new NullPointerException("localEndpoint == null"); this.localEndpoint = localEndpoint; return this; } /** * Controls how spans are reported. Defaults to logging, but often an {@link AsyncReporter} * which batches spans before sending to Zipkin. * * The {@link AsyncReporter} includes a {@link Sender}, which is a driver for transports like * http, kafka and scribe. * * <p>For example, here's how to batch send spans via http: * * <pre>{@code * reporter = AsyncReporter.create(URLConnectionSender.create("http://localhost:9411/api/v1/spans")); * * tracerBuilder.reporter(reporter); * }</pre> * * <p>See https://github.com/openzipkin/zipkin-reporter-java */ public Builder reporter(Reporter<zipkin.Span> reporter) { if (reporter == null) throw new NullPointerException("reporter == null"); this.reporter = reporter; return this; } /** See {@link Tracing#clock()} */ public Builder clock(Clock clock) { if (clock == null) throw new NullPointerException("clock == null"); this.clock = clock; return this; } /** * Sampler is responsible for deciding if a particular trace should be "sampled", i.e. whether * the overhead of tracing will occur and/or if a trace will be reported to Zipkin. */ public Builder sampler(Sampler sampler) { this.sampler = sampler; return this; } /** * Responsible for implementing {@link Tracer#currentSpan()} and {@link * Tracer#withSpanInScope(Span)}. By default a simple thread-local is used. Override to support * other mechanisms or to synchronize with other mechanisms such as SLF4J's MDC. */ public Builder currentTraceContext(CurrentTraceContext currentTraceContext) { this.currentTraceContext = currentTraceContext; return this; } /** When true, new root spans will have 128-bit trace IDs. Defaults to false (64-bit) */ public Builder traceId128Bit(boolean traceId128Bit) { this.traceId128Bit = traceId128Bit; return this; } public Tracing build() { if (clock == null) clock = Platform.get(); if (localEndpoint == null) { localEndpoint = Platform.get().localEndpoint(); if (localServiceName != null) { localEndpoint = localEndpoint.toBuilder().serviceName(localServiceName).build(); } } if (reporter == null) reporter = Platform.get(); return new Default(this); } Builder() { } } static { Internal.instance = new Internal() { @Override public Long timestamp(Tracer tracer, TraceContext context) { return tracer.recorder.timestamp(context); } }; } static final class Default extends Tracing { final Tracer tracer; final Propagation.Factory propagationFactory; final Propagation<String> stringPropagation; final CurrentTraceContext currentTraceContext; final Clock clock; Default(Builder builder) { this.tracer = new Tracer(builder); this.propagationFactory = builder.propagationFactory; this.stringPropagation = builder.propagationFactory.create(Propagation.KeyFactory.STRING); this.currentTraceContext = builder.currentTraceContext; this.clock = builder.clock; maybeSetCurrent(); } @Override public Tracer tracer() { return tracer; } @Override public Propagation<String> propagation() { return stringPropagation; } @Override public Propagation.Factory propagationFactory() { return propagationFactory; } @Override public CurrentTraceContext currentTraceContext() { return currentTraceContext; } @Override public Clock clock() { return clock; } private void maybeSetCurrent() { if (current != null) return; synchronized (Tracing.class) { if (current == null) current = this; } } @Override public void close() { if (current != this) return; // don't blindly set most recent to null as there could be a race synchronized (Tracing.class) { if (current == this) current = null; } } } }