Statamic Logbook
Latest Release License Open Issues Last Commit
A production-ready logging and audit trail addon for Statamic.
Statamic Logbook provides a centralized place to review:
- System logs (Laravel / Monolog)
- User audit logs (who changed what, and when)
All inside the Statamic Control Panel, with filtering, analytics, and CSV export.
Features
System logs
- Captures Laravel log events automatically (no manual
logging.phpwiring required) - Stores structured records in Logbook DB tables
- Captures request context (URL, method, IP, user, request id)
- Supports noise filtering by channel/message fragment
Audit logs
- Captures high-signal Statamic mutation events by default
- Stores action, subject metadata, and entry-level before/after diffs
- Supports field-level ignore rules and value truncation
- Supports optional broader event discovery mode
Control Panel
- Native Statamic CP styling/components
- Dashboard widgets (overview, trends, live pulse)
- Utility views with filtering and CSV export
- Widget set includes:
- Logbook Overview (24h health cards)
- Logbook Trends (daily stacked volume)
- Logbook Pulse (live mixed feed + quick filters)
Widget preview
Overview cards
Logbook Overview Cards
Trends
Logbook Trends Volume
Live pulse
Logbook Live Pulse
Widget slugs (handles)
Use these widget handles when configuring dashboard widgets:
logbook_stats(Overview cards)logbook_trends(Volume by day + 7×24 heatmap)logbook_pulse(Live feed)
Control Panel walkthrough
The utility lives under Utilities → Logbook. The same page hosts three tabs: System, Audit, and Timeline. Every feature below works the same on Statamic 4, 5, and 6 — the addon ships its own stylesheet and script bundle so nothing depends on the host CP's Tailwind purge configuration.
Dashboard widgets
- Overview (
logbook_stats): four KPI cards showing total system lines, errors, audit events, and the busiest hour in the last 24 h. Each card carries a sparkline, a period-over-period delta chip (↑ +12.4%,↓ −3%), a status pill, and — for errors — a "Last error 3h ago" chip plus a "Top error signatures · 24h" panel that groups similar errors by a normalised fingerprint. - Trends (
logbook_trends): daily stacked bars for the last 14 days followed by a 7×24 channel heatmap so you can spot what hour of what day is loudest at a glance. - Pulse (
logbook_pulse): live mixed feed of system + audit events with quick-filter pills (System · Audit · Errors · Warnings).
Utility page features
Filtering & search. Each tab has a sticky filter bar with date-range pickers, level / channel / action / subject dropdowns, and a full-text search on message / user / subject. Filters compose with the URL so they're shareable; empty fields are stripped so ?level=error stays clean.
Sortable columns. Click any column header to sort. sort and dir are whitelisted server-side per table.
Per-page + pagination. A footer chip-style paginator (Prev · 1 · 2 · … · Next) with a [25 | 50 | 100 | 200] per-page dropdown. The selector preserves every other query param.
Saved filter presets. A Presets ▾ button on System + Audit tabs lets you snapshot the current filter URL under a name. Presets are stored per-tab in localStorage under statamic-logbook.presets.<scope>. Opening a preset restores the full filter state.
Live tail. A pulsing toggle next to Export CSV polls a JSON endpoint every few seconds. When new rows land, the label becomes "N new · click to refresh". The poll automatically:
- pauses when the tab is hidden (
visibilitychange) or offline, - resumes on
online/ visible, - backs off exponentially on consecutive errors (5 s → 10 s → 20 s → 40 s, capped at 60 s with jitter),
- relaxes toward the upper bound when the server returns no new rows for several ticks,
- cleans up on
pagehide/beforeunload.
Density toggle. Compact · Cozy · Spacious in the toolbar. This is not a font-size switch: Compact hides secondary meta rows, forces single-line truncation, and shrinks chips + the filter grid; Cozy is the default; Spacious releases the cell clamp, lets long messages wrap, and enlarges the toolbar. The preference is persisted under statamic-logbook.density and synced across devices when preferences are linked to your CP user (see User preferences below).
Cell truncation + JSON viewer. Long messages, user ids / emails, action strings and subject titles are clamped to a single line. Every row has a JSON action that opens the full record as pretty-printed JSON in a modal (copy-to-clipboard in two clicks). The full value is never lost.
Human-readable audit actions. Raw event strings like statamic.user.saved are shown as User updated via an AuditActionPresenter. On update events, the row carries an inline ribbon with a truncated "from → to" summary of the first 1–2 changed fields (e.g. title: "Old" → "New") using the existing changes column. Zero schema changes; the raw event name stays on disk so ?action=statamic.user.saved keeps working.
Unified timeline. The Timeline tab interleaves system + audit events into a single chronological rail grouped by day (Today / Yesterday / explicit dates). Filterable by type (system / audit) and severity (error / warn / info).
CSV export. The Export CSV button downloads the currently-filtered rows as a CSV respecting all filters + sort.
Keyboard shortcuts
/— focus the search input on the current tab.g s— go to System logs.g a— go to Audit logs.
Shortcuts are suppressed while typing in form fields.
User preferences
logbook:install creates a third table, logbook_user_prefs, in the logbook database (not the project database). One row per CP user, a single JSON prefs blob. The UI uses localStorage as a zero-config fallback for density / saved presets / per-page default; when the preferences table is available, a set of CP endpoints allows those values to sync across devices:
GET /cp/utilities/logbook/prefs— return every pref for the current userGET /cp/utilities/logbook/prefs/{key}— return one prefPUT /cp/utilities/logbook/prefs/{key}— set one pref (body:{ "value": ... })DELETE /cp/utilities/logbook/prefs/{key}— remove one pref
All four endpoints are gated by can:view logbook and fail soft — if the table is missing (pre-upgrade install) or the logbook DB is unreachable, the UI continues to use localStorage and no error is surfaced to the user. See src/Support/UserPrefsRepository.php for the storage contract and rationale for living in the logbook DB rather than the project DB (self-contained addon, clean uninstall by dropping the logbook DB, respects teams that deliberately separate logs from prod).
Compatibility
| Component | Supported |
|---|---|
| Statamic | v4, v5, v6 |
| Laravel | 9, 10, 11, 12 |
| PHP | 8.1, 8.2, 8.3, 8.4 |
Statamic 3 users stay on the dedicated 1.x LTS branch.
Installation
composer require emran-alhaddad/statamic-logbookphp artisan vendor:publish --tag=logbookphp artisan logbook:install
The logbook tag publishes the config file, the CP stylesheet, and the CP script bundle. Statamic re-runs this automatically on php artisan statamic:install, so most teams only need to run it once on initial setup.
Setup (Required)
1) Configure Logbook database credentials in .env
These are required for Logbook to work:
LOGBOOK_DB_CONNECTION=mysqlLOGBOOK_DB_HOST=127.0.0.1LOGBOOK_DB_PORT=3306LOGBOOK_DB_DATABASE=logbook_databaseLOGBOOK_DB_USERNAME=logbook_userLOGBOOK_DB_PASSWORD=secret
Then clear config cache:
php artisan config:clear
2) Install database tables
php artisan logbook:install
Environment Variables
All variables used by the addon:
# Required DB connectionLOGBOOK_DB_CONNECTION=mysqlLOGBOOK_DB_HOST=127.0.0.1LOGBOOK_DB_PORT=3306LOGBOOK_DB_DATABASE=logbook_databaseLOGBOOK_DB_USERNAME=logbook_userLOGBOOK_DB_PASSWORD=secret # Optional DB tuningLOGBOOK_DB_SOCKET=LOGBOOK_DB_CHARSET=utf8mb4LOGBOOK_DB_COLLATION=utf8mb4_unicode_ci # System loggingLOGBOOK_SYSTEM_LOGS_ENABLED=trueLOGBOOK_SYSTEM_LOGS_LEVEL=debugLOGBOOK_SYSTEM_LOGS_BUBBLE=trueLOGBOOK_SYSTEM_LOGS_IGNORE_CHANNELS=deprecationsLOGBOOK_SYSTEM_LOGS_IGNORE_MESSAGES=Since symfony/http-foundation,Unable to create configured logger. Using emergency logger. # Audit loggingLOGBOOK_AUDIT_DISCOVER_EVENTS=falseLOGBOOK_AUDIT_EXCLUDE_EVENTS=LOGBOOK_AUDIT_IGNORE_FIELDS=updated_at,created_at,date,uri,slugLOGBOOK_AUDIT_MAX_VALUE_LENGTH=2000 # RetentionLOGBOOK_RETENTION_DAYS=365 # Ingestion modeLOGBOOK_INGEST_MODE=syncLOGBOOK_SPOOL_PATH=storage/app/logbook/spoolLOGBOOK_SPOOL_MAX_MB=256LOGBOOK_SPOOL_BACKPRESSURE=drop_oldest # Addon scheduler (flush spool)LOGBOOK_SCHEDULER_FLUSH_SPOOL_ENABLED=trueLOGBOOK_SCHEDULER_FLUSH_SPOOL_EVERY_MINUTES=60LOGBOOK_SCHEDULER_FLUSH_SPOOL_WITHOUT_OVERLAPPING=true
Short .env example (minimal working setup)
LOGBOOK_DB_CONNECTION=mysqlLOGBOOK_DB_HOST=127.0.0.1LOGBOOK_DB_PORT=3306LOGBOOK_DB_DATABASE=logbook_databaseLOGBOOK_DB_USERNAME=logbook_userLOGBOOK_DB_PASSWORD=secret LOGBOOK_INGEST_MODE=spoolLOGBOOK_SPOOL_PATH=storage/app/logbook/spool
Required variables
LOGBOOK_DB_CONNECTIONLOGBOOK_DB_HOSTLOGBOOK_DB_PORTLOGBOOK_DB_DATABASELOGBOOK_DB_USERNAMELOGBOOK_DB_PASSWORD
Optional variables and behavior
LOGBOOK_DB_SOCKET: unix socket path.LOGBOOK_DB_CHARSET: DB charset (defaultutf8mb4).LOGBOOK_DB_COLLATION: DB collation (defaultutf8mb4_unicode_ci).LOGBOOK_SYSTEM_LOGS_ENABLED: enable/disable system log capture (defaulttrue).LOGBOOK_SYSTEM_LOGS_LEVEL: minimum system level (defaultdebug).LOGBOOK_SYSTEM_LOGS_BUBBLE: Monolog bubble behavior (defaulttrue).LOGBOOK_SYSTEM_LOGS_IGNORE_CHANNELS: comma-separated ignored channels.LOGBOOK_SYSTEM_LOGS_IGNORE_MESSAGES: comma-separated ignored message fragments.LOGBOOK_AUDIT_DISCOVER_EVENTS: whentrue, merges discovered Statamic events with curated defaults.LOGBOOK_AUDIT_EXCLUDE_EVENTS: comma-separated audit event classes to exclude.LOGBOOK_AUDIT_IGNORE_FIELDS: comma-separated fields ignored in diffs.LOGBOOK_AUDIT_MAX_VALUE_LENGTH: max stored value length before truncation.LOGBOOK_RETENTION_DAYS: retention period for prune command.LOGBOOK_INGEST_MODE:sync(direct DB) orspool(local file spool + background flush).LOGBOOK_SPOOL_PATH: spool directory path.LOGBOOK_SPOOL_MAX_MB: max spool size before backpressure policy applies.LOGBOOK_SPOOL_BACKPRESSURE: currently supportsdrop_oldest.LOGBOOK_SCHEDULER_FLUSH_SPOOL_ENABLED: enable/disable addon-level scheduler for flush command (defaulttrue).LOGBOOK_SCHEDULER_FLUSH_SPOOL_EVERY_MINUTES: interval (minutes) for addon-level flush scheduling (default60).LOGBOOK_SCHEDULER_FLUSH_SPOOL_WITHOUT_OVERLAPPING: apply overlap protection for scheduled flush runs (defaulttrue).
Ingestion Modes
sync mode
- Writes system/audit rows directly to DB in request lifecycle.
spool mode
- Writes NDJSON records to local spool files in request lifecycle.
- Flushes spool files to DB in background via command/scheduler.
- If enqueue fails, Logbook falls back to direct DB insert (prevents silent drops).
Spool Flush and Background Scheduling
Flush command:
php artisan logbook:flush-spool
Common usage:
php artisan logbook:flush-spool --type=all --limit=1000php artisan logbook:flush-spool --type=system --dry-run
Command output includes:
- queued files (before/after)
- queued bytes (before/after)
- failed files (before/after)
- failure reason and failed-file destination when flush fails
Built-in addon scheduler (spool mode)
When LOGBOOK_INGEST_MODE=spool, the addon auto-registers logbook:flush-spool in Laravel Scheduler.
Default behavior:
- Runs every 60 minutes
- Uses
withoutOverlapping()by default - Can be tuned via:
LOGBOOK_SCHEDULER_FLUSH_SPOOL_ENABLEDLOGBOOK_SCHEDULER_FLUSH_SPOOL_EVERY_MINUTESLOGBOOK_SCHEDULER_FLUSH_SPOOL_WITHOUT_OVERLAPPING
Important:
- This scheduler is only active in
spoolmode. logbook:pruneis not auto-scheduled by the addon.
Application-level scheduler entry (optional override)
Add to your app scheduler (routes/console.php or Console\Kernel):
use Illuminate\Support\Facades\Schedule; Schedule::command('logbook:flush-spool --type=all --limit=1000') ->everyFiveMinutes() ->withoutOverlapping();
Short scheduler example:
Schedule::command('logbook:flush-spool')->everyFiveMinutes();
OS cron (host level, required)
* * * * * cd /absolute/path/to/your-laravel-app && php artisan schedule:run >> /dev/null 2>&1
Short cron example:
* * * * * php /absolute/path/to/your-laravel-app/artisan schedule:run >> /dev/null 2>&1
Operational Commands
- Install tables:
php artisan logbook:install - Prune old rows:
php artisan logbook:prune - Flush spool:
php artisan logbook:flush-spool
Run maintenance from Control Panel
From Utilities -> Logbook, use the header action buttons:
Prune Logs: executesphp artisan logbook:pruneFlush Spool: executesphp artisan logbook:flush-spool
Each action shows a CP toast status lifecycle:
in-progresswhen starteddoneon successfailedon command/transport error
Implementation note: CP action requests are submitted as form-encoded POST with _token to satisfy Laravel/Statamic CSRF validation.
Quick Verification
- Set required DB env vars.
- Run
php artisan config:clear. - Run
php artisan logbook:install. - Trigger a test log:
\Log::error('logbook smoke test', ['source' => 'manual-check']);
- If in spool mode, run
php artisan logbook:flush-spool --type=all. - Confirm rows appear in CP (System Logs / Audit Logs).
Test Coverage
This repository includes a PHPUnit suite focused on regression checks for critical behavior:
EventMapTest— per-major event resolution, silent filtering of missing event classes, exclusion semanticsStatamicAuditSubscriberResolutionTest— cross-major class-not-found safety, exclude-list round-tripWidgetRegistryShimTest— capability-gated shim firing only when core registration is absent, idempotency- Audit action normalization mapping
- Curated audit default mode (
discover_events=false) - Pulse widget filter listener singleton guard
Run tests:
./vendor/bin/phpunit --configuration phpunit.xml
Rebuilding the CP bundles
The addon ships pre-minified statamic-logbook.min.css and statamic-logbook.min.js in resources/dist/. To rebuild from the source files in the same directory:
npm installnpm run build
What To Do / What Not To Do
Do
- Use a dedicated DB/schema for Logbook where possible.
- Keep scheduler and cron configured if using
spoolmode. - Keep
LOGBOOK_AUDIT_DISCOVER_EVENTS=falseunless you need wider coverage. - Monitor failed spool files under
storage/app/logbook/spool/failed/.
Do not
- Do not commit real credentials.
- Do not disable scheduler while using
spoolmode. - Do not point Logbook to an uncontrolled DB.
- Do not treat audit logs as editable content.
Troubleshooting
- Spool files are not created:
- run
php artisan config:clear - verify
LOGBOOK_INGEST_MODE=spool - verify spool directory write permissions for the PHP-FPM user
- run
- Flush fails:
- read the
Flush error:line from command output - look under
storage/app/logbook/spool/failed/for a file named like20240101_12.ndjson.20250122093015.failed— the CP UI reports only the basename on purpose, never the absolute path - fix the root cause, requeue the failed file back into
spool/<type>/, and runphp artisan logbook:flush-spoolagain
- read the
- "Unknown database" on install:
php artisan logbook:installauto-creates the database on MySQL/MariaDB if the configured DB user hasCREATE DATABASE. If your user doesn't, create the DB manually (CREATE DATABASE logbook_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci) and re-run install. - CP widgets render as unstyled gray boxes on Statamic 6: run
php artisan vendor:publish --tag=logbook --forceto refresh the published stylesheet + script, then clear the browser cache. - Pulse widget filter pills ignore clicks on Statamic 6: symptom of a stale
statamic-logbook.min.jspublish; re-run the vendor:publish line above. - "Presets ▾" dropdown doesn't appear: known fix in v2.0.0 (the button now portals its menu into
<body>to escape the filter toolbar'sbackdrop-filtercontaining block); upgrade to v2.0.0+.
Release and History
Known tags:
v2.0.0(current) — Statamic 6 support + CP redesign + UX polish passv1.5.1v1.5.0v1.4.0v1.3.1v1.3.0v1.2.0v1.1.0v1.0.0
Statamic 3 users continue on the 1.x LTS branch. See CHANGELOG.md for the full per-version history.
Current release (v2.0.0)
The v2 release targets three things at once:
- First-class Statamic 6 support. The core widget registry binding conflict that broke the dashboard on Statamic 6 is gone. Event references are string FQCNs filtered through
class_exists()so missing-event-class fatals across majors never happen.Audit\EventMapships a curated per-major event registry (majors 3–6) that returns only the event classes that exist on the running major. - Self-contained CP surface. The addon ships its own stylesheet (
resources/dist/statamic-logbook.min.css) and script bundle (resources/dist/statamic-logbook.min.js) registered via Statamic's$stylesheets/$scripts. Rendering is independent of the host CP's Tailwind purge configuration, and the script runs outside the Vue-compiled widget subtree so Statamic 6'sDynamicHtmlRenderercan't strip it. - Deep CP UX pass. Sortable columns, density toggle, saved filter presets, live tail with adaptive polling, keyboard shortcuts, unified timeline, per-page selector, chip-style paginator, error fingerprint grouping, 7×24 channel heatmap, humanised audit actions with inline "from → to" ribbons, cross-device user preferences table, and more. See
CHANGELOG.mdfor the full list.
License
MIT License. See LICENSE.
Author
Built and maintained by Emran Alhaddad
GitHub: https://github.com/emran-alhaddad
Changelog
See CHANGELOG.md for release history.