Live dashboard, anomaly alerts and health score

A per-channel ringbuffer of the last 50 events, 30 days of per-day counters, a 0-10 health score per channel, and an automatic email when a channel drops 50 percent or spikes 4x. Three classes that share two autoload=false wp-Options. No new database tables.

Reading time: ~5 minLast updated: 2026-05-09

What it does

The live dashboard answers three questions an operator runs into the first time their boss asks: "Is Meta still tracking?" Card one: yes, here are the last 12 events with timestamps and values. Card two: yes, here is a 7-day per-channel chart so you can spot a flat line. Card three: every channel has a 0-10 health score that turns red, yellow or green at a glance. And if any channel suddenly stops or suddenly explodes, you get an email without having to look at the dashboard at all.

How to enable

Nothing to enable. The recorder hooks bcnr_post_dispatch_event automatically when the tracking pipeline boots (license active, master toggle on). The card appears on the Beaconry Dashboard tab the first time an event lands. The anomaly cron runs daily once at least 14 events have accumulated in the 7-day baseline, so test pings during setup don't trip alerts.

The alert recipient is the same alert_email you set on the Advanced tab for BCNR_Health. If you want a separate inbox for anomalies, change that field, both alerts will follow.

How it works (under the hood)

Three classes, one shared storage, one REST endpoint.

  1. BCNR_Dashboard_Stats recorder. Listens on the per-channel bcnr_post_dispatch_event action, fires once per channel per event after the channel's dispatch_* call returned. Writes to two autoload=false wp-Options on every dispatched event:
    • bcnr_dashboard_recent, ringbuffer of the last 50 events with channel slug, event_name, timestamp, value, currency. Shown in the recent-events list on the card.
    • bcnr_dashboard_counters, per-channel-per-day count + value-sum, last 30 days, prune-on-write. Shown as the 7-day or 30-day chart on the card.
    Two update_option calls per dispatched event. Deliberate trade-off against a new wpdb table, justified by Beaconry's traffic profile (well under 1k events per hour on a real customer site).
  2. REST endpoint. GET /wp-json/beaconry/v1/dashboard?range=today|7d|30d returns the snapshot for the card. Permission gate is manage_options + wp_rest nonce. The card's vanilla-JS in the admin polls every 30 seconds with visibilitychange-pause (no traffic when the tab is in the background). A second route DELETE /wp-json/beaconry/v1/dashboard/recent clears the ringbuffer (counters stay, the chart history is preserved).
  3. BCNR_Anomaly daily cron. A WP-Cron event bcnr_anomaly_check reads the per-day counters and compares yesterday's per-channel count against the 7-day rolling average from days 2 through 8 (yesterday excluded from the baseline so a real anomaly doesn't pull the average down with it). Drop ≥ 50 percent or spike ≥ 4x triggers an email. Two safety belts: a min-baseline of 14 events per 7 days so test pings don't trip alerts, and a Tagesgate transient bcnr_anomaly_alert:<channel>:<date> with 23-hour TTL so you don't get the same alert twice.
  4. BCNR_Health_Score per-channel score. Pure function for_channel($slug) returns 0-10 from three components: Configured (4 points if BCNR_Forwarder::has_*() returns true for this channel), Active 7d (4 points if at least one event landed in the last 7 days), Stable (2 points if the coefficient of variation across the last 7 days is below 1, meaning daily volume is reasonably steady). Tier bucketing maps to red (0-3), yellow (4-7), green (8-10) for the badge on each card. No vendor-API calls, all from the same in-WP counter table the recorder writes.

Per-channel notes

The recorder treats every channel slug equally (ga4|meta|tiktok|linkedin|google_ads|microsoft_ads|pinterest|reddit|snapchat|x_ads), so a card appears for every channel that's actually configured. Channels you haven't set up don't surface on the dashboard, no empty zero-bars to ignore. The health-score tier comes from per-channel data only, so you can have a green Meta card next to a red TikTok card without one polluting the other.

Constants for power-users

None. The dashboard is wired automatically. Three filters let you tune the anomaly detector without touching code-side defaults:

  • bcnr_anomaly_drop_ratio, default 0.5, fraction-of-baseline that triggers a "drop" alert. Lower it to 0.7 to alert on a 30 percent drop, raise it to 0.3 to only alert on a 70 percent drop.
  • bcnr_anomaly_spike_ratio, default 4.0, multiple-of-baseline that triggers a "spike" alert. Useful when you run a known traffic event (Black Friday) and want to silence false spikes.
  • bcnr_anomaly_min_baseline, default 14 events per 7 days, below which a channel is considered "too quiet to alert on". Raise it on high-volume sites to avoid noisy small-channel alerts.

Troubleshooting

  • "Card shows no data": the recorder fires on dispatched events. Check that the master toggle is on (Dashboard tab) and that at least one channel is configured (Tracking tab). The Logs tab will show "no dispatch attempted" reasons.
  • "I deleted recent events but the chart still shows them": the chart reads per-day counters, the recent-events list reads the ringbuffer. They are two separate stores by design, deleting recent does not delete the chart history. To wipe the chart too, the operator-supported path is to wait 30 days for natural prune-on-write, or to delete the option bcnr_dashboard_counters directly via WP-CLI.
  • "I got an anomaly alert for a channel I just disabled": that's the detector working as intended (yesterday-vs-baseline comparison). The Tagesgate transient prevents a second alert for the same channel on the same day, and the daily cron will see zero events on that channel from now on, so the 7-day baseline will normalise within a week.
  • "My health score is yellow but the channel works": most likely the Stable component (CoV < 1) failed because of one outlier day. Score recovers naturally as days roll out of the 7-day window. Configured + Active are the two components that matter for "is it actually working", together they max at 8 of 10.