Skip to content

Commit 36c2650

Browse files
feat(metrics): Unify datetime format (#2409)
This somewhat unifies the APIs with regards to timestamps. The span system uses datetime objects, this now also permits these values in metrics and vice versa.* feat(metrics): Allow metrics emission for spans --------- Co-authored-by: Anton Pirker <[email protected]>
1 parent 76af9d2 commit 36c2650

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

sentry_sdk/metrics.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
import random
66
import time
77
import zlib
8+
from datetime import datetime
89
from functools import wraps, partial
910
from threading import Event, Lock, Thread
1011
from contextlib import contextmanager
1112

13+
import sentry_sdk
1214
from sentry_sdk._compat import text_type
13-
from sentry_sdk.hub import Hub
14-
from sentry_sdk.utils import now, nanosecond_time
15+
from sentry_sdk.utils import now, nanosecond_time, to_timestamp
1516
from sentry_sdk.envelope import Envelope, Item
1617
from sentry_sdk.tracing import (
1718
TRANSACTION_SOURCE_ROUTE,
@@ -29,6 +30,7 @@
2930
from typing import Optional
3031
from typing import Generator
3132
from typing import Tuple
33+
from typing import Union
3234

3335
from sentry_sdk._types import BucketKey
3436
from sentry_sdk._types import DurationUnit
@@ -406,14 +408,16 @@ def add(
406408
value, # type: MetricValue
407409
unit, # type: MeasurementUnit
408410
tags, # type: Optional[MetricTags]
409-
timestamp=None, # type: Optional[float]
411+
timestamp=None, # type: Optional[Union[float, datetime]]
410412
):
411413
# type: (...) -> None
412414
if not self._ensure_thread() or self._flusher is None:
413415
return
414416

415417
if timestamp is None:
416418
timestamp = time.time()
419+
elif isinstance(timestamp, datetime):
420+
timestamp = to_timestamp(timestamp)
417421

418422
bucket_timestamp = int(
419423
(timestamp // self.ROLLUP_IN_SECONDS) * self.ROLLUP_IN_SECONDS
@@ -500,7 +504,7 @@ def _serialize_tags(
500504
def _get_aggregator_and_update_tags(key, tags):
501505
# type: (str, Optional[MetricTags]) -> Tuple[Optional[MetricsAggregator], Optional[MetricTags]]
502506
"""Returns the current metrics aggregator if there is one."""
503-
hub = Hub.current
507+
hub = sentry_sdk.Hub.current
504508
client = hub.client
505509
if client is None or client.metrics_aggregator is None:
506510
return None, tags
@@ -531,7 +535,7 @@ def incr(
531535
value=1.0, # type: float
532536
unit="none", # type: MeasurementUnit
533537
tags=None, # type: Optional[MetricTags]
534-
timestamp=None, # type: Optional[float]
538+
timestamp=None, # type: Optional[Union[float, datetime]]
535539
):
536540
# type: (...) -> None
537541
"""Increments a counter."""
@@ -545,7 +549,7 @@ def __init__(
545549
self,
546550
key, # type: str
547551
tags, # type: Optional[MetricTags]
548-
timestamp, # type: Optional[float]
552+
timestamp, # type: Optional[Union[float, datetime]]
549553
value, # type: Optional[float]
550554
unit, # type: DurationUnit
551555
):
@@ -597,7 +601,7 @@ def timing(
597601
value=None, # type: Optional[float]
598602
unit="second", # type: DurationUnit
599603
tags=None, # type: Optional[MetricTags]
600-
timestamp=None, # type: Optional[float]
604+
timestamp=None, # type: Optional[Union[float, datetime]]
601605
):
602606
# type: (...) -> _Timing
603607
"""Emits a distribution with the time it takes to run the given code block.
@@ -620,7 +624,7 @@ def distribution(
620624
value, # type: float
621625
unit="none", # type: MeasurementUnit
622626
tags=None, # type: Optional[MetricTags]
623-
timestamp=None, # type: Optional[float]
627+
timestamp=None, # type: Optional[Union[float, datetime]]
624628
):
625629
# type: (...) -> None
626630
"""Emits a distribution."""
@@ -634,7 +638,7 @@ def set(
634638
value, # type: MetricValue
635639
unit="none", # type: MeasurementUnit
636640
tags=None, # type: Optional[MetricTags]
637-
timestamp=None, # type: Optional[float]
641+
timestamp=None, # type: Optional[Union[float, datetime]]
638642
):
639643
# type: (...) -> None
640644
"""Emits a set."""
@@ -648,7 +652,7 @@ def gauge(
648652
value, # type: float
649653
unit="none", # type: MetricValue
650654
tags=None, # type: Optional[MetricTags]
651-
timestamp=None, # type: Optional[float]
655+
timestamp=None, # type: Optional[Union[float, datetime]]
652656
):
653657
# type: (...) -> None
654658
"""Emits a gauge."""

sentry_sdk/tracing.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import uuid
22
import random
33

4-
from datetime import timedelta
4+
from datetime import datetime, timedelta
55

66
import sentry_sdk
77
from sentry_sdk.consts import INSTRUMENTER
@@ -14,13 +14,13 @@
1414
if TYPE_CHECKING:
1515
import typing
1616

17-
from datetime import datetime
1817
from typing import Any
1918
from typing import Dict
2019
from typing import Iterator
2120
from typing import List
2221
from typing import Optional
2322
from typing import Tuple
23+
from typing import Union
2424

2525
import sentry_sdk.profiler
2626
from sentry_sdk._types import Event, MeasurementUnit, SamplingContext
@@ -131,7 +131,7 @@ def __init__(
131131
status=None, # type: Optional[str]
132132
transaction=None, # type: Optional[str] # deprecated
133133
containing_transaction=None, # type: Optional[Transaction]
134-
start_timestamp=None, # type: Optional[datetime]
134+
start_timestamp=None, # type: Optional[Union[datetime, float]]
135135
):
136136
# type: (...) -> None
137137
self.trace_id = trace_id or uuid.uuid4().hex
@@ -146,7 +146,11 @@ def __init__(
146146
self._tags = {} # type: Dict[str, str]
147147
self._data = {} # type: Dict[str, Any]
148148
self._containing_transaction = containing_transaction
149-
self.start_timestamp = start_timestamp or datetime_utcnow()
149+
if start_timestamp is None:
150+
start_timestamp = datetime.utcnow()
151+
elif isinstance(start_timestamp, float):
152+
start_timestamp = datetime.utcfromtimestamp(start_timestamp)
153+
self.start_timestamp = start_timestamp
150154
try:
151155
# profiling depends on this value and requires that
152156
# it is measured in nanoseconds
@@ -439,7 +443,7 @@ def is_success(self):
439443
return self.status == "ok"
440444

441445
def finish(self, hub=None, end_timestamp=None):
442-
# type: (Optional[sentry_sdk.Hub], Optional[datetime]) -> Optional[str]
446+
# type: (Optional[sentry_sdk.Hub], Optional[Union[float, datetime]]) -> Optional[str]
443447
# Note: would be type: (Optional[sentry_sdk.Hub]) -> None, but that leads
444448
# to incompatible return types for Span.finish and Transaction.finish.
445449
"""Sets the end timestamp of the span.
@@ -463,6 +467,8 @@ def finish(self, hub=None, end_timestamp=None):
463467

464468
try:
465469
if end_timestamp:
470+
if isinstance(end_timestamp, float):
471+
end_timestamp = datetime.utcfromtimestamp(end_timestamp)
466472
self.timestamp = end_timestamp
467473
else:
468474
elapsed = nanosecond_time() - self._start_timestamp_monotonic_ns
@@ -627,7 +633,7 @@ def containing_transaction(self):
627633
return self
628634

629635
def finish(self, hub=None, end_timestamp=None):
630-
# type: (Optional[sentry_sdk.Hub], Optional[datetime]) -> Optional[str]
636+
# type: (Optional[sentry_sdk.Hub], Optional[Union[float, datetime]]) -> Optional[str]
631637
"""Finishes the transaction and sends it to Sentry.
632638
All finished spans in the transaction will also be sent to Sentry.
633639
@@ -935,7 +941,7 @@ def get_trace_context(self):
935941
return {}
936942

937943
def finish(self, hub=None, end_timestamp=None):
938-
# type: (Optional[sentry_sdk.Hub], Optional[datetime]) -> Optional[str]
944+
# type: (Optional[sentry_sdk.Hub], Optional[Union[float, datetime]]) -> Optional[str]
939945
pass
940946

941947
def set_measurement(self, name, value, unit=""):

0 commit comments

Comments
 (0)