laboiteacode/filament-logs-explorer
Read Laravel log files inside your Filament panel, grouped by channel with a slide-over viewer, in-file search, match navigation, keyboard shortcuts, large-file truncation, and file deletion behind authorization.
1. Install and register the plugin. The package is a Filament panel plugin, so it goes in your panel provider, not in a service provider:
composer require laboiteacode/filament-logs-explorer
php artisan vendor:publish --tag="filament-logs-explorer-config" # if a config is published
// app/Providers/Filament/AdminPanelProvider.php
use LaBoiteACode\FilamentLogsExplorer\FilamentLogsExplorerPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugins([
FilamentLogsExplorerPlugin::make(),
]);
}
Verify the exact plugin FQCN and any make() options against the README for your installed version — the namespace follows the vendor prefix and has moved between releases of packages in this family.
If you cache Filament components, clear it after install or upgrade. php artisan filament:optimize caches the plugin/page registration, including which cluster a page was filed under. On v1.0.2 specifically, the cluster() option was fixed — but the fix only takes effect after you re-run php artisan filament:optimize. Until then you'll still see the old, unclustered registration and assume the update didn't work.
2. Look at two things first, not the UI. Before clicking around, open config/logging.php and confirm your default channel resolves to a file. The plugin reads channels from config rather than globbing storage/logs, so single, daily, and monolog stream handlers with a path/stream show up; stderr, syslog, errorlog, and PaaS-managed channels do not. Then check that the PHP user serving the panel can actually read storage/logs/*.log (ls -l, sudo -u www-data cat ...).
3. First real use case. The highest-value moment is: a user reports a 500, you have no SSH session handy, and you want the stack trace. Open the panel → Logs Explorer → pick the daily channel for today → press /, paste the exception class or the request ID from your log context → n to walk matches. That single loop replaces "tail the file over SSH, grep, scroll" for the majority of small incidents.
Gate it before you ship it. Log files leak credentials, tokens, SQL, and personal data. Either register the plugin only on a restricted panel, or wrap it in an authorization check:
->plugins([
...(auth()->user()?->can('viewLogs') ? [FilamentLogsExplorerPlugin::make()] : []),
])
If the plugin exposes an authorization closure (check the README's config/API for authorize() or canAccess()), prefer that over conditional array spreading — it keeps the navigation entry defined but denied, which is easier to debug than a mysteriously missing menu item.
Make the channel grouping meaningful. The package's channel-aware listing is only as good as your logging config. If everything goes to the default stack, you get one giant file. Split by domain and you get navigable buckets:
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'payments', 'jobs'],
],
'payments' => [
'driver' => 'daily',
'path' => storage_path('logs/payments.log'),
'days' => 14,
],
'jobs' => [
'driver' => 'daily',
'path' => storage_path('logs/jobs.log'),
'days' => 7,
],
],
Log::channel('payments')->error('Gateway declined', ['order' => $order->id, 'code' => $code]);
Now an incident has a short list to check instead of one 400 MB file.
Add searchable context to every log line. The viewer's in-file search is a plain text match, so give it handles: request IDs, job UUIDs, user IDs. A small middleware plus a Log::withContext([...]) call makes / searches dramatically more useful:
Log::withContext([
'request_id' => (string) Str::uuid(),
'user_id' => auth()->id(),
]);
File it under a cluster on multi-panel apps. As of v1.0.2 the cluster() option actually works, via either the fluent method or the cluster config key:
FilamentLogsExplorerPlugin::make()
->cluster('System'),
The bug it fixes is worth knowing about because it shaped behaviour you may have relied on: the page used to register before the plugin could be resolved from its panel, so it landed unclustered — and on apps with several panels it could silently inherit the cluster configured on a different panel. If you previously worked around this by moving the resource or hacking the page class, remove the workaround and re-run the optimize cache command.
Use it as the fast path, not the record. Day to day this is the "what just happened" tool: check the newest daily file after a deploy, confirm a queued job's exception, verify a webhook handler logged what you expected. Keep your real observability (Sentry, Flare, or an aggregation stack) for history, alerting, and correlation across services — the package has no retention of its own beyond what LOG_DAILY_DAYS leaves on disk.
Pair it with the environment. Register it in local, staging, and a tightly-gated production panel; skip it entirely in CI. If you use a container platform where LOG_CHANNEL=stderr, the package will render an empty channel list on production even though logs exist — that's expected, not a bug.
storage/logs; or daily files are being written by a different process (queue worker as another user) into a directory you can't read. Check ownership, not just mode bits.daily file may be empty. That's the healthy state. If you're hunting a recent error and the current file is blank, look at yesterday's — Monolog rotates on the date boundary, and long-running workers may still hold a handle to the previous file.LOG_DAILY_DAYS low, split into per-domain channels as above, and treat any single log over a few tens of MB as a job for grep/rg on the server rather than the panel./, n, N, g are only safe when the slide-over owns the keyboard. If shortcuts feel dead, click inside the viewer first; if they fire while you're typing in a Livewire search field elsewhere, that's a conflict to report/disable rather than work around.stack lists only its file-based members. If your stack is ['daily', 'stderr'], don't expect a stderr entry — there's no file to read.plugins([...]), the cluster key, or the navigation methods has no visible effect until php artisan filament:optimize runs again (or you clear the cached file). Symptom: the page sits in the old nav group/cluster, or changes you made "revert". Add the optimize command to your deploy script rather than remembering it manually.LogFile-style value objects are exposed for custom pages, and whether file resolution can be swapped for an S3/remote stream. If the package doesn't expose these, the supported extension is a custom Filament page that reuses your own reader — don't fork for it.How can I help you explore Laravel packages today?