Cron Monitor

توضیحات

WordPress runs background tasks, called cron jobs, for things like publishing scheduled posts,
sending emails and running backups. It keeps no record of them, so when one fails or stops you
never find out.

Cron Monitor keeps that record. It shows what ran, what failed and which plugin is behind each
job, and it tells you when something goes wrong.

Features

  • Run history: every job that ran, how long it took and whether it worked.
  • Catches crashes: fatal errors and timeouts are recorded too.
  • Plugin owner: see which plugin or theme created each job.
  • Safe cleanup: finds jobs left behind by deleted plugins and tells you which are safe to delete.
  • Duplicate finder: spots jobs scheduled twice and removes the extras, with a preview first.
  • Health check: a list of cron problems on your site, most important first.
  • Alerts: email, Slack or Discord when a job crashes, runs late or stops running.
  • Manage jobs: add, edit, run now, pause or delete any scheduled job.
  • Pause a job: stop one job without turning off the plugin that owns it.
  • Custom schedules: create your own intervals, like every 10 minutes.
  • Fixed time of day: keep a daily job at 03:00, even when the clocks change.
  • What changed: see which jobs appeared or disappeared in the last week.
  • WooCommerce: logs failed Action Scheduler tasks.
  • CSV export: download jobs and run history.
  • Developer tools: WP-CLI commands and a REST API (see Technical details below).

Good to know

  • Alerts are off until you turn them on.
  • Nothing is sent to anyone but you. No tracking, no ads.
  • Deleting the plugin removes everything it created.

Technical details

WP-CLI

  • wp cronmon runs: recorded runs. Filters: --hook, --status, --since, --limit.
  • wp cronmon health: the Cron Health findings. Exits non-zero when one is critical.
  • wp cronmon orphans: every event with its safe-to-delete verdict.

All three accept --format=json.

REST API

Both routes require manage_options. Use Application Passwords over HTTPS.

  • GET /wp-json/cronmon/v1/health: the Cron Health findings, a critical count and an ok
    boolean for Uptime Kuma, Zabbix, Better Stack and similar monitors.
  • GET /wp-json/cronmon/v1/runs?status=fatal&since=-1%20hour&limit=20: recorded runs. Timestamps
    are ISO 8601 UTC.

Logging from your own code

Inside a cron callback, attach a note to the current run:

do_action( 'cronmon_log', 'reindexed 412 products' );

With Cron Monitor inactive, the call does nothing.

Server cron setup

Add to wp-config.php:

define( 'DISABLE_WP_CRON', true );

Then add a system cron entry, every five minutes:

*/5 * * * * cd /path/to/wordpress && wp cron event run --due-now > /dev/null 2>&1

Or, without WP-CLI:

*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Cron Monitor never writes to wp-config.php.

How it works

  • A run is recorded when it starts, not when it finishes, so crashes and timeouts still leave a
    record.
  • Cron Monitor attaches itself to every scheduled hook, so a hook with no callbacks now fires and
    is recorded as “0 callbacks”. Nothing else changes.
  • Duplicates are matched on hook, arguments and schedule together. One-off events are never
    removed automatically.
  • Late-running cron is traced to the job that held the cron lock too long, and a
    WP_CRON_LOCK_TIMEOUT value is recommended.

Performance

  • Front-end page views load two small PHP files and run no queries. With alerts on, one or two
    queries are added unless you have a persistent object cache.
  • Each cron job adds two database writes, one when it starts and one when it finishes.
  • Measured instrumentation cost is about 0.005 ms and 2.7 KB per cron job (PHP 8.4, one machine).

What this plugin stores

Two database tables per site:

  • cronmon_runs: one row per run. Hook name, an md5 hash of the arguments (never the values),
    timing, memory, query count, outcome, and the first error message (up to 2,000 characters).
  • cronmon_hooks: one row per scheduled event, with the callbacks last seen on it and who owns
    them.

Settings live in the cronmon_settings option. The stopped-running check uses one transient and
one small option. Successful runs are kept for 7 days and failures for 14, capped at 50,000 rows.
All of it is removed on uninstall.

No third-party libraries are bundled and nothing is minified.

External services

This plugin contacts no third-party service of its own. No analytics, no update checker. It makes
at most two kinds of outbound request, both under your control:

  • Alert webhook (optional, off by default): sent to the URL you enter, typically Slack or
    Discord. The JSON body contains the hook name, event type, start time, duration and, for a
    failure, the truncated error message.
  • Loopback check: the Cron Health screen, Site Health test and REST health route can ask your
    own wp-cron.php whether it answers. This goes to your own site_url(). It never runs during
    cron or WP-CLI, is cached for an hour, and is skipped where something else manages cron
    (Cavalcade, Cron Control, DISABLE_WP_CRON, ALTERNATE_WP_CRON).

Alert emails are sent through wp_mail() to the address you enter, using your site’s own mail
setup.

عکس‌های صفحه

نصب

  1. Install the plugin from Plugins Add New, or upload it to /wp-content/plugins/mcron-monitor/.
  2. Activate it.
  3. Go to Tools Cron Monitor.

Duplicates and the health check work straight away. Run history fills in as your jobs run.

سوالات متداول

Where do I see my cron jobs?

Go to Tools Cron Monitor. Scheduled Events lists every job. Run History shows what has
actually run.

Which plugin created this cron job?

Check the Owner column. “Not observed yet” means the job has not run since you installed
Cron Monitor. Check back after it runs.

Is this cron job safe to delete?

Cron Monitor watches each job as it runs and tells you. “0 callbacks observed across 14 runs” is
safe to delete. “Callbacks observed during cron” is not. If it has not seen the job run yet, it
makes no recommendation.

Why is my cron job running twice?

It is almost always scheduled twice. The Events screen flags duplicates and shows exactly what it
would remove before removing anything.

Why is WP-Cron not running?

Open the Cron Health screen. It checks the usual causes and tells you what to fix. The most
common one is a low-traffic site: WP-Cron only runs when someone visits. The fix is a real server
cron job, and the Health screen gives you the lines to copy.

Will it tell me when a cron job stops running?

Yes. It checks from normal page visits and alerts you when a job is overdue twice in a row,
fifteen minutes apart. A site that gets no visits at all cannot check itself, so use the REST
health route with an external monitor for that.

Can I get alerts by email?

Yes. Go to Settings Alerts and enter an email address, a Slack or Discord webhook, or both.
The Send test alert button checks that they work.

Does it slow my site down?

No. On normal page views it does almost nothing. It only does real work while cron jobs run.

Does it work with other cron plugins?

Yes. It only watches and reports. It does not take over WordPress cron.

Can it run PHP code on a schedule?

No, and it never will. Running stored code is a security risk.

Does it work with WooCommerce?

Yes. If Action Scheduler is present, failed tasks are logged. WooCommerce is not required.

Does it support multisite?

Yes. Each site has its own history and settings. There is no network-wide dashboard.

نقد و بررسی‌ها

نقد و بررسی‌ای برای این افزونه یافت نشد.

توسعه دهندگان و همکاران

“Cron Monitor” نرم افزار متن باز است. افراد زیر در این افزونه مشارکت کرده‌اند.

مشارکت کنندگان

ترجمه “Cron Monitor” به زبان شما.

علاقه‌ مند به توسعه هستید؟

کد را مرور کنید، مخزن SVN را بررسی کنید، یا از طریق RSS در گزارش توسعه مشترک شوید.

گزارش تغییرات

1.5.6

  • Added: screenshots of every screen on the plugin page.
  • Added: an up-to-date translation template (languages/mcron-monitor.pot).

1.5.5

  • Security: event references from the screens’ links and forms are strictly validated the moment
    they are read, and an edit of an event that no longer exists is refused before anything is
    stored.
  • Security: event references in forms are printed with WordPress’s own escaping functions.

1.5.4

  • Fixed: events whose hook name contains unusual characters (angle brackets, percent-encoding,
    doubled spaces) can now be run, edited, paused, pinned and deleted from the screens; bulk delete
    reports any it could not match.
  • Fixed: the event edit form’s security token is tied to the specific event being edited.
  • Fixed: a callback that raises huge numbers of PHP warnings no longer makes the run recorder use
    unbounded memory.
  • Fixed: Run History and Cron Health links keep hook names containing &, + and # intact.
  • Fixed: the Run History date filter rejects impossible dates.
  • Fixed: the hourly pruner is rescheduled if an earlier attempt to schedule it failed.
  • Fixed: on a new install, two requests recording the schedule change log at once can no longer
    overwrite each other’s first snapshot.

1.5.3

  • Changed: every database query is now fully prepared, and form and argument input is sanitized
    field by field, as required by the wordpress.org review.
  • Fixed: WordPress 6.5 no longer hits an undefined function when the database upgrade runs.
  • Fixed: activating or upgrading the plugin no longer rewrites every column of its tables.
  • Fixed: an alert for a fatal cron run is sent in the same request, not on a later page load.
  • Fixed: a failed table install is no longer recorded as a finished upgrade; it is retried.
  • Changed: one-off events are no longer offered for duplicate removal.
  • Fixed: pinning a duplicate occurrence moves the one selected, not its earliest sibling.
  • Changed: duplicate removal goes through wp_unschedule_event() in batches of at most 100, so
    unschedule filters and concurrent cron changes are respected.
  • Fixed: alerts queued by concurrent requests are no longer lost or delivered twice.
  • Fixed: when a cron callback runs a different cron hook, what happens after it returns is
    recorded against the right run.
  • Fixed: {} is refused as event arguments; they must be a JSON list.
  • Fixed: two requests updating the new and disappeared events list at once no longer erase each
    other’s changes.
  • Fixed: hooks whose names share the first 150 characters are no longer merged into one.
  • Fixed: turning recording on or off no longer undoes a settings save made at the same moment.

1.5.2

  • Changed: the error recorder no longer reads PHP’s error-reporting level, as required by the
    wordpress.org review. Errors silenced with @ inside a cron callback are now counted against the
    run. Notices and deprecations are recorded only when WP_DEBUG is on, matching what WordPress
    itself reports. PHP’s own error handling is unchanged.
  • Fixed: “Run now” and pinning a time no longer lose the event if WordPress refuses the new
    schedule. The original occurrence is put back, with a separate error if even that fails, and a
    pin is only saved once the event has actually moved.
  • Changed: webhook alerts are now sent with wp_safe_remote_post(), so a redirect to a local or
    private-network address is refused.
  • Fixed: the p95 duration now uses the nearest-rank percentile; it could previously show the
    slowest run instead.
  • Fixed: custom schedule intervals must be whole seconds. Values such as 60.9 or 1e3 are now
    rejected instead of silently truncated.
  • Fixed: callbacks from other plugins whose names merely start with “cronmon” (for example
    cronmonitor_run) are no longer hidden as this plugin’s own.
  • Fixed: the Events and Cron Health screens, and the REST and WP-CLI health checks, no longer
    query this plugin’s tables when they are missing.
  • Fixed: network-activated sites now schedule the hourly retention pruning on every subsite, not
    only the main site.
  • Fixed: searching Run History on very large logs no longer stalls the database.
  • Changed: with alerts on, one fewer database query per page load.

1.5.1

Maintenance release from a pre-submission audit. No feature changes and no database change.

  • Fixed: the Scheduled Events, Run History and Schedules screens raised a fatal error on hosts
    built without the mbstring PHP extension. WordPress does not require that extension, so the
    affected screens were unreachable on those hosts.
  • Fixed: a failure message is no longer carried in the address bar. It travels in a short-lived,
    per-user record instead, so a crafted link can no longer put a stranger’s wording inside a real
    WordPress admin notice.
  • Fixed: a pinned recurring event whose reschedule was refused stopped recurring altogether.
    WordPress now completes the reschedule at its ordinary interval, so the event keeps running and
    only loses its pinned time for that one cycle.
  • Fixed: the duplicate-scheduling guard could trigger a “translation loading triggered too early”
    notice on WordPress 6.7 and later, which on a cron request was then recorded as a warning against
    the run being watched.
  • Documentation: the outbound requests this plugin can make are now set out under their own
    External services heading, and the webhook entry states exactly what the request body contains.

1.5.0

The Alerts section of the Settings screen is now three switches, and each one hides and disables
everything that depends on it. Alerting behaves exactly as it did before for anybody who does not
touch them.

  • “Send alerts” is a slider rather than a checkbox, and switching it off takes the whole section
    with it — both channels, both addresses, the throttle and the test button.
  • Slack and email are separate switches. Switching one off stops that channel delivering while
    leaving the other one alone, and the address is kept, so switching it back on restores it. Before
    this, silencing a channel meant clearing the field and typing it back in later.
  • The throttle and the test button appear only once at least one channel is on: with neither there
    is nothing to throttle and nowhere to send a test.
  • Both channels are on after upgrading. A site that already had a webhook URL or an alert address
    saved keeps delivering to it with nothing to change.

1.4.0

Load-time release. Nothing about what the plugin does has changed — every screen, every alert, every
recorded row and every REST and WP-CLI response is identical. What changed is how much of the plugin
PHP reads on a request that is not using it.

  • A front-end page view now loads 2 PHP files (35,307 bytes) instead of 15 (357,569 bytes), and
    declares 1 PHP class instead of 14. The hooks are still attached at exactly the same moment; the
    class behind each one is loaded only once its callback has fired and its guard has passed.
  • Measured on the machine it was built on (Windows, PHP 8.4, no opcode cache), the PHP that a
    front-end request no longer reads and compiles was taking 5.76 ms and 889 KB of memory per
    request
    . It now takes 0.55 ms and 46 KB. With an opcode cache in front of it the time saving is
    smaller — that is the point of an opcode cache — but the work removed is the same work.
  • Classes are loaded on first use through an autoloader instead of being required up front.
  • The schema version check on every page load no longer loads the storage class to compare one
    integer.
  • On a site with Action Scheduler (WooCommerce and others), the Action Scheduler recorder is no
    longer loaded on every request — only in a request that is actually running the queue.
  • With run recording switched off, a cron request no longer loads the recorder at all.
  • tests/test-loadmap.php asserts the new load map, so a future change cannot quietly put it back.

1.3.0

  • Run history can be switched off. The toggle sits above the status filters on the Run History
    screen: with it off no bracket is attached to any cron callback, so nothing is measured, nothing
    is written, and the plugin costs a cron request nothing at all. Action Scheduler logging stops
    with it — it writes into the same history, and on a busy store it is the larger of the two.
  • Everything that reads run history says so while recording is off, rather than showing a number
    that can no longer move: the Cron Health screen explains it and skips the “cron has stopped”
    check entirely (that check reads when anything last ran, which a switched-off recorder freezes —
    it would otherwise report a healthy site as dead), the cron-budget breakdown says it is frozen,
    the retention projection says there is nothing arriving to project from, and the Settings screen
    names the alerts that cannot fire. The overdue alert, the Scheduled Events screen, Cron Health’s
    configuration findings and the Site Health tests all read the schedule instead and are unaffected.
  • The “Add a custom schedule” form moved into a panel beside the schedules table.
  • Fixed: the “Add event” button sat three pixels above the buttons next to it in the table
    navigation.

1.2.0

  • Alert when scheduled events have stopped running. Checked from ordinary page requests, because
    a dead cron runs nothing else; fires only when the same overdue event is seen across two checks
    fifteen minutes apart, so a quiet site with a working cron is never told otherwise.
  • Email as an alert destination, beside or instead of the webhook. One address, through
    wp_mail(). The test button now exercises every saved destination.
  • REST API: GET /cronmon/v1/health and GET /cronmon/v1/runs, manage_options, the same
    findings and rows as the screens and the CLI.
  • Fatal alerts now carry the callback’s error text under a new error key — in the webhook payload
    as well as in the alert email. Before this, the summary overwrote it and it was lost. A webhook
    consumer written against 1.1.0 sees one added key; no existing key changed meaning.

1.1.0

  • Pause a hook without deactivating its plugin. A hook paused by another cron plugin is recognised and
    reported as paused rather than as mysteriously interrupted here, and a run suppressed by ours is
    recorded, not silently skipped. One limit worth knowing: a callback that registers itself on the
    same hook while that hook is already firing can still run that one time, because WordPress has
    already taken its copy of the list being walked.
  • An event editor: change a scheduled event’s arguments, time and recurrence in place, without losing
    its pin or its duplicate guard.
  • Custom recurring schedules, with a guard against deleting one an event still uses — the warning
    names the event.
  • Export the scheduled events list to CSV.
  • A loopback probe on the Cron Health screen and in the Site Health test: asks your own wp-cron.php
    whether it answers. Admin-initiated only, cached for an hour, never runs on a cron request or under
    WP-CLI.
  • Diagnoses why cron spawning might not be happening at all, including Cavalcade, Cron Control,
    DISABLE_WP_CRON and ALTERNATE_WP_CRON.
  • A stall check for events that are scheduled but have not actually run.
  • Aggregate blame: cron time over the last seven days attributed to the plugin or theme that owns the
    callbacks.
  • A recommended WP_CRON_LOCK_TIMEOUT value based on what this site’s events actually take.
  • WP-CLI commands: wp cronmon runs, wp cronmon health and wp cronmon orphans.
  • Interface pass across all six screens.

1.0.0

  • First release.
  • Per-run history: duration, peak memory, query count, callback count and errors for every cron event.
  • Rows are written when an event starts, so fatals, out-of-memory kills and timeouts are still recorded.
  • Callback ownership resolved to the owning plugin, theme, mu-plugin or core file.
  • Verified orphan verdicts based on observed runs, distinguishing a real orphan from a cron-only registration.
  • Duplicate detection on hook, arguments and schedule together, with dry-run removal.
  • Optional per-event guard against duplicate recurring schedules.
  • Lock-starvation diagnosis, including the handover delay between cron processes.
  • Cron option health, including the autoload size finding.
  • Optional webhook alerts for fatals, killed runs, interval drift and starvation. Off by default.
  • Action Scheduler failure logging when Action Scheduler is present. Failures only by default.
  • Runs triggered by WP-CLI are detected and recorded as such, so they are not mistaken for a web
    request. (The wp cronmon commands themselves arrived in 1.1.)
  • Site Health integration using core’s own overdue thresholds.
  • Recurring events that appeared in the last week are badged as new; recurring events that disappeared are listed on the Health screen.
  • Pin a daily or weekly event to a time of day in the site timezone, held through daylight-saving changes.
  • do_action( 'cronmon_log', ... ) lets any cron callback attach notes to its own run.