Skip to content

Commit

Permalink
feat(profiling): Introduce continuous profiling mode (#2830)
Browse files Browse the repository at this point in the history
This is a new profiling mode that is mutually exclusive from the existing
profiling modes. In the current profiling modes, a profile is always directly
attached to a transaction. This new mode will continuously emit chunks of
profiling data that will be connected to the span data.
  • Loading branch information
Zylphrex committed Jun 11, 2024
1 parent 1a6a66e commit 852cdc7
Show file tree
Hide file tree
Showing 19 changed files with 1,145 additions and 234 deletions.
2 changes: 1 addition & 1 deletion docs/apidocs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ API Docs
.. autoclass:: sentry_sdk.tracing.Span
:members:

.. autoclass:: sentry_sdk.profiler.Profile
.. autoclass:: sentry_sdk.profiler.transaction_profiler.Profile
:members:

.. autoclass:: sentry_sdk.session.Session
Expand Down
4 changes: 3 additions & 1 deletion sentry_sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,14 @@
"session",
"internal",
"profile",
"profile_chunk",
"metric_bucket",
"monitor",
]
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]

ProfilerMode = Literal["sleep", "thread", "gevent", "unknown"]
ContinuousProfilerMode = Literal["thread", "gevent", "unknown"]
ProfilerMode = Union[ContinuousProfilerMode, Literal["sleep"]]

# Type of the metric.
MetricType = Literal["d", "s", "g", "c"]
Expand Down
15 changes: 14 additions & 1 deletion sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
from sentry_sdk.utils import ContextVar
from sentry_sdk.sessions import SessionFlusher
from sentry_sdk.envelope import Envelope
from sentry_sdk.profiler import has_profiling_enabled, Profile, setup_profiler
from sentry_sdk.profiler.continuous_profiler import setup_continuous_profiler
from sentry_sdk.profiler.transaction_profiler import (
has_profiling_enabled,
Profile,
setup_profiler,
)
from sentry_sdk.scrubber import EventScrubber
from sentry_sdk.monitor import Monitor
from sentry_sdk.spotlight import setup_spotlight
Expand Down Expand Up @@ -378,6 +383,14 @@ def _capture_envelope(envelope):
setup_profiler(self.options)
except Exception as e:
logger.debug("Can not set up profiler. (%s)", e)
else:
try:
setup_continuous_profiler(
self.options,
capture_func=_capture_envelope,
)
except Exception as e:
logger.debug("Can not set up continuous profiler. (%s)", e)

finally:
_client_init_debug.set(old_debug)
Expand Down
9 changes: 9 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class EndpointType(Enum):

from sentry_sdk._types import (
BreadcrumbProcessor,
ContinuousProfilerMode,
Event,
EventProcessor,
Hint,
Expand All @@ -55,6 +56,8 @@ class EndpointType(Enum):
"attach_explain_plans": dict[str, Any],
"max_spans": Optional[int],
"record_sql_params": Optional[bool],
"continuous_profiling_auto_start": Optional[bool],
"continuous_profiling_mode": Optional[ContinuousProfilerMode],
"otel_powered_performance": Optional[bool],
"transport_zlib_compression_level": Optional[int],
"transport_num_pools": Optional[int],
Expand Down Expand Up @@ -364,6 +367,12 @@ class SPANDATA:
Example: "MainThread"
"""

PROFILER_ID = "profiler.id"
"""
Label identifying the profiler id that the span occurred in. This should be a string.
Example: "5249fbada8d5416482c2f6e47e337372"
"""


class OP:
ANTHROPIC_MESSAGES_CREATE = "ai.messages.create.anthropic"
Expand Down
10 changes: 10 additions & 0 deletions sentry_sdk/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ def add_profile(
# type: (...) -> None
self.add_item(Item(payload=PayloadRef(json=profile), type="profile"))

def add_profile_chunk(
self, profile_chunk # type: Any
):
# type: (...) -> None
self.add_item(
Item(payload=PayloadRef(json=profile_chunk), type="profile_chunk")
)

def add_checkin(
self, checkin # type: Any
):
Expand Down Expand Up @@ -265,6 +273,8 @@ def data_category(self):
return "internal"
elif ty == "profile":
return "profile"
elif ty == "profile_chunk":
return "profile_chunk"
elif ty == "statsd":
return "metric_bucket"
elif ty == "check_in":
Expand Down
41 changes: 41 additions & 0 deletions sentry_sdk/profiler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from sentry_sdk.profiler.continuous_profiler import start_profiler, stop_profiler
from sentry_sdk.profiler.transaction_profiler import (
MAX_PROFILE_DURATION_NS,
PROFILE_MINIMUM_SAMPLES,
Profile,
Scheduler,
ThreadScheduler,
GeventScheduler,
has_profiling_enabled,
setup_profiler,
teardown_profiler,
)
from sentry_sdk.profiler.utils import (
DEFAULT_SAMPLING_FREQUENCY,
MAX_STACK_DEPTH,
get_frame_name,
extract_frame,
extract_stack,
frame_id,
)

__all__ = [
"start_profiler",
"stop_profiler",
# Re-exported for backwards compatibility
"MAX_PROFILE_DURATION_NS",
"PROFILE_MINIMUM_SAMPLES",
"Profile",
"Scheduler",
"ThreadScheduler",
"GeventScheduler",
"has_profiling_enabled",
"setup_profiler",
"teardown_profiler",
"DEFAULT_SAMPLING_FREQUENCY",
"MAX_STACK_DEPTH",
"get_frame_name",
"extract_frame",
"extract_stack",
"frame_id",
]
Loading

0 comments on commit 852cdc7

Please sign in to comment.