/* ========================================================================================= * Copyright © 2013-2017 the kamon project * * 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 kamon.trace import java.net.{URLDecoder, URLEncoder} import java.nio.ByteBuffer import java.util.concurrent.ThreadLocalRandom import kamon.trace.SpanContext.{Baggage, SamplingDecision, Source} import scala.collection.JavaConverters._ import kamon.util.HexCodec trait SpanContextCodec[T] { def inject(spanContext: SpanContext, carrier: T): Unit def extract(carrier: T): Option[SpanContext] } trait TextMap { def get(key: String): Option[String] def put(key: String, value: String): Unit def values: Iterator[(String, String)] } object SpanContextCodec { trait Format[C] object Format { case object TextMap extends Format[TextMap] case object HttpHeaders extends Format[TextMap] case object Binary extends Format[ByteBuffer] } // val ExtendedB3: SpanContextCodec[TextMap] = new TextMapSpanCodec( // traceIDKey = "X-B3-TraceId", // parentIDKey = "X-B3-ParentSpanId", // spanIDKey = "X-B3-SpanId", // sampledKey = "X-B3-Sampled", // baggageKey = "X-Kamon-Baggage-", // baggageValueEncoder = urlEncode, // baggageValueDecoder = urlDecode // ) private def urlEncode(s: String): String = URLEncoder.encode(s, "UTF-8") private def urlDecode(s: String): String = URLDecoder.decode(s, "UTF-8") private class ExtendedB3(identityProvider: IdentityProvider) extends SpanContextCodec[TextMap] { import ExtendedB3.Headers override def inject(spanContext: SpanContext, carrier: TextMap): Unit = { carrier.put(Headers.TraceIdentifier, baggageValueEncoder(spanContext.traceID.string)) carrier.put(parentIDKey, baggageValueEncoder(spanContext.parentID.string)) carrier.put(spanIDKey, baggageValueEncoder(spanContext.spanID.string)) spanContext.baggage.getAll().foreach { case (key, value) => carrier.put(baggageKey + key, baggageValueEncoder(value)) } } override def extract(carrier: TextMap): Option[SpanContext] = { val traceID = carrier.get(Headers.TraceIdentifier) .map(identityProvider.traceIdentifierGenerator().from) .getOrElse(IdentityProvider.NoIdentifier) val spanID = carrier.get(Headers.SpanIdentifier) .map(identityProvider.spanIdentifierGenerator().from) .getOrElse(IdentityProvider.NoIdentifier) if(traceID != IdentityProvider.NoIdentifier && spanID != IdentityProvider.NoIdentifier) { val parentID = carrier.get(Headers.ParentSpanIdentifier) .map(identityProvider.spanIdentifierGenerator().from) .getOrElse(IdentityProvider.NoIdentifier) val samplingDecision = carrier.get(Headers.Flags).orElse(carrier.get(Headers.Sampled)) match { case Some(sampled) if sampled == "1" => SamplingDecision.Sample case Some(sampled) if sampled == "0" => SamplingDecision.DoNotSample case _ => SamplingDecision.Unknown } Some(SpanContext(traceID, spanID, parentID, samplingDecision, ???, Source.Remote)) } else None val minimalSpanContext = for { traceID <- carrier.get(traceIDKey).map(identityProvider.traceIdentifierGenerator().from) spanID <- carrier.get(spanIDKey).map(identityProvider.spanIdentifierGenerator().from) } yield { } // var traceID: String = null // var parentID: String = null // var spanID: String = null // var sampled: String = null // var baggage: Map[String, String] = Map.empty // // carrier.iterator().asScala.foreach { entry => // if(entry.getKey.equals(traceIDKey)) // traceID = baggageValueDecoder(entry.getValue) // else if(entry.getKey.equals(parentIDKey)) // parentID = baggageValueDecoder(entry.getValue) // else if(entry.getKey.equals(spanIDKey)) // spanID = baggageValueDecoder(entry.getValue) // else if(entry.getKey.equals(sampledKey)) // sampled = entry.getValue // else if(entry.getKey.startsWith(baggagePrefix)) // baggage = baggage + (entry.getKey.substring(baggagePrefix.length) -> baggageValueDecoder(entry.getValue)) // } // // if(traceID != null && spanID != null) { // val actualParent = if(parentID == null) 0L else decodeLong(parentID) // val isSampled = if(sampled == null) sampler.decide(ThreadLocalRandom.current().nextLong()) else sampled.equals("1") // // new SpanContext(decodeLong(traceID), decodeLong(spanID), actualParent, isSampled, baggage) // } else null // // None } private def encodeBaggage(baggage: Baggage): String = { if(baggage.getAll().nonEmpty) { baggage.getAll().foreach { case (key, value) => } } else "" } private def decodeLong(input: String): Long = HexCodec.lowerHexToUnsignedLong(input) private def encodeLong(input: Long): String = HexCodec.toLowerHex(input) } object ExtendedB3 { object Headers { val TraceIdentifier = "X-B3-TraceId" val ParentSpanIdentifier = "X-B3-ParentSpanId" val SpanIdentifier = "X-B3-SpanId" val Sampled = "X-B3-Sampled" val Flags = "X-B3-Flags" val Baggage = "X-B3-Extra-Baggage" } } }