Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 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<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