Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Sentry Crons to Celery Beat #1935

Merged
merged 14 commits into from Mar 16, 2023
Merged
9 changes: 7 additions & 2 deletions sentry_sdk/client.py
Expand Up @@ -441,9 +441,11 @@ def capture_event(
.pop("dynamic_sampling_context", {})
)

# Transactions or events with attachments should go to the /envelope/
is_checkin = event_opt.get("type") == "check_in"

# Transactions, events with attachments, and checkins should go to the /envelope/
# endpoint.
if is_transaction or attachments:
if is_transaction or is_checkin or attachments:

headers = {
"event_id": event_opt["event_id"],
Expand All @@ -459,11 +461,14 @@ def capture_event(
if profile is not None:
envelope.add_profile(profile.to_json(event_opt, self.options))
envelope.add_transaction(event_opt)
elif is_checkin:
envelope.add_checkin(event_opt)
else:
envelope.add_event(event_opt)

for attachment in attachments or ():
envelope.add_item(attachment.to_envelope_item())

self.transport.capture_envelope(envelope)
else:
# All other events go to the /store/ endpoint.
Expand Down
72 changes: 72 additions & 0 deletions sentry_sdk/crons.py
@@ -0,0 +1,72 @@
from functools import wraps
import sys
import uuid

from sentry_sdk import Hub
from sentry_sdk._compat import reraise


class MonitorStatus:
IN_PROGRESS = "in_progress"
OK = "ok"
ERROR = "error"


def _create_checkin_event(monitor_id=None, status=None):
checkin = {
"type": "check_in",
"monitor_id": monitor_id,
"check_in_id": uuid.uuid4().hex,
"status": status,
}

return checkin


def capture_checkin(monitor_id=None, status=None):
hub = Hub.current
checkin_event = _create_checkin_event(monitor_id, status)
hub.capture_event(checkin_event)


def monitor(monitor_id=None):
"""
Decorator to capture checkin events for a monitor.

Usage:
```
from sentry_sdk.crons import monitor

app = Celery()

@app.task
@monitor(monitor_id='3475c0de-0258-44fc-8c88-07350cb7f9af')
antonpirker marked this conversation as resolved.
Show resolved Hide resolved
def test(arg):
print(arg)
```

This does not have to be used with Celery, but if you do use it with celery,
put the `monitor` decorator under Celery's `@app.task` decorator.
"""

def decorate(func):
if not monitor_id:
return func

@wraps(func)
def wrapper(*args, **kwargs):
capture_checkin(monitor_id=monitor_id, status=MonitorStatus.IN_PROGRESS)

try:
result = func(*args, **kwargs)
except Exception:
capture_checkin(monitor_id=monitor_id, status=MonitorStatus.ERROR)
exc_info = sys.exc_info()
reraise(*exc_info)

capture_checkin(monitor_id=monitor_id, status=MonitorStatus.OK)
return result

return wrapper

return decorate
6 changes: 6 additions & 0 deletions sentry_sdk/envelope.py
Expand Up @@ -68,6 +68,12 @@ def add_profile(
# type: (...) -> None
self.add_item(Item(payload=PayloadRef(json=profile), type="profile"))

def add_checkin(
self, checkin # type: Any
):
# type: (...) -> None
self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in"))

def add_session(
self, session # type: Union[Session, Any]
):
Expand Down