|
23 | 23 | ) |
24 | 24 |
|
25 | 25 | if TYPE_CHECKING: |
26 | | - from typing import Any, Optional, Union |
| 26 | + from typing import Any, Callable, Optional, ParamSpec, TypeVar, Union |
27 | 27 | from sentry_sdk._types import Attributes, AttributeValue, SamplingContext |
28 | 28 | from sentry_sdk.scope import Scope |
29 | 29 |
|
| 30 | + P = ParamSpec("P") |
| 31 | + R = TypeVar("R") |
| 32 | + |
30 | 33 |
|
31 | 34 | FLAGS_CAPACITY = 10 |
32 | 35 |
|
| 36 | + |
| 37 | +class SpanStatus(str, Enum): |
| 38 | + OK = "ok" |
| 39 | + ERROR = "error" |
| 40 | + |
| 41 | + def __str__(self) -> str: |
| 42 | + return self.value |
| 43 | + |
| 44 | + |
| 45 | +# Segment source, see |
| 46 | +# https://getsentry.github.io/sentry-conventions/generated/attributes/sentry.html#sentryspansource |
| 47 | +class SegmentSource(str, Enum): |
| 48 | + COMPONENT = "component" |
| 49 | + CUSTOM = "custom" |
| 50 | + ROUTE = "route" |
| 51 | + TASK = "task" |
| 52 | + URL = "url" |
| 53 | + VIEW = "view" |
| 54 | + |
| 55 | + def __str__(self) -> str: |
| 56 | + return self.value |
| 57 | + |
| 58 | + |
| 59 | +# These are typically high cardinality and the server hates them |
| 60 | +LOW_QUALITY_SEGMENT_SOURCES = [ |
| 61 | + SegmentSource.URL, |
| 62 | +] |
| 63 | + |
| 64 | + |
| 65 | +SOURCE_FOR_STYLE = { |
| 66 | + "endpoint": SegmentSource.COMPONENT, |
| 67 | + "function_name": SegmentSource.COMPONENT, |
| 68 | + "handler_name": SegmentSource.COMPONENT, |
| 69 | + "method_and_path_pattern": SegmentSource.ROUTE, |
| 70 | + "path": SegmentSource.URL, |
| 71 | + "route_name": SegmentSource.COMPONENT, |
| 72 | + "route_pattern": SegmentSource.ROUTE, |
| 73 | + "uri_template": SegmentSource.ROUTE, |
| 74 | + "url": SegmentSource.ROUTE, |
| 75 | +} |
| 76 | + |
33 | 77 | """ |
34 | 78 | TODO[span-first] / notes |
35 | 79 | - redis, http, subprocess breadcrumbs (maybe_create_breadcrumbs_from_span) work |
@@ -138,47 +182,6 @@ def new_trace() -> None: |
138 | 182 | sentry_sdk.get_current_scope().set_new_propagation_context() |
139 | 183 |
|
140 | 184 |
|
141 | | -class SpanStatus(str, Enum): |
142 | | - OK = "ok" |
143 | | - ERROR = "error" |
144 | | - |
145 | | - def __str__(self) -> str: |
146 | | - return self.value |
147 | | - |
148 | | - |
149 | | -# Segment source, see |
150 | | -# https://getsentry.github.io/sentry-conventions/generated/attributes/sentry.html#sentryspansource |
151 | | -class SegmentSource(str, Enum): |
152 | | - COMPONENT = "component" |
153 | | - CUSTOM = "custom" |
154 | | - ROUTE = "route" |
155 | | - TASK = "task" |
156 | | - URL = "url" |
157 | | - VIEW = "view" |
158 | | - |
159 | | - def __str__(self) -> str: |
160 | | - return self.value |
161 | | - |
162 | | - |
163 | | -# These are typically high cardinality and the server hates them |
164 | | -LOW_QUALITY_SEGMENT_SOURCES = [ |
165 | | - SegmentSource.URL, |
166 | | -] |
167 | | - |
168 | | - |
169 | | -SOURCE_FOR_STYLE = { |
170 | | - "endpoint": SegmentSource.COMPONENT, |
171 | | - "function_name": SegmentSource.COMPONENT, |
172 | | - "handler_name": SegmentSource.COMPONENT, |
173 | | - "method_and_path_pattern": SegmentSource.ROUTE, |
174 | | - "path": SegmentSource.URL, |
175 | | - "route_name": SegmentSource.COMPONENT, |
176 | | - "route_pattern": SegmentSource.ROUTE, |
177 | | - "uri_template": SegmentSource.ROUTE, |
178 | | - "url": SegmentSource.ROUTE, |
179 | | -} |
180 | | - |
181 | | - |
182 | 185 | class NoOpStreamedSpan: |
183 | 186 | pass |
184 | 187 |
|
@@ -580,3 +583,66 @@ def _set_grouping_attributes(self): |
580 | 583 | self.set_attribute("sentry.segment.id", self.segment.span_id) |
581 | 584 |
|
582 | 585 | self.set_attribute("sentry.segment.name", self.segment.name) |
| 586 | + |
| 587 | + |
| 588 | +def trace( |
| 589 | + func: "Optional[Callable[P, R]]" = None, |
| 590 | + *, |
| 591 | + name: "Optional[str]" = None, |
| 592 | + attributes: "Optional[dict[str, Any]]" = None, |
| 593 | +) -> "Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]": |
| 594 | + """ |
| 595 | + Decorator to start a span around a function call. |
| 596 | +
|
| 597 | + This decorator automatically creates a new span when the decorated function |
| 598 | + is called, and finishes the span when the function returns or raises an exception. |
| 599 | +
|
| 600 | + :param func: The function to trace. When used as a decorator without parentheses, |
| 601 | + this is the function being decorated. When used with parameters (e.g., |
| 602 | + ``@trace(op="custom")``, this should be None. |
| 603 | + :type func: Callable or None |
| 604 | +
|
| 605 | + :param name: The human-readable name/description for the span. If not provided, |
| 606 | + defaults to the function name. This provides more specific details about |
| 607 | + what the span represents (e.g., "GET /api/users", "process_user_data"). |
| 608 | + :type name: str or None |
| 609 | +
|
| 610 | + :param attributes: A dictionary of key-value pairs to add as attributes to the span. |
| 611 | + Attribute values must be strings, integers, floats, or booleans. These |
| 612 | + attributes provide additional context about the span's execution. |
| 613 | + :type attributes: dict[str, Any] or None |
| 614 | +
|
| 615 | + :returns: When used as ``@trace``, returns the decorated function. When used as |
| 616 | + ``@trace(...)`` with parameters, returns a decorator function. |
| 617 | + :rtype: Callable or decorator function |
| 618 | +
|
| 619 | + Example:: |
| 620 | +
|
| 621 | + import sentry_sdk |
| 622 | +
|
| 623 | + # Simple usage with default values |
| 624 | + @sentry_sdk.trace |
| 625 | + def process_data(): |
| 626 | + # Function implementation |
| 627 | + pass |
| 628 | +
|
| 629 | + # With custom parameters |
| 630 | + @sentry_sdk.trace( |
| 631 | + name="Get user data", |
| 632 | + attributes={"postgres": True} |
| 633 | + ) |
| 634 | + def make_db_query(sql): |
| 635 | + # Function implementation |
| 636 | + pass |
| 637 | + """ |
| 638 | + from sentry_sdk.tracing_utils import create_streaming_span_decorator |
| 639 | + |
| 640 | + decorator = create_streaming_span_decorator( |
| 641 | + name=name, |
| 642 | + attributes=attributes, |
| 643 | + ) |
| 644 | + |
| 645 | + if func: |
| 646 | + return decorator(func) |
| 647 | + else: |
| 648 | + return decorator |
0 commit comments