diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs
index f0b93c82b4075a..8e0f944e09cf6f 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs
@@ -210,6 +210,8 @@ public readonly struct ActivityEvent
public System.Diagnostics.ActivityTraceFlags TraceFlags { get { throw null; } }
public string? TraceState { get { throw null; } }
public bool IsRemote { get { throw null; } }
+ public static bool TryParse(string traceParent, string? traceState, out System.Diagnostics.ActivityContext context) { throw null; }
+ public static System.Diagnostics.ActivityContext Parse(string traceParent, string? traceState) { throw null; }
public static bool operator ==(System.Diagnostics.ActivityContext left, System.Diagnostics.ActivityContext right) { throw null; }
public static bool operator !=(System.Diagnostics.ActivityContext left, System.Diagnostics.ActivityContext right) { throw null; }
public bool Equals(System.Diagnostics.ActivityContext value) { throw null; }
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/Resources/Strings.resx b/src/libraries/System.Diagnostics.DiagnosticSource/src/Resources/Strings.resx
index 52df21c79f1eea..76242301beb3db 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/Resources/Strings.resx
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/Resources/Strings.resx
@@ -156,4 +156,7 @@
"The collection already contains item with same key '{0}''"
+
+ "Invalid trace parent."
+
\ No newline at end of file
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
index d842a0c7dabe3b..b5cd641c3eea42 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
@@ -839,24 +839,29 @@ private static bool IsW3CId(string id)
#if ALLOW_PARTIALLY_TRUSTED_CALLERS
[System.Security.SecuritySafeCriticalAttribute]
#endif
- internal static bool TryConvertIdToContext(string id, out ActivityContext context)
+ internal static bool TryConvertIdToContext(string traceParent, string? traceState, out ActivityContext context)
{
context = default;
- if (!IsW3CId(id))
+ if (!IsW3CId(traceParent))
{
return false;
}
- ReadOnlySpan traceIdSpan = id.AsSpan(3, 32);
- ReadOnlySpan spanIdSpan = id.AsSpan(36, 16);
+ ReadOnlySpan traceIdSpan = traceParent.AsSpan(3, 32);
+ ReadOnlySpan spanIdSpan = traceParent.AsSpan(36, 16);
if (!ActivityTraceId.IsLowerCaseHexAndNotAllZeros(traceIdSpan) || !ActivityTraceId.IsLowerCaseHexAndNotAllZeros(spanIdSpan) ||
- !HexConverter.IsHexLowerChar(id[53]) || !HexConverter.IsHexLowerChar(id[54]))
+ !HexConverter.IsHexLowerChar(traceParent[53]) || !HexConverter.IsHexLowerChar(traceParent[54]))
{
return false;
}
- context = new ActivityContext(new ActivityTraceId(traceIdSpan.ToString()), new ActivitySpanId(spanIdSpan.ToString()), (ActivityTraceFlags) ActivityTraceId.HexByteFromChars(id[53], id[54]));
+ context = new ActivityContext(
+ new ActivityTraceId(traceIdSpan.ToString()),
+ new ActivitySpanId(spanIdSpan.ToString()),
+ (ActivityTraceFlags) ActivityTraceId.HexByteFromChars(traceParent[53], traceParent[54]),
+ traceState);
+
return true;
}
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityContext.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityContext.cs
index 454eba3ab0c60c..d94640ad23fa07 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityContext.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivityContext.cs
@@ -59,6 +59,40 @@ public ActivityContext(ActivityTraceId traceId, ActivitySpanId spanId, ActivityT
///
public bool IsRemote { get; }
+ ///
+ /// Parse W3C trace context headers to ActivityContext object.
+ ///
+ /// W3C trace parent header.
+ /// W3C trace state.
+ /// The ActivityContext object created from the parsing operation.
+ public static bool TryParse(string traceParent, string? traceState, out ActivityContext context)
+ {
+ if (traceParent == null)
+ {
+ throw new ArgumentNullException(nameof(traceParent));
+ }
+
+ return Activity.TryConvertIdToContext(traceParent, traceState, out context);
+ }
+
+ ///
+ /// Parse W3C trace context headers to ActivityContext object.
+ ///
+ /// W3C trace parent header.
+ /// Trace state.
+ ///
+ /// The ActivityContext object created from the parsing operation.
+ ///
+ public static ActivityContext Parse(string traceParent, string? traceState)
+ {
+ if (!TryParse(traceParent, traceState, out ActivityContext context))
+ {
+ throw new ArgumentException(SR.InvalidTraceParent);
+ }
+
+ return context;
+ }
+
public bool Equals(ActivityContext value) => SpanId.Equals(value.SpanId) && TraceId.Equals(value.TraceId) && TraceFlags == value.TraceFlags && TraceState == value.TraceState;
public override bool Equals(object? obj) => (obj is ActivityContext context) ? Equals(context) : false;
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs
index a89068f6c11bb2..ac110fd590e524 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs
@@ -145,7 +145,7 @@ public bool HasListeners()
{
if (!canUseContext.HasValue)
{
- canUseContext = Activity.TryConvertIdToContext(parentId, out ActivityContext ctx);
+ canUseContext = Activity.TryConvertIdToContext(parentId, traceState: null, out ActivityContext ctx);
if (canUseContext.Value)
{
dataWithContext = new ActivityCreationOptions(data.Source, data.Name, ctx, data.Kind, data.Tags, data.Links);
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs
index 5b22f1638161a0..99bafcd319d504 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivitySourceTests.cs
@@ -382,6 +382,32 @@ public void TestDefaultParentContext()
}).Dispose();
}
+ [Fact]
+ public void TestActivityContextParsing()
+ {
+ const string w3cId = "00-99d43cb30a4cdb4fbeee3a19c29201b0-e82825765f051b47-01";
+ Assert.True(ActivityContext.TryParse(w3cId, "k=v", out ActivityContext context));
+ Assert.Equal("99d43cb30a4cdb4fbeee3a19c29201b0", context.TraceId.ToHexString());
+ Assert.Equal("e82825765f051b47", context.SpanId.ToHexString());
+ Assert.Equal(ActivityTraceFlags.Recorded, context.TraceFlags);
+ Assert.Equal("k=v", context.TraceState);
+
+ context = ActivityContext.Parse(w3cId, "k=v");
+ Assert.Equal("99d43cb30a4cdb4fbeee3a19c29201b0", context.TraceId.ToHexString());
+ Assert.Equal("e82825765f051b47", context.SpanId.ToHexString());
+ Assert.Equal(ActivityTraceFlags.Recorded, context.TraceFlags);
+ Assert.Equal("k=v", context.TraceState);
+
+ context = ActivityContext.Parse(w3cId, null);
+ Assert.Null(context.TraceState);
+
+ Assert.Throws(() => ActivityContext.TryParse(null, "k=v", out context));
+ Assert.Throws(() => ActivityContext.Parse(null, null));
+ Assert.Throws(() => ActivityContext.Parse("BadW3C", null));
+
+ const string invalidW3CContext = "00-Z9d43cb30a4cdb4fbeee3a19c29201b0-e82825765f051b47-01";
+ Assert.False(ActivityContext.TryParse(invalidW3CContext, null, out context));
+ }
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestCreatingActivityUsingDifferentParentIds()