Skip to content

Instantly share code, notes, and snippets.

@jvican
Created March 28, 2019 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jvican/b31743fb8f3f81381bee3ab6ce2b58c9 to your computer and use it in GitHub Desktop.
Save jvican/b31743fb8f3f81381bee3ab6ce2b58c9 to your computer and use it in GitHub Desktop.
This is a Scala wrapper around Brave tracer (Zipkin) APIs.
package bloop.tracing
import brave.{Span, Tracer}
import brave.propagation.TraceContext
import monix.eval.Task
import monix.execution.misc.NonFatal
import scala.util.Failure
import scala.util.Success
final class BraveTracer private (
tracer: Tracer,
val currentSpan: Span,
closeCurrentSpan: () => Unit
) {
def startNewChildTracer(name: String, tags: (String, String)*): BraveTracer = {
import brave.propagation.TraceContext
val span = tags.foldLeft(tracer.newChild(currentSpan.context).name(name)) {
case (span, (tagKey, tagValue)) => span.tag(tagKey, tagValue)
}
span.start()
new BraveTracer(tracer, span, () => span.finish())
}
def trace[T](name: String, tags: (String, String)*)(
thunk: BraveTracer => T
): T = {
val newTracer = startNewChildTracer(name, tags: _*)
try thunk(newTracer) // Don't catch and report errors in spans
catch { case NonFatal(t) => newTracer.currentSpan.error(t); throw t } finally {
try newTracer.terminate()
catch { case NonFatal(t) => () }
}
}
def traceTask[T](name: String, tags: (String, String)*)(
thunk: BraveTracer => Task[T]
): Task[T] = {
val newTracer = startNewChildTracer(name, tags: _*)
thunk(newTracer).materialize.map { value =>
value match {
case Failure(t) => newTracer.currentSpan.error(t)
case Success(value) => ()
}
try newTracer.terminate()
catch { case NonFatal(t) => () }
value
}.dematerialize
}
private var terminated: Boolean = false
def terminate(): Unit = this.synchronized {
if (terminated) ()
else {
closeCurrentSpan()
terminated = true
}
}
/** Create an independent tracer that propagates this current context
* and that whose completion in zipkin will happen independently. This
* is ideal for tracing background tasks that outlive their parent trace. */
def toIndependentTracer(name: String, tags: (String, String)*): BraveTracer =
BraveTracer(name, Some(currentSpan.context), tags: _*)
}
object BraveTracer {
def apply(name: String, tags: (String, String)*): BraveTracer = {
BraveTracer(name, None, tags: _*)
}
def apply(name: String, ctx: Option[TraceContext], tags: (String, String)*): BraveTracer = {
import brave._
import zipkin2.reporter.AsyncReporter
import zipkin2.reporter.urlconnection.URLConnectionSender
val zipkinServerUrl = Option(System.getProperty("zipkin.server.url")).getOrElse(
"http://127.0.0.1:9411/api/v2/spans"
)
import java.util.concurrent.TimeUnit
val sender = URLConnectionSender.create(zipkinServerUrl)
val spanReporter = AsyncReporter.builder(sender).build()
val tracing =
Tracing.newBuilder().localServiceName("bloop").spanReporter(spanReporter).build()
val tracer = tracing.tracer()
val newParentTrace = ctx.map(c => tracer.newChild(c)).getOrElse(tracer.newTrace())
val rootSpan = tags.foldLeft(newParentTrace.name(name)) {
case (span, (tagKey, tagValue)) => span.tag(tagKey, tagValue)
}
rootSpan.start()
val closeEverything = () => {
rootSpan.finish()
tracing.close()
spanReporter.close()
sender.close()
}
new BraveTracer(tracer, rootSpan, closeEverything)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment