Skip to content

Commit

Permalink
[otlp] Implement Span and SpanLink flags (#5563)
Browse files Browse the repository at this point in the history
Co-authored-by: Mikel Blanchard <mblanchard@macrosssoftware.com>
  • Loading branch information
stevejgordon and CodeBlanch committed Apr 23, 2024
1 parent 4a1601b commit efe97bf
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
Expand Up @@ -7,6 +7,12 @@
to `OTel-OTLP-Exporter-Dotnet/{NuGet Package Version}`.
([#5528](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5528))

* Implementation of [OTLP
specification](https://github.com/open-telemetry/opentelemetry-proto/blob/v1.2.0/opentelemetry/proto/trace/v1/trace.proto#L112-L133)
for propagating `Span` and `SpanLink` flags containing W3C trace flags and
`parent_is_remote` information.
([#5563](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5563))

## 1.8.1

Released 2024-Apr-17
Expand Down
Expand Up @@ -174,6 +174,8 @@ internal static Span ToOtlpSpan(this Activity activity, SdkLimitOptions sdkLimit
};
otlpLinks.EnumerateLinks(activity, sdkLimitOptions.SpanLinkCountLimit ?? int.MaxValue);

otlpSpan.Flags = ToOtlpSpanFlags(activity.Context.TraceFlags, activity.HasRemoteParent);

return otlpSpan;
}

Expand Down Expand Up @@ -250,6 +252,8 @@ private static Span.Types.Link ToOtlpLink(in ActivityLink activityLink, SdkLimit
}
}

otlpLink.Flags = ToOtlpSpanFlags(activityLink.Context.TraceFlags, activityLink.Context.IsRemote);

return otlpLink;
}

Expand Down Expand Up @@ -281,6 +285,21 @@ private static Span.Types.Event ToOtlpEvent(in ActivityEvent activityEvent, SdkL
return otlpEvent;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint ToOtlpSpanFlags(ActivityTraceFlags activityTraceFlags, bool isRemote)
{
SpanFlags flags = (SpanFlags)activityTraceFlags;

flags |= SpanFlags.ContextHasIsRemoteMask;

if (isRemote)
{
flags |= SpanFlags.ContextIsRemoteMask;
}

return (uint)flags;
}

private struct TagEnumerationState : PeerServiceResolver.IPeerServiceState
{
public SdkLimitOptions SdkLimitOptions;
Expand Down
Expand Up @@ -401,6 +401,10 @@ public void ToOtlpSpanTest()
{
OtlpTestHelpers.AssertOtlpAttributes(childLinks[i].Tags.ToList(), otlpSpan.Links[i].Attributes);
}

var flags = (OtlpTrace.SpanFlags)otlpSpan.Flags;
Assert.True(flags.HasFlag(OtlpTrace.SpanFlags.ContextHasIsRemoteMask));
Assert.False(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask));
}

[Fact]
Expand Down Expand Up @@ -731,4 +735,99 @@ public void NamedOptionsMutateSeparateInstancesTest()
Assert.Equal("http://localhost/traces", tracerOptions.Endpoint.OriginalString);
Assert.Equal("http://localhost/metrics", meterOptions.Endpoint.OriginalString);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public void SpanFlagsTest(bool isRecorded, bool isRemote)
{
using var activitySource = new ActivitySource(nameof(this.SpanFlagsTest));

ActivityContext ctx = new ActivityContext(
ActivityTraceId.CreateRandom(),
ActivitySpanId.CreateRandom(),
isRecorded ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None,
isRemote: isRemote);

using var rootActivity = activitySource.StartActivity("root", ActivityKind.Server, ctx);

var otlpSpan = rootActivity.ToOtlpSpan(DefaultSdkLimitOptions);

var flags = (OtlpTrace.SpanFlags)otlpSpan.Flags;

ActivityTraceFlags traceFlags = (ActivityTraceFlags)(flags & OtlpTrace.SpanFlags.TraceFlagsMask);

if (isRecorded)
{
Assert.True(traceFlags.HasFlag(ActivityTraceFlags.Recorded));
}
else
{
Assert.False(traceFlags.HasFlag(ActivityTraceFlags.Recorded));
}

Assert.True(flags.HasFlag(OtlpTrace.SpanFlags.ContextHasIsRemoteMask));

if (isRemote)
{
Assert.True(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask));
}
else
{
Assert.False(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask));
}
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public void SpanLinkFlagsTest(bool isRecorded, bool isRemote)
{
using var activitySource = new ActivitySource(nameof(this.SpanLinkFlagsTest));

ActivityContext ctx = new ActivityContext(
ActivityTraceId.CreateRandom(),
ActivitySpanId.CreateRandom(),
isRecorded ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None,
isRemote: isRemote);

var links = new[]
{
new ActivityLink(ctx),
};

using var rootActivity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), links: links);

var otlpSpan = rootActivity.ToOtlpSpan(DefaultSdkLimitOptions);

var spanLink = Assert.Single(otlpSpan.Links);

var flags = (OtlpTrace.SpanFlags)spanLink.Flags;

ActivityTraceFlags traceFlags = (ActivityTraceFlags)(flags & OtlpTrace.SpanFlags.TraceFlagsMask);

if (isRecorded)
{
Assert.True(traceFlags.HasFlag(ActivityTraceFlags.Recorded));
}
else
{
Assert.False(traceFlags.HasFlag(ActivityTraceFlags.Recorded));
}

Assert.True(flags.HasFlag(OtlpTrace.SpanFlags.ContextHasIsRemoteMask));

if (isRemote)
{
Assert.True(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask));
}
else
{
Assert.False(flags.HasFlag(OtlpTrace.SpanFlags.ContextIsRemoteMask));
}
}
}

0 comments on commit efe97bf

Please sign in to comment.