Skip to content

Commit 38f3c24

Browse files
authored
Propagated Sampling Rates (#1793)
1 parent 792f349 commit 38f3c24

8 files changed

+163
-14
lines changed

src/State/Hub.php

+15-9
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ public function startTransaction(TransactionContext $context, array $customSampl
269269
$samplingContext->setAdditionalContext($customSamplingContext);
270270

271271
$sampleSource = 'context';
272+
$sampleRand = $context->getMetadata()->getSampleRand();
272273

273274
if ($transaction->getSampled() === null) {
274275
$tracesSampler = $options->getTracesSampler();
@@ -278,12 +279,17 @@ public function startTransaction(TransactionContext $context, array $customSampl
278279

279280
$sampleSource = 'config:traces_sampler';
280281
} else {
281-
$sampleRate = $this->getSampleRate(
282-
$samplingContext->getParentSampled(),
283-
$options->getTracesSampleRate() ?? 0
284-
);
285-
286-
$sampleSource = $samplingContext->getParentSampled() ? 'parent' : 'config:traces_sample_rate';
282+
$parentSampleRate = $context->getMetadata()->getParentSamplingRate();
283+
if ($parentSampleRate !== null) {
284+
$sampleRate = $parentSampleRate;
285+
$sampleSource = 'parent:sample_rate';
286+
} else {
287+
$sampleRate = $this->getSampleRate(
288+
$samplingContext->getParentSampled(),
289+
$options->getTracesSampleRate() ?? 0
290+
);
291+
$sampleSource = $samplingContext->getParentSampled() ? 'parent:sampling_decision' : 'config:traces_sample_rate';
292+
}
287293
}
288294

289295
if (!$this->isValidSampleRate($sampleRate)) {
@@ -304,7 +310,7 @@ public function startTransaction(TransactionContext $context, array $customSampl
304310
return $transaction;
305311
}
306312

307-
$transaction->setSampled($this->sample($sampleRate));
313+
$transaction->setSampled($sampleRand < $sampleRate);
308314
}
309315

310316
if (!$transaction->getSampled()) {
@@ -376,11 +382,11 @@ private function getStackTop(): Layer
376382
private function getSampleRate(?bool $hasParentBeenSampled, float $fallbackSampleRate): float
377383
{
378384
if ($hasParentBeenSampled === true) {
379-
return 1;
385+
return 1.0;
380386
}
381387

382388
if ($hasParentBeenSampled === false) {
383-
return 0;
389+
return 0.0;
384390
}
385391

386392
return $fallbackSampleRate;

src/Tracing/DynamicSamplingContext.php

+5
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ public static function fromTransaction(Transaction $transaction, HubInterface $h
186186
$samplingContext->set('sampled', $transaction->getSampled() ? 'true' : 'false');
187187
}
188188

189+
if ($transaction->getMetadata()->getSampleRand() !== null) {
190+
$samplingContext->set('sample_rand', (string) $transaction->getMetadata()->getSampleRand());
191+
}
192+
189193
$samplingContext->freeze();
190194

191195
return $samplingContext;
@@ -195,6 +199,7 @@ public static function fromOptions(Options $options, Scope $scope): self
195199
{
196200
$samplingContext = new self();
197201
$samplingContext->set('trace_id', (string) $scope->getPropagationContext()->getTraceId());
202+
$samplingContext->set('sample_rand', (string) $scope->getPropagationContext()->getSampleRand());
198203

199204
if ($options->getTracesSampleRate() !== null) {
200205
$samplingContext->set('sample_rate', (string) $options->getTracesSampleRate());

src/Tracing/PropagationContext.php

+53
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ final class PropagationContext
2828
*/
2929
private $parentSpanId;
3030

31+
/**
32+
* @var bool|null The parent's sampling decision
33+
*/
34+
private $parentSampled;
35+
36+
/**
37+
* @var float|null
38+
*/
39+
private $sampleRand;
40+
3141
/**
3242
* @var DynamicSamplingContext|null The dynamic sampling context
3343
*/
@@ -44,6 +54,8 @@ public static function fromDefaults(): self
4454
$context->traceId = TraceId::generate();
4555
$context->spanId = SpanId::generate();
4656
$context->parentSpanId = null;
57+
$context->parentSampled = null;
58+
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
4759
$context->dynamicSamplingContext = null;
4860

4961
return $context;
@@ -159,6 +171,19 @@ public function setDynamicSamplingContext(DynamicSamplingContext $dynamicSamplin
159171
return $this;
160172
}
161173

174+
public function getSampleRand(): ?float
175+
{
176+
return $this->sampleRand;
177+
}
178+
179+
public function setSampleRand(?float $sampleRand): self
180+
{
181+
$this->sampleRand = $sampleRand;
182+
183+
return $this;
184+
}
185+
186+
// TODO add same logic as in TransactionContext
162187
private static function parseTraceparentAndBaggage(string $traceparent, string $baggage): self
163188
{
164189
$context = self::fromDefaults();
@@ -174,6 +199,11 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
174199
$context->parentSpanId = new SpanId($matches['span_id']);
175200
$hasSentryTrace = true;
176201
}
202+
203+
if (isset($matches['sampled'])) {
204+
$context->parentSampled = $matches['sampled'] === '1';
205+
$hasSentryTrace = true;
206+
}
177207
} elseif (preg_match(self::W3C_TRACEPARENT_HEADER_REGEX, $traceparent, $matches)) {
178208
if (!empty($matches['trace_id'])) {
179209
$context->traceId = new TraceId($matches['trace_id']);
@@ -184,6 +214,11 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
184214
$context->parentSpanId = new SpanId($matches['span_id']);
185215
$hasSentryTrace = true;
186216
}
217+
218+
if (isset($matches['sampled'])) {
219+
$context->parentSampled = $matches['sampled'] === '01';
220+
$hasSentryTrace = true;
221+
}
187222
}
188223

189224
$samplingContext = DynamicSamplingContext::fromHeader($baggage);
@@ -201,6 +236,24 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
201236
$context->dynamicSamplingContext = $samplingContext;
202237
}
203238

239+
// Store the propagated trace sample rand or generate a new one
240+
if ($samplingContext->has('sample_rand')) {
241+
$context->sampleRand = (float) $samplingContext->get('sample_rand');
242+
} else {
243+
if ($samplingContext->has('sample_rate') && $context->parentSampled !== null) {
244+
if ($context->parentSampled === true) {
245+
// [0, rate)
246+
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (float) $samplingContext->get('sample_rate'), 6);
247+
} else {
248+
// [rate, 1)
249+
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (1 - (float) $samplingContext->get('sample_rate')) + (float) $samplingContext->get('sample-rate'), 6);
250+
}
251+
} elseif ($context->parentSampled !== null) {
252+
// [0, 1)
253+
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
254+
}
255+
}
256+
204257
return $context;
205258
}
206259
}

src/Tracing/SamplingContext.php

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ final class SamplingContext
1616
*/
1717
private $parentSampled;
1818

19+
/**
20+
* @var float|null The parent sample rate
21+
*/
22+
private $sampleRand;
23+
1924
/**
2025
* @var array<string, mixed>|null Additional context, depending on where the SDK runs
2126
*/
@@ -29,6 +34,7 @@ public static function getDefault(TransactionContext $transactionContext): self
2934
$context = new self();
3035
$context->transactionContext = $transactionContext;
3136
$context->parentSampled = $transactionContext->getParentSampled();
37+
$context->sampleRand = $transactionContext->getMetadata()->getSampleRand();
3238

3339
return $context;
3440
}
@@ -46,6 +52,11 @@ public function getParentSampled(): ?bool
4652
return $this->parentSampled;
4753
}
4854

55+
public function getSampleRand(): ?float
56+
{
57+
return $this->sampleRand;
58+
}
59+
4960
/**
5061
* Sets the sampling decision from the parent transaction, if any.
5162
*/

src/Tracing/TransactionContext.php

+23
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,29 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag
198198
$context->getMetadata()->setDynamicSamplingContext($samplingContext);
199199
}
200200

201+
// Store the propagated traces sample rate
202+
if ($samplingContext->has('sample_rate')) {
203+
$context->getMetadata()->setParentSamplingRate((float) $samplingContext->get('sample_rate'));
204+
}
205+
206+
// Store the propagated trace sample rand or generate a new one
207+
if ($samplingContext->has('sample_rand')) {
208+
$context->getMetadata()->setSampleRand((float) $samplingContext->get('sample_rand'));
209+
} else {
210+
if ($samplingContext->has('sample_rate') && $context->parentSampled !== null) {
211+
if ($context->parentSampled === true) {
212+
// [0, rate)
213+
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (float) $samplingContext->get('sample_rate'), 6));
214+
} else {
215+
// [rate, 1)
216+
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (1 - (float) $samplingContext->get('sample_rate')) + (float) $samplingContext->get('sample-rate'), 6));
217+
}
218+
} elseif ($context->parentSampled !== null) {
219+
// [0, 1)
220+
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6));
221+
}
222+
}
223+
201224
return $context;
202225
}
203226
}

src/Tracing/TransactionMetadata.php

+41-1
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,37 @@ final class TransactionMetadata
2121
*/
2222
private $source;
2323

24+
/**
25+
* @var float|int|null
26+
*/
27+
private $parentSamplingRate;
28+
29+
/**
30+
* @var float|int|null
31+
*/
32+
private $sampleRand;
33+
2434
/**
2535
* Constructor.
2636
*
2737
* @param float|int|null $samplingRate The sampling rate
2838
* @param DynamicSamplingContext|null $dynamicSamplingContext The Dynamic Sampling Context
2939
* @param TransactionSource|null $source The transaction source
40+
* @param float|null $parentSamplingRate The parent sampling rate
41+
* @param float|null $sampleRand The trace sample rand
3042
*/
3143
public function __construct(
3244
$samplingRate = null,
3345
?DynamicSamplingContext $dynamicSamplingContext = null,
34-
?TransactionSource $source = null
46+
?TransactionSource $source = null,
47+
?float $parentSamplingRate = null,
48+
?float $sampleRand = null
3549
) {
3650
$this->samplingRate = $samplingRate;
3751
$this->dynamicSamplingContext = $dynamicSamplingContext;
3852
$this->source = $source ?? TransactionSource::custom();
53+
$this->parentSamplingRate = $parentSamplingRate;
54+
$this->sampleRand = $sampleRand ?? round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
3955
}
4056

4157
/**
@@ -56,6 +72,30 @@ public function setSamplingRate($samplingRate): self
5672
return $this;
5773
}
5874

75+
public function getParentSamplingRate(): ?float
76+
{
77+
return $this->parentSamplingRate;
78+
}
79+
80+
public function setParentSamplingRate(?float $parentSamplingRate): self
81+
{
82+
$this->parentSamplingRate = $parentSamplingRate;
83+
84+
return $this;
85+
}
86+
87+
public function getSampleRand(): ?float
88+
{
89+
return $this->sampleRand;
90+
}
91+
92+
public function setSampleRand(?float $sampleRand): self
93+
{
94+
$this->sampleRand = $sampleRand;
95+
96+
return $this;
97+
}
98+
5999
public function getDynamicSamplingContext(): ?DynamicSamplingContext
60100
{
61101
return $this->dynamicSamplingContext;

tests/FunctionsTest.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ public function testBaggageWithTracingDisabled(): void
503503
{
504504
$propagationContext = PropagationContext::fromDefaults();
505505
$propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
506+
$propagationContext->setSampleRand(0.25);
506507

507508
$scope = new Scope($propagationContext);
508509

@@ -520,7 +521,7 @@ public function testBaggageWithTracingDisabled(): void
520521

521522
$baggage = getBaggage();
522523

523-
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-release=1.0.0,sentry-environment=development', $baggage);
524+
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rand=0.25,sentry-release=1.0.0,sentry-environment=development', $baggage);
524525
}
525526

526527
public function testBaggageWithTracingEnabled(): void
@@ -541,6 +542,7 @@ public function testBaggageWithTracingEnabled(): void
541542
$transactionContext = new TransactionContext();
542543
$transactionContext->setName('Test');
543544
$transactionContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
545+
$transactionContext->getMetadata()->setSampleRand(0.25);
544546

545547
$transaction = startTransaction($transactionContext);
546548

@@ -552,7 +554,7 @@ public function testBaggageWithTracingEnabled(): void
552554

553555
$baggage = getBaggage();
554556

555-
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1,sentry-transaction=Test,sentry-release=1.0.0,sentry-environment=development,sentry-sampled=true', $baggage);
557+
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1,sentry-transaction=Test,sentry-release=1.0.0,sentry-environment=development,sentry-sampled=true,sentry-sample_rand=0.25', $baggage);
556558
}
557559

558560
public function testContinueTrace(): void

0 commit comments

Comments
 (0)