diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index 83588d1a042..403cee4e7c5 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -6,12 +6,13 @@ Notes](../../RELEASENOTES.md). ## Unreleased -* The library is now marked as trim and AOT compatible. - ([#7441](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7441)) - * Fixed `TraceContextPropagator` to normalize empty `tracestate` header values to `null` when extracting trace context. - ([#7407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7407)) + ([#7407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7407), + [#7433](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7433)) + +* The library is now marked as trim and AOT compatible. + ([#7441](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7441)) * **Experimental (pre-release builds only):** Updated `EnvironmentVariableCarrier.Get` to read only the normalized environment variable name, following the updated diff --git a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs index 49554de17cb..9351fdc85ea 100644 --- a/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs +++ b/src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs @@ -381,6 +381,11 @@ private static bool TryExtractMultipleTracestate(IEnumerable tracestateC foreach (var tracestateEntry in tracestateCollection) { + if (tracestateEntry is not { Length: > 0 }) + { + continue; + } + var tracestate = tracestateEntry.AsSpan(); var begin = 0; while (begin < tracestate.Length) @@ -465,11 +470,11 @@ private static bool TryExtractMultipleTracestate(IEnumerable tracestateC return true; } - private static bool TryExtractSingleTracestate(string tracestate, out string tracestateResult) + private static bool TryExtractSingleTracestate(string? tracestate, out string tracestateResult) { tracestateResult = string.Empty; - if (tracestate.Length == 0) + if (tracestate is not { Length: > 0 }) { return true; } diff --git a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs index 8942cd1e244..5c76b605a4e 100644 --- a/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs +++ b/test/OpenTelemetry.Tests/Trace/Propagation/TraceContextPropagatorTests.cs @@ -336,6 +336,48 @@ public void TryExtractTracestate_NullCollectionReturnsEmpty() Assert.Empty(actual); } + [Fact] + public void Extract_HandlesNullTracestateValue() + { + var headers = new Dictionary + { + { TraceParent, $"00-{TraceId}-{SpanId}-01" }, + }; + + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, (_, name) => headers.TryGetValue(name, out var value) ? [value] : [null!]); + + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); + + Assert.True(context.ActivityContext.IsRemote); + Assert.True(context.ActivityContext.IsValid()); + Assert.NotEqual(0, (int)(context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); + + Assert.Null(context.ActivityContext.TraceState); + } + + [Fact] + public void Extract_HandlesNullTracestateValues() + { + var headers = new Dictionary + { + { TraceParent, $"00-{TraceId}-{SpanId}-01" }, + }; + + var propagator = new TraceContextPropagator(); + var context = propagator.Extract(default, headers, (_, name) => headers.TryGetValue(name, out var value) ? [value] : [string.Empty, null!]); + + Assert.Equal(ActivityTraceId.CreateFromString(TraceId.AsSpan()), context.ActivityContext.TraceId); + Assert.Equal(ActivitySpanId.CreateFromString(SpanId.AsSpan()), context.ActivityContext.SpanId); + + Assert.True(context.ActivityContext.IsRemote); + Assert.True(context.ActivityContext.IsValid()); + Assert.NotEqual(0, (int)(context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded)); + + Assert.Null(context.ActivityContext.TraceState); + } + [Fact] public void Inject_NoTracestate() {