Skip to content

System.Diagnostics.ActivitySource API Addition #40046

@CodeBlanch

Description

@CodeBlanch

Background and Motivation

I'm working on implementing the new System.Diagnostics.ActivitySource API using the OpenTelemetry-dotnet beta that was just released on top of it. I've run into a couple of pain points that I think an API tweak or two would help alleviate.

I'm consuming messages off a queue. These messages have W3C TraceParent and TraceState values stored as message headers. If I wanted to create an Activity using these values as a parent the API provides:

public sealed class ActivitySource
{
  public Activity? StartActivity(string name, ActivityKind kind, string parentId, IEnumerable<KeyValuePair<string, string?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default);
}

That would work fine but I don't want to create a parented Activity, I want to add this TraceContext as a link on that Activity.

ActivityLink only accepts ActivityContext in its ctor. There's actually no way I can find to parse a string TraceParent + TraceState into an ActivityContext. That functionality is available internally.

Proposed API

namespace System.Diagnostics
{
    public readonly partial struct ActivityContext : IEquatable<ActivityContext>
    {
+        public static bool TryParse(string w3cId, string? traceState, out ActivityContext context);
    }
}

What I'm looking for is a way to expose to users the internal parsing so I can turn my strings into a valid ActivityContext without a lot of additional work.

Workaround

What I'm doing right now is just implementing some of the internal logic myself...

		public static ActivityContext CreateActivityContextFromW3CTraceContext(string traceParent, string? traceState = null)
		{
			if (string.IsNullOrEmpty(traceParent))
				throw new ArgumentNullException(nameof(traceParent));

			if (traceParent.Length != 55)
				throw new FormatException($"TraceParent [{traceParent}] format is not supported.");

			ReadOnlySpan<char> traceIdSpan = traceParent.AsSpan(3, 32);
			ReadOnlySpan<char> spanIdSpan = traceParent.AsSpan(36, 16);

			return new ActivityContext(
				ActivityTraceId.CreateFromString(traceIdSpan),
				ActivitySpanId.CreateFromString(spanIdSpan),
				(ActivityTraceFlags)ConversionExtensions.HexByteFromChars(traceParent[53], traceParent[54]),
				traceState);
		}

/cc @tarekgh @noahfalk @cijothomas @eddynaka @alanwest

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions