Skip to content

Commit f56fdd4

Browse files
committed
Perform sampling based on trace
1 parent a688ca6 commit f56fdd4

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

sentry_sdk/metrics.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
"""
55

66
import time
7-
from typing import Any, Optional, TYPE_CHECKING, Union
7+
from typing import TYPE_CHECKING
88

99
import sentry_sdk
1010
from sentry_sdk.utils import safe_repr
11+
from sentry_sdk.tracing_utils import _generate_sample_rand
1112

1213
if TYPE_CHECKING:
14+
from typing import Any, Optional, Union
1315
from sentry_sdk._types import Metric, MetricType
1416

1517

@@ -48,6 +50,24 @@ def _capture_metric(
4850
)
4951
return
5052

53+
trace_id = None
54+
scope = sentry_sdk.get_current_scope()
55+
if scope.span is not None:
56+
trace_id = scope.span.trace_id
57+
elif scope._propagation_context is not None:
58+
trace_id = scope._propagation_context.trace_id
59+
60+
if trace_id is not None and sample_rate < 1.0:
61+
sample_rand = _generate_sample_rand(trace_id)
62+
if sample_rand >= sample_rate:
63+
if client.transport is not None:
64+
client.transport.record_lost_event(
65+
"sample_rate",
66+
data_category="trace_metric",
67+
quantity=1,
68+
)
69+
return
70+
5171
if sample_rate != 1.0:
5272
attrs["sentry.client_sample_rate"] = sample_rate
5373

tests/test_metrics.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import sys
33
from typing import List, Any, Mapping
4+
from unittest import mock
45
import pytest
56

67
import sentry_sdk
@@ -337,5 +338,43 @@ def test_metrics_no_sample_rate(sentry_init, capture_envelopes):
337338

338339
assert len(metrics) == 1
339340

340-
# Should not have sample_rate attribute when not provided
341341
assert "sentry.client_sample_rate" not in metrics[0]["attributes"]
342+
343+
344+
@pytest.mark.parametrize("sample_rand", (0.0, 0.25, 0.5, 0.75))
345+
@pytest.mark.parametrize("sample_rate", (0.0, 0.25, 0.5, 0.75, 1.0))
346+
def test_metrics_sampling_decision(
347+
sentry_init, capture_envelopes, sample_rate, sample_rand, monkeypatch
348+
):
349+
sentry_init(traces_sample_rate=1.0)
350+
envelopes = capture_envelopes()
351+
client = sentry_sdk.get_client()
352+
353+
lost_event_calls = []
354+
355+
def record_lost_event(reason, data_category, quantity):
356+
lost_event_calls.append((reason, data_category, quantity))
357+
358+
monkeypatch.setattr(client.transport, "record_lost_event", record_lost_event)
359+
360+
with mock.patch(
361+
"sentry_sdk.tracing_utils.Random.randrange",
362+
return_value=int(sample_rand * 1000000),
363+
):
364+
with sentry_sdk.start_transaction() as transaction:
365+
sentry_sdk.metrics.count("test.counter", 1, sample_rate=sample_rate)
366+
367+
get_client().flush()
368+
metrics = envelopes_to_metrics(envelopes)
369+
370+
should_be_sampled = sample_rand < sample_rate and sample_rate > 0.0
371+
assert len(metrics) == int(should_be_sampled)
372+
373+
if sample_rate <= 0.0:
374+
assert len(lost_event_calls) == 1
375+
assert lost_event_calls[0] == ("invalid_sample_rate", "trace_metric", 1)
376+
elif not should_be_sampled:
377+
assert len(lost_event_calls) == 1
378+
assert lost_event_calls[0] == ("sample_rate", "trace_metric", 1)
379+
else:
380+
assert len(lost_event_calls) == 0

0 commit comments

Comments
 (0)