Created
March 28, 2019 17:04
-
-
Save jvican/b31743fb8f3f81381bee3ab6ce2b58c9 to your computer and use it in GitHub Desktop.
This is a Scala wrapper around Brave tracer (Zipkin) APIs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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