Package brave.handler

Class MutableSpan

  • All Implemented Interfaces:
    Cloneable

    public final class MutableSpan
    extends Object
    implements Cloneable
    This represents a span except for its TraceContext. It is mutable, for late adjustments.

    Notes

    Between SpanHandler.begin(TraceContext, MutableSpan, TraceContext) and SpanHandler.end(TraceContext, MutableSpan, SpanHandler.Cause), Brave owns this reference, synchronizing where necessary as updates come from different application threads.

    Upon end, Brave no longer makes updates. It invokes each SpanHandler, one-by-one on the same thread. This means subsequent handlers do not have to synchronize to view updates from a prior one. However, it does imply they must make mutations on the same thread.

    In other words, this type is not thread safe. If you need to mutate this span in a different thread, use the copy constructor.

    MutableSpan.error() vs MutableSpan.tag("error")

    If tag(String) returns a result for "error", it was from a layered api, instrumentation or the user. error() is usually an uncaught exception and does not imply there's a tag "error".

    Here are examples of a span with error(), but no "error" tag:

    • brave.Span.error(new OutOfMemoryError()) -> MutableSpan.error(new OutOfMemoryError())
    • brave.Span.error(new RpcException()) -> MutableSpan.error(new RpcException())
    • brave.Span.error(new NullPointerException()) -> MutableSpan.error(new NullPointerException())

    The above are examples of exceptions that users typically do not process, so are unlikely to parse into an "error" tag. The opposite is also true as not all errors are derived from Throwable. Particularly, RPC frameworks often do not use exceptions as error signals.

    Here are examples of a span with an "error" tag, but no error():

    • io.opentracing.Span.tag(ERROR, true) -> MutableSpan.tag("error", "true")
    • brave.SpanCustomizer.tag("error", "") -> MutableSpan.tag("error", "")
    • brave.Span.tag("error", "CANCELLED") -> MutableSpan.tag("error", "CANCELLED")

    The above examples are using in-band apis in Brave. SpanHandler is after the fact. Since there is no default "error" tag, span handlers here can tell the difference between explicitly set error messages, and what's needed by their format. For example, those only looking at Zipkin clones may forget that error() exists for custom formats including metrics!

    Here are examples of SpanHandler.end(TraceContext, MutableSpan, SpanHandler.Cause) implementations that process errors:

    • MutableSpan.tag("error", "") to redact the error message from Zipkin
    • MutableSpan.error() -> MutableSpan.tag("exception", normalized) to match metrics dimension
    • MutableSpan.error() -> CustomFormat.stackTrace for sophisticated trace formats

    In summary, Brave intentionally does not default an "error" tag(String) from error(). This allows SpanHandler instances that report data be as simple as an error bit, or advanced enough to keep a stacktrace and also a user tag.

    Since:
    5.4