@@ -23,12 +23,14 @@ def setup_once():
2323 def sentry_build_tracer (name , task , * args , ** kwargs ):
2424 # Need to patch both methods because older celery sometimes
2525 # 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 )
26+ task .__call__ = _wrap_task_call (task , task .__call__ )
27+ task .run = _wrap_task_call (task , task .run )
2828 return _wrap_tracer (task , old_build_tracer (name , task , * args , ** kwargs ))
2929
3030 trace .build_tracer = sentry_build_tracer
3131
32+ _patch_worker_exit ()
33+
3234 # This logger logs every status of every task that ran on the worker.
3335 # Meaning that every task's breadcrumbs are full of stuff like "Task
3436 # <foo> raised unexpected <bar>".
@@ -57,14 +59,17 @@ def _inner(*args, **kwargs):
5759 return _inner
5860
5961
60- def _wrap_task_call (f ):
62+ def _wrap_task_call (task , f ):
6163 # Need to wrap task call because the exception is caught before we get to
6264 # see it. Also celery's reported stacktrace is untrustworthy.
6365 def _inner (* args , ** kwargs ):
6466 try :
6567 return f (* args , ** kwargs )
6668 except Exception :
67- reraise (* _capture_exception ())
69+ exc_info = sys .exc_info ()
70+ with capture_internal_exceptions ():
71+ _capture_exception (task , exc_info )
72+ reraise (* exc_info )
6873
6974 return _inner
7075
@@ -83,15 +88,6 @@ def event_processor(event, hint):
8388 }
8489
8590 if "exc_info" in hint :
86- with capture_internal_exceptions ():
87- if isinstance (hint ["exc_info" ][1 ], Retry ):
88- return None
89-
90- if hasattr (task , "throws" ) and isinstance (
91- hint ["exc_info" ][1 ], task .throws
92- ):
93- return None
94-
9591 with capture_internal_exceptions ():
9692 if issubclass (hint ["exc_info" ][0 ], SoftTimeLimitExceeded ):
9793 event ["fingerprint" ] = [
@@ -105,16 +101,39 @@ def event_processor(event, hint):
105101 return event_processor
106102
107103
108- def _capture_exception ():
104+ def _capture_exception (task , exc_info ):
109105 hub = Hub .current
110- exc_info = sys .exc_info ()
111106
112- if hub .get_integration (CeleryIntegration ) is not None :
113- event , hint = event_from_exception (
114- exc_info ,
115- client_options = hub .client .options ,
116- mechanism = {"type" : "celery" , "handled" : False },
117- )
118- hub .capture_event (event , hint = hint )
107+ if hub .get_integration (CeleryIntegration ) is None :
108+ return
109+ if isinstance (exc_info [1 ], Retry ):
110+ return
111+ if hasattr (task , "throws" ) and isinstance (exc_info [1 ], task .throws ):
112+ return
113+
114+ event , hint = event_from_exception (
115+ exc_info ,
116+ client_options = hub .client .options ,
117+ mechanism = {"type" : "celery" , "handled" : False },
118+ )
119+
120+ hub .capture_event (event , hint = hint )
121+
122+
123+ def _patch_worker_exit ():
124+ # Need to flush queue before worker shutdown because a crashing worker will
125+ # call os._exit
126+ from billiard .pool import Worker # type: ignore
127+
128+ old_workloop = Worker .workloop
129+
130+ def sentry_workloop (* args , ** kwargs ):
131+ try :
132+ return old_workloop (* args , ** kwargs )
133+ finally :
134+ with capture_internal_exceptions ():
135+ hub = Hub .current
136+ if hub .get_integration (CeleryIntegration ) is not None :
137+ hub .flush ()
119138
120- return exc_info
139+ Worker . workloop = sentry_workloop
0 commit comments