Class ThreadLocalSpan
public class ThreadLocalSpan extends Object
Many libraries expose a callback model as opposed to an interceptor one. When creating new instrumentation, you may find places where you need to place a span in scope in one callback (like `onStart()`) and end the scope in another callback (like `onFinish()`).
Provided the library guarantees these run on the same thread, you can simply propagate the
result of Tracer.startScopedSpan(String)
or Tracer.withSpanInScope(Span)
from the
starting callback to the closing one. This is typically done with a request-scoped attribute.
Here's an example:
class MyFilter extends Filter {
public void onStart(Request request, Attributes attributes) {
// Assume you have code to start the span and add relevant tags...
// We now set the span in scope so that any code between here and
// the end of the request can see it with Tracer.currentSpan()
SpanInScope spanInScope = tracer.withSpanInScope(span);
// We don't want to leak the scope, so we place it somewhere we can
// lookup later
attributes.put(SpanInScope.class, spanInScope);
}
public void onFinish(Response response, Attributes attributes) {
// as long as we are on the same thread, we can read the span started above
Span span = tracer.currentSpan();
// Assume you have code to complete the span
// We now remove the scope (which implicitly detaches it from the span)
attributes.remove(SpanInScope.class).close();
}
}
Sometimes you have to instrument a library where There's no attribute namespace shared across
request and response. For this scenario, you can use ThreadLocalSpan
to temporarily store
the span between callbacks.
Here's an example:
class MyFilter extends Filter {
final ThreadLocalSpan threadLocalSpan;
public void onStart(Request request) {
// Allocates a span and places it in scope so that code between here and onFinish can see it
Span span = threadLocalSpan.next();
if (span == null || span.isNoop()) return; // skip below logic on noop
// Assume you have code to start the span and add relevant tags...
}
public void onFinish(Response response, Attributes attributes) {
// as long as we are on the same thread, we can read the span started above
Span span = threadLocalSpan.remove();
if (span == null || span.isNoop()) return; // skip below logic on noop
// Assume you have code to complete the span
}
}
-
Field Summary
Fields Modifier and Type Field Description static ThreadLocalSpan
CURRENT_TRACER
This uses theTracing.currentTracer()
, which means calls tonext()
may return null. -
Method Summary
Modifier and Type Method Description static ThreadLocalSpan
create(Tracer tracer)
Span
next()
Returns theTracer.nextSpan()
or null ifCURRENT_TRACER
and tracing isn't available.Span
next(TraceContextOrSamplingFlags extracted)
Returns theTracer.nextSpan(TraceContextOrSamplingFlags)
or null ifCURRENT_TRACER
and tracing isn't available.Span
remove()
Returns the span set in scope vianext()
or null if there was none.
-
Field Details
-
CURRENT_TRACER
This uses theTracing.currentTracer()
, which means calls tonext()
may return null. Use this when you have no other means to get a reference to the tracer. For example, JDBC connections, as they often initialize prior to the tracing component.
-
-
Method Details
-
create
-
next
Returns theTracer.nextSpan(TraceContextOrSamplingFlags)
or null ifCURRENT_TRACER
and tracing isn't available. -
next
Returns theTracer.nextSpan()
or null ifCURRENT_TRACER
and tracing isn't available. -
remove
Returns the span set in scope vianext()
or null if there was none.When assertions are on, this will throw an assertion error if the span returned was not the one currently in context. This could happen if someone called
Tracer.withSpanInScope(Span)
orCurrentTraceContext.newScope(TraceContext)
outside a try/finally block.
-