Skip to content

Commit

Permalink
feat(measurements): Add experimental set_measurement api on transacti…
Browse files Browse the repository at this point in the history
…on (#1359)
  • Loading branch information
sl0thentr0py committed May 6, 2022
1 parent e73b417 commit 7a3b0e5
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 12 deletions.
31 changes: 31 additions & 0 deletions sentry_sdk/_types.py
Expand Up @@ -48,3 +48,34 @@
]
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
EndpointType = Literal["store", "envelope"]

DurationUnit = Literal[
"nanosecond",
"microsecond",
"millisecond",
"second",
"minute",
"hour",
"day",
"week",
]

InformationUnit = Literal[
"bit",
"byte",
"kilobyte",
"kibibyte",
"megabyte",
"mebibyte",
"gigabyte",
"gibibyte",
"terabyte",
"tebibyte",
"petabyte",
"pebibyte",
"exabyte",
"exbibyte",
]

FractionUnit = Literal["ratio", "percent"]
MeasurementUnit = Union[DurationUnit, InformationUnit, FractionUnit, str]
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Expand Up @@ -33,6 +33,7 @@
"record_sql_params": Optional[bool],
"smart_transaction_trimming": Optional[bool],
"propagate_tracestate": Optional[bool],
"custom_measurements": Optional[bool],
},
total=False,
)
Expand Down
40 changes: 28 additions & 12 deletions sentry_sdk/tracing.py
Expand Up @@ -20,7 +20,7 @@
from typing import Tuple
from typing import Iterator

from sentry_sdk._types import SamplingContext
from sentry_sdk._types import SamplingContext, MeasurementUnit


class _SpanRecorder(object):
Expand Down Expand Up @@ -487,6 +487,7 @@ class Transaction(Span):
"_sentry_tracestate",
# tracestate data from other vendors, of the form `dogs=yes,cats=maybe`
"_third_party_tracestate",
"_measurements",
)

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

def __repr__(self):
# type: () -> str
Expand Down Expand Up @@ -594,17 +596,30 @@ def finish(self, hub=None):
# to be garbage collected
self._span_recorder = None

return hub.capture_event(
{
"type": "transaction",
"transaction": self.name,
"contexts": {"trace": self.get_trace_context()},
"tags": self._tags,
"timestamp": self.timestamp,
"start_timestamp": self.start_timestamp,
"spans": finished_spans,
}
)
event = {
"type": "transaction",
"transaction": self.name,
"contexts": {"trace": self.get_trace_context()},
"tags": self._tags,
"timestamp": self.timestamp,
"start_timestamp": self.start_timestamp,
"spans": finished_spans,
}

if has_custom_measurements_enabled():
event["measurements"] = self._measurements

return hub.capture_event(event)

def set_measurement(self, name, value, unit=""):
# type: (str, float, MeasurementUnit) -> None
if not has_custom_measurements_enabled():
logger.debug(
"[Tracing] Experimental custom_measurements feature is disabled"
)
return

self._measurements[name] = {"value": value, "unit": unit}

def to_json(self):
# type: () -> Dict[str, Any]
Expand Down Expand Up @@ -727,4 +742,5 @@ def _set_initial_sampling_decision(self, sampling_context):
has_tracing_enabled,
is_valid_sample_rate,
maybe_create_breadcrumbs_from_span,
has_custom_measurements_enabled,
)
7 changes: 7 additions & 0 deletions sentry_sdk/tracing_utils.py
Expand Up @@ -406,6 +406,13 @@ def has_tracestate_enabled(span=None):
return bool(options and options["_experiments"].get("propagate_tracestate"))


def has_custom_measurements_enabled():
# type: () -> bool
client = sentry_sdk.Hub.current.client
options = client and client.options
return bool(options and options["_experiments"].get("custom_measurements"))


# Circular imports

if MYPY:
Expand Down
28 changes: 28 additions & 0 deletions tests/tracing/test_misc.py
Expand Up @@ -246,3 +246,31 @@ def test_has_tracestate_enabled(sentry_init, tracestate_enabled):
assert has_tracestate_enabled() is True
else:
assert has_tracestate_enabled() is False


def test_set_meaurement(sentry_init, capture_events):
sentry_init(traces_sample_rate=1.0, _experiments={"custom_measurements": True})

events = capture_events()

transaction = start_transaction(name="measuring stuff")

with pytest.raises(TypeError):
transaction.set_measurement()

with pytest.raises(TypeError):
transaction.set_measurement("metric.foo")

transaction.set_measurement("metric.foo", 123)
transaction.set_measurement("metric.bar", 456, unit="second")
transaction.set_measurement("metric.baz", 420.69, unit="custom")
transaction.set_measurement("metric.foobar", 12, unit="percent")
transaction.set_measurement("metric.foobar", 17.99, unit="percent")

transaction.finish()

(event,) = events
assert event["measurements"]["metric.foo"] == {"value": 123, "unit": ""}
assert event["measurements"]["metric.bar"] == {"value": 456, "unit": "second"}
assert event["measurements"]["metric.baz"] == {"value": 420.69, "unit": "custom"}
assert event["measurements"]["metric.foobar"] == {"value": 17.99, "unit": "percent"}

0 comments on commit 7a3b0e5

Please sign in to comment.