Skip to content

Commit dc518b5

Browse files
Fixed inversions
1 parent acf14a4 commit dc518b5

2 files changed

Lines changed: 33 additions & 17 deletions

File tree

src/Sentry.OpenTelemetry.Exporter/OtelPropagationContext.cs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,29 @@ public SpanId? ParentSpanId
3434

3535
public bool IsSampled => Activity.Current?.Recorded ?? false;
3636

37-
// https://opentelemetry.io/docs/specs/otel/trace/tracestate-handling/#predefined-opentelemetry-sub-keys
38-
public double? SampleRate => GetOtelTraceStateValue("th") is { } th ? ParseOtelHexFraction(th) : null;
37+
/// <summary>
38+
/// th is a rejection threshold: T = (1 - sampling_probability) * 2^56, so we invert to get the sample rate.
39+
/// </summary>
40+
public double? SampleRate => GetOtelTraceStateValue("th") is { } th && ParseOtelHexFraction(th) is { } v ? 1.0 - v : null;
3941

40-
// https://opentelemetry.io/docs/specs/otel/trace/tracestate-handling/#predefined-opentelemetry-sub-keys
41-
public double? SampleRand => GetOtelTraceStateValue("rv") is { } rv ? ParseOtelHexFraction(rv) : null;
42+
/// <summary>
43+
/// Parses the SampleRand from the rv (random value) OTEL equivalent from the TraceStateString
44+
/// </summary>
45+
public double? SampleRand =>
46+
// OTel keeps a trace when rv ≥ th; Sentry keeps it when sample_rand < sample_rate.
47+
// Mapping sample_rand = 1 − rv makes the decisions equivalent: 1 − rv < 1 − th ↔ rv > th
48+
GetOtelTraceStateValue("rv") is { } rv && ParseOtelHexFraction(rv) is { } v ? 1.0 - v : null;
4249

43-
// Parses a sub-key value from the OTel vendor entry in the W3C tracestate string.
44-
// The tracestate format is comma-separated vendor entries (e.g. "ot=th:8;rv:a0b1c2d3e4f5a0,other=x").
45-
// The OTel entry uses semicolon-separated sub-keys with colon-delimited values (e.g. "th:8;rv:...").
50+
/// <summary>
51+
/// <para>
52+
/// Parses a sub-key value from the OTel vendor entry in the W3C tracestate string.
53+
/// The tracestate format is comma-separated vendor entries (e.g. "ot=th:8;rv:a0b1c2d3e4f5a0,other=x").
54+
/// The OTel entry uses semicolon-separated sub-keys with colon-delimited values (e.g. "th:8;rv:...").
55+
/// </para>
56+
/// <para>
57+
/// See https://opentelemetry.io/docs/specs/otel/trace/tracestate-handling/
58+
/// </para>
59+
/// </summary>
4660
private static string? GetOtelTraceStateValue(string subKey)
4761
{
4862
var traceState = Activity.Current?.TraceStateString;
@@ -68,8 +82,10 @@ public SpanId? ParentSpanId
6882
return null;
6983
}
7084

71-
// Converts an OTel 56-bit hex fraction to a double in [0, 1).
72-
// The value is encoded as up to 14 lowercase hex digits with trailing zeros omitted, so "8" means 0.5.
85+
/// <summary>
86+
/// Converts an OTel 56-bit hex fraction to a double in [0, 1).
87+
/// The value is encoded as up to 14 lowercase hex digits with trailing zeros omitted, so "8" means 0.5.
88+
/// </summary>
7389
private static double? ParseOtelHexFraction(string hexValue)
7490
{
7591
if (hexValue.Length == 0 || hexValue.Length > 14)

test/Sentry.OpenTelemetry.Exporter.Tests/OtelPropagationContextTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ public void SampleRate_ActivityWithOtEntryButNoThKey_ReturnsNull()
210210
}
211211

212212
[Theory]
213-
[InlineData("8", 0.5)] // "8" -> 0x80000000000000 / 2^56 = 0.5
214-
[InlineData("4", 0.25)] // "4" -> 0x40000000000000 / 2^56 = 0.25
215-
[InlineData("0", 0.0)] // threshold 0 = never sample
216-
[InlineData("ffffffffffffff", 1.0 - 1.0 / (1UL << 56))] // max 56-bit value
213+
[InlineData("8", 0.5)] // th=0.5 rejection → 50% sample rate
214+
[InlineData("4", 0.75)] // th=0.25 rejection → 75% sample rate
215+
[InlineData("0", 1.0)] // th=0 (no rejection) → 100% sample rate
216+
[InlineData("ffffffffffffff", 1.0 / (1UL << 56))] // max rejection → nearly 0% sample rate
217217
public void SampleRate_ActivityWithThValue_ReturnsParsedRate(string th, double expected)
218218
{
219219
using var activity = new Activity("test").Start();
@@ -264,22 +264,22 @@ public void SampleRand_ActivityWithOtEntryButNoRvKey_ReturnsNull()
264264
[Fact]
265265
public void SampleRand_ActivityWithRvValue_ReturnsParsedValue()
266266
{
267-
// "a" -> 0xa0000000000000 / 2^56 = 0.625
267+
// "a" -> 0xa0000000000000 / 2^56 = 0.625; inverted → 0.375
268268
using var activity = new Activity("test").Start();
269269
activity.TraceStateString = "ot=rv:a";
270270
var sut = new OtelPropagationContext();
271271

272-
sut.SampleRand.Should().BeApproximately(0.625, 1e-15);
272+
sut.SampleRand.Should().BeApproximately(0.375, 1e-15);
273273
}
274274

275275
[Fact]
276276
public void SampleRand_ActivityWithBothThAndRv_ReturnsRvValue()
277277
{
278-
// "4" -> 0x40000000000000 / 2^56 = 0.25
278+
// "4" -> 0x40000000000000 / 2^56 = 0.25; inverted → 0.75
279279
using var activity = new Activity("test").Start();
280280
activity.TraceStateString = "ot=th:8;rv:4";
281281
var sut = new OtelPropagationContext();
282282

283-
sut.SampleRand.Should().BeApproximately(0.25, 1e-15);
283+
sut.SampleRand.Should().BeApproximately(0.75, 1e-15);
284284
}
285285
}

0 commit comments

Comments
 (0)