public abstract 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.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
}
}
Modifier and Type | Field | Description |
---|---|---|
static ThreadLocalSpan |
CURRENT_TRACER |
This uses the
Tracing.currentTracer() , which means calls to next() may return
null. |
Modifier and Type | Method | Description |
---|---|---|
static ThreadLocalSpan |
create(Tracer tracer) |
|
Span |
next() |
Returns the
Tracer.nextSpan() or null if CURRENT_TRACER and tracing isn't
available. |
Span |
next(TraceContextOrSamplingFlags extracted) |
Returns the
Tracer.nextSpan(TraceContextOrSamplingFlags) or null if CURRENT_TRACER
and tracing isn't available. |
Span |
remove() |
Returns the span set in scope via
next() or null if there was none. |
public static final ThreadLocalSpan CURRENT_TRACER
Tracing.currentTracer()
, which means calls to next()
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.public static ThreadLocalSpan create(Tracer tracer)
@Nullable public Span next(TraceContextOrSamplingFlags extracted)
Tracer.nextSpan(TraceContextOrSamplingFlags)
or null if CURRENT_TRACER
and tracing isn't available.@Nullable public Span next()
Tracer.nextSpan()
or null if CURRENT_TRACER
and tracing isn't
available.@Nullable public Span remove()
next()
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)
or CurrentTraceContext.newScope(TraceContext)
outside a try/finally block.
Copyright © 2018 OpenZipkin. All rights reserved.