1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
package xyz.driver.tracing
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.server._
import akka.http.scaladsl.server.Directives._
import java.util.UUID
import scala.util.Random
import java.time._
import scala.concurrent._
import scala.collection.immutable.Seq
trait TracingDirectives {
import TracingDirectives._
def optionalTraceContext: Directive1[Option[TraceContext]] =
extractRequest.map { req =>
TraceContext.fromHeaders(req.headers)
}
def withTraceContext(ctx: TraceContext): Directive0 =
mapRequest(req => req.withHeaders(ctx.headers))
def trace(tracer: Tracer, name: String, extraLabels: Map[String, String] = Map.empty): Directive0 =
extractRequest.flatMap { request =>
val labels = Map(
"/http/user_agent" -> "driver-tracer",
"/http/method" -> request.method.name,
"/http/url" -> request.uri.path.toString,
"/http/host" -> request.uri.authority.host.toString
) ++ extraLabels
val span: Span = TraceContext.fromHeaders(request.headers) match {
case None => // no parent span, create new trace
Span(
name = name,
labels = labels
)
case Some(TraceContext(traceId, parentSpanId)) =>
Span(
name = name,
traceId = traceId,
parentSpanId = parentSpanId,
labels = labels
)
}
withTraceContext(TraceContext.fromSpan(span)) & mapRouteResult { res =>
tracer.submit(span.end())
res
}
}
/*
def span2(name2: String, tracer: Tracer): Directive0 = {
val f: RouteResult ⇒ RouteResult = ???
Directive { inner ⇒ ctx ⇒
inner(())(ctx).map(f)(ctx.executionContext)
}
}
*/
}
object TracingDirectives extends TracingDirectives {
case class TraceContext(traceId: UUID, parentSpanId: Option[UUID]) {
import TraceContext._
def headers: Seq[HttpHeader] =
Seq(RawHeader(TraceHeaderName, traceId.toString)) ++
parentSpanId.toSeq.map(id => RawHeader(SpanHeaderName, id.toString))
}
object TraceContext {
val TraceHeaderName = "Tracing-Trace-Id"
val SpanHeaderName = "Tracing-Span-Id"
def fromHeaders(headers: Seq[HttpHeader]): Option[TraceContext] = {
val traceId = headers
.find(_.name == TraceHeaderName)
.map(_.value)
.map(UUID.fromString)
val parentSpanId =
headers.find(_.name == SpanHeaderName).map(_.value).map(UUID.fromString)
traceId.map { tid =>
TraceContext(tid, parentSpanId)
}
}
def fromSpan(span: Span) = TraceContext(span.traceId, Some(span.spanId))
}
}
|