Skip to content

Commit 30649d5

Browse files
committed
feat(bottle): Add span streaming support to Bottle integration
Fixes PY-2310 Fixes #6008
1 parent c3aab39 commit 30649d5

2 files changed

Lines changed: 346 additions & 16 deletions

File tree

sentry_sdk/integrations/bottle.py

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
)
1111
from sentry_sdk.integrations._wsgi_common import RequestExtractor
1212
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
13+
from sentry_sdk.traces import SegmentSource
1314
from sentry_sdk.tracing import SOURCE_FOR_STYLE
15+
from sentry_sdk.tracing_utils import has_span_streaming_enabled
1416
from sentry_sdk.utils import (
1517
capture_internal_exceptions,
1618
ensure_integration_enabled,
@@ -100,7 +102,38 @@ def _patched_handle(self: "Bottle", environ: "Dict[str, Any]") -> "Any":
100102
scope.add_event_processor(
101103
_make_request_event_processor(self, bottle_request, integration)
102104
)
103-
res = old_handle(self, environ)
105+
106+
if has_span_streaming_enabled(sentry_sdk.get_client().options):
107+
# Using SegmentSource.COMPONENT as the default since this is the also value for "handler_name"
108+
# within SOURCE_FOR_STYLE.
109+
# See https://github.com/getsentry/sentry-python/blob/c3aab3932f5a7e89ad3aff551a206db710acf0e6/sentry_sdk/tracing.py#L151-L161
110+
with sentry_sdk.traces.start_span(
111+
name="bottle",
112+
attributes={"sentry.span.source": SegmentSource.COMPONENT},
113+
) as span:
114+
res = old_handle(self, environ)
115+
116+
try:
117+
if integration.transaction_style == "url":
118+
span.name = bottle_request.route.rule or "bottle"
119+
span.set_attribute("sentry.span.source", SegmentSource.URL)
120+
elif integration.transaction_style == "endpoint":
121+
name = (
122+
bottle_request.route.name
123+
or transaction_from_function(
124+
bottle_request.route.callback
125+
)
126+
or "bottle"
127+
)
128+
span.name = name
129+
span.set_attribute(
130+
"sentry.span.source", SegmentSource.COMPONENT
131+
)
132+
except RuntimeError:
133+
pass
134+
135+
else:
136+
res = old_handle(self, environ)
104137

105138
return res
106139

@@ -119,17 +152,33 @@ def patched_make_callback(
119152
return prepared_callback
120153

121154
def wrapped_callback(*args: object, **kwargs: object) -> "Any":
122-
try:
123-
res = prepared_callback(*args, **kwargs)
124-
except Exception as exception:
125-
_capture_exception(exception, handled=False)
126-
raise exception
127-
128-
if (
129-
isinstance(res, HTTPResponse)
130-
and res.status_code in integration.failed_request_status_codes
131-
):
132-
_capture_exception(res, handled=True)
155+
if has_span_streaming_enabled(sentry_sdk.get_client().options):
156+
with sentry_sdk.traces.start_span(name="bottle"):
157+
try:
158+
res = prepared_callback(*args, **kwargs)
159+
except Exception as exception:
160+
_capture_exception(exception, handled=False)
161+
raise exception
162+
163+
if (
164+
isinstance(res, HTTPResponse)
165+
and res.status_code
166+
in integration.failed_request_status_codes
167+
):
168+
_capture_exception(res, handled=True)
169+
170+
else:
171+
try:
172+
res = prepared_callback(*args, **kwargs)
173+
except Exception as exception:
174+
_capture_exception(exception, handled=False)
175+
raise exception
176+
177+
if (
178+
isinstance(res, HTTPResponse)
179+
and res.status_code in integration.failed_request_status_codes
180+
):
181+
_capture_exception(res, handled=True)
133182

134183
return res
135184

0 commit comments

Comments
 (0)