csun-metalab/laravel-multiple-logs
Laravel package for configuring and using multiple log channels in one application. It helps route logs to separate files or handlers, making it easier to organize, isolate, and troubleshoot logs across different parts of a Laravel project.
Start by reading the package's README.md, composer.json, and src/ directory in that order — this is a zero-star, zero-dependent package, so the source is the documentation. Identify the service provider (usually auto-discovered via extra.laravel.providers) and any config file it registers (typically config/multiple-logs.php or similar).
Minimal install:
composer require csun-metalab/laravel-multiple-logs
php artisan vendor:publish --provider="CSUNMetalab\MultipleLogs\MultipleLogsServiceProvider" --tag=config
Then confirm the package actually registers channels with Laravel's logging stack by inspecting config/logging.php after a test boot. A "multiple logs" package typically either (a) adds a custom Monolog handler that fans a single Log:: call out to N destinations, or (b) registers dynamically-named channels per user/tenant/context. Figure out which model this one uses before writing application code — it dictates everything downstream.
First use case: replace your single stack channel with purpose-specific channels — e.g. app (general), audit (model events), payments (financial events) — so that when something breaks you can tail the exact file you need instead of grepping one monolithic laravel.log. This is the lowest-risk, highest-value adoption path and doesn't require touching domain logic.
Per-context log channels. The most common pattern for packages like this is a method that returns a channel name derived from request context:
Log::channel(multiple_logs()->channelFor(auth()->user()))->info('...');
The important shift is from Log::info() everywhere to a small helper or facade that resolves the channel. Centralize that resolution so you can change the strategy (per-user, per-tenant, per-queue-job) in one place.
Fan-out / stack composition. If the package provides a custom driver, you can drive it from a normal stack channel in config/logging.php:
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'multiple-logs'],
],
This lets you adopt the package incrementally — keep existing channels, add the package's driver alongside. If the package is a stack replacement, mirror this pattern with the package's own config key so you don't lose existing destinations.
Audit / domain-event logging. Wire model events to a dedicated channel via an observer:
class AuditObserver
{
public function created(Model $m): void
{
Log::channel('audit')->info('created', ['model' => $m::class, 'id' => $m->getKey()]);
}
}
Register observers in a service provider rather than scattering Log:: calls in controllers. This keeps the "which channel" decision declarative.
Middleware-scoped logging. For per-request or per-user logs, push the resolved channel/user into a container binding in middleware, then have your logging helper read from the container. Avoid calling auth()->user() inside the package's resolution layer — queue workers, console commands, and event listeners often have no authenticated user, and you'll get null-dereferences in production long before you see them in local dev.
Testing. Use Log::spy() / Log::shouldReceive() against the resolved channel name. Assert on the channel, not just the message — the whole point of the package is routing, so a test that only checks "something was logged" is worthless. If the package wraps LoggerInterface, bind a fake logger into the container in TestCase::setUp().
config:cache will bite you. Dynamically-generated channel names computed at runtime must not depend on config values that get frozen during php artisan config:cache. Verify by running config:cache locally and exercising the log path — a surprising number of these packages pass in local and silently fail in production.storage/logs/users/. Confirm the package creates directories (many don't) and that your deploy pipeline's storage/logs permissions survive git clean -fdx. Add a mkdir -p to your deploy script if unsure.config/logging.php at boot, it can overwrite a channel you defined manually (e.g. a channel also named audit). Grep config/logging.php for the package's channel names and namespace yours (e.g. app-audit rather than audit).daily driver. Add a scheduled storage:prune or a custom RotatingFileHandler — otherwise a busy multi-tenant app can fill a disk in weeks.auth()->user() and request(). If the package resolves channel names from these, jobs will crash or write to a fallback channel. Explicitly pass context via job payloads or a scoped container binding.$hidden/$visible guards on any model whose events you log, and prefer an explicit toLogArray() on your models.single handler, check whether both write to laravel.log. You'll get duplicate lines and confusing timestamps. Inspect the resolved handler stack with logger()->getHandlers() in a tinker session.composer.json), read the entire src/ (~a few hundred lines at most), and be prepared to own the code. The license is MIT, so forking is safe. Treat the package as reference code rather than a maintained dependency.ChannelResolver contract), bind your own implementation in a service provider rather than editing published config — that's the one place where your customizations will survive a composer update.How can I help you explore Laravel packages today?