We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Celery integration not capturing error with max_tasks_per_child = 1
The celery integration is failing to capture the exception when I use a celery factory pattern which patches the celery task with Flask's context.

This is `web/celery_factory.py`
```
# Source: https://stackoverflow.com/questions/12044776/how-to-use-flask-sqlalchemy-in-a-celery-task

from celery import Celery
import flask


class FlaskCelery(Celery):

    def __init__(self, *args, **kwargs):
        super(FlaskCelery, self).__init__(*args, **kwargs)
        self.patch_task()

        if 'app' in kwargs:
            self.init_app(kwargs['app'])

    def patch_task(self):
        TaskBase = self.Task
        _celery = self

        class ContextTask(TaskBase):
            abstract = True

            def __call__(self, *args, **kwargs):
                if flask.has_app_context():
                    return TaskBase.__call__(self, *args, **kwargs)
                else:
                    with _celery.app.app_context():
                        return TaskBase.__call__(self, *args, **kwargs)

        self.Task = ContextTask

    def init_app(self, app):
        self.app = app
        self.config_from_object(app.config)


celery_app = FlaskCelery()
```

I am adding a random `raise` inside a simple task

```
import celery_app from celery_factory.py
@celery_app.task
def simple_task():
    raise Exception("Testing Celery exception")
```

The error I get printed is:
```
[2019-03-08 21:24:21,117: ERROR/ForkPoolWorker-31] Task simple_task[d6e959b1-7253-4e55-861d-c1968ae14e1c] raised unexpected: RuntimeError('No active exception to reraise')
Traceback (most recent call last):
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/celery/app/trace.py", line 382, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/Users/okomarov/Documents/repos/myproject/web/celery_factory.py", line 28, in __call__
    return TaskBase.__call__(self, *args, **kwargs)
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/celery/app/trace.py", line 641, in __protected_call__
    return self.run(*args, **kwargs)
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py", line 66, in _inner
    reraise(*_capture_exception())
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/_compat.py", line 52, in reraise
    raise value
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py", line 64, in _inner
    return f(*args, **kwargs)
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py", line 66, in _inner
    reraise(*_capture_exception())
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/_compat.py", line 52, in reraise
    raise value
  File "/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py", line 64, in _inner
    return f(*args, **kwargs)
  File "/Users/okomarov/Documents/repos/myproject/web/simple_task.py", line 4, in simple_task
    raise Exception("Testing Celery exception")
RuntimeError: No active exception to reraise
```

Relevant pip packages:
```
Celery==4.2.1
Flask==1.0.2
sentry-sdk==0.7.4
```

The integration is called as following (flask integration works as expected):
```
from flask import Flask
from celery_factory import celery_app
from config import config_to_use


def create_app():
    app = Flask()
    app.config.from_object(config_to_use)

    init_logging(app)

    register_extensions(app)
    register_blueprints(app)
    register_jinja_extras(app)

    return app


def init_logging(app):
    import sentry_sdk
    from sentry_sdk.integrations.flask import FlaskIntegration
    from sentry_sdk.integrations.celery import CeleryIntegration

    sentry_sdk.init(
        dsn=app.config.get('FLASK_SENTRY_DSN'),
        integrations=[FlaskIntegration(), CeleryIntegration()]
        )

...
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sentry_sdk/integrations/celery.py`
Content:
```
1 from __future__ import absolute_import
2 
3 import sys
4 
5 from celery.exceptions import SoftTimeLimitExceeded, Retry  # type: ignore
6 
7 from sentry_sdk.hub import Hub
8 from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
9 from sentry_sdk._compat import reraise
10 from sentry_sdk.integrations import Integration
11 from sentry_sdk.integrations.logging import ignore_logger
12 
13 
14 class CeleryIntegration(Integration):
15     identifier = "celery"
16 
17     @staticmethod
18     def setup_once():
19         import celery.app.trace as trace  # type: ignore
20 
21         old_build_tracer = trace.build_tracer
22 
23         def sentry_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))
29 
30         trace.build_tracer = sentry_build_tracer
31 
32         # This logger logs every status of every task that ran on the worker.
33         # Meaning that every task's breadcrumbs are full of stuff like "Task
34         # <foo> raised unexpected <bar>".
35         ignore_logger("celery.worker.job")
36 
37 
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.
45     def _inner(*args, **kwargs):
46         hub = Hub.current
47         if hub.get_integration(CeleryIntegration) is None:
48             return f(*args, **kwargs)
49 
50         with hub.push_scope() as scope:
51             scope._name = "celery"
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())
67 
68     return _inner
69 
70 
71 def _make_event_processor(task, uuid, args, kwargs, request=None):
72     def event_processor(event, hint):
73         with capture_internal_exceptions():
74             event["transaction"] = task.name
75 
76         with capture_internal_exceptions():
77             extra = event.setdefault("extra", {})
78             extra["celery-job"] = {
79                 "task_name": task.name,
80                 "args": args,
81                 "kwargs": kwargs,
82             }
83 
84         if "exc_info" in hint:
85             with capture_internal_exceptions():
86                 if isinstance(hint["exc_info"][1], Retry):
87                     return None
88 
89                 if hasattr(task, "throws") and isinstance(
90                     hint["exc_info"][1], task.throws
91                 ):
92                     return None
93 
94             with capture_internal_exceptions():
95                 if issubclass(hint["exc_info"][0], SoftTimeLimitExceeded):
96                     event["fingerprint"] = [
97                         "celery",
98                         "SoftTimeLimitExceeded",
99                         getattr(task, "name", task),
100                     ]
101 
102         return event
103 
104     return event_processor
105 
106 
107 def _capture_exception():
108     hub = Hub.current
109     exc_info = sys.exc_info()
110 
111     if hub.get_integration(CeleryIntegration) is not None:
112         event, hint = event_from_exception(
113             exc_info,
114             client_options=hub.client.options,
115             mechanism={"type": "celery", "handled": False},
116         )
117         hub.capture_event(event, hint=hint)
118 
119     return exc_info
120
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py
--- a/sentry_sdk/integrations/celery.py
+++ b/sentry_sdk/integrations/celery.py
@@ -23,12 +23,14 @@
         def sentry_build_tracer(name, task, *args, **kwargs):
             # Need to patch both methods because older celery sometimes
             # short-circuits to task.run if it thinks it's safe.
-            task.__call__ = _wrap_task_call(task.__call__)
-            task.run = _wrap_task_call(task.run)
+            task.__call__ = _wrap_task_call(task, task.__call__)
+            task.run = _wrap_task_call(task, task.run)
             return _wrap_tracer(task, old_build_tracer(name, task, *args, **kwargs))
 
         trace.build_tracer = sentry_build_tracer
 
+        _patch_worker_exit()
+
         # This logger logs every status of every task that ran on the worker.
         # Meaning that every task's breadcrumbs are full of stuff like "Task
         # <foo> raised unexpected <bar>".
@@ -56,14 +58,17 @@
     return _inner
 
 
-def _wrap_task_call(f):
+def _wrap_task_call(task, f):
     # Need to wrap task call because the exception is caught before we get to
     # see it. Also celery's reported stacktrace is untrustworthy.
     def _inner(*args, **kwargs):
         try:
             return f(*args, **kwargs)
         except Exception:
-            reraise(*_capture_exception())
+            exc_info = sys.exc_info()
+            with capture_internal_exceptions():
+                _capture_exception(task, exc_info)
+            reraise(*exc_info)
 
     return _inner
 
@@ -82,15 +87,6 @@
             }
 
         if "exc_info" in hint:
-            with capture_internal_exceptions():
-                if isinstance(hint["exc_info"][1], Retry):
-                    return None
-
-                if hasattr(task, "throws") and isinstance(
-                    hint["exc_info"][1], task.throws
-                ):
-                    return None
-
             with capture_internal_exceptions():
                 if issubclass(hint["exc_info"][0], SoftTimeLimitExceeded):
                     event["fingerprint"] = [
@@ -104,16 +100,39 @@
     return event_processor
 
 
-def _capture_exception():
+def _capture_exception(task, exc_info):
     hub = Hub.current
-    exc_info = sys.exc_info()
 
-    if hub.get_integration(CeleryIntegration) is not None:
-        event, hint = event_from_exception(
-            exc_info,
-            client_options=hub.client.options,
-            mechanism={"type": "celery", "handled": False},
-        )
-        hub.capture_event(event, hint=hint)
+    if hub.get_integration(CeleryIntegration) is None:
+        return
+    if isinstance(exc_info[1], Retry):
+        return
+    if hasattr(task, "throws") and isinstance(exc_info[1], task.throws):
+        return
+
+    event, hint = event_from_exception(
+        exc_info,
+        client_options=hub.client.options,
+        mechanism={"type": "celery", "handled": False},
+    )
+
+    hub.capture_event(event, hint=hint)
+
+
+def _patch_worker_exit():
+    # Need to flush queue before worker shutdown because a crashing worker will
+    # call os._exit
+    from billiard.pool import Worker  # type: ignore
+
+    old_workloop = Worker.workloop
+
+    def sentry_workloop(*args, **kwargs):
+        try:
+            return old_workloop(*args, **kwargs)
+        finally:
+            with capture_internal_exceptions():
+                hub = Hub.current
+                if hub.get_integration(CeleryIntegration) is not None:
+                    hub.flush()
 
-    return exc_info
+    Worker.workloop = sentry_workloop

{"golden_diff": "diff --git a/sentry_sdk/integrations/celery.py b/sentry_sdk/integrations/celery.py\n--- a/sentry_sdk/integrations/celery.py\n+++ b/sentry_sdk/integrations/celery.py\n@@ -23,12 +23,14 @@\n         def sentry_build_tracer(name, task, *args, **kwargs):\n             # Need to patch both methods because older celery sometimes\n             # short-circuits to task.run if it thinks it's safe.\n-            task.__call__ = _wrap_task_call(task.__call__)\n-            task.run = _wrap_task_call(task.run)\n+            task.__call__ = _wrap_task_call(task, task.__call__)\n+            task.run = _wrap_task_call(task, task.run)\n             return _wrap_tracer(task, old_build_tracer(name, task, *args, **kwargs))\n \n         trace.build_tracer = sentry_build_tracer\n \n+        _patch_worker_exit()\n+\n         # This logger logs every status of every task that ran on the worker.\n         # Meaning that every task's breadcrumbs are full of stuff like \"Task\n         # <foo> raised unexpected <bar>\".\n@@ -56,14 +58,17 @@\n     return _inner\n \n \n-def _wrap_task_call(f):\n+def _wrap_task_call(task, f):\n     # Need to wrap task call because the exception is caught before we get to\n     # see it. Also celery's reported stacktrace is untrustworthy.\n     def _inner(*args, **kwargs):\n         try:\n             return f(*args, **kwargs)\n         except Exception:\n-            reraise(*_capture_exception())\n+            exc_info = sys.exc_info()\n+            with capture_internal_exceptions():\n+                _capture_exception(task, exc_info)\n+            reraise(*exc_info)\n \n     return _inner\n \n@@ -82,15 +87,6 @@\n             }\n \n         if \"exc_info\" in hint:\n-            with capture_internal_exceptions():\n-                if isinstance(hint[\"exc_info\"][1], Retry):\n-                    return None\n-\n-                if hasattr(task, \"throws\") and isinstance(\n-                    hint[\"exc_info\"][1], task.throws\n-                ):\n-                    return None\n-\n             with capture_internal_exceptions():\n                 if issubclass(hint[\"exc_info\"][0], SoftTimeLimitExceeded):\n                     event[\"fingerprint\"] = [\n@@ -104,16 +100,39 @@\n     return event_processor\n \n \n-def _capture_exception():\n+def _capture_exception(task, exc_info):\n     hub = Hub.current\n-    exc_info = sys.exc_info()\n \n-    if hub.get_integration(CeleryIntegration) is not None:\n-        event, hint = event_from_exception(\n-            exc_info,\n-            client_options=hub.client.options,\n-            mechanism={\"type\": \"celery\", \"handled\": False},\n-        )\n-        hub.capture_event(event, hint=hint)\n+    if hub.get_integration(CeleryIntegration) is None:\n+        return\n+    if isinstance(exc_info[1], Retry):\n+        return\n+    if hasattr(task, \"throws\") and isinstance(exc_info[1], task.throws):\n+        return\n+\n+    event, hint = event_from_exception(\n+        exc_info,\n+        client_options=hub.client.options,\n+        mechanism={\"type\": \"celery\", \"handled\": False},\n+    )\n+\n+    hub.capture_event(event, hint=hint)\n+\n+\n+def _patch_worker_exit():\n+    # Need to flush queue before worker shutdown because a crashing worker will\n+    # call os._exit\n+    from billiard.pool import Worker  # type: ignore\n+\n+    old_workloop = Worker.workloop\n+\n+    def sentry_workloop(*args, **kwargs):\n+        try:\n+            return old_workloop(*args, **kwargs)\n+        finally:\n+            with capture_internal_exceptions():\n+                hub = Hub.current\n+                if hub.get_integration(CeleryIntegration) is not None:\n+                    hub.flush()\n \n-    return exc_info\n+    Worker.workloop = sentry_workloop\n", "issue": "Celery integration not capturing error with max_tasks_per_child = 1\nThe celery integration is failing to capture the exception when I use a celery factory pattern which patches the celery task with Flask's context.\r\n\r\nThis is `web/celery_factory.py`\r\n```\r\n# Source: https://stackoverflow.com/questions/12044776/how-to-use-flask-sqlalchemy-in-a-celery-task\r\n\r\nfrom celery import Celery\r\nimport flask\r\n\r\n\r\nclass FlaskCelery(Celery):\r\n\r\n    def __init__(self, *args, **kwargs):\r\n        super(FlaskCelery, self).__init__(*args, **kwargs)\r\n        self.patch_task()\r\n\r\n        if 'app' in kwargs:\r\n            self.init_app(kwargs['app'])\r\n\r\n    def patch_task(self):\r\n        TaskBase = self.Task\r\n        _celery = self\r\n\r\n        class ContextTask(TaskBase):\r\n            abstract = True\r\n\r\n            def __call__(self, *args, **kwargs):\r\n                if flask.has_app_context():\r\n                    return TaskBase.__call__(self, *args, **kwargs)\r\n                else:\r\n                    with _celery.app.app_context():\r\n                        return TaskBase.__call__(self, *args, **kwargs)\r\n\r\n        self.Task = ContextTask\r\n\r\n    def init_app(self, app):\r\n        self.app = app\r\n        self.config_from_object(app.config)\r\n\r\n\r\ncelery_app = FlaskCelery()\r\n```\r\n\r\nI am adding a random `raise` inside a simple task\r\n\r\n```\r\nimport celery_app from celery_factory.py\r\n@celery_app.task\r\ndef simple_task():\r\n    raise Exception(\"Testing Celery exception\")\r\n```\r\n\r\nThe error I get printed is:\r\n```\r\n[2019-03-08 21:24:21,117: ERROR/ForkPoolWorker-31] Task simple_task[d6e959b1-7253-4e55-861d-c1968ae14e1c] raised unexpected: RuntimeError('No active exception to reraise')\r\nTraceback (most recent call last):\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/celery/app/trace.py\", line 382, in trace_task\r\n    R = retval = fun(*args, **kwargs)\r\n  File \"/Users/okomarov/Documents/repos/myproject/web/celery_factory.py\", line 28, in __call__\r\n    return TaskBase.__call__(self, *args, **kwargs)\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/celery/app/trace.py\", line 641, in __protected_call__\r\n    return self.run(*args, **kwargs)\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py\", line 66, in _inner\r\n    reraise(*_capture_exception())\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/_compat.py\", line 52, in reraise\r\n    raise value\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py\", line 64, in _inner\r\n    return f(*args, **kwargs)\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py\", line 66, in _inner\r\n    reraise(*_capture_exception())\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/_compat.py\", line 52, in reraise\r\n    raise value\r\n  File \"/Users/okomarov/.virtualenvs/myenv/lib/python3.7/site-packages/sentry_sdk/integrations/celery.py\", line 64, in _inner\r\n    return f(*args, **kwargs)\r\n  File \"/Users/okomarov/Documents/repos/myproject/web/simple_task.py\", line 4, in simple_task\r\n    raise Exception(\"Testing Celery exception\")\r\nRuntimeError: No active exception to reraise\r\n```\r\n\r\nRelevant pip packages:\r\n```\r\nCelery==4.2.1\r\nFlask==1.0.2\r\nsentry-sdk==0.7.4\r\n```\r\n\r\nThe integration is called as following (flask integration works as expected):\r\n```\r\nfrom flask import Flask\r\nfrom celery_factory import celery_app\r\nfrom config import config_to_use\r\n\r\n\r\ndef create_app():\r\n    app = Flask()\r\n    app.config.from_object(config_to_use)\r\n\r\n    init_logging(app)\r\n\r\n    register_extensions(app)\r\n    register_blueprints(app)\r\n    register_jinja_extras(app)\r\n\r\n    return app\r\n\r\n\r\ndef init_logging(app):\r\n    import sentry_sdk\r\n    from sentry_sdk.integrations.flask import FlaskIntegration\r\n    from sentry_sdk.integrations.celery import CeleryIntegration\r\n\r\n    sentry_sdk.init(\r\n        dsn=app.config.get('FLASK_SENTRY_DSN'),\r\n        integrations=[FlaskIntegration(), CeleryIntegration()]\r\n        )\r\n\r\n...\r\n```\n", "before_files": [{"content": "from __future__ import absolute_import\n\nimport sys\n\nfrom celery.exceptions import SoftTimeLimitExceeded, Retry  # type: ignore\n\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.utils import capture_internal_exceptions, event_from_exception\nfrom sentry_sdk._compat import reraise\nfrom sentry_sdk.integrations import Integration\nfrom sentry_sdk.integrations.logging import ignore_logger\n\n\nclass CeleryIntegration(Integration):\n    identifier = \"celery\"\n\n    @staticmethod\n    def setup_once():\n        import celery.app.trace as trace  # type: ignore\n\n        old_build_tracer = trace.build_tracer\n\n        def sentry_build_tracer(name, task, *args, **kwargs):\n            # Need to patch both methods because older celery sometimes\n            # short-circuits to task.run if it thinks it's safe.\n            task.__call__ = _wrap_task_call(task.__call__)\n            task.run = _wrap_task_call(task.run)\n            return _wrap_tracer(task, old_build_tracer(name, task, *args, **kwargs))\n\n        trace.build_tracer = sentry_build_tracer\n\n        # This logger logs every status of every task that ran on the worker.\n        # Meaning that every task's breadcrumbs are full of stuff like \"Task\n        # <foo> raised unexpected <bar>\".\n        ignore_logger(\"celery.worker.job\")\n\n\ndef _wrap_tracer(task, f):\n    # Need to wrap tracer for pushing the scope before prerun is sent, and\n    # popping it after postrun is sent.\n    #\n    # This is the reason we don't use signals for hooking in the first place.\n    # Also because in Celery 3, signal dispatch returns early if one handler\n    # crashes.\n    def _inner(*args, **kwargs):\n        hub = Hub.current\n        if hub.get_integration(CeleryIntegration) is None:\n            return f(*args, **kwargs)\n\n        with hub.push_scope() as scope:\n            scope._name = \"celery\"\n            scope.add_event_processor(_make_event_processor(task, *args, **kwargs))\n\n            return f(*args, **kwargs)\n\n    return _inner\n\n\ndef _wrap_task_call(f):\n    # Need to wrap task call because the exception is caught before we get to\n    # see it. Also celery's reported stacktrace is untrustworthy.\n    def _inner(*args, **kwargs):\n        try:\n            return f(*args, **kwargs)\n        except Exception:\n            reraise(*_capture_exception())\n\n    return _inner\n\n\ndef _make_event_processor(task, uuid, args, kwargs, request=None):\n    def event_processor(event, hint):\n        with capture_internal_exceptions():\n            event[\"transaction\"] = task.name\n\n        with capture_internal_exceptions():\n            extra = event.setdefault(\"extra\", {})\n            extra[\"celery-job\"] = {\n                \"task_name\": task.name,\n                \"args\": args,\n                \"kwargs\": kwargs,\n            }\n\n        if \"exc_info\" in hint:\n            with capture_internal_exceptions():\n                if isinstance(hint[\"exc_info\"][1], Retry):\n                    return None\n\n                if hasattr(task, \"throws\") and isinstance(\n                    hint[\"exc_info\"][1], task.throws\n                ):\n                    return None\n\n            with capture_internal_exceptions():\n                if issubclass(hint[\"exc_info\"][0], SoftTimeLimitExceeded):\n                    event[\"fingerprint\"] = [\n                        \"celery\",\n                        \"SoftTimeLimitExceeded\",\n                        getattr(task, \"name\", task),\n                    ]\n\n        return event\n\n    return event_processor\n\n\ndef _capture_exception():\n    hub = Hub.current\n    exc_info = sys.exc_info()\n\n    if hub.get_integration(CeleryIntegration) is not None:\n        event, hint = event_from_exception(\n            exc_info,\n            client_options=hub.client.options,\n            mechanism={\"type\": \"celery\", \"handled\": False},\n        )\n        hub.capture_event(event, hint=hint)\n\n    return exc_info\n", "path": "sentry_sdk/integrations/celery.py"}], "after_files": [{"content": "from __future__ import absolute_import\n\nimport sys\n\nfrom celery.exceptions import SoftTimeLimitExceeded, Retry  # type: ignore\n\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.utils import capture_internal_exceptions, event_from_exception\nfrom sentry_sdk._compat import reraise\nfrom sentry_sdk.integrations import Integration\nfrom sentry_sdk.integrations.logging import ignore_logger\n\n\nclass CeleryIntegration(Integration):\n    identifier = \"celery\"\n\n    @staticmethod\n    def setup_once():\n        import celery.app.trace as trace  # type: ignore\n\n        old_build_tracer = trace.build_tracer\n\n        def sentry_build_tracer(name, task, *args, **kwargs):\n            # Need to patch both methods because older celery sometimes\n            # short-circuits to task.run if it thinks it's safe.\n            task.__call__ = _wrap_task_call(task, task.__call__)\n            task.run = _wrap_task_call(task, task.run)\n            return _wrap_tracer(task, old_build_tracer(name, task, *args, **kwargs))\n\n        trace.build_tracer = sentry_build_tracer\n\n        _patch_worker_exit()\n\n        # This logger logs every status of every task that ran on the worker.\n        # Meaning that every task's breadcrumbs are full of stuff like \"Task\n        # <foo> raised unexpected <bar>\".\n        ignore_logger(\"celery.worker.job\")\n\n\ndef _wrap_tracer(task, f):\n    # Need to wrap tracer for pushing the scope before prerun is sent, and\n    # popping it after postrun is sent.\n    #\n    # This is the reason we don't use signals for hooking in the first place.\n    # Also because in Celery 3, signal dispatch returns early if one handler\n    # crashes.\n    def _inner(*args, **kwargs):\n        hub = Hub.current\n        if hub.get_integration(CeleryIntegration) is None:\n            return f(*args, **kwargs)\n\n        with hub.push_scope() as scope:\n            scope._name = \"celery\"\n            scope.add_event_processor(_make_event_processor(task, *args, **kwargs))\n\n            return f(*args, **kwargs)\n\n    return _inner\n\n\ndef _wrap_task_call(task, f):\n    # Need to wrap task call because the exception is caught before we get to\n    # see it. Also celery's reported stacktrace is untrustworthy.\n    def _inner(*args, **kwargs):\n        try:\n            return f(*args, **kwargs)\n        except Exception:\n            exc_info = sys.exc_info()\n            with capture_internal_exceptions():\n                _capture_exception(task, exc_info)\n            reraise(*exc_info)\n\n    return _inner\n\n\ndef _make_event_processor(task, uuid, args, kwargs, request=None):\n    def event_processor(event, hint):\n        with capture_internal_exceptions():\n            event[\"transaction\"] = task.name\n\n        with capture_internal_exceptions():\n            extra = event.setdefault(\"extra\", {})\n            extra[\"celery-job\"] = {\n                \"task_name\": task.name,\n                \"args\": args,\n                \"kwargs\": kwargs,\n            }\n\n        if \"exc_info\" in hint:\n            with capture_internal_exceptions():\n                if issubclass(hint[\"exc_info\"][0], SoftTimeLimitExceeded):\n                    event[\"fingerprint\"] = [\n                        \"celery\",\n                        \"SoftTimeLimitExceeded\",\n                        getattr(task, \"name\", task),\n                    ]\n\n        return event\n\n    return event_processor\n\n\ndef _capture_exception(task, exc_info):\n    hub = Hub.current\n\n    if hub.get_integration(CeleryIntegration) is None:\n        return\n    if isinstance(exc_info[1], Retry):\n        return\n    if hasattr(task, \"throws\") and isinstance(exc_info[1], task.throws):\n        return\n\n    event, hint = event_from_exception(\n        exc_info,\n        client_options=hub.client.options,\n        mechanism={\"type\": \"celery\", \"handled\": False},\n    )\n\n    hub.capture_event(event, hint=hint)\n\n\ndef _patch_worker_exit():\n    # Need to flush queue before worker shutdown because a crashing worker will\n    # call os._exit\n    from billiard.pool import Worker  # type: ignore\n\n    old_workloop = Worker.workloop\n\n    def sentry_workloop(*args, **kwargs):\n        try:\n            return old_workloop(*args, **kwargs)\n        finally:\n            with capture_internal_exceptions():\n                hub = Hub.current\n                if hub.get_integration(CeleryIntegration) is not None:\n                    hub.flush()\n\n    Worker.workloop = sentry_workloop\n", "path": "sentry_sdk/integrations/celery.py"}]}