@@ -21,9 +21,11 @@ def setup_once():
21
21
old_build_tracer = trace .build_tracer
22
22
23
23
def sentry_build_tracer (name , task , * args , ** kwargs ):
24
- task .__call__ = _wrap_task_call (task , task .__call__ )
25
- task .run = _wrap_task_call (task , task .run )
26
- return old_build_tracer (name , task , * args , ** kwargs )
24
+ # Need to patch both methods because older celery sometimes
25
+ # short-circuits to task.run if it thinks it's safe.
26
+ task .__call__ = _wrap_task_call (task .__call__ )
27
+ task .run = _wrap_task_call (task .run )
28
+ return _wrap_tracer (task , old_build_tracer (name , task , * args , ** kwargs ))
27
29
28
30
trace .build_tracer = sentry_build_tracer
29
31
@@ -33,28 +35,40 @@ def sentry_build_tracer(name, task, *args, **kwargs):
33
35
ignore_logger ("celery.worker.job" )
34
36
35
37
36
- def _wrap_task_call (self , f ):
38
+ def _wrap_tracer (task , f ):
39
+ # Need to wrap tracer for pushing the scope before prerun is sent, and
40
+ # popping it after postrun is sent.
41
+ #
42
+ # This is the reason we don't use signals for hooking in the first place.
43
+ # Also because in Celery 3, signal dispatch returns early if one handler
44
+ # crashes.
37
45
def _inner (* args , ** kwargs ):
38
46
hub = Hub .current
39
47
if hub .get_integration (CeleryIntegration ) is None :
40
48
return f (* args , ** kwargs )
41
49
42
- with hub .configure_scope () as scope :
43
- if scope ._name == "celery" :
44
- return f (* args , ** kwargs )
45
-
46
50
with hub .push_scope () as scope :
47
51
scope ._name = "celery"
48
- scope .add_event_processor (_make_event_processor (args , kwargs , self ))
49
- try :
50
- return f (* args , ** kwargs )
51
- except Exception :
52
- reraise (* _capture_exception (hub ))
52
+ scope .add_event_processor (_make_event_processor (task , * args , ** kwargs ))
53
+
54
+ return f (* args , ** kwargs )
55
+
56
+ return _inner
57
+
58
+
59
+ def _wrap_task_call (f ):
60
+ # Need to wrap task call because the exception is caught before we get to
61
+ # see it. Also celery's reported stacktrace is untrustworthy.
62
+ def _inner (* args , ** kwargs ):
63
+ try :
64
+ return f (* args , ** kwargs )
65
+ except Exception :
66
+ reraise (* _capture_exception ())
53
67
54
68
return _inner
55
69
56
70
57
- def _make_event_processor (args , kwargs , task ):
71
+ def _make_event_processor (task , uuid , args , kwargs , request = None ):
58
72
def event_processor (event , hint ):
59
73
with capture_internal_exceptions ():
60
74
event ["transaction" ] = task .name
@@ -87,12 +101,15 @@ def event_processor(event, hint):
87
101
return event_processor
88
102
89
103
90
- def _capture_exception (hub ):
91
- exc_info = sys .exc_info ()
92
- event , hint = event_from_exception (
93
- exc_info ,
94
- client_options = hub .client .options ,
95
- mechanism = {"type" : "celery" , "handled" : False },
96
- )
97
- hub .capture_event (event , hint = hint )
104
+ def _capture_exception ():
105
+ hub = Hub .current
106
+ if hub .get_integration (CeleryIntegration ) is not None :
107
+ exc_info = sys .exc_info ()
108
+ event , hint = event_from_exception (
109
+ exc_info ,
110
+ client_options = hub .client .options ,
111
+ mechanism = {"type" : "celery" , "handled" : False },
112
+ )
113
+ hub .capture_event (event , hint = hint )
114
+
98
115
return exc_info
0 commit comments