|
15 | 15 |
|
16 | 16 | import sentry_sdk |
17 | 17 | from sentry_sdk.consts import SPANDATA |
| 18 | +from sentry_sdk.profiler.continuous_profiler import ( |
| 19 | + get_profiler_id, |
| 20 | + try_autostart_continuous_profiler, |
| 21 | + try_profile_lifecycle_trace_start, |
| 22 | +) |
18 | 23 | from sentry_sdk.tracing_utils import Baggage |
19 | 24 | from sentry_sdk.utils import ( |
20 | 25 | capture_internal_exceptions, |
|
28 | 33 | if TYPE_CHECKING: |
29 | 34 | from typing import Any, Callable, Iterator, Optional, ParamSpec, TypeVar, Union |
30 | 35 | from sentry_sdk._types import Attributes, AttributeValue |
| 36 | + from sentry_sdk.profiler.continuous_profiler import ContinuousProfile |
31 | 37 |
|
32 | 38 | P = ParamSpec("P") |
33 | 39 | R = TypeVar("R") |
@@ -232,6 +238,7 @@ class StreamedSpan: |
232 | 238 | "_baggage", |
233 | 239 | "_sample_rand", |
234 | 240 | "_sample_rate", |
| 241 | + "_continuous_profile", |
235 | 242 | ) |
236 | 243 |
|
237 | 244 | def __init__( |
@@ -284,6 +291,10 @@ def __init__( |
284 | 291 |
|
285 | 292 | self._update_active_thread() |
286 | 293 |
|
| 294 | + self._continuous_profile: "Optional[ContinuousProfile]" = None |
| 295 | + self._start_profile() |
| 296 | + self._set_profile_id(get_profiler_id()) |
| 297 | + |
287 | 298 | self._start() |
288 | 299 |
|
289 | 300 | def __repr__(self) -> str: |
@@ -340,6 +351,11 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None |
340 | 351 | # This span is already finished, ignore. |
341 | 352 | return |
342 | 353 |
|
| 354 | + # Stop the profiler |
| 355 | + if self._is_segment() and self._continuous_profile is not None: |
| 356 | + with capture_internal_exceptions(): |
| 357 | + self._continuous_profile.stop() |
| 358 | + |
343 | 359 | # Detach from scope |
344 | 360 | if self._active: |
345 | 361 | with capture_internal_exceptions(): |
@@ -528,6 +544,24 @@ def _get_trace_context(self) -> "dict[str, Any]": |
528 | 544 |
|
529 | 545 | return context |
530 | 546 |
|
| 547 | + def _set_profile_id(self, profiler_id: "Optional[str]") -> None: |
| 548 | + if profiler_id is not None: |
| 549 | + self.set_attribute("sentry.profiler_id", profiler_id) |
| 550 | + |
| 551 | + def _start_profile(self) -> None: |
| 552 | + if not self._is_segment(): |
| 553 | + return |
| 554 | + |
| 555 | + try_autostart_continuous_profiler() |
| 556 | + |
| 557 | + self._continuous_profile = try_profile_lifecycle_trace_start() |
| 558 | + |
| 559 | + # Typically, the profiler is set when the segment is created. But when |
| 560 | + # using the auto lifecycle, the profiler isn't running when the first |
| 561 | + # segment is started. So make sure we update the profiler id on it. |
| 562 | + if self._continuous_profile is not None: |
| 563 | + self._set_profile_id(get_profiler_id()) |
| 564 | + |
531 | 565 |
|
532 | 566 | class NoOpStreamedSpan(StreamedSpan): |
533 | 567 | __slots__ = ( |
|
0 commit comments