Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
125 changes: 91 additions & 34 deletions src/OpenTelemetry.Api/Context/Propagation/TraceContextPropagator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ public override void Inject<T>(PropagationContext context, T carrier, Action<T,
var traceparent = string.Create(55, context.ActivityContext, WriteTraceParentIntoSpan);
#else
var traceparent = string.Concat("00-", context.ActivityContext.TraceId.ToHexString(), "-", context.ActivityContext.SpanId.ToHexString());
traceparent = string.Concat(traceparent, (context.ActivityContext.TraceFlags & ActivityTraceFlags.Recorded) != 0 ? "-01" : "-00");
var traceFlags = FormatActivityTraceFlags(context.ActivityContext.TraceFlags);
traceparent = string.Concat(traceparent, traceFlags);
#endif

setter(carrier, TraceParent, traceparent);
Expand All @@ -131,6 +132,78 @@ public override void Inject<T>(PropagationContext context, T carrier, Action<T,
}
}
}

#if NET
static void WriteTraceParentIntoSpan(Span<char> 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 enem values are added in the future the Fallback path will ensure
Comment thread
martincostello marked this conversation as resolved.
Outdated
// 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<char> 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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -248,12 +328,10 @@ private static bool TryExtractTracestate(IEnumerable<string>? 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<string> readOnlyList)
Expand All @@ -264,12 +342,11 @@ private static bool TryExtractTracestate(IEnumerable<string>? 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();
Expand All @@ -280,12 +357,10 @@ private static bool TryExtractTracestate(IEnumerable<string>? 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<string> EnumerateFrom(string first, IEnumerator<string> enumerator)
Expand Down Expand Up @@ -721,22 +796,4 @@ private static bool ValidateValue(ReadOnlySpan<char> value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsAsciiLetterOrDigitLower(char c)
=> char.IsAsciiDigit(c) || char.IsAsciiLetterLower(c);

#if NET
private static void WriteTraceParentIntoSpan(Span<char> 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
}
3 changes: 3 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
#endif
using System.Diagnostics.Tracing;
using System.Globalization;
using Microsoft.Extensions.Configuration;

namespace OpenTelemetry.Internal;
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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}]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string?>(StringComparer.Ordinal);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Loading
Loading