Package brave

Class Tracer

java.lang.Object
brave.Tracer

public class Tracer
extends Object
Using a tracer, you can create a root span capturing the critical path of a request. Child spans can be created to allocate latency relating to outgoing requests. When tracing single-threaded code, just run it inside a scoped span:

 // Start a new trace or a span within an existing trace representing an operation
 ScopedSpan span = tracer.startScopedSpan("encode");
 try {
   // The span is in "scope" so that downstream code such as loggers can see trace IDs
   return encoder.encode();
 } catch (RuntimeException | Error e) {
   span.error(e); // Unless you handle exceptions, you might not know the operation failed!
   throw e;
 } finally {
   span.finish();
 }
 

When you need more features, or finer control, use the Span type:


 // Start a new trace or a span within an existing trace representing an operation
 Span span = tracer.nextSpan().name("encode").start();
 // Put the span in "scope" so that downstream code such as loggers can see trace IDs
 try (SpanInScope ws = tracer.withSpanInScope(span)) {
   return encoder.encode();
 } catch (RuntimeException | Error e) {
   span.error(e); // Unless you handle exceptions, you might not know the operation failed!
   throw e;
 } finally {
   span.finish(); // note the scope is independent of the span. Always finish a span.
 }
 

Both of the above examples report the exact same span on finish!

See Also:
Span, ScopedSpan, Propagation
  • Method Details

    • withSampler

      @Deprecated public Tracer withSampler​(Sampler sampler)
      Since:
      4.19
    • newTrace

      public Span newTrace()
      Explicitly creates a new trace. The result will be a root span (no parent span ID).

      To implicitly create a new trace, or a span within an existing one, use nextSpan().

    • joinSpan

      public final Span joinSpan​(TraceContext context)
      Joining is re-using the same trace and span ids extracted from an incoming RPC request. This should not be used for messaging operations, as nextSpan(TraceContextOrSamplingFlags) is a better choice.

      When this incoming context is sampled, we assume this is a shared span, one where the caller and the current tracer report to the same span IDs. If no sampling decision occurred yet, we have exclusive access to this span ID.

      Here's an example of conditionally joining a span, depending on if a trace context was extracted from an incoming request.

      
       extracted = extractor.extract(request);
       span = contextOrFlags.context() != null
                ? tracer.joinSpan(contextOrFlags.context())
                : tracer.nextSpan(extracted);
       

      Note: When Propagation.Factory.supportsJoin() is false, this will always fork a new child via newChild(TraceContext).

      See Also:
      Propagation, TraceContext.Extractor.extract(Object), TraceContextOrSamplingFlags.context(), nextSpan(TraceContextOrSamplingFlags)
    • newChild

      public Span newChild​(TraceContext parent)
      Explicitly creates a child within an existing trace. The result will be have its parent ID set to the input's span ID. If a sampling decision has not yet been made, one will happen here.

      To implicitly create a new trace, or a span within an existing one, use nextSpan().

    • nextSpan

      public Span nextSpan​(TraceContextOrSamplingFlags extracted)
      This creates a new span based on parameters extracted from an incoming request. This will always result in a new span. If no trace identifiers were extracted, a span will be created based on the implicit context in the same manner as nextSpan(). If a sampling decision has not yet been made, one will happen here.

      Ex.

      
       extracted = extractor.extract(request);
       span = tracer.nextSpan(extracted);
       

      Note: Unlike joinSpan(TraceContext), this does not attempt to re-use extracted span IDs. This means the extracted context (if any) is the parent of the span returned.

      Note: If a context could be extracted from the input, that trace is resumed, not whatever the currentSpan() was. Make sure you re-apply withSpanInScope(Span) so that data is written to the correct trace.

      See Also:
      Propagation, TraceContext.Extractor.extract(Object), nextSpan(SamplerFunction, Object)
    • toSpan

      public Span toSpan​(TraceContext context)
      Converts the context to a Span object after decorating it for propagation.

      This api is not advised for routine use. It is better to hold a reference to a span created elsewhere vs rely on implicit lookups.

    • withSpanInScope

      public Tracer.SpanInScope withSpanInScope​(@Nullable Span span)
      Makes the given span the "current span" and returns an object that exits that scope on close. Calls to currentSpan() and currentSpanCustomizer() will affect this span until the return value is closed.

      The most convenient way to use this method is via the try-with-resources idiom. Ex.

      
       // Assume a framework interceptor uses this method to set the inbound span as current
       try (SpanInScope ws = tracer.withSpanInScope(span)) {
         return inboundRequest.invoke();
       // note: try-with-resources closes the scope *before* the catch block
       } catch (RuntimeException | Error e) {
         span.error(e);
         throw e;
       } finally {
         span.finish();
       }
      
       // An unrelated framework interceptor can now lookup the correct parent for outbound requests
       Span parent = tracer.currentSpan()
       Span span = tracer.nextSpan().name("outbound").start(); // parent is implicitly looked up
       try (SpanInScope ws = tracer.withSpanInScope(span)) {
         return outboundRequest.invoke();
       // note: try-with-resources closes the scope *before* the catch block
       } catch (RuntimeException | Error e) {
         span.error(e);
         throw e;
       } finally {
         span.finish();
       }
       

      When tracing in-process commands, prefer startScopedSpan(String) which scopes by default.

      Note: While downstream code might affect the span, calling this method, and calling close on the result have no effect on the input. For example, calling close on the result does not finish the span. Not only is it safe to call close, you must call close to end the scope, or risk leaking resources associated with the scope.

      Parameters:
      span - span to place into scope or null to clear the scope
    • currentSpanCustomizer

      public SpanCustomizer currentSpanCustomizer()
      Returns a customizer for current span in scope or noop if there isn't one.

      Unlike CurrentSpanCustomizer, this represents a single span. Accordingly, this reference should not be saved as a field. That said, it is more efficient to save this result as a method-local variable vs repeated calls.

    • currentSpan

      @Nullable public Span currentSpan()
      Returns the current span in scope or null if there isn't one.

      When entering user code, prefer currentSpanCustomizer() as it is a stable type and will never return null.

    • nextSpan

      public Span nextSpan()
      Returns a new child span if there's a currentSpan() or a new trace if there isn't.

      Prefer startScopedSpan(String) if you are tracing a synchronous function or code block.

    • startScopedSpan

      public ScopedSpan startScopedSpan​(String name)
      Returns a new child span if there's a currentSpan() or a new trace if there isn't. The result is the "current span" until ScopedSpan.finish() is called. Here's an example:
      
       ScopedSpan span = tracer.startScopedSpan("encode");
       try {
         // The span is in "scope" so that downstream code such as loggers can see trace IDs
         return encoder.encode();
       } catch (RuntimeException | Error e) {
         span.error(e); // Unless you handle exceptions, you might not know the operation failed!
         throw e;
       } finally {
         span.finish();
       }
       
    • startScopedSpan

      public <T> ScopedSpan startScopedSpan​(String name, SamplerFunction<T> samplerFunction, T arg)
      Like startScopedSpan(String) except when there is no trace in process, the sampler triggers against the supplied argument.
      Parameters:
      name - the span name
      samplerFunction - invoked if there's no current trace
      arg - parameter to SamplerFunction.trySample(Object)
      Since:
      5.8
      See Also:
      nextSpan(SamplerFunction, Object)
    • nextSpan

      public <T> Span nextSpan​(SamplerFunction<T> samplerFunction, T arg)
      Like nextSpan() except when there is no trace in process, the sampler triggers against the supplied argument.
      Parameters:
      samplerFunction - invoked if there's no current trace
      arg - parameter to SamplerFunction.trySample(Object)
      Since:
      5.8
      See Also:
      startScopedSpan(String, SamplerFunction, Object), nextSpan(TraceContextOrSamplingFlags)
    • nextSpanWithParent

      public <T> Span nextSpanWithParent​(SamplerFunction<T> samplerFunction, T arg, @Nullable TraceContext parent)
      Like nextSpan(SamplerFunction, Object) except this controls the parent context explicitly. This is useful when an invocation context is propagated manually, commonly the case with asynchronous client frameworks.
      Parameters:
      samplerFunction - invoked if there's no current trace
      arg - parameter to SamplerFunction.trySample(Object)
      parent - of the new span, or null if it should have no parent
      Since:
      5.10
      See Also:
      nextSpan(SamplerFunction, Object)
    • startScopedSpanWithParent

      public ScopedSpan startScopedSpanWithParent​(String name, @Nullable TraceContext parent)
      Same as startScopedSpan(String), except ignores the current trace context.

      Use this when you are creating a scoped span in a method block where the parent was created. You can also use this to force a new trace by passing null parent.

    • toString

      public String toString()
      Overrides:
      toString in class Object