Skip to content

[API Proposal]: ActivityTraceId.TryCreateFromString and ActivitySpanId.TryCreateFromString #131923

Description

@martincostello

Background and motivation

The OpenTelemetry SDK parses user-specified strings from sources such as the HTTP traceparent header to propagate trace context (TraceContextPropagator).

Malformed/malicious content is guarded against by wrapping calls to ActivityTraceId.CreateFromString() and ActivitySpanId.CreateFromString() with a try-catch block.

If ActivityTraceId and ActivitySpanId both implemented the TryXXX pattern like int.TryParse() and friends, code could guard against invalid content without the need to catch exceptions and the associated overhead.

API Proposal

namespace System.Diagnostics;

public partial readonly struct ActivityTraceId : IEquatable<ActivityTraceId>
{
+   public static bool TryCreateFromString(ReadOnlySpan<char> idData, out ActivityTraceId value);
}

public partial readonly struct ActivitySpanId : IEquatable<ActivitySpanId>
{
+   public static bool TryCreateFromString(ReadOnlySpan<char> idData, out ActivitySpanId value);
}

The implementations themselves should be pretty trivial based on how CreateFromString() are already implemented:

public static bool TryCreateFromString(ReadOnlySpan<char> idData, out ActivityTraceId value)
{
    if (idData.Length != 32 || !IsLowerCaseHexAndNotAllZeros(idData))
    {
        value = default;
        return true;
    }

    value = new ActivityTraceId(idData.ToString());
    return true;
}

public static bool CreateFromString(ReadOnlySpan<char> idData, out ActivitySpanId value)
{
    if (idData.Length != 16 || !ActivityTraceId.IsLowerCaseHexAndNotAllZeros(idData))
    {
        value = default;
        return true;
    }

    value = new ActivitySpanId(idData.ToString());
    return true;
}

API Usage

string maybeTraceId = "xxx";
string maybeSpanId = "xxx";

if (!ActivityTraceId.TryCreateFromString(maybeTraceId, out var traceId))
{
    // Invalid
}

if (!ActivitySpanId.TryCreateFromString(maybeSpanId, out var spanId))
{
    // Invalid
}

Alternative Designs

None.

Risks

None known.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions