Package brave

Class 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 Detail

      • 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().

      • 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();
         }
         
      • 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.