Skip to content

Commit 7a3b0e5

Browse files
authored
feat(measurements): Add experimental set_measurement api on transaction (#1359)
1 parent e73b417 commit 7a3b0e5

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

sentry_sdk/_types.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,34 @@
4848
]
4949
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
5050
EndpointType = Literal["store", "envelope"]
51+
52+
DurationUnit = Literal[
53+
"nanosecond",
54+
"microsecond",
55+
"millisecond",
56+
"second",
57+
"minute",
58+
"hour",
59+
"day",
60+
"week",
61+
]
62+
63+
InformationUnit = Literal[
64+
"bit",
65+
"byte",
66+
"kilobyte",
67+
"kibibyte",
68+
"megabyte",
69+
"mebibyte",
70+
"gigabyte",
71+
"gibibyte",
72+
"terabyte",
73+
"tebibyte",
74+
"petabyte",
75+
"pebibyte",
76+
"exabyte",
77+
"exbibyte",
78+
]
79+
80+
FractionUnit = Literal["ratio", "percent"]
81+
MeasurementUnit = Union[DurationUnit, InformationUnit, FractionUnit, str]

sentry_sdk/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"record_sql_params": Optional[bool],
3434
"smart_transaction_trimming": Optional[bool],
3535
"propagate_tracestate": Optional[bool],
36+
"custom_measurements": Optional[bool],
3637
},
3738
total=False,
3839
)

sentry_sdk/tracing.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from typing import Tuple
2121
from typing import Iterator
2222

23-
from sentry_sdk._types import SamplingContext
23+
from sentry_sdk._types import SamplingContext, MeasurementUnit
2424

2525

2626
class _SpanRecorder(object):
@@ -487,6 +487,7 @@ class Transaction(Span):
487487
"_sentry_tracestate",
488488
# tracestate data from other vendors, of the form `dogs=yes,cats=maybe`
489489
"_third_party_tracestate",
490+
"_measurements",
490491
)
491492

492493
def __init__(
@@ -515,6 +516,7 @@ def __init__(
515516
# first time an event needs it for inclusion in the captured data
516517
self._sentry_tracestate = sentry_tracestate
517518
self._third_party_tracestate = third_party_tracestate
519+
self._measurements = {} # type: Dict[str, Any]
518520

519521
def __repr__(self):
520522
# type: () -> str
@@ -594,17 +596,30 @@ def finish(self, hub=None):
594596
# to be garbage collected
595597
self._span_recorder = None
596598

597-
return hub.capture_event(
598-
{
599-
"type": "transaction",
600-
"transaction": self.name,
601-
"contexts": {"trace": self.get_trace_context()},
602-
"tags": self._tags,
603-
"timestamp": self.timestamp,
604-
"start_timestamp": self.start_timestamp,
605-
"spans": finished_spans,
606-
}
607-
)
599+
event = {
600+
"type": "transaction",
601+
"transaction": self.name,
602+
"contexts": {"trace": self.get_trace_context()},
603+
"tags": self._tags,
604+
"timestamp": self.timestamp,
605+
"start_timestamp": self.start_timestamp,
606+
"spans": finished_spans,
607+
}
608+
609+
if has_custom_measurements_enabled():
610+
event["measurements"] = self._measurements
611+
612+
return hub.capture_event(event)
613+
614+
def set_measurement(self, name, value, unit=""):
615+
# type: (str, float, MeasurementUnit) -> None
616+
if not has_custom_measurements_enabled():
617+
logger.debug(
618+
"[Tracing] Experimental custom_measurements feature is disabled"
619+
)
620+
return
621+
622+
self._measurements[name] = {"value": value, "unit": unit}
608623

609624
def to_json(self):
610625
# type: () -> Dict[str, Any]
@@ -727,4 +742,5 @@ def _set_initial_sampling_decision(self, sampling_context):
727742
has_tracing_enabled,
728743
is_valid_sample_rate,
729744
maybe_create_breadcrumbs_from_span,
745+
has_custom_measurements_enabled,
730746
)

sentry_sdk/tracing_utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,13 @@ def has_tracestate_enabled(span=None):
406406
return bool(options and options["_experiments"].get("propagate_tracestate"))
407407

408408

409+
def has_custom_measurements_enabled():
410+
# type: () -> bool
411+
client = sentry_sdk.Hub.current.client
412+
options = client and client.options
413+
return bool(options and options["_experiments"].get("custom_measurements"))
414+
415+
409416
# Circular imports
410417

411418
if MYPY:

tests/tracing/test_misc.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,31 @@ def test_has_tracestate_enabled(sentry_init, tracestate_enabled):
246246
assert has_tracestate_enabled() is True
247247
else:
248248
assert has_tracestate_enabled() is False
249+
250+
251+
def test_set_meaurement(sentry_init, capture_events):
252+
sentry_init(traces_sample_rate=1.0, _experiments={"custom_measurements": True})
253+
254+
events = capture_events()
255+
256+
transaction = start_transaction(name="measuring stuff")
257+
258+
with pytest.raises(TypeError):
259+
transaction.set_measurement()
260+
261+
with pytest.raises(TypeError):
262+
transaction.set_measurement("metric.foo")
263+
264+
transaction.set_measurement("metric.foo", 123)
265+
transaction.set_measurement("metric.bar", 456, unit="second")
266+
transaction.set_measurement("metric.baz", 420.69, unit="custom")
267+
transaction.set_measurement("metric.foobar", 12, unit="percent")
268+
transaction.set_measurement("metric.foobar", 17.99, unit="percent")
269+
270+
transaction.finish()
271+
272+
(event,) = events
273+
assert event["measurements"]["metric.foo"] == {"value": 123, "unit": ""}
274+
assert event["measurements"]["metric.bar"] == {"value": 456, "unit": "second"}
275+
assert event["measurements"]["metric.baz"] == {"value": 420.69, "unit": "custom"}
276+
assert event["measurements"]["metric.foobar"] == {"value": 17.99, "unit": "percent"}

0 commit comments

Comments
 (0)