Package brave.handler

Class MutableSpan

java.lang.Object
brave.handler.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