Skip to content

Commit

Permalink
Move trace (part 2)
Browse files Browse the repository at this point in the history
* Move Trace to zipkin-common
* Pull out thrift dependencies into zipkin-scrooge

Author: @franklinhu
Fixes #67
URL: #67
  • Loading branch information
Franklin Hu committed Jul 9, 2012
1 parent 3a32463 commit db5487c
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,27 @@
*/
package com.twitter.zipkin.adapter

import com.twitter.zipkin.query.{TraceTimeline, TimelineAnnotation}
import com.twitter.zipkin.common.Trace
import com.twitter.zipkin.query.{TraceCombo, TraceTimeline, TimelineAnnotation}

/**
* Adapter for query related structs
*/
trait QueryAdapter {
type timelineAnnotationType /* corresponds to com.twitter.zipkin.query.TimelineAnnotation */
type traceTimelineType /* corresponds to com.twitter.zipkin.query.TraceTimeline */
type traceComboType /* corresponds to com.twitter.zipkin.query.TraceCombo */
type traceType /* corresponds to com.twitter.zipkin.query.Trace */

def apply(t: timelineAnnotationType): TimelineAnnotation
def apply(t: TimelineAnnotation): timelineAnnotationType

def apply(t: traceTimelineType): TraceTimeline
def apply(t: TraceTimeline): traceTimelineType

def apply(t: traceComboType): TraceCombo
def apply(t: TraceCombo): traceComboType

def apply(t: traceType): Trace
def apply(t: Trace): traceType
}
Original file line number Diff line number Diff line change
@@ -1,51 +1,38 @@
/*
* Copyright 2012 Twitter Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twitter.zipkin.common

import com.twitter.zipkin.gen
import collection.mutable
import mutable.HashMap
import com.twitter.logging.Logger
import java.nio.ByteBuffer
import scala.collection.mutable
import com.twitter.finagle.tracing.{Trace => FTrace}
import com.twitter.zipkin.query.conversions.TraceToTimeline
import com.twitter.zipkin.adapter.{ThriftQueryAdapter, ThriftAdapter}
import com.twitter.zipkin.query.TraceTimeline
import com.twitter.zipkin.query.SpanTreeEntry

/**
* A chunk of time, between a start and an end.
*/
case class Timespan(start: Long, end: Long)

/**
* Represents a trace, a bundle of spans.
*/
object Trace {

def apply(spanTree: SpanTreeEntry): Trace = Trace(spanTree.toList)

def fromThrift(trace: gen.Trace): Trace = {
new Trace(trace.spans.map(ThriftAdapter(_)).toList)
}

}


/**
* A chunk of time, between a start and an end.
*/
case class Timespan(start: Long, end: Long)


case class Trace(spans: Seq[Span]) {

val log = Logger.get(getClass.getName)
Expand All @@ -61,7 +48,9 @@ case class Trace(spans: Seq[Span]) {
/**
* Find the root span of this trace and return
*/
def getRootSpan: Option[Span] = spans.find { s => s.parentId == None }
def getRootSpan: Option[Span] = spans.find {
s => s.parentId == None
}

/**
* In some cases we don't care if it's the actual root span or just the span
Expand All @@ -72,8 +61,9 @@ case class Trace(spans: Seq[Span]) {
lazy val getRootMostSpan: Option[Span] = {
getRootSpan.orElse {
val idSpan = getIdToSpanMap
spans.headOption.map { s =>
recursiveGetRootMostSpan(idSpan, s)
spans.headOption.map {
s =>
recursiveGetRootMostSpan(idSpan, s)
}
}
}
Expand All @@ -95,8 +85,8 @@ case class Trace(spans: Seq[Span]) {
a => a.timestamp
}
} match {
case Nil => None // No annotations
case s @ _ => Some(Timespan(s.min, s.max))
case Nil => None // No annotations
case s@_ => Some(Timespan(s.min, s.max))
}
}

Expand Down Expand Up @@ -133,31 +123,6 @@ case class Trace(spans: Seq[Span]) {
}
}

def toThrift: gen.Trace = {
FTrace.record("toThrift")
gen.Trace(spans.map { ThriftAdapter(_) })
}

/**
* Return a summary of this trace or none if we
* cannot construct a trace summary. Could be that we have no spans.
*/
def toTraceSummary: Option[TraceSummary] = {
FTrace.record("toTraceSummary")
for (traceId <- id; startEnd <- getStartAndEndTimestamp)
yield TraceSummary(traceId, startEnd.start, startEnd.end, (startEnd.end - startEnd.start).toInt,
serviceCounts, endpoints.toList)
}

def toTimeline: Option[TraceTimeline] = {
FTrace.record("toTimeline")
TraceToTimeline(this)
}

def toTraceCombo: gen.TraceCombo = {
gen.TraceCombo(toThrift, toTraceSummary.map(ThriftAdapter(_)), toTimeline.map(ThriftQueryAdapter(_)), toSpanDepths)
}

/**
* Figures out the "span depth". This is used in the ui
* to figure out how to lay out the spans in the visualization.
Expand All @@ -179,7 +144,7 @@ case class Trace(spans: Seq[Span]) {
*/
def getBinaryAnnotationsByKey(key: String): Seq[ByteBuffer] = {
spans.flatMap(_.binaryAnnotations.collect {
case gen.BinaryAnnotation(bKey, bValue, _, _) if (bKey == key) => bValue
case BinaryAnnotation(bKey, bValue, _, _) if (bKey == key) => bValue
}.toSeq)
}

Expand All @@ -202,13 +167,13 @@ case class Trace(spans: Seq[Span]) {
new Trace(mergeBySpanId(spans).toList)
}

/**
/**
* Merge all the spans objects with the same span ids into one per id.
* We store parts of spans in different columns in order to make writes
* faster and simpler. This means we have to merge them correctly on read.
*/
private def mergeBySpanId(spans: Iterable[Span]) : Iterable[Span] = {
val spanMap = new HashMap[Long, Span]
private def mergeBySpanId(spans: Iterable[Span]): Iterable[Span] = {
val spanMap = new mutable.HashMap[Long, Span]
spans.foreach(s => {
val oldSpan = spanMap.get(s.id)
oldSpan match {
Expand Down Expand Up @@ -239,7 +204,9 @@ case class Trace(spans: Seq[Span]) {
/*
* Turn the Trace into a map of Span Id -> Span
*/
def getIdToSpanMap: Map[Long, Span] = spans.map{ s => (s.id, s)}.toMap
def getIdToSpanMap: Map[Long, Span] = spans.map {
s => (s.id, s)
}.toMap

/**
* Get the spans of this trace in a tree form. SpanTreeEntry wraps a Span and it's children.
Expand All @@ -262,10 +229,11 @@ case class Trace(spans: Seq[Span]) {
*/
def sortedByTimestamp: Trace = {
Trace {
spans.sortWith{(a, b) =>
val aTimestamp = a.firstAnnotation.map(_.timestamp).getOrElse(Long.MaxValue)
val bTimestamp = b.firstAnnotation.map(_.timestamp).getOrElse(Long.MaxValue)
aTimestamp < bTimestamp
spans.sortWith {
(a, b) =>
val aTimestamp = a.firstAnnotation.map(_.timestamp).getOrElse(Long.MaxValue)
val bTimestamp = b.firstAnnotation.map(_.timestamp).getOrElse(Long.MaxValue)
aTimestamp < bTimestamp
}
}
}
Expand All @@ -279,5 +247,4 @@ case class Trace(spans: Seq[Span]) {
case None => println("No root node found")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ package com.twitter.zipkin.common

import scala.collection.Map

object TraceSummary {

/**
* Return a summary of this trace or none if we
* cannot construct a trace summary. Could be that we have no spans.
*/
def apply(trace: Trace): Option[TraceSummary] = {
for (traceId <- trace.id; startEnd <- trace.getStartAndEndTimestamp)
yield TraceSummary(traceId, startEnd.start, startEnd.end, (startEnd.end - startEnd.start).toInt,
trace.serviceCounts, trace.endpoints.toList)
}
}

/**
* @param traceId id of this trace
* @param startTimestamp when did the trace start?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2012 Twitter Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
Expand All @@ -14,18 +14,20 @@
* limitations under the License.
*
*/
package com.twitter.zipkin.common
package com.twitter.zipkin.query

import com.twitter.zipkin.common.Span

/**
* This represents a tree version of a Trace.
*/
case class SpanTreeEntry(span: Span, children: List[SpanTreeEntry]) {

def toList : List[Span] = {
def toList: List[Span] = {
childrenToList(this)
}

private def childrenToList(span: SpanTreeEntry) : List[Span] = {
private def childrenToList(span: SpanTreeEntry): List[Span] = {
if (span.children.isEmpty) {
List[Span](span.span)
} else {
Expand All @@ -43,7 +45,7 @@ case class SpanTreeEntry(span: Span, children: List[SpanTreeEntry]) {
// start out with this span's depth (at startDepth)
// fold in the childrens depth (increase the current one by 1)
children.foldLeft(Map(span.id -> startDepth))((prevMap, child) =>
prevMap ++ child.depths(startDepth+1)
prevMap ++ child.depths(startDepth + 1)
)
}

Expand All @@ -53,7 +55,7 @@ case class SpanTreeEntry(span: Span, children: List[SpanTreeEntry]) {
*/
def printTree(indent: Int) {
println("%s%s".format(" " * indent, span.toString))
children foreach(s => {
children foreach (s => {
s.printTree(indent + 2)
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2012 Twitter Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twitter.zipkin.query

import com.twitter.zipkin.common.{Trace, TraceSummary}

object TraceCombo {
def apply(trace: Trace): TraceCombo = {
TraceCombo(trace, TraceSummary(trace), TraceTimeline(trace), trace.toSpanDepths)
}
}

/**
* Combined trace, summary, timeline
*/
case class TraceCombo(trace: Trace, traceSummary: Option[TraceSummary], traceTimeline: Option[TraceTimeline],
spanDepths: Option[Map[Long, Int]])
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,45 @@
*/
package com.twitter.zipkin.query

import com.twitter.zipkin.common.BinaryAnnotation
import com.twitter.zipkin.common.{Trace, Endpoint, BinaryAnnotation}

object TraceTimeline {
def apply(trace: Trace): Option[TraceTimeline] = {
if (trace.spans.isEmpty) {
return None
}

// convert span and annotation to timeline annotation
val annotations = trace.spans.flatMap(s =>
s.annotations.map{ a =>
TimelineAnnotation(
a.timestamp,
a.value,
a.host match {
case Some(s) => s
case None => Endpoint.Unknown
},
s.id,
s.parentId,
a.host match {
case Some(s) => s.serviceName
case None => "Unknown"
},
s.name)
}
).sortWith((a, b) => {
a.timestamp < b.timestamp

// TODO also sort so that events that must have happened first (cs before sr for example)
// end up in the right order
})

val rootSpanId = trace.getRootMostSpan.getOrElse(return None).id
val id = trace.id.getOrElse(return None)

Some(TraceTimeline(id, rootSpanId, annotations, trace.getBinaryAnnotations))
}
}

/**
* Query side struct that contains
Expand Down
Loading

0 comments on commit db5487c

Please sign in to comment.