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

Log Viewer Laravel Package

laravel-admin-ext/log-viewer

A laravel-admin extension that brings a log viewer directly into your admin panel, letting you browse, filter, and inspect application log files without leaving the dashboard. Convenient for quick debugging and monitoring in Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin laravel-admin extension: it ships a controller + Blade view that lists files in storage/logs, lets you pick one, and renders entries with level filtering. There is no CRUD, no model, no migrations — it's a read-only UI over the filesystem. That means setup is three steps and no admin:install-style scaffolding.

1. Install into an existing laravel-admin project

composer require laravel-admin-ext/log-viewer
php artisan vendor:publish --provider="Encore\Admin\LogViewer\LogViewerServiceProvider"

You must already have encore/laravel-admin installed and app/Admin/ present. If composer resolves to an old illuminate/* constraint, check composer why-not before fighting it — see Gotchas.

2. Register the route inside the admin middleware group

In app/Admin/routes.php (this file is already wrapped in the admin middleware stack — auth + permission — by Encore\Admin\Providers\AdminServiceProvider):

$router->get('logs', '\Encore\Admin\LogViewer\LogViewerController@index');

3. Add the menu entry

Same file, or app/Admin/bootstrap.php:

$menu->add('Logs', ['url' => 'logs']);

Then hit /admin/logs. If the menu item 404s, run php artisan route:list | grep logs to confirm whether the service provider auto-registered a route and your manual one collided or shadowed it.

Where to look first

  • The controller (vendor/laravel-admin-ext/log-viewer/src/LogViewerController.php) — it's short, and it tells you exactly which directory and glob pattern it reads, plus which query params (file, level, per_page-style) the view uses.
  • The published view under resources/views/vendor/... — this is the extension point for anything cosmetic or filter-related.

First real use case: staging throws a 500 and Sentry isn't wired up yet. Open /admin/logs, select laravel.log (or today's laravel-YYYY-MM-DD.log if LOG_CHANNEL=daily), filter level to error, read the stack trace without SSH-ing into the box or tailing over a jump host. That's the 90% use case — fast triage from a browser you already have open.

Implementation Patterns

Gate it per-role, not just per-login. The default menu item is visible to every administrator. Logs routinely contain bearer tokens, passwords in exception messages, and full request payloads. Restrict with laravel-admin's own RBAC or with a super-admin check:

// app/Admin/routes.php
$router->get('logs', '\Encore\Admin\LogViewer\LogViewerController@index')
       ->middleware('admin.permission:logs'); // or a custom IP/role middleware

And in app/Admin/bootstrap.php, only add the menu for trusted users:

if (Admin::user() && Admin::user()->isAdministrator()) {
    Admin::menu()->add('Logs', ['url' => 'logs', 'icon' => 'fa-file-text-o']);
}

Better still, define it as a real permission row (admin_permissions) and let the standard admin.permission middleware do the work — that survives menu edits in the UI.

Disable in production (or on multi-node apps). Wrap the route registration:

if (!app()->isProduction() || config('admin.log_viewer.enabled')) {
    $router->get('logs', '\Encore\Admin\LogViewer\LogViewerController@index');
}

The viewer reads the local filesystem. Behind a load balancer with three app servers, you'll see one-third of your logs and get confused. In that topology, use centralized logging (Loki, Papertrail, CloudWatch) and keep this package for single-node staging.

Make your log lines viewer-friendly. The viewer renders whatever is in the file. Multi-line context is fine, but structure pays off when you scan:

Log::error('Payment capture failed', [
    'order_id' => $order->id,
    'gateway'  => $gateway,
    'code'     => $e->getCode(),
]);

This produces a single readable entry instead of a 60-line var_dump, and it's greppable from the UI.

Custom controller for extra filters. Rather than forking the package, subclass it:

namespace App\Admin\Controllers;

use Encore\Admin\LogViewer\LogViewerController as BaseController;

class LogController extends BaseController
{
    public function index()
    {
        // e.g. force a specific pattern, or block non-whitelisted files
        return parent::index();
    }
}

Route to your class and add whatever guardrails you need (file whitelist, max file size, request-scoped audit logging of who viewed logs).

Pair it with the debug tools you already run. Telescope / Debugbar for local request-level debugging; this viewer for environments where you can't install those. Don't duplicate — the log viewer is the "I'm already in /admin and something broke" tool.

Deep links are the underrated feature. Because state lives in query params, you can paste /admin/logs?file=laravel-2024-05-01.log into an incident channel and everyone lands on the same view. Encode the incident date in the URL when you hand off.

Auto-refresh for live incidents. The package doesn't poll. Add a <meta http-equiv="refresh" content="30"> to a published copy of the view, or a tiny Alpine/JS interval that re-fetches — enough for "watch it while I redeploy."

Gotchas and Tips

The package is frozen at October 2021. laravel-admin itself is effectively unmaintained, and this extension depends on it. On modern Laravel (10/11/12) you will hit composer constraint conflicts on illuminate/support, illuminate/routing, etc. Options: pin to an older Laravel, or treat the package as source — copy the controller and view into app/Admin/ and drop the dependency. Copying is genuinely the sanest path for a two-file package.

Permissions are the #1 silent failure. The web server user (www-data, nginx) must be able to read storage/logs. When queue workers or cron run as a different user, files are created 0640 and the UI shows an empty or error state instead of the log. Check with sudo -u www-data cat storage/logs/laravel.log. Fix with matching umask/group ownership, not chmod 777.

daily channel means dozens of files. If LOG_CHANNEL=daily, you get laravel-2024-05-01.log, laravel-2024-05-02.log, … and the dropdown grows forever. Set LOG_DAILY_DAYS to something sane, and note that the default file-selection logic usually picks the newest — which may be today's nearly-empty file while the error you're chasing is in yesterday's.

Big files will hang or OOM the request. Most implementations of this pattern read the whole file into memory and then slice. A 500 MB laravel.log from a runaway loop will time out PHP-FPM. Mitigate upstream: log rotation, LOG_STACK/LOG_LEVEL tuned so you're not writing debug in production, and a max file size guard in your subclassed controller.

No file deletion guard is safe by default. If the view exposes a delete action, any admin can nuke your audit trail. Verify what the published view actually renders and remove destructive actions in the published copy before rollout.

Config cache bites you after route/menu edits. If php artisan config:cache is on, new config keys you add won't appear until you re-cache. Same for route:cache — laravel-admin route files are dynamic, so cached routes with added entries need a route:clear.

Menu items are DB-backed, not file-backed. $menu->add() in bootstrap.php writes to admin_menu. Editing the file and re-running won't update an existing row with the same title/URI; check the admin_menu table when a change appears to be ignored. Also, the menu is rebuilt on every admin page load via a query — don't put this in a hot loop.

per_page / level filters are request params, not config. If you expect to change defaults, that's a view edit or a controller override, not a config key. Don't hunt for a config/log-viewer.php that isn't there.

Stack traces and multi-line entries render as one block in some builds. If exception traces look mangled, the splitter is regex-based on the log line prefix ([YYYY-MM-DD HH:MM:SS] channel.LEVEL: message). Malformed lines from a custom Monolog formatter will break alignment — verify your logging.channels.*.formatter still emits the standard prefix.

Don't route it outside the admin middleware group. Adding it to routes/web.php "because it was easier" makes it publicly reachable. The admin group's admin.auth + admin.permission is the whole security model here.

Extension points, in order of preference: (1) publish and edit the view for display changes; (2) subclass the controller for auth/filtering/file-selection logic; (3) only then fork the package. The service provider is registered via package discovery, so overriding in config/app.php providers requires dont-discover — usually unnecessary.

Keep a tail -f muscle memory. For anything real-time, the viewer is the wrong tool; it's a snapshot. Use it for "what happened five minutes ago," not "what's happening now."

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log