Skip to content

Commit 0b489c6

Browse files
ref(profiling): Use the transaction timestamps to anchor the profile (#1898)
We want the profile to be as closely aligned with the transaction's timestamps as possible to make aligning the two visualizations as accurate as possible. Here we change the transaction's internal `_start_timestamp_monotonic` to contain an unit for each of the possible clocks we use in the various python versions. This allows us to use the `start_timestamp` of the transaction as the timestamp of the profile, and we can use the `_start_timestamp_monontonic` as the anchor for all the relative timestamps in the profile. Co-authored-by: Neel Shah <[email protected]>
1 parent bb20fc6 commit 0b489c6

File tree

3 files changed

+15
-15
lines changed

3 files changed

+15
-15
lines changed

sentry_sdk/profiler.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,11 @@ def __init__(
426426
self._default_active_thread_id = get_current_thread_id() or 0 # type: int
427427
self.active_thread_id = None # type: Optional[int]
428428

429-
self.start_ns = 0 # type: int
429+
try:
430+
self.start_ns = transaction._start_timestamp_monotonic_ns # type: int
431+
except AttributeError:
432+
self.start_ns = 0
433+
430434
self.stop_ns = 0 # type: int
431435
self.active = False # type: bool
432436

@@ -524,7 +528,8 @@ def start(self):
524528
assert self.scheduler, "No scheduler specified"
525529
logger.debug("[Profiling] Starting profile")
526530
self.active = True
527-
self.start_ns = nanosecond_time()
531+
if not self.start_ns:
532+
self.start_ns = nanosecond_time()
528533
self.scheduler.start_profiling(self)
529534

530535
def stop(self):
@@ -643,7 +648,7 @@ def to_json(self, event_opt, options):
643648
"platform": "python",
644649
"profile": profile,
645650
"release": event_opt.get("release", ""),
646-
"timestamp": event_opt["timestamp"],
651+
"timestamp": event_opt["start_timestamp"],
647652
"version": "1",
648653
"device": {
649654
"architecture": platform.machine(),

sentry_sdk/tracing.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import uuid
22
import random
3-
import time
43

54
from datetime import datetime, timedelta
65

76
import sentry_sdk
87
from sentry_sdk.consts import INSTRUMENTER
9-
from sentry_sdk.utils import logger
8+
from sentry_sdk.utils import logger, nanosecond_time
109
from sentry_sdk._types import MYPY
1110

1211

@@ -87,7 +86,7 @@ class Span(object):
8786
"op",
8887
"description",
8988
"start_timestamp",
90-
"_start_timestamp_monotonic",
89+
"_start_timestamp_monotonic_ns",
9190
"status",
9291
"timestamp",
9392
"_tags",
@@ -142,11 +141,9 @@ def __init__(
142141
self._containing_transaction = containing_transaction
143142
self.start_timestamp = start_timestamp or datetime.utcnow()
144143
try:
145-
# TODO: For Python 3.7+, we could use a clock with ns resolution:
146-
# self._start_timestamp_monotonic = time.perf_counter_ns()
147-
148-
# Python 3.3+
149-
self._start_timestamp_monotonic = time.perf_counter()
144+
# profiling depends on this value and requires that
145+
# it is measured in nanoseconds
146+
self._start_timestamp_monotonic_ns = nanosecond_time()
150147
except AttributeError:
151148
pass
152149

@@ -483,9 +480,9 @@ def finish(self, hub=None, end_timestamp=None):
483480
if end_timestamp:
484481
self.timestamp = end_timestamp
485482
else:
486-
duration_seconds = time.perf_counter() - self._start_timestamp_monotonic
483+
elapsed = nanosecond_time() - self._start_timestamp_monotonic_ns
487484
self.timestamp = self.start_timestamp + timedelta(
488-
seconds=duration_seconds
485+
microseconds=elapsed / 1000
489486
)
490487
except AttributeError:
491488
self.timestamp = datetime.utcnow()

sentry_sdk/utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,12 +1173,10 @@ def nanosecond_time():
11731173

11741174
def nanosecond_time():
11751175
# type: () -> int
1176-
11771176
return int(time.perf_counter() * 1e9)
11781177

11791178
else:
11801179

11811180
def nanosecond_time():
11821181
# type: () -> int
1183-
11841182
raise AttributeError

0 commit comments

Comments
 (0)