onamfc/laravel-devlogger
Laravel DevLogger stores application logs in the database with automatic exception catching, tagging, queue and request/user context, advanced filtering, and configurable retention cleanup. Easily configurable via environment variables.
Architecture Fit
DevLogger::info()) plus a model over a developer_logs table. It does not plug into Laravel's logging.php channel stack, so it duplicates rather than replaces the existing log pipeline. Adopters will run two logging systems (file/stderr via Monolog, structured rows via DevLogger) unless they explicitly retire the other — budget for that duplication in both cost and cognitive overhead.updated_by, user attribution — is effectively lightweight incident triage. It competes with Sentry/Flare/Bugsnag on workflow and loses on alerting, grouping, and breadcrumbs. Where a team already pays for Sentry, this package adds little beyond SQL-queryable history.level(), open(), dateRange(), queue()) and a DeveloperLog model. There is no dashboard, no alerting, no grouping/dedup. Any stakeholder promise of "a log UI" is a build item we own, not an install item. This is the single largest scope risk in the evaluation.Integration Feasibility
composer require → publish config → php artisan migrate is a 30-minute path to first row. The real work is environment config, exception-handler integration, retention scheduling, and PII scoping.DevLoggerExceptionHandler replaces our existing app/Exceptions/Handler.php base class — unacceptable in any app with custom report()/render() logic. Use Option 2 (a report() hook) or the Laravel 12 withExceptions(...) closure, both of which are additive and reversible. Note the README's Laravel 12 sample uses request()?->fullUrl() and request()?->except() — the nullsafe call on a helper function is a documentation smell that signals console-context paths are under-tested.request_url, request_method, ip_address, user_agent, user_id, and the README's sample context includes request input with only password fields stripped by hand-written code. Nothing in the package enforces redaction. This needs a written policy and a wrapper logging API before it touches production, especially under GDPR/CCPA-adjacent obligations.Technical Risk
devlogger:cleanup --days "changed the output and nothing else," and that cleanup() deleted through a model with SoftDeletes, so expired rows were only stamped deleted_at and re-stamped on every run. A retention control that reports success while doing nothing is the worst failure class for a log store: the table grows unbounded while dashboards say otherwise. Mitigation: pin >=1.1.11, and independently verify row counts before/after a run.Schema::connection() signature fixes. Five consecutive patch releases to make vendor:publish and migrations resolve correctly is a signal that the package is not tested against a clean install matrix. Assume we are the integration test.DEVLOGGER_DB_CONNECTION=null means every log write lands on the app's primary connection. Logging inside a transaction that later rolls back takes the log row with it — we lose the record of the very failure we wanted to capture. fallback_channels defaults to null, so a DB outage means logs vanish rather than degrade.Key Questions
input, request_url, and ip_address, and who signs it off?Stack Fit
^12.0 on Illuminate while retaining older-version patterns.json column and index behavior on our engine before committing; MySQL JSON filtering has different index semantics than Postgres jsonb.onQueue()), not an async write path — logs are written synchronously.Migration Path
developer_logs), one config file, no changes to existing tables or models. Rollback is php artisan migrate:rollback plus deleting the config and any DevLogger:: call sites.DEVLOGGER_ENABLE_DB=true with DEVLOGGER_AUTO_CATCH=false and manual logging only at a single choke point, (3) run one week of dual-write against the existing channel and reconcile, (4) enable auto-catch, (5) add retention and confirm deletions. Explicitly do not enable auto-catch on day one — the log volume, and the PII surface, becomes immediately real.fallback_channels to our existing channel name at step 3; leaving it null is a silent data-loss configuration.DevLogger:: calls break. Wrap them behind a small internal App\Support\AppLogger facade from day one so the vendor is swappable.Compatibility
bootstrap/app.php integration differs materially from 9–11 handler extension. The two paths must be validated separately if we run mixed-version applications.Schema::connection() stricter signature fixes (v1.1.2, v1.1.3) — confirm migrations run cleanly on our exact minor version, not just "Laravel 12."database_connection config is global, not per-tenant. Confirm one table is acceptable before adoption.Sequencing
vendor/, load-test ~10k inserts, confirm cleanup actually deletes rows (count before/after, plus OPTIMIZE TABLE).^1.1.11.DeveloperLog is worth building, or whether SQL access is sufficient.Maintenance
composer update.Support
status open/closed workflow if teams adopt it.Scaling
longtext message plus longtext stack trace plus JSON context and JSON tags. Realistically 5–20 KB per exception row. At 50k exceptions/day, that is roughly 250 MB–1 GB/day before indexes. Default 30-day retention implies tens of GB. Plan storage and indexing (level, created_at, status, queue at minimum) explicitly, and confirm the package's shipped indexes match our query patterns.DELETE does not return disk to the OS — a scheduled OPTIMIZE TABLE (or engine-appropriate equivalent) is an operational commitment, not an optimization. On a first run against a table with a soft-delete backlog, expect an unusually large delete; schedule it off-peak.Failure Modes
cleanupCandidates() assertion in a health check — not trust in the command's output.DEVLOGGER_DB_CONNECTION, or accept the gap knowingly.fallback_channels=null by default means logs are dropped exactly when they matter most. Must be configured to an existing Monolog channel.request_url, request_method, ip_address, user_agent are null in queue workers and scheduled jobs. Queries and UI must tolerate nulls; the README's nullsafe-call pattern on request() suggests this path is lightly exercised upstream.input context and ip_address captured by default with no enforced redaction. A single unredacted payload in a shared log table is a compliance incident, and it survives until retention deletes it — which, per the above, may not happen.Ramp-up
info, error, withTags, onQueue, chaining) mirrors Laravel's Log facade closely enough to be intuitive.How can I help you explore Laravel packages today?