OpenTracing instrumentation for scala.concurrent
package.
libraryDependencies += "io.opentracing.contrib" % "opentracing-scala-concurrent_2.12" % "VERSION"
libraryDependencies += "io.opentracing.contrib" % "opentracing-scala-concurrent_2.13" % "VERSION"
Please see the examples directory. Overall, an ExecutionContext
is wrapped
so the active Span can be captured and activated for a given Scala Future
.
Create a TracedExecutionContext
wrapping the actually used ExecutionContext
,
and pass it around when creating Future
s:
// Instantiate tracer
val tracer: Tracer = ...
val ec: ExecutionContext = new TracedExecutionContext(executionContext, tracer);
Future {
// The active Span at Future creation time, if any,
// will be captured and restored here.
tracer.activeSpan().setTag("status.code", getStatusCode())
}(ec)
Future.onComplete
and other Future
methods will
capture too any active Span
by the time they were registered, so you have
to make sure that both happened under the same active Span
/Scope
for this
to work smoothly.
Span
lifetime handling is not done at the TracedExecutionContext
,
and hence explicit calls to Span.finish()
must be put in place - usually
either in the last Future
/message block or in a onComplete
callback
function:
Future {
...
}(ec).onComplete { _ => {
tracer.activeSpan().finish()
}
}(ec)
Span auto-finish is supported through a reference-count system using the specific
AutoFinishScopeManager
-which needs to be provided at Tracer
creation time-,
along with using TracedAutoFinishExecutionContext
:
val scopeManager = new AutoFinishScopeManager();
val tracer: Tracer = ??? // Use the created scopeManager here.
val ec = new TracedAutoFinishExecutionContext(executionContext, tracer)
...
val span = tracer.buildSpan("request").start()
val scope = tracer.activateSpan(span)
try {
Future {
// Span will be reactivated here
...
Future {
// Span will be reactivated here as well.
// By the time this future is done,
// the Span will be automatically finished.
} (ec)
} (ec)
} finally {
scope.close()
}
Reference count for Span
s is set to 1 at creation, and is increased when
registering onComplete
, andThen
, map
, and similar
Future
methods - and is decreased upon having such function/callback executed:
Future {
...
}(ec)
.map {...}(ec)
.onComplete {
// No need to call `Span.finish()` here at all, as
// lifetime handling is done implicitly.
}(ec)