Logger
Unlike Csrf/RateLimiter/CORS/Jsonify,
Logger has no __invoke() — it's filed under
Middlewares because that's where Clarity ships it, but it's a plain PSR-3
logger (extends Psr\Log\AbstractLogger) you construct directly and call wherever
logging is needed, never registered via ->middleware().
Construction
new Logger(
string $channel,
string $path,
bool $json = false,
?DateTimeZone $timezone = null,
int $maxBytesBeforeRotation = 10_485_760,
int $maxRotatedFiles = 5,
)
This site's own app/Middlewares/Logger.php is the app-level error channel, handed
straight to Mediator::configure():
final class Logger extends \Monad\Clarity\Middlewares\Logger
{
public function __construct()
{
parent::__construct(channel: 'app', path: PATH['error_log'] . 'app.log');
}
}
Mediator::configure(debug: false, logger: new Logger());
DeploymentTopology.md names three channels: error/app.log,
error/db.log, event/timeline.log — construct one Logger
instance per channel wherever that logging happens, rather than overloading one instance with all
three.
Every PSR-3 method, for free
Inherited from AbstractLogger: emergency(), alert(), critical(), error(), warning(), notice(), info(), debug(), and the general-purpose log($level, $message, $context).
$logger->info('User {email} logged in', ['email' => $user->email, 'request_id' => $requestId]);
$logger->error('Payment failed', ['exception' => $exception]);
Real output for the two calls above:
[2026-08-08T02:22:37+00:00] app.INFO: User marshal@example.com logged in request_id=abc123 {"email":"marshal@example.com"}
[2026-08-08T02:22:37+00:00] app.ERROR: Payment failed exception=RuntimeException("Card declined" at ...)
{email} is PSR-3 message interpolation — replaced from $context
automatically. request_id, user_id, and exception in
$context are promoted to their own fields rather than nested; every other key passes
through Utils\Redactor before being appended as JSON.
Rotation
Size-based: once the current file exceeds maxBytesBeforeRotation, it shifts
path.1 → path.2 → … up the chain and starts a fresh path,
discarding anything past maxRotatedFiles.
Next steps
- Mediator — the main consumer of a configured
Logger.