spatie/laravel-flare
Send Laravel errors to Flare. With a valid Flare API key, production errors are tracked and you're notified when they happen. Works with Laravel 11+ on PHP 8.2+, and pairs with spatie/laravel-ignition for beautiful error pages.
Install and wire it up (5 minutes):
composer require spatie/laravel-flare
Add the key to .env — this is the only thing that actually turns reporting on:
FLARE_KEY=your-api-key
FLARE_ENABLED=true
Publish the config file (php artisan vendor:publish and pick the Flare config) so you have config/flare.php to edit. Then verify end-to-end:
php artisan flare:test
That command exercises the report callbacks the package has registered, so it's the fastest way to tell "key wrong" from "collector broken" from "sampler ate the event".
Where to look first: config/flare.php. It is the single source of truth for what gets collected (recorders/collectors), what gets censored, sampling rules, and the sender. Skim it top to bottom once; most "why isn't this in Flare?" questions are answered there. Second stop is the official Flare Laravel docs (linked from the README), which go deeper than the package README does.
Your first real use case: ship it to production with APP_DEBUG=false and let unhandled exceptions report themselves. There is nothing to register in bootstrap/app.php — the service provider hooks the exception handler, request lifecycle, queue events, and console events for you. The value shows up on day one as "the 500 a user hit at 02:00 now has a stack trace, route, request body, and the query that blew up."
After that, the natural second step is putting the flare log channel into your stack so Log::error() calls land in the same place as exceptions.
Exceptions: zero-config, but be deliberate about context. The default recorder set captures request attributes (route, middleware, user, headers, body), queries, jobs, and the git commit. In v3.1+ route metadata is attached as route attributes, and unmatched 4xx routes get grouped under errors::{status_code} rather than each URL becoming its own issue. If grouping is wrong for your domain (e.g. one noisy exception class per tenant), override the grouping callback rather than muting the error.
Logs as a first-class signal (v3+). Add a channel and fan out:
// config/logging.php
'flare' => [
'driver' => 'flare',
],
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'flare'],
],
Then set LOG_STACK=single,flare (or whatever your stack is) per environment. Filter noise at the source with minimal_log_level in config/flare.php so info/debug chatter never leaves the app — this is both a cost control and a signal-to-noise control. Structured context arrays on log calls come through as attributes, so Log::error('Payment failed', ['order_id' => $id, 'gateway' => $gw]) is far more useful than string interpolation.
Sampling for traces/performance. v3 ships RateSampler and DynamicSampler with Laravel-aware rules — route name, route action (accepts [Controller::class, 'method'] tuples), queue name, and queue connection. The pattern that works well in production:
Do this by route action, not by URL pattern, so refactors don't silently drop coverage.
Custom context without touching package internals. v2.6+ lets you configure request and console attribute providers — small classes that add attributes to every event (tenant id, feature flags, deploy sha). Register them in config/flare.php rather than calling a global Flare::context() from middleware, because providers run for console/queue events too, where middleware never executes.
Mutate config before boot. When a value depends on runtime state (multi-tenant key, environment-derived sample rate, secrets from a vault), use the configuring() hook to adjust FlareConfig before the client boots, instead of building config arrays in a service provider that may run in the wrong order.
Queues, Octane, Vapor. The package carries context across job boundaries and deliberately does not reset context on sync queues (v2.2.0), which is what you want when a job dispatches synchronously inside a request. On Octane, performance monitoring was fixed in 2.0.7 — but your own long-lived singletons still need care. On Vapor, sampling and dispatch were fixed in 2.2.4; if you're on Vapor, stay current.
Daemon sender for volume. Default is LaravelHttpSender; switch to DaemonSender when you run the local Flare daemon (useful for local dev against a self-hosted instance, or to decouple sending from the request lifecycle at high throughput).
Local development. Leave FLARE_KEY unset locally — v2.0.6 added clean disable-on-no-key, so the package becomes a no-op instead of erroring. Keep spatie/laravel-ignition if you want the pretty local error page; they serve different jobs and coexist.
A missing key is a silent no-op, not an error. If nothing appears in Flare, check in this order: FLARE_KEY set in that environment, FLARE_ENABLED not false, config not cached (php artisan config:clear), then php artisan flare:test. Nine out of ten "Flare is broken" reports are an env var missing from the deploy target, especially on Vapor/Forge/ECS where .env is injected.
Sampling looks exactly like breakage. If errors from one route vanish but others arrive, suspect a DynamicSampler rule matching by route action or queue name before you suspect the client. Temporarily set the rate to 1.0 to confirm, then restore.
Config cache and queue workers. php artisan config:cache bakes config/flare.php; changing minimal_log_level or sampler rules requires a cache clear and a queue:restart. Long-running workers keep the old Flare config in memory otherwise, and you'll debug a phantom.
Privacy is your responsibility, not the package's default. Request bodies, headers, session, and cookies are collected. v2.6.2 added cookie/session censoring — turn it on for anything with auth or payment traffic, and audit your custom attribute providers for PII. This matters for GDPR and for the moment a support engineer opens an issue and sees a customer's address in the payload.
Payload size limits. v3.3.0 added max_attribute_size_in_kb for traces. Large request bodies or huge query result sets attached as attributes will bloat traces and slow sending; cap it. Also remember v2.2.3's fix: streamed HTTP responses are no longer read to compute size — if you use PrismPHP or any streaming client, pin at least that version or your SSE/AI streaming calls will hang or error inside the HTTP client instrumentation.
v3 removed things you may have in your codebase. reportMessage() and friends are gone (use the log channel), error solutions and the AddSolutions middleware are gone, and the old config file was replaced wholesale in 2.0.0. Read UPGRADING.md before bumping a 2.x app — the failure mode is a boot-time exception or silently dropped messages, not a deprecation notice.
Version matrix moved around in the 3.x line. The README states PHP 8.2+/Laravel 11+ for v3, while 3.2.0 added Laravel 10 support and 2.7.2/3.x added Laravel 13 support. Don't trust the README's headline — check composer.json constraints for the exact tag you're installing, and pin the minor if you're on an edge combination (Livewire v4 SFC tracing, for instance, needed 2.8.0+).
Don't confuse it with Ignition. spatie/laravel-flare is the reporting client. It is not the beautiful local error page, and it is not the thing that renders debug output. Teams regularly install the wrong package expecting the Ignition UI.
Debugging technique that pays for itself: wrap a suspicious failure in a test, run the suite with Flare disabled (no key), and assert on your own logging/context rather than on network calls. For the integration side, the package's own suite needs a workbench server (vendor/bin/testbench serve + queue:work); run pest --exclude-group=integration for fast local runs. Mirror that split in your app — don't put Flare network behaviour in your unit tests.
How can I help you explore Laravel packages today?