Dead-simple cron job monitoring with auto-registration. One primary function: monitor(). Advanced: sync(). Self-healing monitors.

Maintainers

Package info

github.com/cronradar/cronradar-php

pkg:composer/cronradar/php

Statistics

Installs: 4

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.0.7 2026-04-29 23:14 UTC

This package is auto-updated.

Last update: 2026-05-09 20:49:12 UTC


README

The base SDK for monitoring scheduled jobs in PHP. Wrap a callable, send a heartbeat, or track full lifecycle — CronRadar tracks duration, alerts on missed schedules, and recovers from accidental monitor deletion.

For Laravel scheduler auto-discovery, install the cronradar/laravel extension on top of this base.

Install

composer require cronradar/cronradar

PHP 8.1+ supported.

Setup

The SDK reads its API key from the CRONRADAR_API_KEY environment variable.

export CRONRADAR_API_KEY=ck_app_xxxxxxxxxxxxxxxxxxxx

Or in .env:

CRONRADAR_API_KEY=ck_app_xxxxxxxxxxxxxxxxxxxx

Get the API key from the CronRadar dashboard at app.cronradar.com under your application's settings. API keys have the form ck_app_<random>.

The SDK does not require explicit initialization. The first call reads the env var; if it's missing, the SDK logs a single warning to error_log and silently no-ops every subsequent call (per the "never break a user's job" guarantee).

Quickstart

<?php
use CronRadar\CronRadar;

// First-time call: pass schedule so the monitor auto-registers.
CronRadar::monitor('daily-backup', '0 2 * * *');

// Subsequent calls: just the key.
CronRadar::monitor('daily-backup');

That's the minimal integration. CronRadar:

  • Creates the daily-backup monitor on the first ping (because schedule was provided)
  • Records each subsequent ping
  • Alerts you (email + any other configured channels) if the monitor doesn't ping within 0 2 * * * plus its grace period
  • Re-creates the monitor if you delete it from the dashboard and the schedule param is still being passed

Manual lifecycle

For finer-grained tracking — duration measurement, explicit failure messages, hung-job detection — use the lifecycle endpoints.

Wrapper (recommended)

<?php
use CronRadar\CronRadar;

$backupJob = CronRadar::wrap('daily-backup', fn() => runBackup(), '0 2 * * *');
$result = $backupJob();

The wrapper:

  • Calls startJob before invoking your callable
  • Calls completeJob if it returns
  • Calls failJob with the exception message if it throws
  • Re-throws the original exception so your error handlers run normally
  • Auto-registers the monitor on first run if schedule is provided

Manual call style

<?php
use CronRadar\CronRadar;

CronRadar::startJob('daily-backup');
try {
    runBackup();
    CronRadar::completeJob('daily-backup');
} catch (\Throwable $e) {
    CronRadar::failJob('daily-backup', $e->getMessage());
    throw $e;   // always re-throw — monitoring observes, never alters behavior
}

Reference

CronRadar::monitor(string $monitorKey, ?string $schedule = null): void

Records a successful execution.

CronRadar::monitor('daily-backup');
CronRadar::monitor('daily-backup', '0 2 * * *');
Parameter Type Required Notes
$monitorKey string yes Monitor key. Lowercase, kebab/snake/dot-case. Max 200 chars.
$schedule ?string optional Standard 5-field cron expression. Required on the first ping for self-healing registration.

Throws: never. Network errors and 4xx/5xx responses are caught and logged via error_log.

Grace period is configured per-monitor on CronRadar (default 60 seconds; set via the dashboard or the gracePeriod argument to syncMonitor). It's not a per-call argument.

CronRadar::startJob(string $key): void

Records that a job has begun executing.

CronRadar::startJob('daily-backup');

Used in conjunction with completeJob or failJob to measure duration and detect hung jobs.

CronRadar::completeJob(string $key): void

Records successful completion.

CronRadar::completeJob('daily-backup');

Pair with a prior startJob($key) to record duration in the dashboard.

CronRadar::failJob(string $key, ?string $message = null): void

Records explicit failure.

CronRadar::failJob('daily-backup', 'Database connection refused');
Parameter Type Required Notes
$key string yes Monitor key.
$message ?string optional Human-readable failure detail, shown in the dashboard.

Triggers an immediate alert (no grace period). The message appears in the alert payload.

CronRadar::wrap(string $monitorKey, callable $fn, ?string $schedule = null): callable

Wraps a callable with full lifecycle tracking. Returns a callable that, when invoked, calls $fn with start/complete/fail tracking.

$wrapped = CronRadar::wrap('daily-backup', fn() => runBackup(), '0 2 * * *');
$result = $wrapped();
Parameter Type Required Notes
$monitorKey string yes Monitor key.
$fn callable yes The callable to instrument.
$schedule ?string optional Cron expression for self-healing registration.

The wrapped callable returns whatever $fn returns. If $fn throws, the wrapper re-throws after recording the failure. Monitoring never swallows your exceptions.

CronRadar::syncMonitor(string $key, string $schedule, ?string $source = null, ?string $name = null): void

Pre-registers a monitor without sending a ping. Used by the cronradar/laravel extension during application startup.

CronRadar::syncMonitor('daily-backup', '0 2 * * *', 'laravel', 'Daily Backup');
Parameter Type Required Notes
$key string yes Monitor key.
$schedule string yes Cron expression.
$source ?string optional Identifier for the framework or origin.
$name ?string optional Human-readable display name.

Used internally by extensions; rarely needed in application code.

Configuration

Environment variable Required Default Purpose
CRONRADAR_API_KEY yes API key from the CronRadar dashboard. Format ck_app_xxxxx.
CRONRADAR_DEBUG no false Set to true to log every request and error via error_log.

The base URL (https://cron.life), HTTP timeout (5 seconds), and default grace period (60 seconds) are constants in src/Constants.php. To customize them, fork the SDK or open an issue for a configurable hook.

Error handling

The SDK upholds two hard guarantees:

  1. Never throws to user code. Network errors, timeouts, 4xx/5xx responses, malformed payloads — all caught and logged via error_log. A failing CronRadar API must not break a user's cron job.
  2. Re-throws user-job exceptions. When using wrap, the original exception from your callable always propagates. Monitoring observes; it does not change behavior.

What this looks like at runtime:

Situation SDK behavior Your code
Network unreachable Logs to error_log; returns Continues normally
5-second timeout Logs to error_log; returns Continues normally
401 Unauthorized Logs to error_log; returns Continues normally
Wrapped callable throws Records failJob; re-throws Receives the original exception
CRONRADAR_API_KEY missing Logs once per process; subsequent calls no-op Continues normally

If you need to know whether a ping succeeded — for example, in tests — every static method returns void either way; success is silent. Set CRONRADAR_DEBUG=true to log every request and error via error_log.

Troubleshooting

401 Unauthorized in error_log

Your CRONRADAR_API_KEY is wrong or missing. Verify:

echo $CRONRADAR_API_KEY    # should print ck_app_xxxxx

The key must match the one shown on app.cronradar.com → your application → Settings → API Keys. Keys are not retrievable after creation — if you lost it, create a new one and rotate.

404 Monitor Not Found on first ping

You called monitor($key) without a $schedule for a brand-new key. The first ping must include $schedule so CronRadar can register the monitor:

CronRadar::monitor('daily-backup', '0 2 * * *');

Subsequent pings can omit $schedule. If you delete the monitor from the dashboard and the next ping doesn't include $schedule, you'll see the same 404.

My cron expression is rejected

CronRadar uses the standard 5-field POSIX cron format: minute hour day-of-month month day-of-week. Six-field expressions (with seconds) and Quartz-style 7-field expressions are not accepted. Common pitfalls:

Wrong Right
0 0 2 * * * (6 fields) 0 2 * * *
0 2 ? * MON-FRI (Quartz ?) 0 2 * * 1-5

The job runs but I never see a ping in the dashboard

Three things to check, in order:

  1. API key — see "401 Unauthorized" above.
  2. Network — outbound HTTPS to https://cron.life may be blocked by firewall.
  3. Process termination — short-lived CLI scripts using fire-and-forget transports may exit before the HTTP request completes. The default transport is synchronous so this is uncommon.

I want to see what the SDK is doing in development

By default the SDK is silent. To trace every request and error:

CRONRADAR_DEBUG=true php my_job.php

If CRONRADAR_API_KEY is unset entirely, the SDK no-ops every call without logging.

Links

License

© 2026 CronRadar · Proprietary — see LICENSE.