From 1c12086503b220bd0c381f8c63363ec8bc39a4a8 Mon Sep 17 00:00:00 2001 From: Pierre-Hugues Jeanneret Date: Wed, 31 May 2017 13:56:39 +0100 Subject: [PATCH] Handle 128 bits trace Ids --- .../Criteo.Profiling.Tracing.UTest.csproj | 1 + .../Dispatchers/T_AsyncDispatcher.cs | 6 +- .../T_SpanState.cs | 68 ++ .../Criteo.Profiling.Tracing.UTest/T_Trace.cs | 18 +- .../Tracers/Zipkin/T_ThriftSpanSerializer.cs | 7 +- .../Transport/T_ZipkinHttpTraceExtractor.cs | 3 +- .../T_ZipkinHttpTraceInjectorExtractor.cs | 21 +- .../Utils/T_NumberUtils.cs | 1 - .../Criteo.Profiling.Tracing/SpanState.cs | 19 +- zipkin4net/Criteo.Profiling.Tracing/Trace.cs | 7 +- .../Criteo.Profiling.Tracing/TraceManager.cs | 5 + .../Tracers/Zipkin/JSONSpanSerializer.cs | 5 +- .../Tracers/Zipkin/ThriftSpanSerializer.cs | 3 +- .../Tracing/Tracers/Zipkin/Thrift/Span.cs | 772 ++++++++++-------- .../Tracers/Zipkin/zipkinCore.thrift | 7 +- .../Transport/ZipkinHttpTraceExtractor.cs | 39 +- .../Transport/ZipkinHttpTraceInjector.cs | 28 +- .../Utils/NumberUtils.cs | 12 +- .../Utils/RandomUtils.cs | 1 - 19 files changed, 631 insertions(+), 392 deletions(-) create mode 100644 zipkin4net/Criteo.Profiling.Tracing.UTest/T_SpanState.cs diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Criteo.Profiling.Tracing.UTest.csproj b/zipkin4net/Criteo.Profiling.Tracing.UTest/Criteo.Profiling.Tracing.UTest.csproj index 9b6dc22..a7c602c 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Criteo.Profiling.Tracing.UTest.csproj +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Criteo.Profiling.Tracing.UTest.csproj @@ -74,6 +74,7 @@ + diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Dispatchers/T_AsyncDispatcher.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/Dispatchers/T_AsyncDispatcher.cs index 62e5b9b..10c4276 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Dispatchers/T_AsyncDispatcher.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Dispatchers/T_AsyncDispatcher.cs @@ -76,9 +76,9 @@ public void RecordShouldnotBeDispatchedIfStopped() public void RecordsShouldBeDispatchedInOrder() { var sync = new CountdownEvent(2); - - var firstRecord = new Record(new SpanState(1, 0, 1, SpanFlags.None), TimeUtils.UtcNow, Annotations.ClientRecv()); - var secondRecord = new Record(new SpanState(1, 0, 1, SpanFlags.None), TimeUtils.UtcNow, Annotations.ClientRecv()); + var traceId = 1; + var firstRecord = new Record(new SpanState(traceId, 0, 1, SpanFlags.None), TimeUtils.UtcNow, Annotations.ClientRecv()); + var secondRecord = new Record(new SpanState(traceId, 0, 1, SpanFlags.None), TimeUtils.UtcNow, Annotations.ClientRecv()); var queue = new ConcurrentQueue(); diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/T_SpanState.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/T_SpanState.cs new file mode 100644 index 0000000..eb7d2c4 --- /dev/null +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/T_SpanState.cs @@ -0,0 +1,68 @@ +using NUnit.Framework; + +namespace Criteo.Profiling.Tracing.UTest +{ + [TestFixture] + public class T_SpanState + { + private const long traceIdHigh = SpanState.NoTraceIdHigh; + private const long traceId = 1; + private const long spanId = 1; + private const SpanFlags flags = SpanFlags.None; + + [Test] + public void HashCodeShouldVaryIfTraceIdsAreNotEqual() + { + var spanState1 = new SpanState(traceIdHigh, 1, null, spanId, flags); + var spanState2 = new SpanState(traceIdHigh, 2, null, spanId, flags); + Assert.AreNotEqual(spanState1.GetHashCode(), spanState2.GetHashCode()); + } + + [Test] + public void EqualsShouldReturnFalseIfTraceIdsAreNotEqual() + { + var spanState1 = new SpanState(traceIdHigh, 1, null, spanId, flags); + var spanState2 = new SpanState(traceIdHigh, 2, null, spanId, flags); + Assert.AreNotEqual(spanState1, spanState2); + } + + [Test] + public void HashCodeShouldVaryIfTraceIdHighsAreNotEqual() + { + var spanState1 = new SpanState(1, traceId, null, spanId, flags); + var spanState2 = new SpanState(2, traceId, null, spanId, flags); + Assert.AreNotEqual(spanState1.GetHashCode(), spanState2.GetHashCode()); + } + + [Test] + public void EqualsShouldReturnFalseIfTraceIdHighsAreNotEqual() + { + var spanState1 = new SpanState(1, traceId, null, spanId, flags); + var spanState2 = new SpanState(2, traceId, null, spanId, flags); + Assert.AreNotEqual(spanState1, spanState2); + } + + [Test] + public void HashCodeShouldVaryIfSpanIdsAreNotEqual() + { + var spanState1 = new SpanState(traceIdHigh, traceId, null, 1, flags); + var spanState2 = new SpanState(traceIdHigh, traceId, null, 2, flags); + Assert.AreNotEqual(spanState1.GetHashCode(), spanState2.GetHashCode()); + } + + [Test] + public void EqualsShouldReturnFalseIfSpanIdsAreNotEqual() + { + var spanState1 = new SpanState(traceIdHigh, traceId, null, 1, flags); + var spanState2 = new SpanState(traceIdHigh, traceId, null, 2, flags); + Assert.AreNotEqual(spanState1, spanState2); + } + + [Test] + public void TraceIdHighDefaultToZero() + { + var spanState = new SpanState(traceId, null, spanId, flags); + Assert.AreEqual(SpanState.NoTraceIdHigh, spanState.TraceIdHigh); + } + } +} diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/T_Trace.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/T_Trace.cs index 41cdbd7..b97a2d2 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/T_Trace.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/T_Trace.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; #if !NET_CORE using System.Runtime.Serialization.Formatters.Binary; @@ -19,6 +19,7 @@ public void SetUp() { TraceManager.ClearTracers(); TraceManager.Stop(); + TraceManager.Trace128Bits = false; } [Test] @@ -127,5 +128,20 @@ public void FlagUnsetShouldForwardForBackwardCompatibility() dispatcher.Verify(d => d.Dispatch(It.IsAny()), Times.Once()); } + [Test] + public void TraceIdHighIsGeneratedIf128BitsActivated() + { + TraceManager.Trace128Bits = true; + var trace = Trace.Create(); + Assert.AreNotEqual(SpanState.NoTraceIdHigh, trace.CurrentSpan.TraceIdHigh); + } + + [Test] + public void TraceIdHighIsNotGeneratedIf128BitsDeactivated() + { + TraceManager.Trace128Bits = false; + var trace = Trace.Create(); + Assert.AreEqual(SpanState.NoTraceIdHigh, trace.CurrentSpan.TraceIdHigh); + } } } diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Tracers/Zipkin/T_ThriftSpanSerializer.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/Tracers/Zipkin/T_ThriftSpanSerializer.cs index 63957bd..0677e5d 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Tracers/Zipkin/T_ThriftSpanSerializer.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Tracers/Zipkin/T_ThriftSpanSerializer.cs @@ -105,7 +105,7 @@ public void SpanCorrectlyConvertedToThrift(long? parentSpanId) const string serviceName = "myCriteoService"; const string methodName = "GET"; - var spanState = new SpanState(1, parentSpanId, 2, SpanFlags.None); + var spanState = new SpanState(2, 1, parentSpanId, 2, SpanFlags.None); var timestamp = TimeUtils.UtcNow; var span = new Span(spanState, timestamp) { Endpoint = new IPEndPoint(hostIp, hostPort), ServiceName = serviceName, Name = methodName }; @@ -129,8 +129,9 @@ public void SpanCorrectlyConvertedToThrift(long? parentSpanId) Service_name = serviceName }; - Assert.AreEqual(1, thriftSpan.Trace_id); - Assert.AreEqual(2, thriftSpan.Id); + Assert.AreEqual(spanState.TraceIdHigh, thriftSpan.Trace_id_high); + Assert.AreEqual(spanState.TraceId, thriftSpan.Trace_id); + Assert.AreEqual(spanState.SpanId, thriftSpan.Id); Assert.True(thriftSpan.Timestamp.HasValue); if (span.IsRoot) diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceExtractor.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceExtractor.cs index 64a982a..303f122 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceExtractor.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceExtractor.cs @@ -63,7 +63,8 @@ public void ShouldParseTraceWithoutFlags(string encodedParentSpanId) Trace trace; Assert.True(ZipkinHttpTraceExtractor.TryParseTrace(encodedTraceId: "0000000000000001", encodedSpanId: "00000000000000FA", encodedParentSpanId: encodedParentSpanId, sampledStr: null, flagsStr: null, trace: out trace)); - Assert.AreEqual(1, trace.CurrentSpan.TraceId); + Assert.AreEqual(1L, trace.CurrentSpan.TraceId); + Assert.AreEqual(SpanState.NoTraceIdHigh, trace.CurrentSpan.TraceIdHigh); Assert.AreEqual(250, trace.CurrentSpan.SpanId); Assert.AreEqual(SpanFlags.None, trace.CurrentSpan.Flags); diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceInjectorExtractor.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceInjectorExtractor.cs index 691f71a..b432d47 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceInjectorExtractor.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Transport/T_ZipkinHttpTraceInjectorExtractor.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using Criteo.Profiling.Tracing.Transport; +using Criteo.Profiling.Tracing.Utils; using NUnit.Framework; namespace Criteo.Profiling.Tracing.UTest.Transport @@ -29,6 +30,19 @@ public void GetTraceThenSetHeadersEqualsOriginal(string encodedTraceId, string e Assert.AreEqual(flagsStr, recreatedHeaders[ZipkinHttpHeaders.Flags]); } + [Test] + public void Supports128BitsTraceId() + { + var traceIdHigh = 1L; + var traceId = 2L; + var encodedTraceId = NumberUtils.EncodeLongToHexString(traceIdHigh) + NumberUtils.EncodeLongToHexString(traceId); + + Trace parsedTrace; + Assert.True(ZipkinHttpTraceExtractor.TryParseTrace(encodedTraceId, "0000000000000000", "0000000000000000", null, "", out parsedTrace)); + Assert.AreEqual(traceIdHigh, parsedTrace.CurrentSpan.TraceIdHigh); + Assert.AreEqual(traceId, parsedTrace.CurrentSpan.TraceId); + } + [TestCase(null)] [TestCase(9845431L)] public void SetHeadersThenGetTraceEqualsOriginal(long? parentSpanId) @@ -39,7 +53,7 @@ public void SetHeadersThenGetTraceEqualsOriginal(long? parentSpanId) private static void CheckSetHeadersThenGetTrace_Dict(long? parentSpanId) { - var spanState = new SpanState(1, parentSpanId, 2, SpanFlags.None); + var spanState = new SpanState(2, 1, parentSpanId, 2, SpanFlags.None); var originalTrace = Trace.CreateFromId(spanState); var headers = new Dictionary(); @@ -51,12 +65,11 @@ private static void CheckSetHeadersThenGetTrace_Dict(long? parentSpanId) Assert.True(extractor.TryExtract(headers, out deserializedTrace)); Assert.AreEqual(originalTrace, deserializedTrace); - Assert.AreEqual(originalTrace.CurrentSpan.Flags, deserializedTrace.CurrentSpan.Flags); } private static void CheckSetHeadersThenGetTrace_NVC(long? parentSpanId) { - var spanState = new SpanState(1, parentSpanId, 2, SpanFlags.None); + var spanState = new SpanState(2, 1, parentSpanId, 2, SpanFlags.None); var originalTrace = Trace.CreateFromId(spanState); var headers = new NameValueCollection(); @@ -68,8 +81,6 @@ private static void CheckSetHeadersThenGetTrace_NVC(long? parentSpanId) Assert.True(extractor.TryExtract(headers, out deserializedTrace)); Assert.AreEqual(originalTrace, deserializedTrace); - Assert.AreEqual(originalTrace.CurrentSpan.Flags, deserializedTrace.CurrentSpan.Flags); } - } } diff --git a/zipkin4net/Criteo.Profiling.Tracing.UTest/Utils/T_NumberUtils.cs b/zipkin4net/Criteo.Profiling.Tracing.UTest/Utils/T_NumberUtils.cs index 4110190..c93fafd 100644 --- a/zipkin4net/Criteo.Profiling.Tracing.UTest/Utils/T_NumberUtils.cs +++ b/zipkin4net/Criteo.Profiling.Tracing.UTest/Utils/T_NumberUtils.cs @@ -6,7 +6,6 @@ namespace Criteo.Profiling.Tracing.UTest.Utils [TestFixture] internal class T_NumberUtils { - [Test] public void TransformationIsReversible() { diff --git a/zipkin4net/Criteo.Profiling.Tracing/SpanState.cs b/zipkin4net/Criteo.Profiling.Tracing/SpanState.cs index 15410ee..b8852a2 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/SpanState.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/SpanState.cs @@ -6,12 +6,16 @@ namespace Criteo.Profiling.Tracing [Serializable] public sealed class SpanState : IEquatable { + public long TraceIdHigh { get; private set; } + public long TraceId { get; private set; } public long? ParentSpanId { get; private set; } public long SpanId { get; private set; } + internal const long NoTraceIdHigh = 0; + public SamplingStatus SamplingStatus { get @@ -28,7 +32,12 @@ public SamplingStatus SamplingStatus public SpanFlags Flags { get; private set; } public SpanState(long traceId, long? parentSpanId, long spanId, SpanFlags flags) + : this(NoTraceIdHigh, traceId, parentSpanId, spanId, flags) + { } + + public SpanState(long traceIdHigh, long traceId, long? parentSpanId, long spanId, SpanFlags flags) { + TraceIdHigh = traceIdHigh; TraceId = traceId; ParentSpanId = parentSpanId; SpanId = spanId; @@ -47,7 +56,10 @@ public bool Equals(SpanState other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return TraceId == other.TraceId && ParentSpanId == other.ParentSpanId && SpanId == other.SpanId; + return TraceIdHigh == other.TraceIdHigh + && TraceId == other.TraceId + && ParentSpanId == other.ParentSpanId + && SpanId == other.SpanId; } public override bool Equals(object obj) @@ -62,7 +74,8 @@ public override int GetHashCode() { unchecked { - var hashCode = TraceId.GetHashCode(); + var hashCode = TraceIdHigh.GetHashCode(); + hashCode = (hashCode * 397) ^ TraceId.GetHashCode(); hashCode = (hashCode * 397) ^ SpanId.GetHashCode(); return hashCode; } @@ -70,7 +83,7 @@ public override int GetHashCode() public override string ToString() { - return string.Format("{0}.{1}<:{2}", TraceId, SpanId, ParentSpanId.HasValue ? ParentSpanId.Value.ToString(CultureInfo.InvariantCulture) : "_"); + return string.Format("{0}{1}.{2}<:{3}", TraceIdHigh == SpanState.NoTraceIdHigh ? "" : TraceIdHigh.ToString(), TraceId, SpanId, ParentSpanId.HasValue ? ParentSpanId.Value.ToString(CultureInfo.InvariantCulture) : "_"); } } diff --git a/zipkin4net/Criteo.Profiling.Tracing/Trace.cs b/zipkin4net/Criteo.Profiling.Tracing/Trace.cs index 987e999..d1b6914 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Trace.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Trace.cs @@ -1,4 +1,4 @@ -using System; +using System; using Criteo.Profiling.Tracing.Annotation; using Criteo.Profiling.Tracing.Utils; @@ -52,6 +52,7 @@ public static Trace Create() private Trace() { var traceId = RandomUtils.NextLong(); + var traceIdHigh = TraceManager.Trace128Bits ? RandomUtils.NextLong() : 0; var spanId = RandomUtils.NextLong(); var isSampled = TraceManager.Sampler.Sample(traceId); @@ -62,7 +63,7 @@ private Trace() flags = flags | SpanFlags.Sampled; } - CurrentSpan = new SpanState(traceId: traceId, parentSpanId: null, spanId: spanId, flags: flags); + CurrentSpan = new SpanState(traceIdHigh: traceIdHigh, traceId: traceId, parentSpanId: null, spanId: spanId, flags: flags); CorrelationId = NumberUtils.LongToGuid(traceId); } @@ -90,7 +91,7 @@ private Trace(SpanState spanState) /// public Trace Child() { - var childState = new SpanState(traceId: CurrentSpan.TraceId, parentSpanId: CurrentSpan.SpanId, spanId: RandomUtils.NextLong(), flags: CurrentSpan.Flags); + var childState = new SpanState(traceIdHigh: CurrentSpan.TraceIdHigh, traceId: CurrentSpan.TraceId, parentSpanId: CurrentSpan.SpanId, spanId: RandomUtils.NextLong(), flags: CurrentSpan.Flags); return new Trace(childState); } diff --git a/zipkin4net/Criteo.Profiling.Tracing/TraceManager.cs b/zipkin4net/Criteo.Profiling.Tracing/TraceManager.cs index 4aa307a..bea4541 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/TraceManager.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/TraceManager.cs @@ -47,6 +47,11 @@ public static bool Started get { return _status == (int)Status.Started; } } + public static bool Trace128Bits + { + get; set; + } + /// /// Start tracing, records will be forwarded to the registered tracers. /// diff --git a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/JSONSpanSerializer.cs b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/JSONSpanSerializer.cs index 02e5458..4f51cac 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/JSONSpanSerializer.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/JSONSpanSerializer.cs @@ -72,7 +72,10 @@ private void SerializeSpan(StreamWriter writer, Span span) writer.Write(comma); writer.WriteField(name, span.Name ?? SerializerUtils.DefaultRpcMethodName); writer.Write(comma); - writer.WriteField(traceId, NumberUtils.EncodeLongToLowerHexString(span.SpanState.TraceId)); + var hexTraceIdHigh = TraceManager.Trace128Bits ? NumberUtils.EncodeLongToHexString(span.SpanState.TraceIdHigh) : ""; + writer.WriteField(traceId, + hexTraceIdHigh + + NumberUtils.EncodeLongToHexString(span.SpanState.TraceId)); if (span.SpanStarted.HasValue) { diff --git a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/ThriftSpanSerializer.cs b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/ThriftSpanSerializer.cs index c95305a..8741de4 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/ThriftSpanSerializer.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/ThriftSpanSerializer.cs @@ -33,6 +33,7 @@ public static Thrift.Span ConvertToThrift(Span span) var thriftSpan = new Thrift.Span() { Id = span.SpanState.SpanId, + Trace_id_high = span.SpanState.TraceIdHigh, Trace_id = span.SpanState.TraceId, Name = span.Name ?? SerializerUtils.DefaultRpcMethodName, Debug = false @@ -118,6 +119,6 @@ private static Thrift.Endpoint ConvertToThrift(IPEndPoint ipEndPoint, string ser Port = (short)ipEndPoint.Port, Service_name = serviceName }; - } + } } } diff --git a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/thrift_gen/gen-csharp/Criteo/Profiling/Tracing/Tracers/Zipkin/Thrift/Span.cs b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/thrift_gen/gen-csharp/Criteo/Profiling/Tracing/Tracers/Zipkin/Thrift/Span.cs index 5f07a29..7853cce 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/thrift_gen/gen-csharp/Criteo/Profiling/Tracing/Tracers/Zipkin/Thrift/Span.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/thrift_gen/gen-csharp/Criteo/Profiling/Tracing/Tracers/Zipkin/Thrift/Span.cs @@ -17,392 +17,462 @@ namespace Criteo.Profiling.Tracing.Tracers.Zipkin.Thrift { - - /// - /// A trace is a series of spans (often RPC calls) which form a latency tree. - /// - /// Spans are usually created by instrumentation in RPC clients or servers, but - /// can also represent in-process activity. Annotations in spans are similar to - /// log statements, and are sometimes created directly by application developers - /// to indicate events of interest, such as a cache miss. - /// - /// The root span is where parent_id = Nil; it usually has the longest duration - /// in the trace. - /// - /// Span identifiers are packed into i64s, but should be treated opaquely. - /// String encoding is fixed-width lower-hex, to avoid signed interpretation. - /// - #if !SILVERLIGHT - [Serializable] - #endif - public partial class Span : TBase - { - private bool _debug; - /// - /// Unique 8-byte identifier for a trace, set on all spans within it. + /// A trace is a series of spans (often RPC calls) which form a latency tree. + /// + /// Spans are usually created by instrumentation in RPC clients or servers, but + /// can also represent in-process activity. Annotations in spans are similar to + /// log statements, and are sometimes created directly by application developers + /// to indicate events of interest, such as a cache miss. + /// + /// The root span is where parent_id = Nil; it usually has the longest duration + /// in the trace. + /// + /// Span identifiers are packed into i64s, but should be treated opaquely. + /// String encoding is fixed-width lower-hex, to avoid signed interpretation. /// - public long? Trace_id { get; set; } +#if !SILVERLIGHT + [Serializable] +#endif + public partial class Span : TBase + { + private bool _debug; - /// - /// Span name in lowercase, rpc method for example. Conventionally, when the - /// span name isn't known, name = "unknown". - /// - public string Name { get; set; } + /// + /// Unique 8-byte identifier for a trace, set on all spans within it. + /// + public long? Trace_id { get; set; } - /// - /// Unique 8-byte identifier of this span within a trace. A span is uniquely - /// identified in storage by (trace_id, id). - /// - public long? Id { get; set; } + /// + /// Span name in lowercase, rpc method for example. Conventionally, when the + /// span name isn't known, name = "unknown". + /// + public string Name { get; set; } - /// - /// The parent's Span.id; absent if this the root span in a trace. - /// - public long? Parent_id { get; set; } + /// + /// Unique 8-byte identifier of this span within a trace. A span is uniquely + /// identified in storage by (trace_id, id). + /// + public long? Id { get; set; } - /// - /// Associates events that explain latency with a timestamp. Unlike log - /// statements, annotations are often codes: for example SERVER_RECV("sr"). - /// Annotations are sorted ascending by timestamp. - /// - public List Annotations { get; set; } + /// + /// The parent's Span.id; absent if this the root span in a trace. + /// + public long? Parent_id { get; set; } - /// - /// Tags a span with context, usually to support query or aggregation. For - /// example, a binary annotation key could be "http.path". - /// - public List Binary_annotations { get; set; } + /// + /// Associates events that explain latency with a timestamp. Unlike log + /// statements, annotations are often codes: for example SERVER_RECV("sr"). + /// Annotations are sorted ascending by timestamp. + /// + public List Annotations { get; set; } - /// - /// True is a request to store this span even if it overrides sampling policy. - /// - public bool? Debug - { - get - { - return _debug; - } - set - { - __isset.debug = value.HasValue; - if (value.HasValue) this._debug = value.Value; - } - } + /// + /// Tags a span with context, usually to support query or aggregation. For + /// example, a binary annotation key could be "http.path". + /// + public List Binary_annotations { get; set; } - /// - /// Epoch microseconds of the start of this span, absent if this an incomplete - /// span. - /// - /// This value should be set directly by instrumentation, using the most - /// precise value possible. For example, gettimeofday or syncing nanoTime - /// against a tick of currentTimeMillis. - /// - /// For compatibilty with instrumentation that precede this field, collectors - /// or span stores can derive this via Annotation.timestamp. - /// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. - /// - /// Timestamp is nullable for input only. Spans without a timestamp cannot be - /// presented in a timeline: Span stores should not output spans missing a - /// timestamp. - /// - /// There are two known edge-cases where this could be absent: both cases - /// exist when a collector receives a span in parts and a binary annotation - /// precedes a timestamp. This is possible when.. - /// - The span is in-flight (ex not yet received a timestamp) - /// - The span's start event was lost - /// - public long? Timestamp { get; set; } + /// + /// True is a request to store this span even if it overrides sampling policy. + /// + public bool? Debug + { + get { return _debug; } + set + { + __isset.debug = value.HasValue; + if (value.HasValue) + this._debug = value.Value; + } + } - /// - /// Measurement in microseconds of the critical path, if known. Durations of - /// less than one microsecond must be rounded up to 1 microsecond. - /// - /// This value should be set directly, as opposed to implicitly via annotation - /// timestamps. Doing so encourages precision decoupled from problems of - /// clocks, such as skew or NTP updates causing time to move backwards. - /// - /// For compatibility with instrumentation that precede this field, collectors - /// or span stores can derive this by subtracting Annotation.timestamp. - /// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. - /// - /// If this field is persisted as unset, zipkin will continue to work, except - /// duration query support will be implementation-specific. Similarly, setting - /// this field non-atomically is implementation-specific. - /// - /// This field is i64 vs i32 to support spans longer than 35 minutes. - /// - public long? Duration { get; set; } + /// + /// Epoch microseconds of the start of this span, absent if this an incomplete + /// span. + /// + /// This value should be set directly by instrumentation, using the most + /// precise value possible. For example, gettimeofday or syncing nanoTime + /// against a tick of currentTimeMillis. + /// + /// For compatibilty with instrumentation that precede this field, collectors + /// or span stores can derive this via Annotation.timestamp. + /// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. + /// + /// Timestamp is nullable for input only. Spans without a timestamp cannot be + /// presented in a timeline: Span stores should not output spans missing a + /// timestamp. + /// + /// There are two known edge-cases where this could be absent: both cases + /// exist when a collector receives a span in parts and a binary annotation + /// precedes a timestamp. This is possible when.. + /// - The span is in-flight (ex not yet received a timestamp) + /// - The span's start event was lost + /// + public long? Timestamp { get; set; } + /// + /// Measurement in microseconds of the critical path, if known. Durations of + /// less than one microsecond must be rounded up to 1 microsecond. + /// + /// This value should be set directly, as opposed to implicitly via annotation + /// timestamps. Doing so encourages precision decoupled from problems of + /// clocks, such as skew or NTP updates causing time to move backwards. + /// + /// For compatibility with instrumentation that precede this field, collectors + /// or span stores can derive this by subtracting Annotation.timestamp. + /// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. + /// + /// If this field is persisted as unset, zipkin will continue to work, except + /// duration query support will be implementation-specific. Similarly, setting + /// this field non-atomically is implementation-specific. + /// + /// This field is i64 vs i32 to support spans longer than 35 minutes. + /// + public long? Duration { get; set; } - public Isset __isset; - #if !SILVERLIGHT - [Serializable] - #endif - public struct Isset { - public bool debug; - } + /// + /// Optional unique 8-byte additional identifier for a trace. If non zero, this + /// means the trace uses 128 bit traceIds instead of 64 bit. + /// + public long? Trace_id_high { get; set; } - public Span() { - this._debug = false; - this.__isset.debug = true; - } - public void Read (TProtocol iprot) - { - TField field; - iprot.ReadStructBegin(); - while (true) - { - field = iprot.ReadFieldBegin(); - if (field.Type == TType.Stop) { - break; + public Isset __isset; +#if !SILVERLIGHT + [Serializable] +#endif + public struct Isset + { + public bool debug; + } + + public Span() + { + this._debug = false; + this.__isset.debug = true; + } + + public void Read(TProtocol iprot) + { + TField field; + iprot.ReadStructBegin(); + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) + { + break; + } + switch (field.ID) + { + case 1: + if (field.Type == TType.I64) + { + Trace_id = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 3: + if (field.Type == TType.String) + { + Name = iprot.ReadString(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 4: + if (field.Type == TType.I64) + { + Id = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 5: + if (field.Type == TType.I64) + { + Parent_id = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 6: + if (field.Type == TType.List) + { + { + Annotations = new List(); + TList _list0 = iprot.ReadListBegin(); + for (int _i1 = 0; _i1 < _list0.Count; ++_i1) + { + Annotation _elem2 = new Annotation(); + _elem2 = new Annotation(); + _elem2.Read(iprot); + Annotations.Add(_elem2); + } + iprot.ReadListEnd(); + } + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 8: + if (field.Type == TType.List) + { + { + Binary_annotations = new List(); + TList _list3 = iprot.ReadListBegin(); + for (int _i4 = 0; _i4 < _list3.Count; ++_i4) + { + BinaryAnnotation _elem5 = new BinaryAnnotation(); + _elem5 = new BinaryAnnotation(); + _elem5.Read(iprot); + Binary_annotations.Add(_elem5); + } + iprot.ReadListEnd(); + } + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 9: + if (field.Type == TType.Bool) + { + Debug = iprot.ReadBool(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 10: + if (field.Type == TType.I64) + { + Timestamp = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 11: + if (field.Type == TType.I64) + { + Duration = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 12: + if (field.Type == TType.I64) + { + Trace_id_high = iprot.ReadI64(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + iprot.ReadFieldEnd(); + } + iprot.ReadStructEnd(); } - switch (field.ID) + + public void Write(TProtocol oprot) { - case 1: - if (field.Type == TType.I64) { - Trace_id = iprot.ReadI64(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + TStruct struc = new TStruct("Span"); + oprot.WriteStructBegin(struc); + TField field = new TField(); + if (Trace_id != null) + { + field.Name = "trace_id"; + field.Type = TType.I64; + field.ID = 1; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Trace_id.Value); + oprot.WriteFieldEnd(); } - break; - case 3: - if (field.Type == TType.String) { - Name = iprot.ReadString(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (Name != null) + { + field.Name = "name"; + field.Type = TType.String; + field.ID = 3; + oprot.WriteFieldBegin(field); + oprot.WriteString(Name); + oprot.WriteFieldEnd(); } - break; - case 4: - if (field.Type == TType.I64) { - Id = iprot.ReadI64(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (Id != null) + { + field.Name = "id"; + field.Type = TType.I64; + field.ID = 4; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Id.Value); + oprot.WriteFieldEnd(); } - break; - case 5: - if (field.Type == TType.I64) { - Parent_id = iprot.ReadI64(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (Parent_id != null) + { + field.Name = "parent_id"; + field.Type = TType.I64; + field.ID = 5; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Parent_id.Value); + oprot.WriteFieldEnd(); } - break; - case 6: - if (field.Type == TType.List) { - { - Annotations = new List(); - TList _list0 = iprot.ReadListBegin(); - for( int _i1 = 0; _i1 < _list0.Count; ++_i1) + if (Annotations != null) + { + field.Name = "annotations"; + field.Type = TType.List; + field.ID = 6; + oprot.WriteFieldBegin(field); { - Annotation _elem2 = new Annotation(); - _elem2 = new Annotation(); - _elem2.Read(iprot); - Annotations.Add(_elem2); + oprot.WriteListBegin(new TList(TType.Struct, Annotations.Count)); + foreach (Annotation _iter6 in Annotations) + { + _iter6.Write(oprot); + } + oprot.WriteListEnd(); } - iprot.ReadListEnd(); - } - } else { - TProtocolUtil.Skip(iprot, field.Type); + oprot.WriteFieldEnd(); } - break; - case 8: - if (field.Type == TType.List) { - { - Binary_annotations = new List(); - TList _list3 = iprot.ReadListBegin(); - for( int _i4 = 0; _i4 < _list3.Count; ++_i4) + if (Binary_annotations != null) + { + field.Name = "binary_annotations"; + field.Type = TType.List; + field.ID = 8; + oprot.WriteFieldBegin(field); { - BinaryAnnotation _elem5 = new BinaryAnnotation(); - _elem5 = new BinaryAnnotation(); - _elem5.Read(iprot); - Binary_annotations.Add(_elem5); + oprot.WriteListBegin(new TList(TType.Struct, Binary_annotations.Count)); + foreach (BinaryAnnotation _iter7 in Binary_annotations) + { + _iter7.Write(oprot); + } + oprot.WriteListEnd(); } - iprot.ReadListEnd(); - } - } else { - TProtocolUtil.Skip(iprot, field.Type); + oprot.WriteFieldEnd(); } - break; - case 9: - if (field.Type == TType.Bool) { - Debug = iprot.ReadBool(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (__isset.debug) + { + field.Name = "debug"; + field.Type = TType.Bool; + field.ID = 9; + oprot.WriteFieldBegin(field); + oprot.WriteBool(Debug.Value); + oprot.WriteFieldEnd(); } - break; - case 10: - if (field.Type == TType.I64) { - Timestamp = iprot.ReadI64(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (Timestamp != null) + { + field.Name = "timestamp"; + field.Type = TType.I64; + field.ID = 10; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Timestamp.Value); + oprot.WriteFieldEnd(); } - break; - case 11: - if (field.Type == TType.I64) { - Duration = iprot.ReadI64(); - } else { - TProtocolUtil.Skip(iprot, field.Type); + if (Duration != null) + { + field.Name = "duration"; + field.Type = TType.I64; + field.ID = 11; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Duration.Value); + oprot.WriteFieldEnd(); } - break; - default: - TProtocolUtil.Skip(iprot, field.Type); - break; + if (Trace_id_high != null) + { + field.Name = "trace_id_high"; + field.Type = TType.I64; + field.ID = 12; + oprot.WriteFieldBegin(field); + oprot.WriteI64(Trace_id_high.Value); + oprot.WriteFieldEnd(); + } + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); } - iprot.ReadFieldEnd(); - } - iprot.ReadStructEnd(); - } - public void Write(TProtocol oprot) { - TStruct struc = new TStruct("Span"); - oprot.WriteStructBegin(struc); - TField field = new TField(); - if (Trace_id != null) { - field.Name = "trace_id"; - field.Type = TType.I64; - field.ID = 1; - oprot.WriteFieldBegin(field); - oprot.WriteI64(Trace_id.Value); - oprot.WriteFieldEnd(); - } - if (Name != null) { - field.Name = "name"; - field.Type = TType.String; - field.ID = 3; - oprot.WriteFieldBegin(field); - oprot.WriteString(Name); - oprot.WriteFieldEnd(); - } - if (Id != null) { - field.Name = "id"; - field.Type = TType.I64; - field.ID = 4; - oprot.WriteFieldBegin(field); - oprot.WriteI64(Id.Value); - oprot.WriteFieldEnd(); - } - if (Parent_id != null) { - field.Name = "parent_id"; - field.Type = TType.I64; - field.ID = 5; - oprot.WriteFieldBegin(field); - oprot.WriteI64(Parent_id.Value); - oprot.WriteFieldEnd(); - } - if (Annotations != null) { - field.Name = "annotations"; - field.Type = TType.List; - field.ID = 6; - oprot.WriteFieldBegin(field); + public override bool Equals(object that) { - oprot.WriteListBegin(new TList(TType.Struct, Annotations.Count)); - foreach (Annotation _iter6 in Annotations) - { - _iter6.Write(oprot); - } - oprot.WriteListEnd(); + var other = that as Span; + if (other == null) + return false; + if (ReferenceEquals(this, other)) + return true; + return System.Object.Equals(Trace_id, other.Trace_id) && System.Object.Equals(Name, other.Name) + && System.Object.Equals(Id, other.Id) && System.Object.Equals(Parent_id, other.Parent_id) + && TCollections.Equals(Annotations, other.Annotations) + && TCollections.Equals(Binary_annotations, other.Binary_annotations) + && ((__isset.debug == other.__isset.debug) + && ((!__isset.debug) || (System.Object.Equals(Debug, other.Debug)))) + && System.Object.Equals(Timestamp, other.Timestamp) && System.Object.Equals(Duration, other.Duration) + && System.Object.Equals(Trace_id_high, other.Trace_id_high); } - oprot.WriteFieldEnd(); - } - if (Binary_annotations != null) { - field.Name = "binary_annotations"; - field.Type = TType.List; - field.ID = 8; - oprot.WriteFieldBegin(field); + + public override int GetHashCode() { - oprot.WriteListBegin(new TList(TType.Struct, Binary_annotations.Count)); - foreach (BinaryAnnotation _iter7 in Binary_annotations) - { - _iter7.Write(oprot); - } - oprot.WriteListEnd(); + int hashcode = 0; + unchecked + { + hashcode = (hashcode * 397) ^ (Trace_id == null ? 0 : (Trace_id.GetHashCode())); + hashcode = (hashcode * 397) ^ (Name == null ? 0 : (Name.GetHashCode())); + hashcode = (hashcode * 397) ^ (Id == null ? 0 : (Id.GetHashCode())); + hashcode = (hashcode * 397) ^ (Parent_id == null ? 0 : (Parent_id.GetHashCode())); + hashcode = (hashcode * 397) ^ (Annotations == null ? 0 : (TCollections.GetHashCode(Annotations))); + hashcode = (hashcode * 397) + ^ (Binary_annotations == null ? 0 : (TCollections.GetHashCode(Binary_annotations))); + hashcode = (hashcode * 397) ^ (Debug == null ? 0 : (Debug.GetHashCode())); + hashcode = (hashcode * 397) ^ (Timestamp == null ? 0 : (Timestamp.GetHashCode())); + hashcode = (hashcode * 397) ^ (Duration == null ? 0 : (Duration.GetHashCode())); + hashcode = (hashcode * 397) ^ (Trace_id_high == null ? 0 : (Trace_id_high.GetHashCode())); + } + return hashcode; } - oprot.WriteFieldEnd(); - } - if (__isset.debug) { - field.Name = "debug"; - field.Type = TType.Bool; - field.ID = 9; - oprot.WriteFieldBegin(field); - oprot.WriteBool(Debug.Value); - oprot.WriteFieldEnd(); - } - if (Timestamp != null) { - field.Name = "timestamp"; - field.Type = TType.I64; - field.ID = 10; - oprot.WriteFieldBegin(field); - oprot.WriteI64(Timestamp.Value); - oprot.WriteFieldEnd(); - } - if (Duration != null) { - field.Name = "duration"; - field.Type = TType.I64; - field.ID = 11; - oprot.WriteFieldBegin(field); - oprot.WriteI64(Duration.Value); - oprot.WriteFieldEnd(); - } - oprot.WriteFieldStop(); - oprot.WriteStructEnd(); - } - - public override bool Equals(object that) { - var other = that as Span; - if (other == null) return false; - if (ReferenceEquals(this, other)) return true; - return System.Object.Equals(Trace_id, other.Trace_id) - && System.Object.Equals(Name, other.Name) - && System.Object.Equals(Id, other.Id) - && System.Object.Equals(Parent_id, other.Parent_id) - && TCollections.Equals(Annotations, other.Annotations) - && TCollections.Equals(Binary_annotations, other.Binary_annotations) - && ((__isset.debug == other.__isset.debug) && ((!__isset.debug) || (System.Object.Equals(Debug, other.Debug)))) - && System.Object.Equals(Timestamp, other.Timestamp) - && System.Object.Equals(Duration, other.Duration); - } - public override int GetHashCode() { - int hashcode = 0; - unchecked { - hashcode = (hashcode * 397) ^ (Trace_id == null ? 0 : (Trace_id.GetHashCode())); - hashcode = (hashcode * 397) ^ (Name == null ? 0 : (Name.GetHashCode())); - hashcode = (hashcode * 397) ^ (Id == null ? 0 : (Id.GetHashCode())); - hashcode = (hashcode * 397) ^ (Parent_id == null ? 0 : (Parent_id.GetHashCode())); - hashcode = (hashcode * 397) ^ (Annotations == null ? 0 : (TCollections.GetHashCode(Annotations))); - hashcode = (hashcode * 397) ^ (Binary_annotations == null ? 0 : (TCollections.GetHashCode(Binary_annotations))); - hashcode = (hashcode * 397) ^ (Debug == null ? 0 : (Debug.GetHashCode())); - hashcode = (hashcode * 397) ^ (Timestamp == null ? 0 : (Timestamp.GetHashCode())); - hashcode = (hashcode * 397) ^ (Duration == null ? 0 : (Duration.GetHashCode())); - } - return hashcode; - } + public override string ToString() + { + StringBuilder sb = new StringBuilder("Span("); + sb.Append("Trace_id: "); + sb.Append(Trace_id); + sb.Append(",Name: "); + sb.Append(Name); + sb.Append(",Id: "); + sb.Append(Id); + sb.Append(",Parent_id: "); + sb.Append(Parent_id); + sb.Append(",Annotations: "); + sb.Append(Annotations); + sb.Append(",Binary_annotations: "); + sb.Append(Binary_annotations); + sb.Append(",Debug: "); + sb.Append(Debug); + sb.Append(",Timestamp: "); + sb.Append(Timestamp); + sb.Append(",Duration: "); + sb.Append(Duration); + sb.Append(",Trace_id_high: "); + sb.Append(Trace_id_high); + sb.Append(")"); + return sb.ToString(); + } - public override string ToString() { - StringBuilder sb = new StringBuilder("Span("); - sb.Append("Trace_id: "); - sb.Append(Trace_id); - sb.Append(",Name: "); - sb.Append(Name); - sb.Append(",Id: "); - sb.Append(Id); - sb.Append(",Parent_id: "); - sb.Append(Parent_id); - sb.Append(",Annotations: "); - sb.Append(Annotations); - sb.Append(",Binary_annotations: "); - sb.Append(Binary_annotations); - sb.Append(",Debug: "); - sb.Append(Debug); - sb.Append(",Timestamp: "); - sb.Append(Timestamp); - sb.Append(",Duration: "); - sb.Append(Duration); - sb.Append(")"); - return sb.ToString(); } - - } - -} +} \ No newline at end of file diff --git a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/zipkinCore.thrift b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/zipkinCore.thrift index eaf8074..3a7c71b 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/zipkinCore.thrift +++ b/zipkin4net/Criteo.Profiling.Tracing/Tracers/Zipkin/zipkinCore.thrift @@ -287,7 +287,7 @@ struct Annotation { * Microseconds from epoch. * * This value should use the most precise value possible. For example, - * gettimeofday or syncing nanoTime against a tick of currentTimeMillis. + * gettimeofday or multiplying currentTimeMillis by 1000. */ 1: i64 timestamp /** @@ -455,4 +455,9 @@ struct Span { * This field is i64 vs i32 to support spans longer than 35 minutes. */ 11: optional i64 duration + /** + * Optional unique 8-byte additional identifier for a trace. If non zero, this + * means the trace uses 128 bit traceIds instead of 64 bit. + */ + 12: optional i64 trace_id_high } \ No newline at end of file diff --git a/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceExtractor.cs b/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceExtractor.cs index a673612..f0161dd 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceExtractor.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceExtractor.cs @@ -10,6 +10,8 @@ namespace Criteo.Profiling.Tracing.Transport */ public class ZipkinHttpTraceExtractor : ITraceExtractor, ITraceExtractor>, ITraceExtractor { + private const int traceId64BitsSerializationLength = 16; + public bool TryExtract(TE carrier, Func extractor, out Trace trace) { return TryParseTrace( @@ -18,7 +20,7 @@ public bool TryExtract(TE carrier, Func extractor, out T extractor(carrier, ZipkinHttpHeaders.ParentSpanId), extractor(carrier, ZipkinHttpHeaders.Sampled), extractor(carrier, ZipkinHttpHeaders.Flags), - out trace + out trace ); } @@ -29,7 +31,8 @@ public bool TryExtract(NameValueCollection carrier, out Trace trace) public bool TryExtract(IDictionary carrier, out Trace trace) { - return TryExtract(carrier, (c, key) => { + return TryExtract(carrier, (c, key) => + { string value; return c.TryGetValue(key, out value) ? value : null; }, out trace); @@ -46,7 +49,8 @@ public static bool TryParseTrace(string encodedTraceId, string encodedSpanId, st try { - var traceId = NumberUtils.DecodeHexString(encodedTraceId); + var traceIdHigh = ExtractTraceIdHigh(encodedTraceId); + var traceId = ExtractTraceId(encodedTraceId); var spanId = NumberUtils.DecodeHexString(encodedSpanId); var parentSpanId = string.IsNullOrWhiteSpace(encodedParentSpanId) ? null : (long?)NumberUtils.DecodeHexString(encodedParentSpanId); var flags = ZipkinHttpHeaders.ParseFlagsHeader(flagsStr); @@ -63,7 +67,7 @@ public static bool TryParseTrace(string encodedTraceId, string encodedSpanId, st } - var state = new SpanState(traceId, parentSpanId, spanId, flags); + var state = new SpanState(traceIdHigh, traceId, parentSpanId, spanId, flags); trace = Trace.CreateFromId(state); return true; } @@ -76,5 +80,32 @@ public static bool TryParseTrace(string encodedTraceId, string encodedSpanId, st return false; } + /// + /// Extracts traceIdHigh. Detects if present and then decode the first 16 bytes + /// + private static long ExtractTraceIdHigh(string encodedTraceId) + { + if (encodedTraceId.Length <= traceId64BitsSerializationLength) + { + return SpanState.NoTraceIdHigh; + } + var traceIdHighLength = encodedTraceId.Length - traceId64BitsSerializationLength; + var traceIdHighStr = encodedTraceId.Substring(0, traceIdHighLength); + return NumberUtils.DecodeHexString(traceIdHighStr); + } + + /// + /// Extracts traceId. Detects if present and then decode the last 16 bytes + /// + private static long ExtractTraceId(string encodedTraceId) + { + var traceIdLength = traceId64BitsSerializationLength; + if (encodedTraceId.Length <= traceId64BitsSerializationLength) + { + traceIdLength = encodedTraceId.Length; + } + var traceIdStartIndex = encodedTraceId.Length - traceIdLength; + return NumberUtils.DecodeHexString(encodedTraceId.Substring(traceIdStartIndex, traceIdLength)); + } } } diff --git a/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceInjector.cs b/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceInjector.cs index e80821b..178afa3 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceInjector.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Transport/ZipkinHttpTraceInjector.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; @@ -13,21 +13,21 @@ public class ZipkinHttpTraceInjector : ITraceInjector, ITra { public bool Inject(Trace trace, TE carrier, Action injector) { - var traceId = trace.CurrentSpan; + var spanState = trace.CurrentSpan; - injector(carrier, ZipkinHttpHeaders.TraceId, NumberUtils.EncodeLongToHexString(traceId.TraceId)); - injector(carrier, ZipkinHttpHeaders.SpanId, NumberUtils.EncodeLongToHexString(traceId.SpanId)); - if (traceId.ParentSpanId != null) + injector(carrier, ZipkinHttpHeaders.TraceId, SerializeTraceId(spanState)); + injector(carrier, ZipkinHttpHeaders.SpanId, NumberUtils.EncodeLongToHexString(spanState.SpanId)); + if (spanState.ParentSpanId != null) { // Cannot be null in theory, the root span must have been created on request receive hence further RPC calls are necessary children - injector(carrier, ZipkinHttpHeaders.ParentSpanId, NumberUtils.EncodeLongToHexString(traceId.ParentSpanId.Value)); + injector(carrier, ZipkinHttpHeaders.ParentSpanId, NumberUtils.EncodeLongToHexString(spanState.ParentSpanId.Value)); } - injector(carrier, ZipkinHttpHeaders.Flags, ((long)traceId.Flags).ToString(CultureInfo.InvariantCulture)); + injector(carrier, ZipkinHttpHeaders.Flags, ((long)spanState.Flags).ToString(CultureInfo.InvariantCulture)); // Add "Sampled" header for compatibility with Finagle - if (traceId.Flags.HasFlag(SpanFlags.SamplingKnown)) + if (spanState.Flags.HasFlag(SpanFlags.SamplingKnown)) { - injector(carrier, ZipkinHttpHeaders.Sampled, traceId.Flags.HasFlag(SpanFlags.Sampled) ? "1" : "0"); + injector(carrier, ZipkinHttpHeaders.Sampled, spanState.Flags.HasFlag(SpanFlags.Sampled) ? "1" : "0"); } return true; } @@ -41,5 +41,15 @@ public bool Inject(Trace trace, IDictionary carrier) { return Inject(trace, carrier, (c, key, value) => c[key] = value); } + + private static string SerializeTraceId(SpanState spanState) + { + var hexTraceId = NumberUtils.EncodeLongToHexString(spanState.TraceId); + if (spanState.TraceIdHigh == SpanState.NoTraceIdHigh) + { + return hexTraceId; + } + return NumberUtils.EncodeLongToHexString(spanState.TraceIdHigh) + hexTraceId; + } } } diff --git a/zipkin4net/Criteo.Profiling.Tracing/Utils/NumberUtils.cs b/zipkin4net/Criteo.Profiling.Tracing/Utils/NumberUtils.cs index 93843e4..b7b3b60 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Utils/NumberUtils.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Utils/NumberUtils.cs @@ -1,8 +1,8 @@ -using System; +using System; namespace Criteo.Profiling.Tracing.Utils { - internal static class NumberUtils + public static class NumberUtils { public static Guid LongToGuid(long value) { @@ -14,7 +14,11 @@ public static Guid LongToGuid(long value) public static long GuidToLong(Guid value) { var b = value.ToByteArray(); - var blong = BitConverter.ToInt64(b, 0); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(b, 8, 8); + } + var blong = BitConverter.ToInt64(b, 8); return blong; } @@ -27,7 +31,7 @@ public static string EncodeLongToLowerHexString(long value) { return value.ToString("x16"); } - + public static long DecodeHexString(string longAsHexString) { return Convert.ToInt64(longAsHexString, 16); diff --git a/zipkin4net/Criteo.Profiling.Tracing/Utils/RandomUtils.cs b/zipkin4net/Criteo.Profiling.Tracing/Utils/RandomUtils.cs index cdc17ea..1a3ff5f 100644 --- a/zipkin4net/Criteo.Profiling.Tracing/Utils/RandomUtils.cs +++ b/zipkin4net/Criteo.Profiling.Tracing/Utils/RandomUtils.cs @@ -21,6 +21,5 @@ public static long NextLong() LocalRandom.Value.NextBytes(buffer); return BitConverter.ToInt64(buffer, 0); } - } }