laravel maintained by logtracker
LogTracker for Laravel
LogTracker is a local, open-source incident tracer for Laravel. It turns the logger and lifecycle signals Laravel already emits into a Rollbar-style Items inbox and execution-story detail page.
No Rollbar package is required. No collector, account, API key, subscription, or hosted service is involved. By default, no telemetry leaves the application.
Status:
1.0.0. The package includes the local recorder, Items dashboard, privacy defaults, issue workflow, optional notifications, upgrade tooling, and Laravel/database compatibility fixtures described below.
Requirements
- PHP 8.3 or newer
- Laravel 11.31, 12, or 13
- SQLite, MySQL, or PostgreSQL through Laravel's database layer
Laravel 11 remains source-compatible and is covered by the package test
matrix. As of July 2026, Composer's security blocking rejects a fresh Laravel
11 dependency solve because of upstream framework advisories. Do not disable
security auditing for a production install; prefer Laravel 12 or 13 for new
applications. The Laravel 11 CI fixture uses --no-blocking only so those
upstream advisories do not hide compatibility regressions in this package.
Install
LogTracker is available on
Packagist. Until the first
stable tag is published, install the current dev-main build:
composer require logtracker/laravel:@dev
Then install and verify the package:
php artisan log-tracker:install
php artisan migrate
php artisan log-tracker:doctor
php artisan log-tracker:demo
Open /log-tracker.
The installer creates config/log-tracker.php with a unique, immutable
installation ID. Do not copy that value between applications. Package
migrations are loaded automatically.
Dashboard
The package owns its Blade views and CSS; the host application does not need a frontend build.


Use Laravel's existing logger
Existing application code is enough:
use Illuminate\Support\Facades\Log;
Log::error('Payment capture failed', [
'payment_id' => $payment->id,
]);
MessageLogged makes that an incident trigger at configured levels. Lower
levels become bounded breadcrumbs when an execution is already active.
Channels built outside Laravel's log manager may not emit MessageLogged and
are therefore outside the automatic guarantee.
Manual capture is available for cases that are not logs:
use LogTracker\Facades\LogTracker;
LogTracker::withContext(['tenant' => $tenant->uuid]);
LogTracker::identifyUser($customer->getKey()); // HMAC only; the ID is not stored
LogTracker::groupBy('payment-provider');
LogTracker::capture($exception);
LogTracker::capture('Checkout degraded', ['provider' => 'acme'], 'warning');
capture() never reports, throws, swallows, or rethrows an exception. The
host application retains control of its exception policy.
Production authorization
Local access is allowed only when both conditions are true:
APP_ENV=local- the request comes from
127.0.0.1or::1
Every other environment denies access until the application defines both gates:
use Illuminate\Support\Facades\Gate;
Gate::define('viewLogTracker', fn ($user) => $user->isDeveloper());
Gate::define('manageLogTracker', fn ($user) => $user->isDeveloper());
Or register one runtime callback in an application service provider:
use LogTracker\Facades\LogTracker;
LogTracker::authUsing(
fn ($request, string $ability) => $request->user()?->isDeveloper() === true
);
Run php artisan log-tracker:doctor --production before enabling the
dashboard in production.
What is captured
The native recorder uses Laravel's public surfaces:
| Surface | Root or story entry | Trigger |
|---|---|---|
| HTTP middleware | request root | escaped exception, 5xx, slow request |
MessageLogged |
breadcrumb | configured log level or throwable |
QueryExecuted |
span without bindings | configured slow query |
| Laravel HTTP client events | span without body/headers/query string | connection failure, 5xx, slow call |
| console events | command root | non-zero exit, slow command |
| queue events | job root or sync breadcrumb | terminal failure, slow job |
| scheduler events | schedule root | exception, non-zero exit, slow task |
| mail events | span without subject, addresses, data, or body | parent failure policy |
| notification events | span | channel failure |
| configured application events | allowlisted scalar breadcrumb | parent failure policy |
| PHP shutdown hook | partial shutdown root or current root | recoverable fatal error |
| facade API | current or short manual root | explicit capture |
Successful roots do not write to the LogTracker database.
Privacy defaults
Every persistence path uses the same recursive sanitizer:
- sensitive keys such as authorization, cookies, passwords, tokens, API keys,
sessions, and private keys are replaced with
[REDACTED]; - bearer/basic credentials and private-key blocks embedded in strings are replaced;
- JWTs, credential-bearing URLs, and common provider-token prefixes are replaced;
- object graphs and resources are never retained;
- request bodies and query values are not collected; only query key names and an explicit request-header allowlist are retained;
- matched request paths use Laravel's route template, so parameter values are
never stored; unmatched failing routes collapse to
/{unmatched}; - SQL bindings, comments, and literal values are not collected; SQL is stored as a bounded placeholder template;
- mail subjects, addresses, bodies, template data, cookies, session contents, and absolute application paths are not collected;
- depth, item, string, span, breadcrumb, trigger, envelope, and execution caps are enforced.
Review config/log-tracker.php for the exact defaults. The database, Blade UI,
and notification adapters all consume the same sanitized representation.
Application-event breadcrumbs are opt-in and copy only explicitly named scalar paths:
'watchers' => [
'application_events' => [
App\Events\OrderPaid::class => ['order.id', 'provider'],
],
],
The event object itself is never serialized.
Affected users
Authenticated request identifiers are HMACed with a key derived from
APP_KEY and the immutable LogTracker installation ID. Only the 64-character
digest enters an execution or signed queue envelope. Raw IDs, emails, names,
and notifiable objects are not stored.
Use LogTracker::identifyUser($id) inside jobs, commands, or manual contexts.
Pass null to clear the current execution identity. Retained distinct counts
are repaired by log-tracker:prune and log-tracker:regroup.
Dedicated database connection
LogTracker clones the configured host connection under the separate
log_tracker connection name. Laravel therefore creates a separate
connection/PDO even when it points to the same local DBngin-managed server and
database.
An incident can commit while the host connection later rolls back. Package writes are also ignored by the query watcher, preventing recursive capture. Three consecutive persistence failures open a process-local circuit for 30 seconds.
To use another database server, define a normal Laravel connection named
log_tracker yourself and set:
LOG_TRACKER_DB_CONNECTION=mysql
The package connection name and the host source connection name must remain different.
Grouping and execution stories
- Fingerprints include environment by default, so staging does not contaminate production triage.
- Exception fingerprints use stable application path/symbol information, not line numbers.
- IDs and queue context follow W3C trace/span formats.
- One retained execution story may link to up to five independently grouped Issues.
- Repeated fingerprints update one Issue and create distinct occurrences.
- A resolved Issue reopens when a new occurrence arrives.
- Items can be assigned to a local developer handle, changed individually or in batches, muted, resolved, reopened, and unassigned.
Queue propagation
Async context uses a reserved _log_tracker payload envelope with:
- schema version;
- W3C
traceparent; - bounded origin name/environment/release;
- the already-HMACed affected-user digest, when present;
- up to five bounded origin breadcrumbs;
- an HKDF-derived HMAC tied to
APP_KEYand the installation ID.
The envelope is authenticated metadata. LogTracker does not claim the
top-level envelope itself is confidential or encrypted. Invalid, old, missing,
or modified envelopes are ignored and the job starts with
origin unavailable.
The default signed-envelope lifetime is seven days with five minutes of allowed clock skew. Change it only when delayed jobs legitimately need a different correlation window.
OpenTelemetry
The domain model is OpenTelemetry-compatible, but version 1.0 deliberately ships one native recorder and no OpenTelemetry runtime dependency.
The architecture spike found that the current Keepsuit integration offers custom processors but owns provider construction and defaults traces, metrics, and logs to OTLP. A separately installed Laravel package cannot guarantee auto-discovery order, safely mutate a provider that is already global, or promise that it never increases a host exporter's volume.
The native recorder keeps W3C-compatible IDs and queue context, so a future explicit interoperability adapter can be added without changing Issue, occurrence, or execution storage. See recorder decision.
Optional email and Slack notifications
Outbound notifications are disabled by default. When enabled, only the sanitized Item title, level, execution identity, environment, release, occurrence count, and dashboard URL are sent. Stack traces, execution context, breadcrumbs, and affected-user hashes are never included.
'notifications' => [
'events' => ['new_issue', 'reopened'],
'dashboard_url' => env('LOG_TRACKER_DASHBOARD_URL', env('APP_URL')),
'max_per_minute' => 60,
'mail' => [
'enabled' => true,
'to' => ['developers@example.com'],
],
'slack' => [
'enabled' => true,
'webhook_url' => env('LOG_TRACKER_SLACK_WEBHOOK_URL'),
],
],
Adapters use the host Laravel mailer and HTTP client. Delivery happens only after the incident transaction commits; a transport failure is diagnosed and never rolls back or changes host behavior. Muted Items do not emit reopen notifications. Dashboard links use the configured URL rather than the inbound request host, Slack control markup is escaped, and each worker enforces the configured per-minute delivery ceiling.
Commands
php artisan log-tracker:install
php artisan log-tracker:doctor
php artisan log-tracker:doctor --production
php artisan log-tracker:upgrade
php artisan log-tracker:upgrade --check
php artisan log-tracker:regroup
php artisan log-tracker:regroup --write --force
php artisan log-tracker:demo
php artisan log-tracker:prune
php artisan log-tracker:prune --days=14
Schedule pruning in the host application:
use Illuminate\Support\Facades\Schedule;
Schedule::command('log-tracker:prune')->daily();
Upgrade from the alpha
Back up the application database, update the package, then run:
php artisan log-tracker:upgrade
php artisan log-tracker:doctor
The upgrade command applies only this package's migrations, advances the published config contract to version 2 when it can do so safely, and records the schema/package version in the installation anchor. It never overwrites the installation ID.
log-tracker:regroup is preview-only by default. Use --write only after
reviewing the projected Item count. New fingerprint groups inherit the first
source Item's state. Alpha exception rows that predate stored exception-class
metadata retain their previous group boundary instead of being guessed.
Runtime and failure boundary
Queue loop/stopping events and available Octane/Horizon lifecycle events clear all singleton execution and watcher state between operations. Successful roots still perform no package database writes. Fixed query-budget tests cap the Items page at four package queries and Issue detail at seven.
Failure isolation is best effort for catchable PHP/Laravel failures. The package cannot guarantee capture during SIGKILL, machine loss, irrecoverable OOM, engine termination, or before Laravel boots.
Develop
composer install
composer test
composer analyse
vendor/bin/pint --test
The Testbench suite verifies every capture surface, request/SQL/mail/ notification privacy, embedded secret detection, payload limits, observer and adapter failure isolation, grouping/regrouping, affected-user HMAC counts, dedicated transaction isolation, worker cleanup, installation upgrades, signed and expiring queue context, real trends, assignment/bulk actions, dashboard authorization, optimistic state conflicts, automatic reopening, fixed query budgets, and signed-cursor recovery. CI runs Laravel 11–13 plus SQLite, MySQL 8.4, and PostgreSQL 17 fixtures.
License
MIT. See LICENSE.md.