mouf/utils.log.psr.multi-logger
A PSR-3 compatible multi-logger for the Mouf framework that fans out each log message to several underlying loggers at once, letting you route logs to multiple destinations through a single PSR-3 interface, wired up via Mouf's dependency injection.
Architecture fit. The package is a thin PSR-3 decorator: a MultiLogger that implements LoggerInterface and fans each log() call out to a set of injected loggers. Conceptually this is a clean seam, but in a Laravel codebase it is functionally redundant — Laravel's stack driver (config/logging.php) already composes multiple channels behind one PSR-3 facade, with config caching, channel-level processors, and withContext semantics that this package does not know about. The only genuinely additive use case is a framework-agnostic seam: shared libraries or packages that must emit to several sinks without depending on Illuminate\Log\LogManager. If the team's need is "write to stderr and an audit channel from Laravel," the built-in stack channel wins on every axis.
Integration feasibility. Low effort, non-trivial wiring risk. There is no Laravel service provider, no config stub, no facade. Two viable paths:
new MultiLogger([Log::channel('app'), Log::channel('audit')]) in AppServiceProvider::register() and bind it to an aliased interface. Do not overwrite the global Psr\Log\LoggerInterface binding without a feature flag; that silently re-routes every Log:: call, including framework internals.Log::extend('multi', fn ($app, $config) => new MultiLogger(...)) plus a channel entry in config/logging.php. This is the more idiomatic path and survives config:cache, but it is real code you now own, which raises the fair question of why you would not just use driver => stack.Technical risk. Code risk is negligible (the surface is ~a few dozen lines). Dependency and stewardship risk is high:
psr/log ^3. A Mouf-era package pinning psr/log ^1.0 will block composer require outright. The LoggerInterface signatures changed across v1/v2/v3 (void return types in v2+), so even a soft constraint can produce fatal signature errors at runtime.mouf/* container/compat dependencies. Any transitive DI framework in a Laravel app is an unacceptable blast radius.withContext/LoggerAwareInterface immutably or mutate? Neither is documented in the description.Key questions for the maintainer/team before proceeding:
psr/log and php constraint in composer.json, and does it resolve against our target Laravel's Monolog 3 stack?psr/log transitive dependencies (specifically mouf/mouf or mouf/utils.common)?RequestId, withContext, Monolog processors) per child, or bypass it?stack driver cannot satisfy?Stack fit. Assume a Laravel 10/11 target on PHP 8.2/8.3. The package is PSR-3, so it speaks the right language, but it sits beside Laravel's logging rather than inside it. Treat it as a library-level decorator, not a logging subsystem.
Migration path.
composer require mouf/utils.log.psr.multi-logger --dry-run in a throwaway branch, then composer why-not psr/log ^3.0. If it hard-conflicts, stop — the evaluation is over and the answer is "write 30 lines in-house."Log::channel('app'), Log::channel('audit')) as children, so enrichment, formatters, and handlers stay where the team already configures them.Log::extend('multi', ...)) over container override — it composes with config:cache, keeps channel config declarative, and is discoverable for the next engineer.Log::info('x', $ctx) reaches every child with identical message and context, plus one test asserting a throwing child does not fail the request.Compatibility checks. Verify LoggerInterface method signatures match the installed psr/log major (return types matter), confirm MultiLogger's constructor shape (array vs. variadic), and confirm no Mouf container coupling. If any of these are ambiguous from source, that ambiguity is itself the finding.
Sequencing. Probe → contract test → pilot in one non-critical service → 30-day observation → go/no-go on broader use. In parallel, price the alternative: Laravel stack channel (zero cost, config-only) and an in-house CompositeLogger implements LoggerInterface (~30 LOC, your license, your tests, no psr/log pinning). Realistically the build option should be the default and this package the challenger.
Recommendation. Do not adopt as a platform dependency. Either use the native stack driver, or vendor a ~30-line composite logger into app/Support/Logging with an equivalent test. Reserve this package for the narrow case where a shared, framework-agnostic library must fan out without Laravel — and even then, confirm the psr/log constraint is ^2 || ^3 first.
Maintenance. This is the dominant cost. Three stars and zero dependents means the package's release cadence will not track Laravel's. Every Laravel major bump becomes a manual compatibility audit of a dependency you cannot influence. Because the surface is tiny, the correct operational posture is assume you will own the fork within 12 months — so plan the exit before the entry. MIT license makes vendoring or forking legally frictionless; that is the package's best attribute.
Support. No vendor, no SLA, GitHub issues only, small Mouf community. Any incident routes to your own on-call. That is acceptable for a 30-line utility and unacceptable for anything on the request path you cannot reason about. Budget an internal owner by name.
Scaling. Fan-out is synchronous and in-process: each log event costs N handler invocations. If any child is a network sink (remote syslog, HTTP log shipper), that child becomes the latency floor for every request that logs. There is no buffering, sampling, or async hand-off. Secondary effect is log volume multiplication — N sinks means N ingest bills. Validate against current Datadog/CloudWatch spend before promoting a multi channel into a high-traffic path; a fan-out that duplicates verbose debug logs across two paid sinks can be a five-figure annual line item.
Failure modes to design against.
config:cache and to anyone reading config/logging.php; the custom-driver route mitigates this.Ramp-up. Effectively zero — it is PSR-3, and any engineer who knows LoggerInterface is productive in under an hour. The cognitive load is not in the API; it is in the organizational load of owning an orphaned dependency and remembering that a global binding override changes every log line in the application. That asymmetry — trivial to learn, easy to forget, expensive to debug — is the core argument for preferring the native stack driver or an in-house implementation.
How can I help you explore Laravel packages today?