Dapper paper. ! Detailed structured logging of individual requests Full view of a request’s path through SOA Built into Finagle Add your own annotations
Structured log of request-specific information begin/end (used to calculate latency) service specific info (arguments, SQL, cache miss, etc) Can show the request’s path through the architecture Are downstream reqs serial when they should be parallel?
Client Send/Receive An event has seven attributes: name: event name (sr/ss; cs/cr; or anything else) traceId: identifies the request spanId: identifies a piece of the request parentId: identifies the piece of the request that initiated this piece flags: a bit field, currently: debug, trace request timestamp: instant the event occurred according to the service creating it endpoint: a name and host/port pair identifying the service that created the event
ZooKeeperClientFactory with ScribeSpanReceiverFactory with ZipkinWebFactory with AnormDBSpanStoreFactory with ZipkinSpanGenerator { val genSampleTraces = flag("genSampleTraces", false, "Generate sample traces") ! def main() { val store = newAnormSpanStore() if (genSampleTraces()) Await.result(generateTraces(store)) ! val convert: Seq[thrift.Span] => Seq[Span] = { _.map(_.toSpan) } val receiver = newScribeSpanReceiver(convert andThen store, statsReceiver.scope("scribeSpanReceiver")) val query = new ThriftQueryService(store, adjusters = DefaultAdjusters) val web = Http.serve(webServerPort(), newWebServer(query, statsReceiver.scope("web"))) ! closeOnExit(Closable.sequence(web, receiver, store)) Await.all(web, receiver, store) } }
with CassandraSpanStoreFactory with ZipkinQueryServerFactory { def main() { val spanStore = newCassandraStore(statsReceiver.scope("cassandra")) val query = newQueryServer(spanStore) closeOnExit(query) Await.ready(query) } }
Scribe or Kafka (or write your own) ! trait WriteSpanStore extends (Seq[Span] => Future[Unit]) Receive Data via the collector Convert to Seq[Span] Send to the SpanStore