Skip to main content

Documentation

No results found.
Features

Error Telemetry (fleet error reporting)

Client installs send a small, privacy-scrubbed digest of their recent server errors to the mothership with the routine license check-in, so a release that starts throwing fleet-wide is visible on the Fleet page within hours — without SSHing...

Client installs send a small, privacy-scrubbed digest of their recent server errors to the mothership with the routine license check-in, so a release that starts throwing fleet-wide is visible on the Fleet page within hours — without SSHing into every site. This complements the §D.4 update health probe (which reports update outcomes); error telemetry covers runtime errors between updates.

What is sent

With each membership check-in (every 6 hours, riding the existing MembershipClient::check() POST — no new endpoint, cron, or queue worker), the install attaches an errors block:

  • Up to 20 deduplicated error signatures — exception class, file and line (relative to the install root), log level, a truncated + scrubbed message, occurrence count, first/last seen timestamps.
  • The install's PHP version (the CMS version already rides the check-in).

Never sent: stack traces, request context, visitor data, IPs, URLs with query strings, or raw log lines. The collector reads only each log entry's header line and scrubs messages of emails, hex/base64-looking tokens, URL query strings, and absolute paths before anything leaves the box. Only ERROR-and-above entries qualify — warnings are noise at fleet scale.

Owner control

Memberships → Settings → Membership client → "Share error reports" — default on, with a plain-language disclosure. Stored as the telemetry.share_errors Setting. When off, the errors block is omitted entirely; nothing is collected or sent.

How the client collects (app/Support/ErrorTelemetry.php)

  • Scans storage/logs/laravel*.log incrementally: per-file byte offsets persisted in storage/app/private/error-telemetry.json (updater-protected), rotation-safe (a shrunken file rescans from the top), and byte-capped (2 MB tail per file per scan) so a huge backlog can never stall the check.
  • Folds occurrences into signatures — sha1(class|file|line), falling back to a digit-normalized message hash when there's no exception context — keeping the newest 100 locally with 30-day retention.
  • Strictly best-effort: any collector failure collapses to "no digest"; it never logs errors itself (that would feed the collector) and never blocks or fails the license check. Updater failures need no special hooks — they log at ERROR and are swept up like any other exception.
  • In the testing environment the collector defaults to isolated storage/framework/testing/ paths (same posture as the page-data sidecars) so test runs can neither scan nor seed live install state.

How the mothership ingests (MembershipCheckController::recordErrorReports())

  • Recognized members only — an anonymous writer would be a spam lever (same posture as the health-report endpoint). Keyless/unknown-key check-ins are answered normally but store nothing.
  • Upserts into install_error_reports keyed by (install_id, signature), every field length-clamped server-side, 20 entries max per report, future-dated timestamps clamped to now.
  • Bounded per install: newest 100 signatures kept, 30-day retention pruned on each ingest — a hostile key cannot grow the table.
  • Best-effort: a malformed block never fails the license check.

Fleet page (license server only)

Dashboard → Fleet gains:

  • An "Erroring (7d)" stat tile — installs with any error signature seen in the last 7 days.
  • An "Errors (7d)" column — red occurrence-count badge per install; click it for the drill-down modal listing that install's signatures (class, file:line, message, counts, CMS/PHP version, last seen).

Key files

File Role
app/Support/ErrorTelemetry.php client-side collector + scrubber
app/Support/Licensing/MembershipClient.php attaches the errors block to the check-in
app/Features/Memberships/Http/Controllers/MembershipCheckController.php mothership ingest + per-install bounds
app/Features/Memberships/Models/InstallErrorReport.php one row per (install, signature)
app/Features/Memberships/resources/views/dashboard/fleet/⚡index.blade.php Fleet page stat, column, drill-down modal
app/Features/Memberships/resources/views/dashboard/⚡settings.blade.php the share-errors toggle