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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
- Removed `SentrySdk.CaptureUserFeedback` and all associated members. Use the newer `SentrySdk.CaptureFeedback` instead.
- Backpressure handling is now enabled by default, meaning that the SDK will monitor system health and reduce the sampling rate of events and transactions when the system is under load. When the system is determined to be healthy again, the sampling rates are returned to their original levels. ([#4615](https://github.com/getsentry/sentry-dotnet/pull/4615))
- ScopeExtensions.Populate is now internal ([#4611](https://github.com/getsentry/sentry-dotnet/pull/4611))
- Add support for W3C traceparent header for outgoing requests ([#4661](https://github.com/getsentry/sentry-dotnet/pull/4661))
This feature is disabled by default. When enabled, outgoing requests will include the W3C traceparent header.
```csharp
SentrySdk.Init(options =>
{
// ...
options.PropagateTraceparent = true;
});
```

See https://develop.sentry.dev/sdk/telemetry/traces/distributed-tracing/#w3c-trace-context-header for more details.

### Fixes

Expand Down
2 changes: 2 additions & 0 deletions src/Sentry/BindableSentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ internal partial class BindableSentryOptions
public bool? EnableTracing { get; set; }
public double? TracesSampleRate { get; set; }
public List<string>? TracePropagationTargets { get; set; }
public bool? PropagateTraceparent { get; set; }
public double? ProfilesSampleRate { get; set; }
public StackTraceMode? StackTraceMode { get; set; }
public long? MaxAttachmentSize { get; set; }
Expand Down Expand Up @@ -98,6 +99,7 @@ public void ApplyTo(SentryOptions options)
options.TracesSampleRate = TracesSampleRate ?? options.TracesSampleRate;
options.ProfilesSampleRate = ProfilesSampleRate ?? options.ProfilesSampleRate;
options.TracePropagationTargets = TracePropagationTargets?.Select(s => new StringOrRegex(s)).ToList() ?? options.TracePropagationTargets;
options.PropagateTraceparent = PropagateTraceparent ?? options.PropagateTraceparent;
options.StackTraceMode = StackTraceMode ?? options.StackTraceMode;
options.MaxAttachmentSize = MaxAttachmentSize ?? options.MaxAttachmentSize;
options.DetectStartupTime = DetectStartupTime ?? options.DetectStartupTime;
Expand Down
5 changes: 5 additions & 0 deletions src/Sentry/Extensibility/DisabledHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public void BindException(Exception exception, ISpan span)
/// </summary>
public BaggageHeader? GetBaggage() => null;

/// <summary>
/// Returns null.
/// </summary>
public W3CTraceparentHeader? GetTraceparentHeader() => null;

/// <summary>
/// Returns sampled out transaction context.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ public void BindException(Exception exception, ISpan span) =>
public BaggageHeader? GetBaggage()
=> SentrySdk.GetBaggage();

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
public W3CTraceparentHeader? GetTraceparentHeader()
=> SentrySdk.GetTraceparentHeader();

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Sentry/IHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public ITransactionTracer StartTransaction(
/// </summary>
public BaggageHeader? GetBaggage();

/// <summary>
/// Gets the W3C Trace Context traceparent header that allows tracing across services
/// </summary>
public W3CTraceparentHeader? GetTraceparentHeader();

/// <summary>
/// Continues a trace based on HTTP header values provided as strings.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/Sentry/Internal/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,18 @@ public BaggageHeader GetBaggage()
return propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession).ToBaggageHeader();
}

public W3CTraceparentHeader? GetTraceparentHeader()
{
if (GetSpan()?.GetTraceHeader() is { } traceHeader)
{
return new W3CTraceparentHeader(traceHeader.TraceId, traceHeader.SpanId, traceHeader.IsSampled);
}

// We fall back to the propagation context
var propagationContext = CurrentScope.PropagationContext;
return new W3CTraceparentHeader(propagationContext.TraceId, propagationContext.SpanId, null);
}

public TransactionContext ContinueTrace(
string? traceHeader,
string? baggageHeader,
Expand Down
16 changes: 16 additions & 0 deletions src/Sentry/SentryMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ private void PropagateTraceHeaders(HttpRequestMessage request, string url, ISpan
{
AddSentryTraceHeader(request, parentSpan);
AddBaggageHeader(request);
if (_options?.PropagateTraceparent is true)
{
AddTraceparentHeader(request, parentSpan);
}
}
}

Expand Down Expand Up @@ -181,4 +185,16 @@ private void AddBaggageHeader(HttpRequestMessage request)
// Set the baggage header
request.Headers.Add(BaggageHeader.HttpHeaderName, baggage.ToString());
}

private void AddTraceparentHeader(HttpRequestMessage request, ISpan? parentSpan)
{
// Set W3C traceparent header if it hasn't already been set
if (!request.Headers.Contains(W3CTraceparentHeader.HttpHeaderName) &&
// Use the span created by this integration as parent, instead of its own parent
(parentSpan?.GetTraceHeader() ?? _hub.GetTraceHeader()) is { } traceHeader)
{
var traceparentHeader = new W3CTraceparentHeader(traceHeader.TraceId, traceHeader.SpanId, traceHeader.IsSampled);
request.Headers.Add(W3CTraceparentHeader.HttpHeaderName, traceparentHeader.ToString());
}
}
}
12 changes: 12 additions & 0 deletions src/Sentry/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,18 @@ public IList<StringOrRegex> TracePropagationTargets
set => _tracePropagationTargets = value.WithConfigBinding();
}

/// <summary>
/// Whether to send W3C Trace Context traceparent headers in outgoing HTTP requests for distributed tracing.
/// When enabled, the SDK will send the <c>traceparent</c> header in addition to the <c>sentry-trace</c> header
/// for requests matching <see cref="TracePropagationTargets"/>.
/// </summary>
/// <remarks>
/// The default value is <c>false</c>. Set to <c>true</c> to enable W3C Trace Context propagation
/// for interoperability with services that support OpenTelemetry standards.
/// </remarks>
/// <seealso href="https://develop.sentry.dev/sdk/telemetry/traces/#propagatetraceparent"/>
Copy link
Collaborator

@jamescrosswell jamescrosswell Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably document this in a more "SDK User" friendly format... I added #4663 as a follow up for this.

public bool PropagateTraceparent { get; set; }

internal ITransactionProfilerFactory? TransactionProfilerFactory { get; set; }

private StackTraceMode? _stackTraceMode;
Expand Down
7 changes: 7 additions & 0 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,13 @@ public static void BindException(Exception exception, ISpan span)
public static BaggageHeader? GetBaggage()
=> CurrentHub.GetBaggage();

/// <summary>
/// Gets the W3C Trace Context traceparent header that allows tracing across services
/// </summary>
[DebuggerStepThrough]
public static W3CTraceparentHeader? GetTraceparentHeader()
=> CurrentHub.GetTraceparentHeader();

/// <summary>
/// Continues a trace based on HTTP header values provided as strings.
/// </summary>
Expand Down
39 changes: 39 additions & 0 deletions src/Sentry/W3CTraceparentHeader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Sentry;

/// <summary>
/// W3C Trace Context traceparent header.
/// </summary>
public class W3CTraceparentHeader
{
internal const string HttpHeaderName = "traceparent";

/// <summary>
/// Trace ID.
/// </summary>
public SentryId TraceId { get; }

/// <summary>
/// Span ID.
/// </summary>
public SpanId SpanId { get; }

/// <summary>
/// Whether the trace is sampled.
/// </summary>
public bool? IsSampled { get; }

/// <summary>
/// Initializes an instance of <see cref="W3CTraceparentHeader"/>.
/// </summary>
public W3CTraceparentHeader(SentryId traceId, SpanId spanId, bool? isSampled)
{
TraceId = traceId;
SpanId = spanId;
IsSampled = isSampled;
}

/// <inheritdoc />
public override string ToString() => IsSampled is { } isSampled
? $"00-{TraceId}-{SpanId}-{(isSampled ? "01" : "00")}"
: $"00-{TraceId}-{SpanId}-00";
}
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ namespace Sentry
Sentry.BaggageHeader? GetBaggage();
Sentry.ISpan? GetSpan();
Sentry.SentryTraceHeader? GetTraceHeader();
Sentry.W3CTraceparentHeader? GetTraceparentHeader();
void PauseSession();
void ResumeSession();
void StartSession();
Expand Down Expand Up @@ -718,6 +719,7 @@ namespace Sentry
public int MaxQueueItems { get; set; }
public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; }
public double? ProfilesSampleRate { get; set; }
public bool PropagateTraceparent { get; set; }
public string? Release { get; set; }
public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; }
public bool RequestBodyCompressionBuffered { get; set; }
Expand Down Expand Up @@ -864,6 +866,7 @@ namespace Sentry
public static Sentry.BaggageHeader? GetBaggage() { }
public static Sentry.ISpan? GetSpan() { }
public static Sentry.SentryTraceHeader? GetTraceHeader() { }
public static Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public static Sentry.ITransactionTracer? GetTransaction() { }
public static System.IDisposable Init() { }
public static System.IDisposable Init(Sentry.SentryOptions options) { }
Expand Down Expand Up @@ -1298,6 +1301,14 @@ namespace Sentry
protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger);
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { }
}
public class W3CTraceparentHeader
{
public W3CTraceparentHeader(Sentry.SentryId traceId, Sentry.SpanId spanId, bool? isSampled) { }
public bool? IsSampled { get; }
public Sentry.SpanId SpanId { get; }
public Sentry.SentryId TraceId { get; }
public override string ToString() { }
}
}
namespace Sentry.Ben.BlockingDetector
{
Expand Down Expand Up @@ -1384,6 +1395,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down Expand Up @@ -1433,6 +1445,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ namespace Sentry
Sentry.BaggageHeader? GetBaggage();
Sentry.ISpan? GetSpan();
Sentry.SentryTraceHeader? GetTraceHeader();
Sentry.W3CTraceparentHeader? GetTraceparentHeader();
void PauseSession();
void ResumeSession();
void StartSession();
Expand Down Expand Up @@ -718,6 +719,7 @@ namespace Sentry
public int MaxQueueItems { get; set; }
public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; }
public double? ProfilesSampleRate { get; set; }
public bool PropagateTraceparent { get; set; }
public string? Release { get; set; }
public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; }
public bool RequestBodyCompressionBuffered { get; set; }
Expand Down Expand Up @@ -864,6 +866,7 @@ namespace Sentry
public static Sentry.BaggageHeader? GetBaggage() { }
public static Sentry.ISpan? GetSpan() { }
public static Sentry.SentryTraceHeader? GetTraceHeader() { }
public static Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public static Sentry.ITransactionTracer? GetTransaction() { }
public static System.IDisposable Init() { }
public static System.IDisposable Init(Sentry.SentryOptions options) { }
Expand Down Expand Up @@ -1298,6 +1301,14 @@ namespace Sentry
protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger);
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { }
}
public class W3CTraceparentHeader
{
public W3CTraceparentHeader(Sentry.SentryId traceId, Sentry.SpanId spanId, bool? isSampled) { }
public bool? IsSampled { get; }
public Sentry.SpanId SpanId { get; }
public Sentry.SentryId TraceId { get; }
public override string ToString() { }
}
}
namespace Sentry.Ben.BlockingDetector
{
Expand Down Expand Up @@ -1384,6 +1395,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down Expand Up @@ -1433,6 +1445,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ namespace Sentry
Sentry.BaggageHeader? GetBaggage();
Sentry.ISpan? GetSpan();
Sentry.SentryTraceHeader? GetTraceHeader();
Sentry.W3CTraceparentHeader? GetTraceparentHeader();
void PauseSession();
void ResumeSession();
void StartSession();
Expand Down Expand Up @@ -718,6 +719,7 @@ namespace Sentry
public int MaxQueueItems { get; set; }
public Sentry.Extensibility.INetworkStatusListener? NetworkStatusListener { get; set; }
public double? ProfilesSampleRate { get; set; }
public bool PropagateTraceparent { get; set; }
public string? Release { get; set; }
public Sentry.ReportAssembliesMode ReportAssembliesMode { get; set; }
public bool RequestBodyCompressionBuffered { get; set; }
Expand Down Expand Up @@ -864,6 +866,7 @@ namespace Sentry
public static Sentry.BaggageHeader? GetBaggage() { }
public static Sentry.ISpan? GetSpan() { }
public static Sentry.SentryTraceHeader? GetTraceHeader() { }
public static Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public static Sentry.ITransactionTracer? GetTransaction() { }
public static System.IDisposable Init() { }
public static System.IDisposable Init(Sentry.SentryOptions options) { }
Expand Down Expand Up @@ -1298,6 +1301,14 @@ namespace Sentry
protected abstract void WriteAdditionalProperties(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger);
public void WriteTo(System.Text.Json.Utf8JsonWriter writer, Sentry.Extensibility.IDiagnosticLogger? logger) { }
}
public class W3CTraceparentHeader
{
public W3CTraceparentHeader(Sentry.SentryId traceId, Sentry.SpanId spanId, bool? isSampled) { }
public bool? IsSampled { get; }
public Sentry.SpanId SpanId { get; }
public Sentry.SentryId TraceId { get; }
public override string ToString() { }
}
}
namespace Sentry.Ben.BlockingDetector
{
Expand Down Expand Up @@ -1384,6 +1395,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down Expand Up @@ -1433,6 +1445,7 @@ namespace Sentry.Extensibility
public Sentry.BaggageHeader? GetBaggage() { }
public Sentry.ISpan? GetSpan() { }
public Sentry.SentryTraceHeader? GetTraceHeader() { }
public Sentry.W3CTraceparentHeader? GetTraceparentHeader() { }
public void PauseSession() { }
public System.IDisposable PushScope() { }
public System.IDisposable PushScope<TState>(TState state) { }
Expand Down
Loading
Loading