diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 9084399a13c..92535998fc0 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -10,6 +10,9 @@ Notes](../../RELEASENOTES.md). Add support for using environment variables as context propagation carriers. ([#7174](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7174)) +* Update `TraceContextPropagator` to support the W3C randomness flag. + ([#7301](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7301)) + ## 1.15.3 Released 2026-Apr-21 diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs index 237e2897b8e..146b4552a17 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs @@ -113,7 +113,8 @@ public override void Inject(PropagationContext context, T carrier, Action(PropagationContext context, T carrier, Action destination, ActivityContext context) + { + "00-".CopyTo(destination); + + context.TraceId.ToHexString().CopyTo(destination.Slice(3)); + + destination[35] = '-'; + + context.SpanId.ToHexString().CopyTo(destination.Slice(36)); + + // https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3867 + // will change this code to use ActivityTraceFlags.RandomTraceId instead of 2. + // If new enum values are added in the future the Fallback path will ensure + // that the handling is functionally correct, but the condition should be updated + // to include the new value(s) for better readability and performance where possible. + if (context.TraceFlags == ActivityTraceFlags.Recorded) + { + "-01".CopyTo(destination.Slice(52)); + } + else if (context.TraceFlags == (ActivityTraceFlags)2) + { + "-02".CopyTo(destination.Slice(52)); + } + else if (context.TraceFlags == (ActivityTraceFlags.Recorded | (ActivityTraceFlags)2)) + { + "-03".CopyTo(destination.Slice(52)); + } + else + { + var flags = (byte)context.TraceFlags; + destination[52] = '-'; + destination[53] = GetHexChar(flags >> 4); + destination[54] = GetHexChar(flags & 0xF); + } + } +#else + static string FormatActivityTraceFlags(ActivityTraceFlags flags) + { + // https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3867 + // will change this code to use ActivityTraceFlags.RandomTraceId instead of 2. + // If new enum values are added in the future the Fallback path will ensure + // that the handling is functionally correct, but the switch should be updated + // to include the new value(s) for better readability and performance where possible. + return flags switch + { + ActivityTraceFlags.None => "-00", + ActivityTraceFlags.Recorded => "-01", + (ActivityTraceFlags)2 => "-02", + ActivityTraceFlags.Recorded | (ActivityTraceFlags)2 => "-03", + _ => Fallback((byte)flags), + }; + + static string Fallback(byte flags) + { + Span buffer = stackalloc char[3]; + + buffer[0] = '-'; + buffer[1] = GetHexChar(flags >> 4); + buffer[2] = GetHexChar(flags & 0xF); + + return buffer.ToString(); + } + } +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static char GetHexChar(int value) + { + return (char)(value + (value < 10 ? '0' : 'a' - 10)); + } } internal static bool TryExtractTraceparent(string traceparent, out ActivityTraceId traceId, out ActivitySpanId spanId, out ActivityTraceFlags traceOptions) @@ -208,6 +281,13 @@ internal static bool TryExtractTraceparent(string traceparent, out ActivityTrace traceOptions |= ActivityTraceFlags.Recorded; } + if ((optionsLowByte & 2) == 2) + { + // https://github.com/open-telemetry/opentelemetry-dotnet/pull/6899 + // will change this to use ActivityTraceFlags.RandomTraceId instead. + traceOptions |= (ActivityTraceFlags)2; + } + if ((!bestAttempt) && (traceparent.Length != VersionAndTraceIdAndSpanIdLength + OptionsLength)) { return false; @@ -248,12 +328,10 @@ private static bool TryExtractTracestate(IEnumerable? tracestateCollecti } hasTraceState = true; - if (list.Count == 1) - { - return TryExtractSingleTracestate(list[0], out tracestateResult); - } - return TryExtractMultipleTracestate(list, out tracestateResult); + return list.Count == 1 ? + TryExtractSingleTracestate(list[0], out tracestateResult) : + TryExtractMultipleTracestate(list, out tracestateResult); } if (tracestateCollection is IReadOnlyList readOnlyList) @@ -264,12 +342,11 @@ private static bool TryExtractTracestate(IEnumerable? tracestateCollecti } hasTraceState = true; - if (readOnlyList.Count == 1) - { - return TryExtractSingleTracestate(readOnlyList[0], out tracestateResult); - } - return TryExtractMultipleTracestate(readOnlyList, out tracestateResult); + return + readOnlyList.Count == 1 ? + TryExtractSingleTracestate(readOnlyList[0], out tracestateResult) : + TryExtractMultipleTracestate(readOnlyList, out tracestateResult); } using var enumerator = tracestateCollection.GetEnumerator(); @@ -280,12 +357,10 @@ private static bool TryExtractTracestate(IEnumerable? tracestateCollecti hasTraceState = true; var singleTraceState = enumerator.Current; - if (!enumerator.MoveNext()) - { - return TryExtractSingleTracestate(singleTraceState, out tracestateResult); - } - return TryExtractMultipleTracestate(EnumerateFrom(singleTraceState, enumerator), out tracestateResult); + return enumerator.MoveNext() ? + TryExtractMultipleTracestate(EnumerateFrom(singleTraceState, enumerator), out tracestateResult) : + TryExtractSingleTracestate(singleTraceState, out tracestateResult); } private static IEnumerable EnumerateFrom(string first, IEnumerator enumerator) @@ -721,22 +796,4 @@ private static bool ValidateValue(ReadOnlySpan value) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsAsciiLetterOrDigitLower(char c) => char.IsAsciiDigit(c) || char.IsAsciiLetterLower(c); - -#if NET - private static void WriteTraceParentIntoSpan(Span destination, ActivityContext context) - { - "00-".CopyTo(destination); - context.TraceId.ToHexString().CopyTo(destination.Slice(3)); - destination[35] = '-'; - context.SpanId.ToHexString().CopyTo(destination.Slice(36)); - if ((context.TraceFlags & ActivityTraceFlags.Recorded) != 0) - { - "-01".CopyTo(destination.Slice(52)); - } - else - { - "-00".CopyTo(destination.Slice(52)); - } - } -#endif } diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 4ee7bea15a7..b2dd3221336 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -14,6 +14,9 @@ Notes](../../RELEASENOTES.md). once per collection cycle. ([#7188](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7188)) +* Update `OpenTelemetrySdkEventSource` to support the W3C randomness flag. + ([#7301](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7301)) + ## 1.15.3 Released 2026-Apr-21 diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 9cd69870706..dcf6300925c 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; #endif using System.Diagnostics.Tracing; +using System.Globalization; using Microsoft.Extensions.Configuration; namespace OpenTelemetry.Internal; @@ -69,7 +70,8 @@ public void ActivityStarted(Activity activity) // correct sampling flags // https://github.com/dotnet/runtime/issues/61857 var activityId = string.Concat("00-", activity.TraceId.ToHexString(), "-", activity.SpanId.ToHexString()); - activityId = string.Concat(activityId, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded) ? "-01" : "-00"); + var traceFlags = ((byte)activity.ActivityTraceFlags).ToString("x2", CultureInfo.InvariantCulture); + activityId = string.Concat(activityId, "-", traceFlags); this.ActivityStarted(activity.DisplayName, activityId); } } @@ -355,7 +357,7 @@ protected override void OnEventWritten(EventWrittenEventArgs e) } var message = e.Message != null && e.Payload != null && e.Payload.Count > 0 - ? string.Format(System.Globalization.CultureInfo.CurrentCulture, e.Message, [.. e.Payload]) + ? string.Format(CultureInfo.CurrentCulture, e.Message, [.. e.Payload]) : e.Message; Debug.WriteLine($"{e.EventSource.Name} - Level: [{e.Level}], EventId: [{e.EventId}], EventName: [{e.EventName}], Message: [{message}]"); diff --git a/test/OpenTelemetry.Api.Tests/Context/Propagation/EnvironmentVariableCarrierTests.cs b/test/OpenTelemetry.Api.Tests/Context/Propagation/EnvironmentVariableCarrierTests.cs index 3130fb661ec..cdacdb3d90a 100644 --- a/test/OpenTelemetry.Api.Tests/Context/Propagation/EnvironmentVariableCarrierTests.cs +++ b/test/OpenTelemetry.Api.Tests/Context/Propagation/EnvironmentVariableCarrierTests.cs @@ -209,13 +209,21 @@ public void Set_TargetsEnvironmentCopyWithoutMutatingProcessEnvironment() } } - [Fact] - public void TraceContextPropagator_RoundTripsThroughEnvironmentVariableCarrier() + [Theory] + [InlineData(ActivityTraceFlags.None, "00")] + [InlineData(ActivityTraceFlags.Recorded, "01")] + //// https://github.com/open-telemetry/opentelemetry-dotnet/pull/6899 + //// will change this to use ActivityTraceFlags.RandomTraceId instead. + [InlineData((ActivityTraceFlags)2, "02")] + [InlineData((ActivityTraceFlags)2 | ActivityTraceFlags.Recorded, "03")] + public void TraceContextPropagator_RoundTripsThroughEnvironmentVariableCarrier( + ActivityTraceFlags flags, + string expectedSuffix) { var activityContext = new ActivityContext( ActivityTraceId.CreateFromString("0af7651916cd43dd8448eb211c80319c"), ActivitySpanId.CreateFromString("b9c7c989f97918e1"), - ActivityTraceFlags.Recorded, + flags, "key1=value1,key2=value2"); var carrier = new Dictionary(StringComparer.Ordinal); @@ -226,7 +234,7 @@ public void TraceContextPropagator_RoundTripsThroughEnvironmentVariableCarrier() var extracted = propagator.Extract(default, EnvironmentVariableCarrier.Capture(carrier), EnvironmentVariableCarrier.Get); - Assert.Equal("00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", carrier["TRACEPARENT"]); + Assert.Equal($"00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-{expectedSuffix}", carrier["TRACEPARENT"]); Assert.Equal("key1=value1,key2=value2", carrier["TRACESTATE"]); Assert.Equal(activityContext.TraceId, extracted.ActivityContext.TraceId); Assert.Equal(activityContext.SpanId, extracted.ActivityContext.SpanId); diff --git a/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/Dockerfile b/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/Dockerfile index 1257df7ede8..0b33d4258b2 100644 --- a/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/Dockerfile +++ b/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/Dockerfile @@ -37,7 +37,7 @@ RUN tdnf install -y python3-pip \ && ln -sf /usr/bin/python3 /usr/bin/python \ && python3 -m pip install --requirement requirements.txt --require-hashes --break-system-packages \ && tdnf clean all -ENV SPEC_LEVEL=1 +ENV SPEC_LEVEL=2 ENV STRICT_LEVEL=1 ENTRYPOINT ["dotnet", "vstest", "OpenTelemetry.Instrumentation.W3cTraceContext.Tests.dll", "--logger:console;verbosity=detailed"] diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs index 5359905da28..0866fdd3029 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs @@ -34,17 +34,17 @@ public void CanParseExampleFromSpec() { TraceState, $"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01" }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId); - Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId); + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); - Assert.True(ctx.ActivityContext.IsRemote); - Assert.True(ctx.ActivityContext.IsValid()); - Assert.NotEqual(0, (int)(ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); + Assert.True(context.ActivityContext.IsRemote); + Assert.True(context.ActivityContext.IsValid()); + Assert.NotEqual(0, (int)(context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); - Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", ctx.ActivityContext.TraceState); + Assert.Equal($"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{TraceId}-00f067aa0ba902b7-01", context.ActivityContext.TraceState); } [Fact] @@ -55,15 +55,59 @@ public void NotSampled() { TraceParent, $"00-{TraceId}-{SpanId}-00" }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); + + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); + Assert.Equal(0, (int)(context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); + + Assert.True(context.ActivityContext.IsRemote); + Assert.True(context.ActivityContext.IsValid()); + } + + [Fact] + public void RandomTraceId() + { + var headers = new Dictionary + { + { TraceParent, $"00-{TraceId}-{SpanId}-02" }, + }; + + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); + + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); + + // https://github.com/open-telemetry/opentelemetry-dotnet/pull/6899 + // will change this to use ActivityTraceFlags.RandomTraceId instead. + Assert.Equal((ActivityTraceFlags)2, context.ActivityContext.TraceFlags); + + Assert.True(context.ActivityContext.IsValid()); + } + + [Fact] + public void RandomTraceIdAndRecorded() + { + var headers = new Dictionary + { + { TraceParent, $"00-{TraceId}-{SpanId}-03" }, + }; + + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); + + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); + + Assert.True(context.ActivityContext.TraceFlags.HasFlag(ActivityTraceFlags.Recorded)); - Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), ctx.ActivityContext.TraceId); - Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), ctx.ActivityContext.SpanId); - Assert.Equal(0, (int)(ctx.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); + // https://github.com/open-telemetry/opentelemetry-dotnet/pull/6899 + // will change this to use ActivityTraceFlags.RandomTraceId instead. + Assert.True(context.ActivityContext.TraceFlags.HasFlag((ActivityTraceFlags)2)); - Assert.True(ctx.ActivityContext.IsRemote); - Assert.True(ctx.ActivityContext.IsValid()); + Assert.True(context.ActivityContext.IsValid()); } [Fact] @@ -71,14 +115,16 @@ public void IsBlankIfNoHeader() { var headers = new Dictionary(); - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - Assert.False(ctx.ActivityContext.IsValid()); + Assert.False(context.ActivityContext.IsValid()); } [Theory] [InlineData($"00-xyz7651916cd43dd8448eb211c80319c-{SpanId}-01")] + [InlineData($"00-xyz7651916cd43dd8448eb211c80319c-{SpanId}-02")] + [InlineData($"00-xyz7651916cd43dd8448eb211c80319c-{SpanId}-03")] [InlineData($"00-{TraceId}-xyz7c989f97918e1-01")] [InlineData($"00-{TraceId}-{SpanId}-x1")] [InlineData($"00-{TraceId}-{SpanId}-1x")] @@ -89,10 +135,10 @@ public void IsBlankIfInvalid(string invalidTraceParent) { TraceParent, invalidTraceParent }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - Assert.False(ctx.ActivityContext.IsValid()); + Assert.False(context.ActivityContext.IsValid()); } [Fact] @@ -103,10 +149,10 @@ public void TracestateToStringEmpty() { TraceParent, $"00-{TraceId}-{SpanId}-01" }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - Assert.Null(ctx.ActivityContext.TraceState); + Assert.Null(context.ActivityContext.TraceState); } [Fact] @@ -118,10 +164,10 @@ public void TracestateToString() { TraceState, "k1=v1,k2=v2,k3=v3" }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - Assert.Equal("k1=v1,k2=v2,k3=v3", ctx.ActivityContext.TraceState); + Assert.Equal("k1=v1,k2=v2,k3=v3", context.ActivityContext.TraceState); } [Fact] @@ -285,8 +331,8 @@ public void Inject_NoTracestate() var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, traceState: null); var propagationContext = new PropagationContext(activityContext, default); var carrier = new Dictionary(); - var f = new TraceContextPropagator(); - f.Inject(propagationContext, carrier, Setter); + var propagator = new TraceContextPropagator(); + propagator.Inject(propagationContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } @@ -305,8 +351,30 @@ public void Inject_WithTracestate() var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, expectedHeaders[TraceState]); var propagationContext = new PropagationContext(activityContext, default); var carrier = new Dictionary(); - var f = new TraceContextPropagator(); - f.Inject(propagationContext, carrier, Setter); + var propagator = new TraceContextPropagator(); + propagator.Inject(propagationContext, carrier, Setter); + + Assert.Equal(expectedHeaders, carrier); + } + + [Fact] + public void Inject_WithRandomTraceId() + { + var traceId = ActivityTraceId.CreateRandom(); + var spanId = ActivitySpanId.CreateRandom(); + var expectedHeaders = new Dictionary + { + { TraceParent, $"00-{traceId}-{spanId}-02" }, + { TraceState, $"congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4,rojo=00-{traceId}-00f067aa0ba902b7-02" }, + }; + + // https://github.com/open-telemetry/opentelemetry-dotnet/pull/6899 + // will change this to use ActivityTraceFlags.RandomTraceId instead. + var activityContext = new ActivityContext(traceId, spanId, (ActivityTraceFlags)2, expectedHeaders[TraceState]); + var propagationContext = new PropagationContext(activityContext, default); + var carrier = new Dictionary(); + var propagator = new TraceContextPropagator(); + propagator.Inject(propagationContext, carrier, Setter); Assert.Equal(expectedHeaders, carrier); } @@ -322,8 +390,8 @@ public void Inject_TruncatesOversizedTracestate() var activityContext = new ActivityContext(traceId, spanId, ActivityTraceFlags.Recorded, oversizedTraceState); var propagationContext = new PropagationContext(activityContext, default); var carrier = new Dictionary(); - var f = new TraceContextPropagator(); - f.Inject(propagationContext, carrier, Setter); + var propagator = new TraceContextPropagator(); + propagator.Inject(propagationContext, carrier, Setter); Assert.Equal($"00-{traceId}-{spanId}-01", carrier[TraceParent]); Assert.Equal(expectedTraceState, carrier[TraceState]); @@ -451,9 +519,9 @@ private static string CallTraceContextPropagatorWithTraceParent(string tracepare { { TraceParent, traceparent }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); - return ctx.ActivityContext.TraceId.ToString(); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); + return context.ActivityContext.TraceId.ToString(); } private static string CallTraceContextPropagator(string tracestate) @@ -463,10 +531,10 @@ private static string CallTraceContextPropagator(string tracestate) { TraceParent, $"00-{TraceId}-{SpanId}-01" }, { TraceState, tracestate }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, Getter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, Getter); - var traceState = ctx.ActivityContext.TraceState; + var traceState = context.ActivityContext.TraceState; Assert.NotNull(traceState); return traceState; } @@ -478,10 +546,10 @@ private static string CallTraceContextPropagator(string[] tracestate) { TraceParent, [$"00-{TraceId}-{SpanId}-01"] }, { TraceState, tracestate }, }; - var f = new TraceContextPropagator(); - var ctx = f.Extract(default, headers, ArrayGetter); + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, ArrayGetter); - var traceState = ctx.ActivityContext.TraceState; + var traceState = context.ActivityContext.TraceState; Assert.NotNull(traceState); return traceState; }