Sentry Crons for Python: Send Check-ins to Cuckoo
Updated
If your Python jobs are already instrumented with the Sentry SDK's cron monitoring, you can send those check-ins
to Cuckoo without rewriting the code. Cuckoo accepts Sentry check_in envelopes. Point the SDK at a
Cuckoo DSN and every @monitor or capture_checkin call becomes a heartbeat. Missed, failed and overrunning
jobs then alert through Cuckoo's email, Slack, Discord or webhook channels.
This guide covers the decorator, manual check-ins, Celery beat auto-instrumentation, and the one real catch: what happens to error events.
How Cuckoo sees Sentry check-ins
- Your organisation has one DSN, shown in the Cuckoo dashboard:
https://PUBLIC_KEY@cuckoo.metastic.ch/PROJECT_ID. - Monitors are matched by monitor slug. If a check-in carries a
monitor_config, Cuckoo creates the monitor or updates it with the schedule, time zone, check-in margin and max runtime. in_progresscounts as a start,okas success,erroras failure. A missing check-in after schedule plus margin counts as late.- Only check-ins are accepted. Error and transaction events sent to this DSN are dropped.
Step 1: Initialise the SDK with the Cuckoo DSN
import sentry_sdk
sentry_sdk.init(
dsn="https://PUBLIC_KEY@cuckoo.metastic.ch/PROJECT_ID",
environment="production",
# Only needed if the brotli package is installed: Cuckoo accepts gzip and deflate.
_experiments={"transport_compression_algo": "gzip"},
)
Step 2: The @monitor decorator
monitor sends in_progress when the function starts, then ok or error when it ends. Exceptions are
re-raised. With monitor_config, the monitor is created or updated automatically:
from sentry_sdk.crons import monitor
MONITOR_CONFIG = {
"schedule": {"type": "crontab", "value": "0 3 * * *"},
"timezone": "Europe/Zurich",
"checkin_margin": 15, # minutes of grace
"max_runtime": 60, # minutes before an in-progress run counts as failed
}
@monitor(monitor_slug="nightly-export", monitor_config=MONITOR_CONFIG)
def nightly_export():
export_orders()
It also works as a context manager: with monitor(monitor_slug="nightly-export"):. Interval schedules use
{"type": "interval", "value": 1, "unit": "hour"}, and the value must be an integer.
Step 3: Manual check-ins with capture_checkin
When start and end happen in different places, send the check-ins yourself and pass the returned id along:
from sentry_sdk.crons import capture_checkin
from sentry_sdk.crons.consts import MonitorStatus
check_in_id = capture_checkin(
monitor_slug="hourly-sync",
status=MonitorStatus.IN_PROGRESS,
monitor_config={"schedule": {"type": "crontab", "value": "0 * * * *"}, "timezone": "UTC"},
)
try:
sync_inventory()
capture_checkin(monitor_slug="hourly-sync", check_in_id=check_in_id, status=MonitorStatus.OK)
except Exception:
capture_checkin(monitor_slug="hourly-sync", check_in_id=check_in_id, status=MonitorStatus.ERROR)
raise
Celery beat auto-instrumentation
CeleryIntegration(monitor_beat_tasks=True) reports every task in the beat schedule, with no decorators:
import sentry_sdk
from celery import signals
from sentry_sdk.integrations.celery import CeleryIntegration
@signals.beat_init.connect # omit if beat runs inside the worker (-B)
@signals.celeryd_init.connect
def init_sentry(**kwargs):
sentry_sdk.init(
dsn="https://PUBLIC_KEY@cuckoo.metastic.ch/PROJECT_ID",
integrations=[CeleryIntegration(monitor_beat_tasks=True, exclude_beat_tasks=["debug-.*"])],
)
How it behaves:
- The beat process sends
in_progresswhen it dispatches a task, together with amonitor_configbuilt from thecrontabor interval schedule and the Celery time zone. Initialise the SDK in beat too, not only in workers. - The worker sends
ok, orerroron failure. Retries also send anerrorcheck-in. - The monitor slug is the beat schedule entry name. Use short, slug-like keys such as
cleanup-sessionsinbeat_schedule. - Don't combine this with
@monitoron the same task, or you get duplicate check-ins.
The catch: errors and the DSN
The SDK sends everything to one DSN per client. If you call sentry_sdk.init() with the Cuckoo DSN, your
exceptions also go to Cuckoo and are dropped. Simple, honest options:
- Separate processes, separate DSNs. Run the scheduled jobs in their own process (a dedicated Celery worker and beat, or a cron container) initialised with the Cuckoo DSN. Your web app keeps its real Sentry DSN. Errors from the job process are then not tracked in Sentry. Log them, or use option 2 for that process.
- Keep Sentry for errors, use plain HTTP pings for heartbeats. Leave
sentry_sdk.init()on your Sentry DSN and ping Cuckoo directly, for examplerequests.get("https://cuckoo.metastic.ch/ping/YOUR-UUID", timeout=10), plus/startand/fail. See the Celery beat and APScheduler guides for complete helpers. - Use Cuckoo only for crons. If you don't use Sentry for errors at all, point the SDK at Cuckoo and you're done.
The SDK also has lower-level Client and Scope APIs, but integrations such as CeleryIntegration report
through the globally initialised client. This guide doesn't depend on wiring a second client.
Schedule and margin advice
checkin_marginis Cuckoo's grace time in minutes. Start with the normal runtime plus a few minutes.- If you leave out
checkin_marginormax_runtime, Cuckoo applies Sentry's defaults: a 1-minute margin (raised to Cuckoo's minimum if needed) and a 30-minute max runtime. max_runtimeonly works whenin_progressis sent, which@monitorand the Celery integration both do.- Always set
timezonefor crontab schedules, otherwise the schedule is read as UTC.
Troubleshooting
- Monitor never appears: the first check-in had no
monitor_config, so Cuckoo had nothing to create it from. Add the config, or create the monitor with that slug in the dashboard. - Celery tasks not reported: the SDK isn't initialised in the beat process, or the entry matches
exclude_beat_tasks. - Errors missing in Sentry: that process uses the Cuckoo DSN. See the DSN section above.
- HTTP 415 in the SDK debug log: the SDK compresses with brotli because the
brotlipackage is installed. Settransport_compression_algotogzipas in step 1.
FAQ
Do I need to change my monitor slugs when switching to Cuckoo?
No. Cuckoo matches monitors by slug and creates them from monitor_config, so existing decorators work as
they are. Only the DSN changes.
Are failure_issue_threshold and recovery_threshold used?
Cuckoo creates monitors from schedule, time zone, check-in margin and max runtime. Other monitor_config keys
are ignored, and alerts follow Cuckoo's own late, failure and recovery rules.
Can I mix SDK check-ins and plain ping URLs?
Yes. SDK monitors and URL-based checks live side by side in the same Cuckoo account, so you can migrate one job at a time.
Related guides
- Sentry Crons for Node.js: Send Check-ins to CuckooUse @sentry/node cron monitoring with Cuckoo: withMonitor, captureCheckIn, monitorConfig and the node-cron and cron instrumentation, plus the DSN trade-offs.
- Cron Job Start and Fail SignalsSend /start, success and /fail pings from cron jobs to measure run time and catch hung jobs. Includes a reusable bash wrapper script with trap and exit codes.
- Cron Schedule, Time Zone and Grace PeriodPick the right monitoring schedule for a cron job: period or cron expression, IANA time zones, how cron handles daylight saving time, and sizing grace time.
- Dead Man's Switch Monitoring ExplainedWhat dead man's switch monitoring is, how push-based heartbeat checks differ from pull-based uptime monitoring, and which jobs and systems benefit from each.