Logger#
A PSR-3 logger that uses Monolog as its implementation. The app depends on the standard Psr\Log\LoggerInterface, so it shares the same binding with third-party libraries that log via PSR-3.
It is always registered in DI with no configuration required (auto-registered, the same as the HTTP client). The output goes to STDERR by default; LOG_FILE directs it to a path, and LOG_LEVEL changes the threshold. The Monolog implementation follows a thin policy that does not add client-builder-style complexity.
Enabling#
The logger is always registered. As with HttpClient, there is no required configuration, so any page/component can receive Psr\Log\LoggerInterface as a dependency with no extra setup. There are two optional environment variables.
LOG_LEVEL=info # threshold. one of the 8 PSR-3 levels
LOG_FILE=/var/log/app.log # output path. php://stderr if unset- The default of
LOG_LEVELis dev=debug/ production=info. It
accepts the 8 PSR-3 levels (debug info notice warning error critical alert emergency). A misspelling soft-fails and falls back to the default (so Monolog is not made to throw).
- If unset, logs go to STDERR (12-factor: collected by
docker logs, journald, or a platform log drain). Only deployments that want file output set LOG_FILE — directory creation, .gitignore, and rotation are your responsibility in that case.
→ See also Environment Variables.
Usage#
Just receive Psr\Log\LoggerInterface by type in the page/component constructor (or as a function-page argument).
<?php
use Polidog\Relayer\Router\Component\PageComponent;
use Polidog\Relayer\Router\Component\Element;
use Psr\Log\LoggerInterface;
final class CheckoutPage extends PageComponent
{
public function __construct(private readonly LoggerInterface $log) {}
public function render(): Element
{
$this->log->info('checkout started for {user}', ['user' => $userId]);
// ...
}
}{placeholder} interpolation (PSR-3 §1.2) is applied to the output (Monolog itself does not interpolate, so PsrLogMessageProcessor handles it). The conventional ['exception' => $e] context key is formatted by Monolog.
What you get automatically (profiler integration)#
In dev, the concrete LoggerInterface is wrapped with TraceableLogger (a PSR-3 decorator), and each entry is mirrored onto the profiler timeline as a log event (labeled with the PSR-3 level). It has the same Traceable* shape as Database / HttpClient / Auth, so log lines appear on the timeline without writing an if profiler branch.
The profiler-side copy is redacted.
- Values whose key name contains
pass/pwd/secret/token/
api_key / auth are masked to ***.
- A
Throwableis shortened toClass: message(raw JSON is not
useful otherwise).
- Strings over 120 bytes are truncated.
This redaction is only the profiler copy (the profile is raw JSON under var/cache/profiler/ — the same policy as TraceableDatabase masking bound values). The actual Monolog output receives the original context exactly as the app chose it.
In production (APP_ENV other than dev) the alias points directly at Monolog with no decorator, so it is zero-cost like the Profiler. Recording happens before the write, so even if the destination (e.g. an unwritable LOG_FILE) fails, the entry remains on the timeline.
Change history (1)
- Created