From 4fe845cb5e514299ba7d51f86660d672c014a103 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 8 Nov 2021 14:11:21 -0800 Subject: [PATCH 1/7] draft --- .../ZipkinActivityConversionExtensions.cs | 27 ++++++- .../ZipkinActivityConversionTest.cs | 80 +++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs index c99f345b603..68b189df847 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs @@ -72,7 +72,32 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l } } - if (tagState.StatusCode == StatusCode.Error) + // Starting version 6.0.0 in System.Diagnostic.DiagnosticSource + // Status and StatusDescription can be set using activity.Setstatus(ActivityStatusCode, string) + // Look up activity.Status and activity.StatusDescription + // only if status was not set using ActivityExtensions in Api + if (!tagState.StatusCode.HasValue || tagState.StatusCode == StatusCode.Unset) + { + if (activity.Status == ActivityStatusCode.Ok || activity.Status == ActivityStatusCode.Error) + { + PooledList>.Add( + ref tagState.Tags, + new KeyValuePair( + SpanAttributeConstants.StatusCodeKey, + activity.Status)); + + if (activity.Status == ActivityStatusCode.Error) + { + // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status + PooledList>.Add( + ref tagState.Tags, + new KeyValuePair( + ZipkinErrorFlagTagName, + activity.StatusDescription ?? string.Empty)); + } + } + } + else if (tagState.StatusCode == StatusCode.Error) { // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status PooledList>.Add( diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs index e422e31436a..1aa933d1716 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs @@ -14,6 +14,7 @@ // limitations under the License. // +using System.Diagnostics; using System.Linq; using OpenTelemetry.Exporter.Zipkin.Tests; using OpenTelemetry.Internal; @@ -125,5 +126,84 @@ public void ToZipkinSpan_Status_ErrorFlagTest(StatusCode expectedStatusCode, str Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == "error"); } } + + [Theory] + [InlineData(ActivityStatusCode.Unset)] + [InlineData(ActivityStatusCode.Ok)] + [InlineData(ActivityStatusCode.Error)] + public void ToZipkinSpan_Activity_Status_And_StatusDescription_is_Set(ActivityStatusCode expectedStatusCode) + { + // Arrange + var activity = ZipkinExporterTests.CreateTestActivity(); + + activity.SetStatus(expectedStatusCode); + + // Act + var zipkinSpan = activity.ToZipkinSpan(DefaultZipkinEndpoint); + + // Assert + + Assert.Equal(expectedStatusCode, activity.Status); + + if (expectedStatusCode == ActivityStatusCode.Unset) + { + Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == SpanAttributeConstants.StatusCodeKey); + } + else + { + Assert.Equal( + expectedStatusCode, + zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); + } + + if (expectedStatusCode == ActivityStatusCode.Error) + { + Assert.Contains(zipkinSpan.Tags, t => t.Key == "error" && (string)t.Value == string.Empty); + } + else + { + Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == "error"); + } + } + + [Theory] + [InlineData(ActivityStatusCode.Ok, "ERROR")] + [InlineData(ActivityStatusCode.Error, "OK")] + public void ActivityStatusSetUsingTags_Takes_precedence_Over_Activity_Properties(ActivityStatusCode activityStatus, string statusCodeTagValue) + { + // Arrange + var activity = ZipkinExporterTests.CreateTestActivity(); + + activity.SetStatus(activityStatus); + activity.SetTag(SpanAttributeConstants.StatusCodeKey, statusCodeTagValue); + + // Act + var zipkinSpan = activity.ToZipkinSpan(DefaultZipkinEndpoint); + + // Assert + Assert.Equal( + statusCodeTagValue, + zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); + + if (statusCodeTagValue == "unset") + { + Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == SpanAttributeConstants.StatusCodeKey); + } + else + { + Assert.Equal( + statusCodeTagValue, + zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); + } + + if (statusCodeTagValue == "ERROR") + { + Assert.Contains(zipkinSpan.Tags, t => t.Key == "error" && (string)t.Value == string.Empty); + } + else + { + Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == "error"); + } + } } } From f45aaedb64100e0eae750697fb48df7c19583508 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 13:48:29 -0800 Subject: [PATCH 2/7] Status and StatusDescription precedence --- .../ZipkinActivityConversionExtensions.cs | 35 +++++++++---------- .../ZipkinActivityConversionTest.cs | 20 ++--------- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs index 68b189df847..07ca09fda2b 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs @@ -73,28 +73,25 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l } // Starting version 6.0.0 in System.Diagnostic.DiagnosticSource - // Status and StatusDescription can be set using activity.Setstatus(ActivityStatusCode, string) - // Look up activity.Status and activity.StatusDescription - // only if status was not set using ActivityExtensions in Api - if (!tagState.StatusCode.HasValue || tagState.StatusCode == StatusCode.Unset) + // Status and StatusDescription can be set using activity.SetStatus(ActivityStatusCode, string) + // Set otel.status_code and error to Status and StatusDescription respectively (If available) + if (activity.Status == ActivityStatusCode.Ok || activity.Status == ActivityStatusCode.Error) { - if (activity.Status == ActivityStatusCode.Ok || activity.Status == ActivityStatusCode.Error) + // This will also overwrite otel.status_code value if it was set using activity tag (otel.status_code). + PooledList>.Add( + ref tagState.Tags, + new KeyValuePair( + SpanAttributeConstants.StatusCodeKey, + activity.Status)); + + if (activity.Status == ActivityStatusCode.Error) { + // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status PooledList>.Add( - ref tagState.Tags, - new KeyValuePair( - SpanAttributeConstants.StatusCodeKey, - activity.Status)); - - if (activity.Status == ActivityStatusCode.Error) - { - // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status - PooledList>.Add( - ref tagState.Tags, - new KeyValuePair( - ZipkinErrorFlagTagName, - activity.StatusDescription ?? string.Empty)); - } + ref tagState.Tags, + new KeyValuePair( + ZipkinErrorFlagTagName, + activity.StatusDescription ?? string.Empty)); } } else if (tagState.StatusCode == StatusCode.Error) diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs index 1aa933d1716..46888c2e205 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs @@ -142,9 +142,6 @@ public void ToZipkinSpan_Activity_Status_And_StatusDescription_is_Set(ActivitySt var zipkinSpan = activity.ToZipkinSpan(DefaultZipkinEndpoint); // Assert - - Assert.Equal(expectedStatusCode, activity.Status); - if (expectedStatusCode == ActivityStatusCode.Unset) { Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == SpanAttributeConstants.StatusCodeKey); @@ -169,7 +166,7 @@ public void ToZipkinSpan_Activity_Status_And_StatusDescription_is_Set(ActivitySt [Theory] [InlineData(ActivityStatusCode.Ok, "ERROR")] [InlineData(ActivityStatusCode.Error, "OK")] - public void ActivityStatusSetUsingTags_Takes_precedence_Over_Activity_Properties(ActivityStatusCode activityStatus, string statusCodeTagValue) + public void ActivityStatus_Takes_precedence_Over_Status_Tags(ActivityStatusCode activityStatus, string statusCodeTagValue) { // Arrange var activity = ZipkinExporterTests.CreateTestActivity(); @@ -182,21 +179,10 @@ public void ActivityStatusSetUsingTags_Takes_precedence_Over_Activity_Properties // Assert Assert.Equal( - statusCodeTagValue, + activityStatus, zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); - if (statusCodeTagValue == "unset") - { - Assert.DoesNotContain(zipkinSpan.Tags, t => t.Key == SpanAttributeConstants.StatusCodeKey); - } - else - { - Assert.Equal( - statusCodeTagValue, - zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); - } - - if (statusCodeTagValue == "ERROR") + if (activityStatus == ActivityStatusCode.Error) { Assert.Contains(zipkinSpan.Tags, t => t.Key == "error" && (string)t.Value == string.Empty); } From 8ea390375a0f65c2d3a1d43d1f86b74e0acdcebe Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 14:58:22 -0800 Subject: [PATCH 3/7] fix precendence --- .../ZipkinActivityConversionExtensions.cs | 34 +++++++++++-------- .../ZipkinExporterTests.cs | 6 ++-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs index 07ca09fda2b..cf9ce1a66d4 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs @@ -77,7 +77,6 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l // Set otel.status_code and error to Status and StatusDescription respectively (If available) if (activity.Status == ActivityStatusCode.Ok || activity.Status == ActivityStatusCode.Error) { - // This will also overwrite otel.status_code value if it was set using activity tag (otel.status_code). PooledList>.Add( ref tagState.Tags, new KeyValuePair( @@ -94,14 +93,23 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l activity.StatusDescription ?? string.Empty)); } } - else if (tagState.StatusCode == StatusCode.Error) + else if (tagState.StatusCode.HasValue && tagState.StatusCode != StatusCode.Unset) { - // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status PooledList>.Add( - ref tagState.Tags, - new KeyValuePair( - ZipkinErrorFlagTagName, - tagState.StatusDescription ?? string.Empty)); + ref tagState.Tags, + new KeyValuePair( + SpanAttributeConstants.StatusCodeKey, + StatusHelper.GetTagValueForStatusCode(tagState.StatusCode.Value))); + + if (tagState.StatusCode == StatusCode.Error) + { + // Error flag rule from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status + PooledList>.Add( + ref tagState.Tags, + new KeyValuePair( + ZipkinErrorFlagTagName, + tagState.StatusDescription ?? string.Empty)); + } } EventEnumerationState eventState = default; @@ -208,14 +216,10 @@ public bool ForEach(KeyValuePair activityTag) { this.StatusCode = StatusHelper.GetStatusCodeForTagValue(strVal); - if (!this.StatusCode.HasValue || this.StatusCode == Trace.StatusCode.Unset) - { - // Unset Status is not sent: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#status - return true; - } - - // Normalize status since it is user-driven. - activityTag = new KeyValuePair(key, StatusHelper.GetTagValueForStatusCode(this.StatusCode.Value)); + // Return without adding it to output tag. + // It will be added later after checking Activity.Status property + // If Activity.Status is set then it will be preferred over otel.status_code tag on Activity. + return true; } else if (key == SpanAttributeConstants.StatusDescriptionKey) { diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs index f3d766ad04b..7449bb1046b 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs @@ -275,13 +275,13 @@ public void IntegrationTest( switch (statusCode) { case StatusCode.Ok: - statusTag = $@"""{SpanAttributeConstants.StatusCodeKey}"":""OK"","; + statusTag = $@",""{SpanAttributeConstants.StatusCodeKey}"":""OK"""; break; case StatusCode.Unset: statusTag = string.Empty; break; case StatusCode.Error: - statusTag = $@"""{SpanAttributeConstants.StatusCodeKey}"":""ERROR"","; + statusTag = $@",""{SpanAttributeConstants.StatusCodeKey}"":""ERROR"""; errorTag = $@",""{ZipkinActivityConversionExtensions.ZipkinErrorFlagTagName}"":""{statusDescription}"""; break; default: @@ -289,7 +289,7 @@ public void IntegrationTest( } Assert.Equal( - $@"[{{""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"":{{{resourceTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""true"",""boolArrayKey"":""true,false"",""http.host"":""http://localhost:44312/"",{statusTag}""otel.library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""{errorTag}}}}}]", + $@"[{{""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"":{{{resourceTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""true"",""boolArrayKey"":""true,false"",""http.host"":""http://localhost:44312/"",""otel.library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""{statusTag}{errorTag}}}}}]", Responses[requestId]); } From 52c5bf63d4859b802a61b51a4c23cb8cc7dc8806 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 15:20:01 -0800 Subject: [PATCH 4/7] fix status value --- src/OpenTelemetry.Api/Internal/StatusHelper.cs | 18 ++++++++++++++++++ .../ZipkinActivityConversionExtensions.cs | 2 +- .../ZipkinActivityConversionTest.cs | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Api/Internal/StatusHelper.cs b/src/OpenTelemetry.Api/Internal/StatusHelper.cs index 4ca8fa2b2a6..20825451dd9 100644 --- a/src/OpenTelemetry.Api/Internal/StatusHelper.cs +++ b/src/OpenTelemetry.Api/Internal/StatusHelper.cs @@ -15,6 +15,7 @@ // using System; +using System.Diagnostics; using System.Runtime.CompilerServices; using OpenTelemetry.Trace; @@ -43,6 +44,23 @@ public static string GetTagValueForStatusCode(StatusCode statusCode) }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetTagValueForActivityStatusCode(ActivityStatusCode activityStatusCode) + { + return activityStatusCode switch + { + /* + * Note: Order here does matter for perf. Unset is + * first because assumption is most spans will be + * Unset, then Error, then Ok. + */ + ActivityStatusCode.Unset => UnsetStatusCodeTagValue, + ActivityStatusCode.Error => ErrorStatusCodeTagValue, + ActivityStatusCode.Ok => OkStatusCodeTagValue, + _ => null, + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StatusCode? GetStatusCodeForTagValue(string statusCodeTagValue) { diff --git a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs index cf9ce1a66d4..024b0093218 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs +++ b/src/OpenTelemetry.Exporter.Zipkin/Implementation/ZipkinActivityConversionExtensions.cs @@ -81,7 +81,7 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint l ref tagState.Tags, new KeyValuePair( SpanAttributeConstants.StatusCodeKey, - activity.Status)); + StatusHelper.GetTagValueForActivityStatusCode(activity.Status))); if (activity.Status == ActivityStatusCode.Error) { diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs index 46888c2e205..42a14912f08 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/Implementation/ZipkinActivityConversionTest.cs @@ -149,7 +149,7 @@ public void ToZipkinSpan_Activity_Status_And_StatusDescription_is_Set(ActivitySt else { Assert.Equal( - expectedStatusCode, + StatusHelper.GetTagValueForActivityStatusCode(expectedStatusCode), zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); } @@ -179,7 +179,7 @@ public void ActivityStatus_Takes_precedence_Over_Status_Tags(ActivityStatusCode // Assert Assert.Equal( - activityStatus, + StatusHelper.GetTagValueForActivityStatusCode(activityStatus), zipkinSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value); if (activityStatus == ActivityStatusCode.Error) From 04f267c893664e061bbd7a0eded0d17fa653eeb4 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 15:24:46 -0800 Subject: [PATCH 5/7] changelog --- src/OpenTelemetry.Api/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index d90fc24dfcc..1cd07363c33 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added support for `Status` and `StatusDescription` properties on Activity + ([#2572](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2572)) + ## 1.2.0-beta1 Released 2021-Oct-08 From 1eec1296c98cc9b363655572437f309450ee5230 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 15:37:51 -0800 Subject: [PATCH 6/7] integration test --- .../ZipkinExporterTests.cs | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs index 7449bb1046b..99eb587596f 100644 --- a/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs +++ b/test/OpenTelemetry.Exporter.Zipkin.Tests/ZipkinExporterTests.cs @@ -192,7 +192,7 @@ public void ErrorGettingUriFromEnvVarSetsDefaultEndpointValue() [InlineData(false, false, false, StatusCode.Ok, null, true)] [InlineData(false, false, false, StatusCode.Error)] [InlineData(false, false, false, StatusCode.Error, "Error description")] - public void IntegrationTest( + public void IntegrationTest_With_Status_Tags( bool useShortTraceIds, bool useTestResource, bool isRootSpan, @@ -293,6 +293,106 @@ public void IntegrationTest( Responses[requestId]); } + [Theory] + [InlineData(true, false, false)] + [InlineData(false, false, false)] + [InlineData(false, true, false)] + [InlineData(false, false, true)] + [InlineData(false, false, false, ActivityStatusCode.Ok)] + [InlineData(false, false, false, ActivityStatusCode.Ok, null, true)] + [InlineData(false, false, false, ActivityStatusCode.Error)] + [InlineData(false, false, false, ActivityStatusCode.Error, "Error description")] + public void IntegrationTest_With_Activity_Status( + bool useShortTraceIds, + bool useTestResource, + bool isRootSpan, + ActivityStatusCode activityStatusCode = ActivityStatusCode.Unset, + string statusDescription = null, + bool addErrorTag = false) + { + Guid requestId = Guid.NewGuid(); + + ZipkinExporter exporter = new ZipkinExporter( + new ZipkinExporterOptions + { + Endpoint = new Uri($"http://{this.testServerHost}:{this.testServerPort}/api/v2/spans?requestId={requestId}"), + UseShortTraceIds = useShortTraceIds, + }); + + var serviceName = (string)exporter.ParentProvider.GetDefaultResource().Attributes + .Where(pair => pair.Key == ResourceSemanticConventions.AttributeServiceName).FirstOrDefault().Value; + var resourceTags = string.Empty; + var activity = CreateTestActivity(isRootSpan: isRootSpan); + + activity.SetStatus(activityStatusCode, statusDescription); + + if (useTestResource) + { + serviceName = "MyService"; + + exporter.SetLocalEndpointFromResource(ResourceBuilder.CreateEmpty().AddAttributes(new Dictionary + { + [ResourceSemanticConventions.AttributeServiceName] = serviceName, + ["service.tag"] = "hello world", + }).Build()); + } + else + { + exporter.SetLocalEndpointFromResource(Resource.Empty); + } + + if (addErrorTag) + { + activity.SetTag(ZipkinActivityConversionExtensions.ZipkinErrorFlagTagName, "This should be removed."); + } + + var processor = new SimpleActivityExportProcessor(exporter); + + processor.OnEnd(activity); + + var context = activity.Context; + + var timestamp = activity.StartTimeUtc.ToEpochMicroseconds(); + var eventTimestamp = activity.Events.First().Timestamp.ToEpochMicroseconds(); + + StringBuilder ipInformation = new StringBuilder(); + if (!string.IsNullOrEmpty(exporter.LocalEndpoint.Ipv4)) + { + ipInformation.Append($@",""ipv4"":""{exporter.LocalEndpoint.Ipv4}"""); + } + + if (!string.IsNullOrEmpty(exporter.LocalEndpoint.Ipv6)) + { + 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; + + string statusTag; + string errorTag = string.Empty; + switch (activityStatusCode) + { + case ActivityStatusCode.Ok: + statusTag = $@",""{SpanAttributeConstants.StatusCodeKey}"":""OK"""; + break; + case ActivityStatusCode.Unset: + statusTag = string.Empty; + break; + case ActivityStatusCode.Error: + statusTag = $@",""{SpanAttributeConstants.StatusCodeKey}"":""ERROR"""; + errorTag = $@",""{ZipkinActivityConversionExtensions.ZipkinErrorFlagTagName}"":""{statusDescription}"""; + break; + default: + throw new NotSupportedException(); + } + + Assert.Equal( + $@"[{{""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"":{{{resourceTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""true"",""boolArrayKey"":""true,false"",""http.host"":""http://localhost:44312/"",""otel.library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""{statusTag}{errorTag}}}}}]", + Responses[requestId]); + } + internal static Activity CreateTestActivity( bool isRootSpan = false, bool setAttributes = true, From 82a3f1d046bf9640b590d979836a165023ec0fac Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 9 Nov 2021 15:51:29 -0800 Subject: [PATCH 7/7] Trigger Build