Skip to content

Celery Beat auto monitoring #1967

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

Merged
merged 33 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e8a071b
Reorganized code in multiple modules
antonpirker Mar 17, 2023
bb3f317
Fixed ordering of imports
antonpirker Mar 21, 2023
4f7ea21
Initial version of auto cron monitoring for celery beat
antonpirker Mar 21, 2023
f23c57f
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Mar 21, 2023
4d7d2d5
Add signal receivers only to patched beat tasks
antonpirker Mar 21, 2023
e701ab6
Fixed test in python 2
antonpirker Mar 21, 2023
efad00a
Added timezone to monitor_config
antonpirker Mar 21, 2023
efce5d9
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Mar 22, 2023
30e94b3
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Mar 22, 2023
9a2f54b
Added tests
antonpirker Mar 22, 2023
de96d3a
Merge branch 'antonpirker/celery-auto-monitor-tasks' of github.com:ge…
antonpirker Mar 22, 2023
62acad0
Skipping tests when celery is not installed
antonpirker Mar 22, 2023
a093d19
Nanoseconds is overkill (and does not work in Python 2)
antonpirker Mar 23, 2023
504c36d
Skipping tests when celery is not installed
antonpirker Mar 23, 2023
69d4ed3
Do not change existing interface
antonpirker Mar 23, 2023
d6c51cf
Removed unused parameter
antonpirker Mar 23, 2023
e012842
Added missing file
antonpirker Mar 23, 2023
bca021a
More tests
antonpirker Mar 23, 2023
f092d71
Trigger ci
antonpirker Mar 23, 2023
88d1a55
Some polishing
antonpirker Mar 24, 2023
a557813
Nicer display of interval
antonpirker Mar 24, 2023
d32c5cb
Updated because of changes in monitor schedule format
antonpirker Mar 27, 2023
005a645
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Mar 29, 2023
4863923
Fixed schedule interval
antonpirker Mar 29, 2023
965cbfb
Fixed tests
antonpirker Mar 29, 2023
6124d8f
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Mar 30, 2023
7add608
Autodiscover celery app and introduce flag for enabling monitoring
antonpirker Apr 3, 2023
dd0063a
Merge branch 'antonpirker/celery-auto-monitor-tasks' of github.com:ge…
antonpirker Apr 3, 2023
c237307
Merge branch 'master' into antonpirker/celery-auto-monitor-tasks
antonpirker Apr 3, 2023
9f1036a
Pinning dependency that brakes tests
antonpirker Apr 4, 2023
62230fb
Merge branch 'antonpirker/celery-auto-monitor-tasks' of github.com:ge…
antonpirker Apr 4, 2023
a3ba739
Pinned another dependency that breaks tests (this time with correct s…
antonpirker Apr 4, 2023
782f139
Fixed package name
antonpirker Apr 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 0 additions & 123 deletions sentry_sdk/crons.py

This file was deleted.

3 changes: 3 additions & 0 deletions sentry_sdk/crons/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from sentry_sdk.crons.api import capture_checkin # noqa
from sentry_sdk.crons.consts import MonitorStatus # noqa
from sentry_sdk.crons.decorator import monitor # noqa
56 changes: 56 additions & 0 deletions sentry_sdk/crons/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import uuid

from sentry_sdk import Hub
from sentry_sdk._types import TYPE_CHECKING


if TYPE_CHECKING:
from typing import Any, Dict, Optional


def _create_check_in_event(
monitor_slug=None,
check_in_id=None,
status=None,
duration_s=None,
monitor_config=None,
):
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> Dict[str, Any]
options = Hub.current.client.options if Hub.current.client else {}
check_in_id = check_in_id or uuid.uuid4().hex # type: str

check_in = {
"type": "check_in",
"monitor_slug": monitor_slug,
"monitor_config": monitor_config or {},
"check_in_id": check_in_id,
"status": status,
"duration": duration_s,
"environment": options.get("environment", None),
"release": options.get("release", None),
}

return check_in


def capture_checkin(
monitor_slug=None,
check_in_id=None,
status=None,
duration=None,
monitor_config=None,
):
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> str
hub = Hub.current

check_in_id = check_in_id or uuid.uuid4().hex
check_in_event = _create_check_in_event(
monitor_slug=monitor_slug,
check_in_id=check_in_id,
status=status,
duration_s=duration,
monitor_config=monitor_config,
)
hub.capture_event(check_in_event)

return check_in_event["check_in_id"]
4 changes: 4 additions & 0 deletions sentry_sdk/crons/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class MonitorStatus:
IN_PROGRESS = "in_progress"
OK = "ok"
ERROR = "error"
74 changes: 74 additions & 0 deletions sentry_sdk/crons/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from functools import wraps
import sys

from sentry_sdk._compat import reraise
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.crons import capture_checkin
from sentry_sdk.crons.consts import MonitorStatus
from sentry_sdk.utils import now


if TYPE_CHECKING:
from typing import Any, Callable, Optional


def monitor(monitor_slug=None):
# type: (Optional[str]) -> Callable[..., Any]
"""
Decorator to capture checkin events for a monitor.

Usage:
```
import sentry_sdk

app = Celery()

@app.task
@sentry_sdk.monitor(monitor_slug='my-fancy-slug')
def test(arg):
print(arg)
```

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

def decorate(func):
# type: (Callable[..., Any]) -> Callable[..., Any]
if not monitor_slug:
return func

@wraps(func)
def wrapper(*args, **kwargs):
# type: (*Any, **Any) -> Any
start_timestamp = now()
check_in_id = capture_checkin(
monitor_slug=monitor_slug, status=MonitorStatus.IN_PROGRESS
)

try:
result = func(*args, **kwargs)
except Exception:
duration_s = now() - start_timestamp
capture_checkin(
monitor_slug=monitor_slug,
check_in_id=check_in_id,
status=MonitorStatus.ERROR,
duration=duration_s,
)
exc_info = sys.exc_info()
reraise(*exc_info)

duration_s = now() - start_timestamp
capture_checkin(
monitor_slug=monitor_slug,
check_in_id=check_in_id,
status=MonitorStatus.OK,
duration=duration_s,
)

return result

return wrapper

return decorate
Loading