Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Flashlight Laravel Package

hesamrad/laravel-flashlight

Laravel Flashlight is a lightweight package for adding customizable flash notifications to Laravel apps. It offers a clean API for success, error, warning, and info messages, helping you give users clear, consistent feedback with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish.

composer require hesamrad/laravel-flashlight
php artisan vendor:publish --provider="HesamRad\Flashlight\FlashlightServiceProvider"
php artisan migrate

The publish step should give you a config/flashlight.php and, if the package ships a database driver, a migration for the request-log table. Run migrate only if you intend to persist logs — check the config first to see whether a file/database driver is selectable.

Where to look first. Open the published config before touching any code. Most request-logging packages expose the same three knobs, and they determine everything else you'll do:

  • the driver / storage (database table vs. log channel vs. custom writer),
  • the middleware class name and whether it's auto-registered,
  • the exclusion list (paths, methods, status codes, headers) and any redaction rules.

Cross-reference the config keys against the middleware's handle() method in vendor/hesamrad/laravel-flashlight/src/ — with a package this small, the source is the documentation, and reading the middleware takes two minutes.

Register the middleware. Either globally (Laravel 11+ bootstrap/app.php):

->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\HesamRad\Flashlight\Http\Middleware\LogRequest::class);
})

…or, better for a first outing, on a single route group so blast radius is small:

Route::middleware(['flashlight'])->prefix('api/v1')->group(function () {
    // ...
});

First use case. Point it at one noisy or high-value surface — your public API, a webhook receiver, or an integration endpoint you're debugging with a partner — for a day. Then query the log table for 4xx/5xx rows and see whether you get enough context to reproduce a failure without SSH-ing into production. That single test tells you whether the package's default payload (method, path, status, IP, user agent, timing, headers/body — whatever it captures) is sufficient or whether you'll need to enrich it.

Implementation Patterns

Scope by route group, not globally. Logging every request including health checks, asset 404s, and Horizon/queue traffic is the fastest way to make the table useless. Default to route-group attachment or a config exclusion list, and widen later if needed:

Route::middleware('flashlight')->group(base_path('routes/api.php'));

Capture the response, not just the request. The interesting part of a log entry is usually the status code and duration. If the package's middleware does its work before $next($request), you'll only have the request — in that case extend the middleware (or add a thin wrapper) that times around the handler:

$start = microtime(true);
$response = $next($request);
$duration = (microtime(true) - $start) * 1000;
// persist $response->getStatusCode(), $duration, ...

Subclassing the shipped middleware and swapping the binding in a service provider is the cleanest extension point and keeps you upgrade-safe-ish.

Order the middleware deliberately. Put the logger after StartSession and your auth middleware if you want user_id populated, and before anything that can throw early (throttling, signature verification) only if you care about rejected requests. For most teams the useful position is: session → auth → flashlight → controller. Rejections by earlier middleware simply won't be logged, which is usually fine and much cheaper.

Enrich with app context. Associate entries with your tenant/user so the log is queryable in a multi-tenant admin:

// in a subclassed middleware, before persisting
$payload['user_id']    = $request->user()?->id;
$payload['tenant_id']  = tenant('id');
$payload['request_id'] = $request->header('X-Request-Id');

A request_id you also emit in your exception handler is the single highest-value addition — it lets you jump from a Sentry/Bugsnag event to the exact request row.

Build the read side immediately. A log nobody queries is a disk leak. In practice you want:

  • an admin screen or Tinker/Nova filter over status >= 400 and a date range,
  • scopes on the model (->failed(), ->forRoute('...'), ->today()),
  • indexes on created_at, status, and whatever column holds the path — add these in a follow-up migration if the package's default migration only indexes the primary key.

Retention from day one. Either schedule pruning or truncate:

// routes/console.php or a scheduled command
Schedule::call(fn () => FlashlightLog::where('created_at', '<', now()->subDays(14))->delete())->daily();

Prefer chunked deletes (->delete() in chunkById batches) once the table is large, and consider OPTIMIZE TABLE / VACUUM depending on the driver.

Testing. If the middleware is global, your test suite will write a row per request and slow down. Disable it in phpunit.xml via the config env toggle, or don't register it globally at all. To assert on logging behavior itself:

$this->postJson('/api/webhooks/stripe', $payload);
$this->assertDatabaseHas('flashlight_logs', ['status' => 422]);

Operational loop. The realistic day-to-day workflow is: customer reports an error → search logs by path + time window + status → read the stored body/headers → reproduce. Make sure the stored body is JSON-decoded on read (cast the column to array/json in your own model or accessor) so you're not eyeballing escaped strings.

Gotchas and Tips

Redact aggressively before you ship. Request bodies routinely contain passwords, tokens, and PII, and headers contain Authorization and cookies. Check whether the package has a redaction/except-keys config; if not, add one in your subclass. This is the number one reason teams rip out request loggers. Also decide explicitly whether you're logging bodies at all — headers + status + timing often give 80% of the value at 5% of the risk.

Never serialize uploaded files. $request->all() on a multipart request can produce enormous or unserializable payloads. Strip UploadedFile instances and cap body size (e.g., truncate to 8–16 KB) before persisting.

Synchronous writes cost latency on every request. A DB insert per request is fine for low-traffic apps and noticeable under load. Options, in order of preference: sample (log 1 in N successes, 100% of errors), queue the write, or switch to the file/log-channel driver if the package supports one. Measure before optimizing, but know the knob exists.

config:cache will bite you. After changing config/flashlight.php, run php artisan config:clear locally and redeploy with a fresh config:cache — a stale cached config silently keeps the old exclusions.

Middleware registration differs by Laravel version. Laravel 11+ uses bootstrap/app.php; older apps use app/Http/Kernel.php ($middleware for global, $middlewareAliases for named). If the package's README predates Laravel 11, translate the snippet yourself.

Octane and long-running workers. Confirm the middleware holds no per-request state on a singleton. If the service provider binds the logger as a singleton, request data can leak between requests under Octane. Test with octane:start before deploying behind it.

Growth is the silent failure mode. Unindexed tables plus months of traffic produce slow admin queries and slow pruning. Set retention and indexes when you adopt the package, not after.

Migration ordering on existing databases. If you publish migrations into an app that already has data, and the package's migration shares a table name with a previous attempt, you'll get duplicate-table errors. Publish, read the migration filename/table name, and rename if there's a collision.

Small-package risk management. With ~14 stars and no dependents, treat this as vendored code rather than infrastructure. Concretely: pin an exact version in composer.json, read the middleware and migration before adopting, and keep your extension in a subclass so you can fork (MIT license permits it) if upstream goes quiet. The surface area is small enough that forking is genuinely cheap — the main cost of this package going unmaintained is a Laravel-major upgrade breaking the service provider or middleware signature, which is a 10-line fix.

Prefer Laravel's built-ins where they suffice. For plain debugging, Log::withContext() plus a single log line in a middleware covers a lot of ground and adds no dependency. Adopt Flashlight when you specifically want queryable, structured, retained request records — an audit/debugging dataset rather than log lines.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource