From ddac284e530c3de02c3443e138b227ee1660b076 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 2 Nov 2020 10:07:41 -0800 Subject: [PATCH] Updated Jaeger & Zipkin exporters to check for default activity.ParentSpanId (#1433) * Updated Jaeger & Zipkin exporters to check for default activity.ParentSpanId. * Tabs are less filling, but spaces taste great. Co-authored-by: Cijo Thomas --- examples/AspNet/Examples.AspNet.csproj | 9 +++ examples/AspNet/Global.asax.cs | 76 +++++++++++++++++-- examples/AspNet/Web.config | 24 +++++- .../JaegerActivityExtensions.cs | 5 +- .../ZipkinActivityConversionExtensions.cs | 10 +-- .../JaegerActivityConversionTest.cs | 13 ++-- .../ZipkinExporterTests.cs | 18 +++-- 7 files changed, 123 insertions(+), 32 deletions(-) diff --git a/examples/AspNet/Examples.AspNet.csproj b/examples/AspNet/Examples.AspNet.csproj index 801be506258..baaa8b4b719 100644 --- a/examples/AspNet/Examples.AspNet.csproj +++ b/examples/AspNet/Examples.AspNet.csproj @@ -44,6 +44,7 @@ + @@ -100,6 +101,14 @@ {ae3e3df5-4083-4c6e-a840-8271b0acde7e} OpenTelemetry + + {1afff251-3b0c-47ca-be94-937083732c0a} + OpenTelemetry.Exporter.Console + + + {7edae7fa-b44e-42ca-80fa-7df2faa2c5dd} + OpenTelemetry.Exporter.Zipkin + 10.0 diff --git a/examples/AspNet/Global.asax.cs b/examples/AspNet/Global.asax.cs index 09282edbe4b..ab63834bb45 100644 --- a/examples/AspNet/Global.asax.cs +++ b/examples/AspNet/Global.asax.cs @@ -15,6 +15,10 @@ // using System; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Text; using System.Web; using System.Web.Http; using System.Web.Mvc; @@ -33,17 +37,34 @@ public class WebApiApplication : HttpApplication protected void Application_Start() { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() + var builder = Sdk.CreateTracerProviderBuilder() .AddAspNetInstrumentation(options => options.Propagator = new B3Propagator()) .AddHttpClientInstrumentation( httpClientOptions => httpClientOptions.Propagator = new B3Propagator(), - httpWebRequestOptions => httpWebRequestOptions.Propagator = new B3Propagator()) - .AddJaegerExporter(jaegerOptions => - { - jaegerOptions.AgentHost = "localhost"; - jaegerOptions.AgentPort = 6831; - }) - .Build(); + httpWebRequestOptions => httpWebRequestOptions.Propagator = new B3Propagator()); + + switch (ConfigurationManager.AppSettings["UseExporter"].ToLowerInvariant()) + { + case "jaeger": + builder.AddJaegerExporter(jaegerOptions => + { + jaegerOptions.AgentHost = ConfigurationManager.AppSettings["JaegerHost"]; + jaegerOptions.AgentPort = int.Parse(ConfigurationManager.AppSettings["JaegerPort"]); + }); + break; + case "zipkin": + builder.AddZipkinExporter(zipkinOptions => + { + zipkinOptions.Endpoint = new Uri(ConfigurationManager.AppSettings["ZipkinEndpoint"]); + }); + break; + default: + Console.SetOut(new DebugStreamWriter()); + builder.AddConsoleExporter(); + break; + } + + this.tracerProvider = builder.Build(); GlobalConfiguration.Configure(WebApiConfig.Register); @@ -55,5 +76,44 @@ protected void Application_End() { this.tracerProvider?.Dispose(); } + + private class DebugStreamWriter : StreamWriter + { + public DebugStreamWriter() + : base(new DebugStream(), Encoding.Unicode, 1024) + { + this.AutoFlush = true; + } + + private sealed class DebugStream : Stream + { + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + + public override void SetLength(long value) => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + { + Debug.Write(Encoding.Unicode.GetString(buffer, offset, count)); + } + + public override void Flush() => Debug.Flush(); + } + } } } diff --git a/examples/AspNet/Web.config b/examples/AspNet/Web.config index 23e5c73f5ed..ef375225f73 100644 --- a/examples/AspNet/Web.config +++ b/examples/AspNet/Web.config @@ -5,6 +5,10 @@ + + + + @@ -23,14 +27,26 @@ + + + + + + + + + + + + - - - - + + + + diff --git a/src/OpenTelemetry.Exporter.Jaeger/Implementation/JaegerActivityExtensions.cs b/src/OpenTelemetry.Exporter.Jaeger/Implementation/JaegerActivityExtensions.cs index 020c9b1a1be..cb5f2fa078b 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/Implementation/JaegerActivityExtensions.cs +++ b/src/OpenTelemetry.Exporter.Jaeger/Implementation/JaegerActivityExtensions.cs @@ -111,7 +111,10 @@ public static JaegerSpan ToJaegerSpan(this Activity activity) // TODO: The check above should be enforced by the usage of the exporter. Perhaps enforce at higher-level. traceId = new Int128(activity.TraceId); spanId = new Int128(activity.SpanId); - parentSpanId = new Int128(activity.ParentSpanId); + if (activity.ParentSpanId != default) + { + parentSpanId = new Int128(activity.ParentSpanId); + } } return new JaegerSpan( diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs index 6a8d3ce8cda..9445ab67c16 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs @@ -29,8 +29,6 @@ internal static class ZipkinActivityConversionExtensions private const long UnixEpochTicks = 621355968000000000L; // = DateTimeOffset.FromUnixTimeMilliseconds(0).Ticks private const long UnixEpochMicroseconds = UnixEpochTicks / TicksPerMicrosecond; - private static readonly string InvalidSpanId = default(ActivitySpanId).ToHexString(); - #if !NET452 private static readonly ConcurrentDictionary<(string, int), ZipkinEndpoint> RemoteEndpointCache = new ConcurrentDictionary<(string, int), ZipkinEndpoint>(); #else @@ -41,11 +39,9 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l { var context = activity.Context; - string parentId = EncodeSpanId(activity.ParentSpanId); - if (string.Equals(parentId, InvalidSpanId, StringComparison.Ordinal)) - { - parentId = null; - } + string parentId = activity.ParentSpanId == default ? + null + : EncodeSpanId(activity.ParentSpanId); var tagState = new TagEnumerationState { diff --git a/test/OpenTelemetry.Exporter.Jaeger.Tests/Implementation/JaegerActivityConversionTest.cs b/test/OpenTelemetry.Exporter.Jaeger.Tests/Implementation/JaegerActivityConversionTest.cs index 8f9ecca0ad7..c73ec5abd99 100644 --- a/test/OpenTelemetry.Exporter.Jaeger.Tests/Implementation/JaegerActivityConversionTest.cs +++ b/test/OpenTelemetry.Exporter.Jaeger.Tests/Implementation/JaegerActivityConversionTest.cs @@ -41,10 +41,12 @@ static JaegerActivityConversionTest() ActivitySource.AddActivityListener(listener); } - [Fact] - public void JaegerActivityConverterTest_ConvertActivityToJaegerSpan_AllPropertiesSet() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void JaegerActivityConverterTest_ConvertActivityToJaegerSpan_AllPropertiesSet(bool isRootSpan) { - var activity = CreateTestActivity(); + var activity = CreateTestActivity(isRootSpan: isRootSpan); var traceIdAsInt = new Int128(activity.Context.TraceId); var spanIdAsInt = new Int128(activity.Context.SpanId); var linkTraceIdAsInt = new Int128(activity.Links.Single().Context.TraceId); @@ -438,14 +440,15 @@ internal static Activity CreateTestActivity( bool addEvents = true, bool addLinks = true, Resource resource = null, - ActivityKind kind = ActivityKind.Client) + ActivityKind kind = ActivityKind.Client, + bool isRootSpan = false) { var startTimestamp = DateTime.UtcNow; var endTimestamp = startTimestamp.AddSeconds(60); var eventTimestamp = DateTime.UtcNow; var traceId = ActivityTraceId.CreateFromString("e8ea7e9ac72de94e91fabc613f9686b2".AsSpan()); - var parentSpanId = ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 }); + var parentSpanId = isRootSpan ? default : ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 }); var attributes = new Dictionary { diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs index 4921e8e651e..ec4b19bc233 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs @@ -133,10 +133,11 @@ public void SuppresssesInstrumentation() } [Theory] - [InlineData(true, false)] - [InlineData(false, false)] - [InlineData(false, true)] - public void IntegrationTest(bool useShortTraceIds, bool useTestResource) + [InlineData(true, false, false)] + [InlineData(false, false, false)] + [InlineData(false, true, false)] + [InlineData(false, false, true)] + public void IntegrationTest(bool useShortTraceIds, bool useTestResource, bool isRootSpan) { Guid requestId = Guid.NewGuid(); @@ -149,7 +150,7 @@ public void IntegrationTest(bool useShortTraceIds, bool useTestResource) var serviceName = ZipkinExporterOptions.DefaultServiceName; var resoureTags = string.Empty; - var activity = CreateTestActivity(); + var activity = CreateTestActivity(isRootSpan: isRootSpan); if (useTestResource) { serviceName = "MyService"; @@ -183,14 +184,17 @@ public void IntegrationTest(bool useShortTraceIds, bool useTestResource) ipInformation.Append($@",""ipv6"":""{exporter.LocalEndpoint.Ipv6}"""); } + var parentId = isRootSpan ? string.Empty : $@"""parentId"":""{ZipkinActivityConversionExtensions.EncodeSpanId(activity.ParentSpanId)}"","; + var traceId = useShortTraceIds ? TraceId.Substring(TraceId.Length - 16, 16) : TraceId; Assert.Equal( - $@"[{{""traceId"":""{traceId}"",""name"":""Name"",""parentId"":""{ZipkinActivityConversionExtensions.EncodeSpanId(activity.ParentSpanId)}"",""id"":""{ZipkinActivityConversionExtensions.EncodeSpanId(context.SpanId)}"",""kind"":""CLIENT"",""timestamp"":{timestamp},""duration"":60000000,""localEndpoint"":{{""serviceName"":""{serviceName}""{ipInformation}}},""remoteEndpoint"":{{""serviceName"":""http://localhost:44312/""}},""annotations"":[{{""timestamp"":{eventTimestamp},""value"":""Event1""}},{{""timestamp"":{eventTimestamp},""value"":""Event2""}}],""tags"":{{{resoureTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""True"",""http.host"":""http://localhost:44312/"",""library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""}}}}]", + $@"[{{""traceId"":""{traceId}"",""name"":""Name"",{parentId}""id"":""{ZipkinActivityConversionExtensions.EncodeSpanId(context.SpanId)}"",""kind"":""CLIENT"",""timestamp"":{timestamp},""duration"":60000000,""localEndpoint"":{{""serviceName"":""{serviceName}""{ipInformation}}},""remoteEndpoint"":{{""serviceName"":""http://localhost:44312/""}},""annotations"":[{{""timestamp"":{eventTimestamp},""value"":""Event1""}},{{""timestamp"":{eventTimestamp},""value"":""Event2""}}],""tags"":{{{resoureTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""True"",""http.host"":""http://localhost:44312/"",""library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""}}}}]", Responses[requestId]); } internal static Activity CreateTestActivity( + bool isRootSpan = false, bool setAttributes = true, Dictionary additionalAttributes = null, bool addEvents = true, @@ -203,7 +207,7 @@ internal static Activity CreateTestActivity( var eventTimestamp = DateTime.UtcNow; var traceId = ActivityTraceId.CreateFromString("e8ea7e9ac72de94e91fabc613f9686b2".AsSpan()); - var parentSpanId = ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 }); + var parentSpanId = isRootSpan ? default : ActivitySpanId.CreateFromBytes(new byte[] { 12, 23, 34, 45, 56, 67, 78, 89 }); var attributes = new Dictionary {