diff --git a/zipkin-common/src/main/scala/com/twitter/zipkin/adapter/QueryAdapter.scala b/zipkin-common/src/main/scala/com/twitter/zipkin/adapter/QueryAdapter.scala new file mode 100644 index 00000000000..268215b358f --- /dev/null +++ b/zipkin-common/src/main/scala/com/twitter/zipkin/adapter/QueryAdapter.scala @@ -0,0 +1,29 @@ +/* + * 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.adapter + +import com.twitter.zipkin.query.{TraceTimeline, TimelineAnnotation} + +trait QueryAdapter { + type timelineAnnotationType /* corresponds to com.twitter.zipkin.query.TimelineAnnotation */ + type traceTimelineType /* corresponds to com.twitter.zipkin.query.TraceTimeline */ + + def apply(t: timelineAnnotationType): TimelineAnnotation + def apply(t: TimelineAnnotation): timelineAnnotationType + + def apply(t: traceTimelineType): TraceTimeline + def apply(t: TraceTimeline): traceTimelineType +} diff --git a/zipkin-common/src/main/scala/com/twitter/zipkin/query/TimelineAnnotation.scala b/zipkin-common/src/main/scala/com/twitter/zipkin/query/TimelineAnnotation.scala new file mode 100644 index 00000000000..175849b3080 --- /dev/null +++ b/zipkin-common/src/main/scala/com/twitter/zipkin/query/TimelineAnnotation.scala @@ -0,0 +1,25 @@ +/* + * 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.Endpoint + +/** + * Extension of `Annotation` that also includes span/service information useful for query side + * responses + */ +case class TimelineAnnotation(timestamp: Long, value: String, host: Endpoint, spanId: Long, parentId: Option[Long], + serviceName: String, spanName: String) diff --git a/zipkin-common/src/main/scala/com/twitter/zipkin/query/TraceTimeline.scala b/zipkin-common/src/main/scala/com/twitter/zipkin/query/TraceTimeline.scala new file mode 100644 index 00000000000..d9a9bf2ea48 --- /dev/null +++ b/zipkin-common/src/main/scala/com/twitter/zipkin/query/TraceTimeline.scala @@ -0,0 +1,29 @@ +/* + * 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.BinaryAnnotation + +/** + * Query side struct that contains + * - trace ID + * - root span (or span closest to the root + * - sorted list of `TimelineAnnotation`s + * - binary annotations + * for a particular trace + */ +case class TraceTimeline(traceId: Long, rootSpanId: Long, annotations: Seq[TimelineAnnotation], + binaryAnnotations: Seq[BinaryAnnotation]) diff --git a/zipkin-scrooge/src/main/scala/com/twitter/zipkin/adapter/ThriftQueryAdapter.scala b/zipkin-scrooge/src/main/scala/com/twitter/zipkin/adapter/ThriftQueryAdapter.scala new file mode 100644 index 00000000000..5ecd9fef676 --- /dev/null +++ b/zipkin-scrooge/src/main/scala/com/twitter/zipkin/adapter/ThriftQueryAdapter.scala @@ -0,0 +1,66 @@ +/* + * 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.adapter + +import com.twitter.zipkin.gen +import com.twitter.zipkin.query.{TraceTimeline, TimelineAnnotation} + +object ThriftQueryAdapter extends QueryAdapter { + type timelineAnnotationType = gen.TimelineAnnotation + type traceTimelineType = gen.TraceTimeline + + /* TimelineAnnotation from Thrift */ + def apply(t: timelineAnnotationType): TimelineAnnotation = { + TimelineAnnotation( + t.`timestamp`, + t.`value`, + ThriftAdapter(t.`host`), + t.`spanId`, + t.`parentId`, + t.`serviceName`, + t.`spanName`) + } + + /* TimelineAnnotation to Thrift */ + def apply(t: TimelineAnnotation): timelineAnnotationType = { + gen.TimelineAnnotation( + t.timestamp, + t.value, + ThriftAdapter(t.host), + t.spanId, + t.parentId, + t.serviceName, + t.spanName) + } + + /* TraceTimeline from Thrift */ + def apply(t: traceTimelineType): TraceTimeline = { + TraceTimeline( + t.`traceId`, + t.`rootMostSpanId`, + t.`annotations`.map { ThriftQueryAdapter(_) }, + t.`binaryAnnotations`.map { ThriftAdapter(_) }) + } + + /* TraceTimeline to Thrift */ + def apply(t: TraceTimeline): traceTimelineType = { + gen.TraceTimeline( + t.traceId, + t.rootSpanId, + t.annotations.map { ThriftQueryAdapter(_) }, + t.binaryAnnotations.map { ThriftAdapter(_) }) + } +} diff --git a/zipkin-server/src/main/scala/com/twitter/zipkin/common/Trace.scala b/zipkin-server/src/main/scala/com/twitter/zipkin/common/Trace.scala index 504b15558c0..cdf696c0882 100644 --- a/zipkin-server/src/main/scala/com/twitter/zipkin/common/Trace.scala +++ b/zipkin-server/src/main/scala/com/twitter/zipkin/common/Trace.scala @@ -19,11 +19,12 @@ package com.twitter.zipkin.common import com.twitter.zipkin.gen import collection.mutable import mutable.HashMap -import com.twitter.zipkin.query.conversions.TraceToTimeline import com.twitter.logging.Logger import java.nio.ByteBuffer -import com.twitter.zipkin.adapter.ThriftAdapter 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 /** * Represents a trace, a bundle of spans. @@ -49,8 +50,6 @@ case class Trace(spans: Seq[Span]) { val log = Logger.get(getClass.getName) - private[this] val traceToTimeline = new TraceToTimeline - /** * Find the trace id for this trace. * Returns none if we have no spans to look up id by @@ -150,13 +149,13 @@ case class Trace(spans: Seq[Span]) { serviceCounts, endpoints.toList) } - def toTimeline: Option[gen.TraceTimeline] = { + def toTimeline: Option[TraceTimeline] = { FTrace.record("toTimeline") - traceToTimeline.toTraceTimeline(this) + TraceToTimeline(this) } def toTraceCombo: gen.TraceCombo = { - gen.TraceCombo(toThrift, toTraceSummary.map(ThriftAdapter(_)), toTimeline, toSpanDepths) + gen.TraceCombo(toThrift, toTraceSummary.map(ThriftAdapter(_)), toTimeline.map(ThriftQueryAdapter(_)), toSpanDepths) } /** @@ -187,11 +186,9 @@ case class Trace(spans: Seq[Span]) { /** * Get all the binary annotations in this trace. */ - def getBinaryAnnotations: Seq[gen.BinaryAnnotation] = { + def getBinaryAnnotations: Seq[BinaryAnnotation] = { spans.map { - _.binaryAnnotations.map { - ThriftAdapter(_) - } + _.binaryAnnotations }.flatten } diff --git a/zipkin-server/src/main/scala/com/twitter/zipkin/query/QueryService.scala b/zipkin-server/src/main/scala/com/twitter/zipkin/query/QueryService.scala index 0f4ffd161f6..8a6bb5deb25 100644 --- a/zipkin-server/src/main/scala/com/twitter/zipkin/query/QueryService.scala +++ b/zipkin-server/src/main/scala/com/twitter/zipkin/query/QueryService.scala @@ -28,7 +28,7 @@ import com.twitter.zipkin.storage.{Aggregates, TraceIdDuration, Index, Storage} import java.nio.ByteBuffer import org.apache.thrift.TException import scala.collection.Set -import com.twitter.zipkin.adapter.ThriftAdapter +import com.twitter.zipkin.adapter.{ThriftQueryAdapter, ThriftAdapter} /** * Able to respond to users queries regarding the traces. Usually does so @@ -202,7 +202,7 @@ class QueryService(storage: Storage, index: Index, aggregates: Aggregates, adjus Stats.timeFutureMillis("query.getTraceTimelinesByIds") { storage.getTracesByIds(traceIds).map { id => - id.flatMap(adjusters.foldLeft(_)((trace, adjuster) => adjuster.adjust(trace)).toTimeline) + id.flatMap(adjusters.foldLeft(_)((trace, adjuster) => adjuster.adjust(trace)).toTimeline.map(ThriftQueryAdapter(_))) } rescue { case e: Exception => log.error(e, "getTraceTimelinesByIds query failed") diff --git a/zipkin-server/src/main/scala/com/twitter/zipkin/query/conversions/TraceToTimeline.scala b/zipkin-server/src/main/scala/com/twitter/zipkin/query/conversions/TraceToTimeline.scala index 68705cf2fd4..df86cb5c21c 100644 --- a/zipkin-server/src/main/scala/com/twitter/zipkin/query/conversions/TraceToTimeline.scala +++ b/zipkin-server/src/main/scala/com/twitter/zipkin/query/conversions/TraceToTimeline.scala @@ -15,13 +15,12 @@ */ package com.twitter.zipkin.query.conversions -import com.twitter.zipkin.gen -import com.twitter.zipkin.common.{IncompleteTraceDataException, Endpoint, Trace} -import com.twitter.zipkin.adapter.ThriftAdapter +import com.twitter.zipkin.common._ +import com.twitter.zipkin.query.{TraceTimeline, TimelineAnnotation} -class TraceToTimeline { +object TraceToTimeline { - def toTraceTimeline(trace: Trace): Option[gen.TraceTimeline] = { + def apply(trace: Trace): Option[TraceTimeline] = { if (trace.spans.isEmpty) { return None @@ -30,16 +29,18 @@ class TraceToTimeline { // convert span and annotation to timeline annotation val annotations = trace.spans.flatMap(s => s.annotations.map{ a => - gen.TimelineAnnotation(a.timestamp, a.value, + TimelineAnnotation( + a.timestamp, + a.value, a.host match { - case Some(s) => ThriftAdapter(s) - case None => ThriftAdapter(Endpoint.Unknown) + case Some(s) => s + case None => Endpoint.Unknown }, s.id, s.parentId, a.host match { case Some(s) => s.serviceName - case None => "Unknown" + case None => "Unknown" }, s.name) } @@ -53,7 +54,7 @@ class TraceToTimeline { val rootSpanId = trace.getRootMostSpan.getOrElse(return None).id val id = trace.id.getOrElse(return None) - Some(gen.TraceTimeline(id, rootSpanId, annotations, trace.getBinaryAnnotations)) + Some(TraceTimeline(id, rootSpanId, annotations, trace.getBinaryAnnotations)) } -} \ No newline at end of file +} diff --git a/zipkin-server/src/test/scala/com/twitter/zipkin/common/TraceSpec.scala b/zipkin-server/src/test/scala/com/twitter/zipkin/common/TraceSpec.scala index c18877d7841..3fc5fd2287d 100644 --- a/zipkin-server/src/test/scala/com/twitter/zipkin/common/TraceSpec.scala +++ b/zipkin-server/src/test/scala/com/twitter/zipkin/common/TraceSpec.scala @@ -119,10 +119,10 @@ class TraceSpec extends Specification { } "getBinaryAnnotations" in { - val ba1 = gen.BinaryAnnotation("key1", ByteBuffer.wrap("value1".getBytes), gen.AnnotationType.String) - val span1 = Span(1L, "", 1L, None, List(), List(ThriftAdapter(ba1))) - val ba2 = gen.BinaryAnnotation("key2", ByteBuffer.wrap("value2".getBytes), gen.AnnotationType.String) - val span2 = Span(1L, "", 2L, None, List(), List(ThriftAdapter(ba2))) + val ba1 = BinaryAnnotation("key1", ByteBuffer.wrap("value1".getBytes), ThriftAdapter(gen.AnnotationType.String), None) + val span1 = Span(1L, "", 1L, None, List(), List(ba1)) + val ba2 = BinaryAnnotation("key2", ByteBuffer.wrap("value2".getBytes), ThriftAdapter(gen.AnnotationType.String), None) + val span2 = Span(1L, "", 2L, None, List(), List(ba2)) val trace = Trace(List[Span](span1, span2)) Seq(ba1, ba2) mustEqual trace.getBinaryAnnotations diff --git a/zipkin-server/src/test/scala/com/twitter/zipkin/query/QueryServiceSpec.scala b/zipkin-server/src/test/scala/com/twitter/zipkin/query/QueryServiceSpec.scala index f06850c9dcc..0a291cbfe80 100644 --- a/zipkin-server/src/test/scala/com/twitter/zipkin/query/QueryServiceSpec.scala +++ b/zipkin-server/src/test/scala/com/twitter/zipkin/query/QueryServiceSpec.scala @@ -25,8 +25,8 @@ import com.twitter.zipkin.common._ import java.nio.ByteBuffer import com.twitter.util.Future import com.twitter.scrooge.BinaryThriftStructSerializer -import com.twitter.zipkin.adapter.ThriftAdapter import com.twitter.zipkin.storage.{Aggregates, TraceIdDuration, Storage, Index} +import com.twitter.zipkin.adapter.{ThriftQueryAdapter, ThriftAdapter} class QueryServiceSpec extends Specification with JMocker with ClassMocker { val ep1 = Endpoint(123, 123, "service1") @@ -181,7 +181,7 @@ class QueryServiceSpec extends Specification with JMocker with ClassMocker { } val trace = trace1.toThrift val summary = ThriftAdapter(TraceSummary(1, 100, 150, 50, Map("service1" -> 1), List(ep1))) - val timeline = trace1.toTimeline + val timeline = trace1.toTimeline.map(ThriftQueryAdapter(_)) val combo = gen.TraceCombo(trace, Some(summary), timeline, Some(Map(666L -> 1))) Seq(combo) mustEqual qs.getTraceCombosByIds(List(traceId), List())() } diff --git a/zipkin-server/src/test/scala/com/twitter/zipkin/query/conversions/TraceToTimelineSpec.scala b/zipkin-server/src/test/scala/com/twitter/zipkin/query/conversions/TraceToTimelineSpec.scala index dde68caec24..d4251d24fa5 100644 --- a/zipkin-server/src/test/scala/com/twitter/zipkin/query/conversions/TraceToTimelineSpec.scala +++ b/zipkin-server/src/test/scala/com/twitter/zipkin/query/conversions/TraceToTimelineSpec.scala @@ -18,11 +18,12 @@ package com.twitter.zipkin.query.conversions import org.specs.Specification import org.specs.mock.{ClassMocker, JMocker} import com.twitter.zipkin.gen -import com.twitter.zipkin.common.{Trace, Span, Annotation, Endpoint} import scala.collection.JavaConversions._ import java.nio.ByteBuffer import com.twitter.zipkin.adapter.ThriftAdapter +import com.twitter.zipkin.query.{TimelineAnnotation, TraceTimeline} +import com.twitter.zipkin.common._ class TraceToTimelineSpec extends Specification with JMocker with ClassMocker { @@ -60,9 +61,9 @@ class TraceToTimelineSpec extends Specification with JMocker with ClassMocker { val endpoint2 = Some(Endpoint(2, 2, cassieName)) //54147 val endpoint3 = Some(Endpoint(3, 3, koalabirdName)) //36516 - val et1 = ThriftAdapter(endpoint1.get) - val et2 = ThriftAdapter(endpoint2.get) - val et3 = ThriftAdapter(endpoint3.get) + val et1 = endpoint1.get + val et2 = endpoint2.get + val et3 = endpoint3.get // This is from a real trace, at least what the data would look like // after being run through the TimeSkewAdjuster @@ -73,10 +74,10 @@ class TraceToTimelineSpec extends Specification with JMocker with ClassMocker { val ann5 = Annotation(85, gen.Constants.CLIENT_RECV, endpoint2) val ann6 = Annotation(87, gen.Constants.CLIENT_RECV, endpoint3) - val ba1 = gen.BinaryAnnotation("key1", ByteBuffer.wrap("value1".getBytes), gen.AnnotationType.String) + val ba1 = BinaryAnnotation("key1", ByteBuffer.wrap("value1".getBytes), ThriftAdapter(gen.AnnotationType.String), None) val span1 = Span(1, "ValuesFromSource", 2209720933601260005L, None, - List(ann3, ann6), List(ThriftAdapter(ba1))) + List(ann3, ann6), List(ba1)) val span2 = Span(1, "ValuesFromSource", 2209720933601260005L, None, List(ann4, ann1), Nil) // the above two spans are part of the same actual span @@ -86,32 +87,30 @@ class TraceToTimelineSpec extends Specification with JMocker with ClassMocker { val trace = new Trace(List(span1, span2, span3)) // annotation numbers match those above, order in list should not though - val tAnn1 = gen.TimelineAnnotation(1, gen.Constants.SERVER_RECV, et1, + val tAnn1 = TimelineAnnotation(1, gen.Constants.SERVER_RECV, et1, 2209720933601260005L, None, cuckooName, "ValuesFromSource") - val tAnn2 = gen.TimelineAnnotation(1, gen.Constants.CLIENT_SEND, et2, + val tAnn2 = TimelineAnnotation(1, gen.Constants.CLIENT_SEND, et2, -855543208864892776L, Some(2209720933601260005L), cassieName, "multiget_slice") - val tAnn3 = gen.TimelineAnnotation(1, gen.Constants.CLIENT_SEND, et3, + val tAnn3 = TimelineAnnotation(1, gen.Constants.CLIENT_SEND, et3, 2209720933601260005L, None, koalabirdName, "ValuesFromSource") - val tAnn4 = gen.TimelineAnnotation(86, gen.Constants.SERVER_SEND, et1, + val tAnn4 = TimelineAnnotation(86, gen.Constants.SERVER_SEND, et1, 2209720933601260005L, None, cuckooName, "ValuesFromSource") - val tAnn5 = gen.TimelineAnnotation(85, gen.Constants.CLIENT_RECV, et2, + val tAnn5 = TimelineAnnotation(85, gen.Constants.CLIENT_RECV, et2, -855543208864892776L, Some(2209720933601260005L), cassieName, "multiget_slice") - val tAnn6 = gen.TimelineAnnotation(87, gen.Constants.CLIENT_RECV, et3, + val tAnn6 = TimelineAnnotation(87, gen.Constants.CLIENT_RECV, et3, 2209720933601260005L, None, koalabirdName, "ValuesFromSource") - val expectedTimeline = gen.TraceTimeline(1, 2209720933601260005L, List(tAnn3, tAnn1, tAnn2, + val expectedTimeline = TraceTimeline(1, 2209720933601260005L, List(tAnn3, tAnn1, tAnn2, tAnn5, tAnn4, tAnn6), List(ba1)) - val traceToTimeline = new TraceToTimeline - "TraceToTimelineSpec" should { "convert to timeline with correct annotations ordering" in { - val actualTimeline = traceToTimeline.toTraceTimeline(trace) + val actualTimeline = TraceToTimeline(trace) Some(expectedTimeline) mustEqual actualTimeline } "return none if empty trace" in { - val actualTimeline = traceToTimeline.toTraceTimeline(new Trace(List())) + val actualTimeline = TraceToTimeline(new Trace(List())) None mustEqual actualTimeline } }