1
1
import sys
2
2
from functools import partial
3
- from threading import Timer
4
3
5
4
import sentry_sdk
6
5
from sentry_sdk ._werkzeug import get_host , _get_headers
7
6
from sentry_sdk .api import continue_trace
8
7
from sentry_sdk .consts import OP
9
- from sentry_sdk .scope import should_send_default_pii , use_isolation_scope , use_scope
8
+ from sentry_sdk .scope import should_send_default_pii
10
9
from sentry_sdk .integrations ._wsgi_common import (
11
10
DEFAULT_HTTP_METHODS_TO_CAPTURE ,
12
11
_filter_headers ,
12
+ nullcontext ,
13
13
)
14
14
from sentry_sdk .sessions import track_session
15
+ from sentry_sdk .scope import use_isolation_scope
15
16
from sentry_sdk .tracing import Transaction , TRANSACTION_SOURCE_ROUTE
16
- from sentry_sdk .tracing_utils import finish_running_transaction
17
17
from sentry_sdk .utils import (
18
18
ContextVar ,
19
19
capture_internal_exceptions ,
@@ -46,9 +46,6 @@ def __call__(self, status, response_headers, exc_info=None): # type: ignore
46
46
pass
47
47
48
48
49
- MAX_TRANSACTION_DURATION_SECONDS = 5 * 60
50
-
51
-
52
49
_wsgi_middleware_applied = ContextVar ("sentry_wsgi_middleware_applied" )
53
50
54
51
@@ -101,7 +98,6 @@ def __call__(self, environ, start_response):
101
98
_wsgi_middleware_applied .set (True )
102
99
try :
103
100
with sentry_sdk .isolation_scope () as scope :
104
- current_scope = sentry_sdk .get_current_scope ()
105
101
with track_session (scope , session_mode = "request" ):
106
102
with capture_internal_exceptions ():
107
103
scope .clear_breadcrumbs ()
@@ -113,7 +109,6 @@ def __call__(self, environ, start_response):
113
109
)
114
110
115
111
method = environ .get ("REQUEST_METHOD" , "" ).upper ()
116
-
117
112
transaction = None
118
113
if method in self .http_methods_to_capture :
119
114
transaction = continue_trace (
@@ -124,43 +119,27 @@ def __call__(self, environ, start_response):
124
119
origin = self .span_origin ,
125
120
)
126
121
127
- timer = None
128
- if transaction is not None :
122
+ with (
129
123
sentry_sdk .start_transaction (
130
124
transaction ,
131
125
custom_sampling_context = {"wsgi_environ" : environ },
132
- ).__enter__ ()
133
- timer = Timer (
134
- MAX_TRANSACTION_DURATION_SECONDS ,
135
- _finish_long_running_transaction ,
136
- args = (current_scope , scope ),
137
126
)
138
- timer .start ()
139
-
140
- try :
141
- response = self .app (
142
- environ ,
143
- partial (
144
- _sentry_start_response ,
145
- start_response ,
146
- transaction ,
147
- ),
148
- )
149
- except BaseException :
150
- exc_info = sys .exc_info ()
151
- _capture_exception (exc_info )
152
- finish_running_transaction (current_scope , exc_info , timer )
153
- reraise (* exc_info )
154
-
127
+ if transaction is not None
128
+ else nullcontext ()
129
+ ):
130
+ try :
131
+ response = self .app (
132
+ environ ,
133
+ partial (
134
+ _sentry_start_response , start_response , transaction
135
+ ),
136
+ )
137
+ except BaseException :
138
+ reraise (* _capture_exception ())
155
139
finally :
156
140
_wsgi_middleware_applied .set (False )
157
141
158
- return _ScopedResponse (
159
- response = response ,
160
- current_scope = current_scope ,
161
- isolation_scope = scope ,
162
- timer = timer ,
163
- )
142
+ return _ScopedResponse (scope , response )
164
143
165
144
166
145
def _sentry_start_response ( # type: ignore
@@ -222,13 +201,13 @@ def get_client_ip(environ):
222
201
return environ .get ("REMOTE_ADDR" )
223
202
224
203
225
- def _capture_exception (exc_info = None ):
226
- # type: (Optional[ExcInfo] ) -> ExcInfo
204
+ def _capture_exception ():
205
+ # type: () -> ExcInfo
227
206
"""
228
207
Captures the current exception and sends it to Sentry.
229
208
Returns the ExcInfo tuple to it can be reraised afterwards.
230
209
"""
231
- exc_info = exc_info or sys .exc_info ()
210
+ exc_info = sys .exc_info ()
232
211
e = exc_info [1 ]
233
212
234
213
# SystemExit(0) is the only uncaught exception that is expected behavior
@@ -246,7 +225,7 @@ def _capture_exception(exc_info=None):
246
225
247
226
class _ScopedResponse :
248
227
"""
249
- Use separate scopes for each response chunk.
228
+ Users a separate scope for each response chunk.
250
229
251
230
This will make WSGI apps more tolerant against:
252
231
- WSGI servers streaming responses from a different thread/from
@@ -255,54 +234,37 @@ class _ScopedResponse:
255
234
- WSGI servers streaming responses interleaved from the same thread
256
235
"""
257
236
258
- __slots__ = ("_response" , "_current_scope" , "_isolation_scope" , "_timer " )
237
+ __slots__ = ("_response" , "_scope " )
259
238
260
- def __init__ (
261
- self ,
262
- response , # type: Iterator[bytes]
263
- current_scope , # type: sentry_sdk.scope.Scope
264
- isolation_scope , # type: sentry_sdk.scope.Scope
265
- timer = None , # type: Optional[Timer]
266
- ):
267
- # type: (...) -> None
239
+ def __init__ (self , scope , response ):
240
+ # type: (sentry_sdk.scope.Scope, Iterator[bytes]) -> None
241
+ self ._scope = scope
268
242
self ._response = response
269
- self ._current_scope = current_scope
270
- self ._isolation_scope = isolation_scope
271
- self ._timer = timer
272
243
273
244
def __iter__ (self ):
274
245
# type: () -> Iterator[bytes]
275
246
iterator = iter (self ._response )
276
247
277
- try :
278
- while True :
279
- with use_isolation_scope (self ._isolation_scope ):
280
- with use_scope (self ._current_scope ):
281
- try :
282
- chunk = next (iterator )
283
- except StopIteration :
284
- break
285
- except BaseException :
286
- reraise (* _capture_exception ())
287
-
288
- yield chunk
248
+ while True :
249
+ with use_isolation_scope (self ._scope ):
250
+ try :
251
+ chunk = next (iterator )
252
+ except StopIteration :
253
+ break
254
+ except BaseException :
255
+ reraise (* _capture_exception ())
289
256
290
- finally :
291
- with use_isolation_scope (self ._isolation_scope ):
292
- with use_scope (self ._current_scope ):
293
- finish_running_transaction (timer = self ._timer )
257
+ yield chunk
294
258
295
259
def close (self ):
296
260
# type: () -> None
297
- with use_isolation_scope (self ._isolation_scope ):
298
- with use_scope (self ._current_scope ):
299
- try :
300
- finish_running_transaction (timer = self ._timer )
301
- self ._response .close () # type: ignore
302
- except AttributeError :
303
- pass
304
- except BaseException :
305
- reraise (* _capture_exception ())
261
+ with use_isolation_scope (self ._scope ):
262
+ try :
263
+ self ._response .close () # type: ignore
264
+ except AttributeError :
265
+ pass
266
+ except BaseException :
267
+ reraise (* _capture_exception ())
306
268
307
269
308
270
def _make_wsgi_event_processor (environ , use_x_forwarded_for ):
@@ -346,18 +308,3 @@ def event_processor(event, hint):
346
308
return event
347
309
348
310
return event_processor
349
-
350
-
351
- def _finish_long_running_transaction (current_scope , isolation_scope ):
352
- # type: (sentry_sdk.scope.Scope, sentry_sdk.scope.Scope) -> None
353
- """
354
- Make sure we don't keep transactions open for too long.
355
- Triggered after MAX_TRANSACTION_DURATION_SECONDS have passed.
356
- """
357
- try :
358
- with use_isolation_scope (isolation_scope ):
359
- with use_scope (current_scope ):
360
- finish_running_transaction ()
361
- except AttributeError :
362
- # transaction is not there anymore
363
- pass
0 commit comments