Skip to content

Instantly share code, notes, and snippets.

@chadselph
Last active July 3, 2022 16:04
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadselph/65f21fc86f873d6569f4cfe4f96ce036 to your computer and use it in GitHub Desktop.
Save chadselph/65f21fc86f873d6569f4cfe4f96ce036 to your computer and use it in GitHub Desktop.
akka-http directive for opentracing.io
import java.util
import java.util.Map.Entry
import akka.http.scaladsl.model.HttpHeader
import io.opentracing.propagation.TextMap
import scala.collection.JavaConverters.asJavaIteratorConverter
/**
* Used to extract an iterator of Entry[String, String] to the
* io.opentracing API from akka-http request
*/
class AkkaHttpHeaderExtractor(headers: Seq[HttpHeader]) extends TextMap {
val headersEntries: Seq[Entry[String, String]] = headers.map(header => new Entry[String, String] {
override def getValue: String = header.value()
override def getKey: String = header.name()
override def setValue(value: String): String = throw new UnsupportedOperationException("Cannot set a value")
})
override def put(key: String, value: String): Unit = {
throw new UnsupportedOperationException("AkkaHttpHeaderExtractor should only be used with Tracer.extract()")
}
override def iterator(): util.Iterator[Entry[String, String]] = headersEntries.iterator.asJava
}
import akka.http.scaladsl.server.{Directive1, Directives, Route}
import akka.http.scaladsl.server.Directives.{extractRequest, mapResponse, provide}
import io.opentracing.{Span, Tracer}
/**
* Directives for tracing requests
*/
trait TracingDirectives {
def trace(tracer: Tracer)(operationName: String): Directive1[Span] = {
extractRequest.flatMap { req =>
val parent = Try(
// This method will throw an IllegalArgumentException for a bad
// tracer header, or return null for no header. Handle both cases as None
tracer.extract(Format.Builtin.HTTP_HEADERS, new AkkaHttpHeaderExtractor(req.headers))
).filter(_ != null).toOption
val span = parent.fold(
tracer.buildSpan(operationName).start())(
p => tracer.buildSpan(operationName).asChildOf(p).start()
)
mapResponse { resp =>
span.setTag("http.status_code", resp.status.intValue())
span.setTag("http.url", req.effectiveUri(securedConnection = false).toString())
span.setTag("http.method", req.method.value)
span.finish()
resp
} & provide(span)
}
}
}
object TracingDirectives extends TracingDirectives
object Example extends Directives with TracingDirectives {
val mytrace: String => Directive1[Span] = trace( new TracerImpl ) // needs an actual impl
val routes: Route = path ("foo" / "bar") {
mytrace("get-foo-bar") { span =>
span.setTag("key", "value")
complete("sweet")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment