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 Log Viewer Laravel Package

rap2hpoutre/laravel-log-viewer

A small log viewer for Laravel 12 and 13. Install via Composer and add a route to LogViewerController to browse logs in your browser. Works with or without log rotation, requires no public assets or vendor routes, and supports custom views and configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install in under two minutes:

composer require rap2hpoutre/laravel-log-viewer
// routes/web.php
Route::get('logs', [\Rap2hpoutre\LaravelLogViewer\LogViewerController::class, 'index']);

Hit /logs. No service provider registration, no asset publishing, no migration. The controller reads storage/logs/, renders a Blade view with a file picker, and parses each log line into a structured array with level-based color coding.

Where to look first:

  1. LogViewerController::index — the whole package is essentially this method. Read it once; it tells you how files are discovered, how the current file is chosen, and how lines are parsed. Everything you might customize hinges on understanding this flow.
  2. config/logviewer.php — publish it (php artisan vendor:publish --provider="Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider") and skim the keys: paths, patterns, max size. This is your primary tuning surface.
  3. resources/views/log.blade.php — publish with --tag=views. This is where 90% of "I want it to also do X" requests land.

First use case: put the route behind auth on staging and link it from your admin nav. You now have browser-based access to laravel.log without SSH or docker exec. Under five minutes of work. If you find yourself needing more than that, you're probably outside what this package is built to do.

Implementation Patterns

Guard the route at registration, not in the deploy checklist. The package ships zero access control. This is the single most important habit:

Route::get('logs', [LogViewerController::class, 'index'])
    ->middleware(['auth', 'can:viewLogs'])
    ->name('logs');

Pair it with a viewLogs Gate so the permission lives in one place and is testable. Adding throttle:30,1 is cheap insurance against someone hammering a large file through the viewer.

Environment-scope the route by default.

if (app()->environment(['local', 'staging'])) {
    Route::get('logs', [LogViewerController::class, 'index'])->middleware('auth');
}

If production exposure is genuinely required, treat it as an explicit decision (signed off in the PR) rather than a default that leaks into every environment.

Multi-channel log directories. If you use custom channels (billing, payments, queues) that write into storage/logs/, the file picker shows them for free — it globs whatever patterns the config supplies. If your channels write elsewhere, add those paths in logviewer.php rather than modifying the controller. This is the intended multi-file extension point.

Deep-link into a specific file via a wrapper route. Useful for "here's the log for the incident" links in Slack:

Route::get('logs/{file}', [LogViewerController::class, 'index'])
    ->where('file', '[A-Za-z0-9_\-.]+')
    ->middleware(['auth', 'can:viewLogs']);

The where constraint is non-negotiable — the file selection parameter is a path-adjacent input and you want to constrain it yourself, not rely on the package to sanitize.

Customize the view, not the controller. The log.blade.php receives parsed entries as plain arrays — trivial to enhance with client-side work:

  • A filter input bound to JS that hides non-matching rows.
  • A "copy line" button per entry.
  • A dark mode toggle via a CSS class swap.
  • Links from stack-trace file paths to your issue tracker or editor scheme (vscode://).

This is the safe, upgrade-friendly customization channel. Overriding the LogViewerController is not — the package ships updates (last release 2026-05-12) and you'll fight merges forever.

Complement, don't replace, streaming tooling. Use php artisan pail or tail -f for live dev feedback; use this viewer for historical inspection and for handing logs to people without shell access — QA, support, PMs. That division is where the package earns its keep.

Gotchas and Tips

InvalidArgumentException in FileViewFinder.php on first run is config-cache staleness, full stop. Run php artisan config:clear, then php artisan cache:clear. Put this in your onboarding notes; it's in the README because it happens constantly.

"Empty page" usually means a permission problem, not a broken package. If storage/logs/ isn't readable by the PHP-FPM user, you get a blank or broken view. Test directly before debugging the viewer:

sudo -u www-data test -r storage/logs/laravel.log && echo ok

Large log files stall the page — there's no pagination. The controller reads and parses the whole file on each request. On logs regularly >50MB expect seconds-long responses and memory pressure. Practical mitigations:

  • Use daily rotation with a small days value to keep individual files browsable.
  • Cap the max size in logviewer.php and rely on rotation to keep current files under the cap.
  • For local dev with a runaway log, truncate -s 0 storage/logs/laravel.log beats troubleshooting. Add truncate to your shell aliases.

No server-side search, filtering, or pagination. Browser find is the search. Teams repeatedly assume otherwise. If search matters, build it into the published view with client-side JS — the parsed entries are already in the DOM.

Compressed logs are invisible. If your channel produces .gz rotations, the viewer won't open them. Either keep compression off for the log(s) you want browsable, or accept the live file as the only view. There is no decompression path and no plan for one.

Daily logs work (since v2.0.0) but the picker lists dated files; timestamps within each line are shown as-is. The viewer reflects one instance's local disk — with load-balanced or containerized deploys you see one pod's slice of history. If QA can't reproduce a bug against the logs they see, this is usually why. Point them at your aggregator for cross-instance truth.

Audit what you log before you expose /logs. Enabling the viewer turns your log files into a web-readable surface. Tokens, PII, and request bodies in Log::debug calls are now one auth bypass away from being readable. Redact at the logging layer, not by hoping the middleware holds.

Deploy order matters. Register the log route after the Gate and auth scaffolding exist in the target environment. If can:viewLogs doesn't resolve on first deploy you get 403s (or an exception) that look like a broken package when it's really a deployment sequencing problem.

Don't fork the controller. Its interface is stable via the view and config; going past those boundaries means maintaining a fork against a package that ships regularly. If you need server-side features (structured JSON parsing, export, filters), write a thin controller of your own that reuses the config paths and renders the published view — don't override the vendor class.

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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core