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

Nova Logs Tool Laravel Package

kabbouchi/nova-logs-tool

A Laravel Nova tool for browsing and reading application log files directly from your admin panel. View, search, and manage log files without SSH access, making debugging and monitoring quicker and more convenient.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the Nova constraint before you type composer require. This package is a Nova 3/4-era tool — last tag is v1.1.0, February 2023. Open the require block of composer.json on the tag you intend to install and confirm laravel/nova matches your app. If you're on Nova 5, decide up front whether you'll pin Nova, use --with-all-dependencies, or vendor the package into your own namespace. That one decision determines whether the rest of this guide applies to you.

composer require kabbouchi/nova-logs-tool:^1.1

Register it exactly once, in app/Providers/NovaServiceProvider::tools() (or the tools array in config/nova.php, not both). Copy the FQCN verbatim — the vendor segment is uppercase:

use KABBOUCHI\LogsTool\LogsTool;

public function tools(): array
{
    return [
        LogsTool::make(),
    ];
}

Verify it mounted before you debug anything else.

php artisan config:clear
php artisan route:list | grep -i logs

You want two signals: a "Logs" entry in the Nova sidebar, and at least one route registered under the Nova route prefix. If the sidebar item appears but the panel is blank, that is almost always a dist/ asset problem, not an install problem — see Gotchas.

First hour: read the vendor source. It's small enough to read in full — one Tool class, a short route file, one controller, one Vue component. Specifically answer these four questions:

  1. Where does the controller resolve the log directory from? Hardcoded storage_path('logs'), a config key, or a filesystem disk? This tells you whether custom channels will show up at all.
  2. How does it read files? file_get_contents / Storage::get on the whole file, or chunked/streamed? This determines your incident-day failure mode.
  3. What are the routes, and what middleware protect them? Nova's auth middleware, or something else? Is there any per-user gate?
  4. How does the Vue panel parse entries? Regex on classic single-line laravel-*.log, or something structured-aware?

Your first real use case, end to end: an exception report comes in, you open the Logs tool, pick today's laravel-YYYY-MM-DD.log, search for the exception class or a request/correlation ID, and read the surrounding lines. If the built-in search can't find it (multi-line stack trace, unusual format), download the file and grep -n -A 40 it locally. That's the whole value proposition: triage without asking an engineer for SSH.

Implementation Patterns

Gate to a named group, not to "holds a Nova seat." Nova seats are typically broader than "people who should read production logs," and log lines regularly contain tokens, PII, and connection strings. The cheapest pattern is conditional registration in NovaServiceProvider:

Gate::define('viewLogsTool', fn (User $user) => $user->hasAnyRole(['on-call', 'devops']));

public function tools(): array
{
    return array_filter([
        Gate::allows('viewLogsTool') ? LogsTool::make() : null,
    ]);
}

Users outside the group never see the sidebar item. If you need defense-in-depth, re-check the gate inside the controller in a fork — there's no policy hook to lean on.

Scope by environment. Most teams want this in local and staging unconditionally and in production only during incidents:

$tools = [/* ... */];

if (! app()->isProduction() || config('logging.nova_viewer_enabled')) {
    $tools[] = LogsTool::make();
}

Keep the flag in config/logging.php or a dedicated config key so on-call can flip it via your normal config-deploy path, not a code change.

Decide your delete posture before rollout, not after the first incident. Delete is destructive, has no soft-delete, and leaves no record of who did it. Two mitigations, in order of preference:

  1. Filesystem-level: mount storage/logs read-only to the PHP-FPM / container user. unlink() fails; the tool cannot delete what the OS won't permit. Zero code, survives upgrades.
  2. Code-level: subclass the tool or override the controller to drop the delete action (and the download action if you're strict) from both the route table and the Vue panel.

Point it at what your app actually writes. Cross-check the tool's directory against config/logging.php and the logs disk in config/filesystems.php. Daily channels, per-service subdirectories, and stderr-only channels are the most common reasons the file list is empty or missing entries you know exist.

Customize by subclassing, not by editing vendor. A thin subclass is the cleanest extension point:

namespace App\Nova\Tools;

class LogsTool extends \KABBOUCHI\LogsTool\LogsTool
{
    public function menu(Request $request)
    {
        return parent::menu($request);
    }
}

Register the subclass in tools(). Anything deeper — streaming reads, per-file size caps, gating download — means overriding the controller or the Vue component in a fork. Keep the diff to a handful of files so rebasing on a new tag stays trivial.

Treat it as a per-node convenience link. On a multi-node estate it shows one node's local files. Write that into the on-call runbook next to your real log platform (Loki, CloudWatch, Datadog): "this tool sees one node; for anything spanning requests or services, use the aggregator."

Pair it with Telescope (or php artisan tail) for local dev. Telescope gives per-request detail; this gives "what does the log say right now" from inside the admin. They solve different problems and don't overlap much.

Pin and keep a fork branch warm. Commit composer.lock, pin the exact tag, and treat the fork as a planned response to a trigger — a Nova major bump, a security finding, a missing feature — rather than an emergency. The surface is small enough that a mid-level Laravel engineer can own it in about a day.

Exercise the worst cases in staging before rollout:

  • a 100–200 MB log against your real memory_limit;
  • a JSON-structured Monolog channel (does it render? is it searchable?);
  • a multi-line stack trace (does each physical line become its own "entry"?);
  • a log in a non-default subdirectory (does it appear at all?).

Gotchas and Tips

Whole-file reads fail exactly when you need them. If the controller slurps the selected file into memory, an incident-day log will blow memory_limit or hit the gateway timeout — precisely when the tool is most valuable. Confirm in the source whether reads are chunked. If not: rotate aggressively to cap file size, limit which files are exposed, or fork in a streamed/fseek-based reader. Bumping memory_limit is an emergency stopgap, not a fix.

Stale compiled assets are the #1 post-upgrade breakage. Symptom: sidebar entry renders, panel is blank or throws a JS console error. Run php artisan nova:publish, and if that doesn't do it, rebuild dist/ from source in your fork. Nova tools go quiet for long stretches, so this is the most likely surprise after a Nova major bump.

Vendor namespace case. KABBOUCHI\LogsTool vs. Kabbouchi\LogsTool. PSR-4 is case-sensitive on Linux. If it works on your Mac and 500s on the deploy box with a class-not-found, this is why.

Config cache hides your environment guard. After adding the if (! app()->isProduction()) conditional or editing config/nova.php, run config:clear locally and config:cache on deploy. A cached config silently bypassing your guard is a genuinely nasty surprise in production.

An empty file list is almost never "no logs." It's the path or permissions. The web user (www-data, php-fpm, container user) needs read access to the directory, and the directory must exist. Silent-empty is worse than a hard error because an operator mid-incident will trust it and conclude there's nothing wrong. Add a smoke check to your deploy runbook: confirm at least one file is listed after each deploy.

Structured logs render poorly. Regex parsing assumes classic single-line laravel-*.log. JSON Monolog output, multi-line traces, and non-default channel paths either render as nonsense or become unsearchable — often without erroring, which is worse. If your logs are structured, plan to fork or route to a real log platform and keep this for legacy channels only.

Deletion has no undo and no audit. The delete action removes the file outright: no soft-delete, no record of who clicked it. Neutralize it at the filesystem level (read-only mount) or in a fork. If you have compliance obligations, log access to the tool at the web-server level — the package provides nothing.

Widening access is a compliance change, not a UI change. Moving from "engineers with SSH" to "anyone with a Nova seat" exposes whatever lands in your log lines. Before rollout, grep a week of production logs for password, token, authorization, and email patterns, and fix the noisiest call sites. Then gate to a named group.

Rotated-away files are gone. The tool lists what's on disk now. Retention and rotation are your responsibility — don't promise history you don't keep.

A 0.7.0 → 1.1.0 jump crosses a pre-1.0 boundary where breaking changes are explicitly allowed. If you're upgrading, re-resolve the lockfile, re-verify dist/ against your Nova major, and re-run the worst-case tests above before it hits production.

Extension points are limited to the fork. There's no plugin API. The realistic surface is: subclass the Tool to adjust routes/menu, override controller methods to add streaming or gate delete/download, and edit the Vue component for UI. Keep the fork small and rebased — the package is small enough that "read the whole thing" is genuinely the intended integration step.

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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect