99 parse_version ,
1010 transaction_from_function ,
1111)
12- from sentry_sdk .integrations import Integration , DidNotEnable
12+ from sentry_sdk .integrations import (
13+ Integration ,
14+ DidNotEnable ,
15+ _DEFAULT_FAILED_REQUEST_STATUS_CODES ,
16+ )
1317from sentry_sdk .integrations .wsgi import SentryWsgiMiddleware
1418from sentry_sdk .integrations ._wsgi_common import RequestExtractor
1519
1620from typing import TYPE_CHECKING
1721
1822if TYPE_CHECKING :
23+ from collections .abc import Set
24+
1925 from sentry_sdk .integrations .wsgi import _ScopedResponse
2026 from typing import Any
2127 from typing import Dict
2834try :
2935 from bottle import (
3036 Bottle ,
37+ HTTPResponse ,
3138 Route ,
3239 request as bottle_request ,
3340 __version__ as BOTTLE_VERSION ,
@@ -45,15 +52,21 @@ class BottleIntegration(Integration):
4552
4653 transaction_style = ""
4754
48- def __init__ (self , transaction_style = "endpoint" ):
49- # type: (str) -> None
55+ def __init__ (
56+ self ,
57+ transaction_style = "endpoint" , # type: str
58+ * ,
59+ failed_request_status_codes = _DEFAULT_FAILED_REQUEST_STATUS_CODES , # type: Set[int]
60+ ):
61+ # type: (...) -> None
5062
5163 if transaction_style not in TRANSACTION_STYLE_VALUES :
5264 raise ValueError (
5365 "Invalid value for transaction_style: %s (must be in %s)"
5466 % (transaction_style , TRANSACTION_STYLE_VALUES )
5567 )
5668 self .transaction_style = transaction_style
69+ self .failed_request_status_codes = failed_request_status_codes
5770
5871 @staticmethod
5972 def setup_once ():
@@ -102,26 +115,29 @@ def _patched_handle(self, environ):
102115
103116 old_make_callback = Route ._make_callback
104117
105- @ensure_integration_enabled ( BottleIntegration , old_make_callback )
118+ @functools . wraps ( old_make_callback )
106119 def patched_make_callback (self , * args , ** kwargs ):
107120 # type: (Route, *object, **object) -> Any
108- client = sentry_sdk .get_client ()
109121 prepared_callback = old_make_callback (self , * args , ** kwargs )
110122
123+ integration = sentry_sdk .get_client ().get_integration (BottleIntegration )
124+ if integration is None :
125+ return prepared_callback
126+
111127 def wrapped_callback (* args , ** kwargs ):
112128 # type: (*object, **object) -> Any
113-
114129 try :
115130 res = prepared_callback (* args , ** kwargs )
116131 except Exception as exception :
117- event , hint = event_from_exception (
118- exception ,
119- client_options = client .options ,
120- mechanism = {"type" : "bottle" , "handled" : False },
121- )
122- sentry_sdk .capture_event (event , hint = hint )
132+ _capture_exception (exception , handled = False )
123133 raise exception
124134
135+ if (
136+ isinstance (res , HTTPResponse )
137+ and res .status_code in integration .failed_request_status_codes
138+ ):
139+ _capture_exception (res , handled = True )
140+
125141 return res
126142
127143 return wrapped_callback
@@ -191,3 +207,13 @@ def event_processor(event, hint):
191207 return event
192208
193209 return event_processor
210+
211+
212+ def _capture_exception (exception , handled ):
213+ # type: (BaseException, bool) -> None
214+ event , hint = event_from_exception (
215+ exception ,
216+ client_options = sentry_sdk .get_client ().options ,
217+ mechanism = {"type" : "bottle" , "handled" : handled },
218+ )
219+ sentry_sdk .capture_event (event , hint = hint )
0 commit comments