|
| 1 | +from __future__ import absolute_import |
| 2 | + |
| 3 | +import weakref |
| 4 | + |
| 5 | +from sentry_sdk.hub import Hub |
| 6 | +from sentry_sdk.integrations import Integration |
| 7 | +from sentry_sdk.utils import capture_internal_exceptions, event_from_exception |
| 8 | + |
| 9 | +from rq.timeouts import JobTimeoutException |
| 10 | +from rq.worker import Worker |
| 11 | + |
| 12 | + |
| 13 | +class RqIntegration(Integration): |
| 14 | + identifier = "rq" |
| 15 | + |
| 16 | + @staticmethod |
| 17 | + def setup_once(): |
| 18 | + |
| 19 | + old_perform_job = Worker.perform_job |
| 20 | + |
| 21 | + def sentry_patched_perform_job(self, job, *args, **kwargs): |
| 22 | + hub = Hub.current |
| 23 | + integration = hub.get_integration(RqIntegration) |
| 24 | + |
| 25 | + if integration is None: |
| 26 | + return old_perform_job(self, job, *args, **kwargs) |
| 27 | + |
| 28 | + with hub.push_scope() as scope: |
| 29 | + scope.add_event_processor(_make_event_processor(weakref.ref(job))) |
| 30 | + return old_perform_job(self, job, *args, **kwargs) |
| 31 | + |
| 32 | + Worker.perform_job = sentry_patched_perform_job |
| 33 | + |
| 34 | + old_handle_exception = Worker.handle_exception |
| 35 | + |
| 36 | + def sentry_patched_handle_exception(self, job, *exc_info, **kwargs): |
| 37 | + _capture_exception(exc_info) |
| 38 | + return old_handle_exception(self, job, *exc_info, **kwargs) |
| 39 | + |
| 40 | + Worker.handle_exception = sentry_patched_handle_exception |
| 41 | + |
| 42 | + |
| 43 | +def _make_event_processor(weak_job): |
| 44 | + def event_processor(event, hint): |
| 45 | + job = weak_job() |
| 46 | + if job is not None: |
| 47 | + with capture_internal_exceptions(): |
| 48 | + if "transaction" not in event: |
| 49 | + event["transaction"] = job.func_name |
| 50 | + |
| 51 | + with capture_internal_exceptions(): |
| 52 | + extra = event.setdefault("extra", {}) |
| 53 | + extra["rq-job"] = { |
| 54 | + "job_id": job.id, |
| 55 | + "func": job.func_name, |
| 56 | + "args": job.args, |
| 57 | + "kwargs": job.kwargs, |
| 58 | + "description": job.description, |
| 59 | + } |
| 60 | + |
| 61 | + if "exc_info" in hint: |
| 62 | + with capture_internal_exceptions(): |
| 63 | + if issubclass(hint["exc_info"][0], JobTimeoutException): |
| 64 | + event["fingerprint"] = ["rq", "JobTimeoutException", job.func_name] |
| 65 | + |
| 66 | + return event |
| 67 | + |
| 68 | + return event_processor |
| 69 | + |
| 70 | + |
| 71 | +def _capture_exception(exc_info, **kwargs): |
| 72 | + hub = Hub.current |
| 73 | + if hub.get_integration(RqIntegration) is None: |
| 74 | + return |
| 75 | + event, hint = event_from_exception( |
| 76 | + exc_info, |
| 77 | + client_options=hub.client.options, |
| 78 | + mechanism={"type": "rq", "handled": False}, |
| 79 | + ) |
| 80 | + |
| 81 | + hub.capture_event(event, hint=hint) |
0 commit comments