kira0269/symfony-log-viewer-bundle
Symfony Log Viewer Bundle adds a web interface for browsing, searching, and filtering application log files directly from your Symfony project. Inspect recent errors, stack traces, and log levels without shell access, making debugging faster for developers and teams.
This is a Symfony bundle, not a Laravel package, so the first mental shift is that there is no service provider, no config/logging.php handler stack, and no php artisan vendor:publish. It is a Monolog log-file browser exposed as routes: you register a bundle, point it at var/log, and browse the files over HTTP instead of tail -f-ing into a container. If you know ARCANEDEV/LogViewer or Telescope's log tab, it is that, minus the polish.
Minimal install:
composer require --dev kira0269/symfony-log-viewer-bundle
There is no Flex recipe (0 stars, 0 dependents — do not expect one), so register it by hand in config/bundles.php:
return [
// ...
Kira0269\LogViewerBundle\LogViewerBundle::class => ['dev' => true],
];
Then find out what it actually gave you before writing any config:
php bin/console debug:router | grep -i log
php bin/console config:dump-reference | grep -i -A20 log_viewer
php bin/console debug:container --tag=controller.service_arguments | grep -i log
That debug:router output is the whole ballgame — the bundle's value is the route(s) it registers, and the prefix/path is what you will secure and what you will hit in the browser. If nothing shows up, the routes were not auto-imported (see Gotchas).
First use case: you are debugging a 500 in dev and want to see the last 200 lines of var/log/dev.log filtered to request.ERROR without leaving the browser. Hit the route, pick dev.log, filter. That is the 80% use case, and it is genuinely faster than docker compose exec php tail -f var/log/dev.log when you are already clicking around the app.
Register it dev-only, and treat that as a hard rule. The ['dev' => true] map above is the cheapest protection. Since this is a 2021-era bundle with an unmaintained-looking surface, never let it into prod:
Kira0269\LogViewerBundle\LogViewerBundle::class => ['dev' => true, 'test' => true],
Secure the route anyway — defense in depth. Dev environments get exposed more often than anyone admits (ngrok, staging boxes with APP_ENV=dev, shared preview servers). In config/packages/security.yaml, restrict the prefix you found in debug:router:
security:
access_control:
- { path: '^/_log', roles: ROLE_ADMIN, requires_channel: https }
In dev you often have security: false on the main firewall, in which case put it behind a security middleware-equivalent: an IP allowlist at the reverse proxy, or a dedicated firewall with http_basic. Do not rely on "nobody knows the URL" — the route is listed by any debug:router call and by common scanners.
Match your Monolog config to what the viewer reads. The viewer reads files; it knows nothing about your handlers. If your handler is fingers_crossed, buffer, syslog, or error_log, there is nothing useful on disk. Concretely, this is what makes it work:
# config/packages/monolog.yaml (when@dev)
monolog:
handlers:
main:
type: stream
path: '%kernel.logs_dir%/%kernel.environment%.log'
level: debug
# separate channels help the viewer stay readable
doctrine:
type: stream
path: '%kernel.logs_dir%/doctrine.log'
channels: [doctrine]
level: info
With channel-per-file you get the day-to-day workflow: open doctrine.log when a query is slow, dev.log when a request explodes, and you never scroll through 40k lines of deprecation noise.
Use it as the "second screen" during a request, not as your only tool. The pattern that actually pays off is: bin/console cache:clear, reproduce the bug, hit the viewer, filter by the exception class or a request_id you added via a Monolog processor. Pair it with bin/console debug:router / debug:container when the question is wiring rather than output.
Override the template instead of forking. Twig bundle template overriding is the extension point: drop a file at
templates/bundles/Kira0269LogViewerBundle/<TemplateName>.html.twig
and Symfony picks it up. If you need to change behaviour (e.g. hard-cap the lines read), decorate the bundle's service rather than editing vendor/:
# config/services.yaml
services:
App\LogViewer\DecoratingController:
decorates: 'Kira0269\LogViewerBundle\...' # confirm id via debug:container
For multi-app setups, set the log directory from an env var rather than relying on the default %kernel.logs_dir%, if the bundle's config exposes a path key (check config:dump-reference for the exact name — the root alias is likely kira0269_log_viewer, but verify):
# config/packages/dev/kira0269_log_viewer.yaml
kira0269_log_viewer:
log_dir: '%env(LOG_VIEWER_DIR)%'
That lets you point one dev viewer at several containers' mounted log volumes instead of installing it in each.
Expect Symfony 5 lock-in. Last release 2021-08-08 means the constraints are almost certainly ^5.0 on symfony/framework-bundle and Twig 2/3. On Symfony 6/7 you will hit dependency resolution failures or, worse, it installs and then breaks on removed APIs. Check vendor/kira0269/symfony-log-viewer-bundle/composer.json before you build a workflow around it. If you are on Symfony 6+, this is a hard pass and you should use EasyCorp/EasyLoggingViewer or just tail -f.
No auto-routing. Without a Flex recipe, the routes may need explicit import. If debug:router shows nothing:
# config/routes/dev/log_viewer.yaml
log_viewer:
resource: '@LogViewerBundle/Resources/config/routing.xml' # path may differ
prefix: /_log
The exact resource path is the single most likely thing to be wrong — ls vendor/kira0269/symfony-log-viewer-bundle/Resources/config/ is faster than guessing.
Log rotation silently breaks the view. logrotate renaming a file out from under an open handle, or rotating with copytruncate, gives you a viewer showing yesterday's content or an empty file while tail shows the real thing. Use Monolog's rotating_file handler, or verify with ls -li var/log/ that the inode you see in the viewer matches what the app is writing.
Permissions differ between CLI and web. The classic: bin/console runs as your user and everything works; PHP-FPM runs as www-data and gets Permission denied on var/log/dev.log. chmod -R g+r plus a shared group, or fix the Docker user: mapping. Symptom is an empty list or a 500 in the viewer while the file is right there.
Large files will hurt. Naive implementations file_get_contents() the whole file before filtering. A 200MB prod.log mounted into dev will blow memory_limit (default 128M). Look for a tail/limit option in the config reference; if there isn't one, this is a good reason to decorate the service or just not point it at prod logs.
Timestamps are raw Monolog output. The viewer does not convert timezones. If php.ini's date.timezone differs from your host's, "5 minutes ago" bugs become "the log is empty" bugs. Also check that your Monolog LineFormatter includes the level — some minimal formats make the level filter useless.
.gz rotated files and hidden files are usually invisible to glob-based discovery. Do not assume rotation history is browsable.
Vendor risk is the real finding. 0 stars, 0 dependents, single 2021 release, MIT. Read the controller before trusting it with anything; it is small enough to audit in ten minutes, and if the code is clean you can vendor it into src/ and own it rather than depending on an unmaintained constraint. Pin the exact version ("kira0269/symfony-log-viewer-bundle": "1.0.0" or a commit hash) so a composer update doesn't quietly change your dev tooling.
When to skip it: if your team already has centralized logs (ELK, Loki, Sentry), adding a browser over var/log is redundant attack surface plus a dependency you cannot upgrade. Keep tail -f and grep -n. The bundle earns its place only when non-CLI people (QA, PMs on a dev box) need to read errors, or when you are SSH-averse in a containerized setup with no log aggregation at all.
How can I help you explore Laravel packages today?