Logging
WebFramework offers a flexible logging system that revolves around PSR-3 loggers, channels, and the ChannelManager
. This document explains how to work with loggers, how default channels are wired, and how you can override or introduce additional channels.
Monolog is used as the underlying logging library and included by default.
ChannelManager and LogService
The WebFramework\\Logging\\ChannelManager
resolves loggers by channel name. It uses the dependency injection container to look up services and caches resolved loggers for reuse. When no matching logger can be found the manager falls back to a NullLogger
, so logging calls never break your application.
WebFramework\\Logging\\LogService
is a convenience wrapper that mirrors the PSR-3 logging methods. You pass the channel name as the first argument and the message/level follows the usual PSR-3 signature:
use WebFramework\Logging\LogService;
class InvoiceService
{
public function __construct(private LogService $logService) {}
public function createInvoice(): void
{
$this->logService->info('billing', 'Invoice created', ['customer' => 123]);
}
}
It provides all the usual PSR-3 logging methods in this way with the channel name as the first argument: log
, emergency
, alert
, critical
, error
, warning
, notice
, info
, and debug
.
Example Usage
To use the default channel in your class, you can just inject LoggerInterface
and use its methods, as LoggerInterface is mapped to retrieve the default channel in the container:
use Psr\Log\LoggerInterface;
class ExampleService
{
public function __construct(private LoggerInterface $logger) {}
public function logInfo(): void
{
$this->logger->info('Invoice created', ['customer' => 123]);
}
}
To be able to send messages to other channels, you can inject LogService
and use its methods:
use WebFramework\Logging\LogService;
class ExampleService
{
public function __construct(private LogService $logService) {}
public function logInfo(): void
{
$this->logService->info('billing', 'Invoice created', ['customer' => 123]);
}
}
Default Channels
Out of the box, WebFramework provides two channels, for which it provides default definition placeholders in definitions/definitions.php
:
channels.default
channels.exception
Both are bound to the NullLogger
.
To define the Logger for these (and other) channels, you can override the definition in your definitions file (e.g. definitions/app_definitions.php
).
Adding or Replacing Channel Definitions
Channel definitions live in the dependency injection container. By convention they use the channels.*
naming scheme. Each definition must return a Psr\Log\LoggerInterface
. For example:
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
return [
'channels.default' => function (): Logger {
$logger = new Logger('default');
$logger->pushHandler(new StreamHandler('/var/log/app.log', Level::Info));
return $logger;
},
'channels.billing' => function (): Logger {
$logger = new Logger('billing');
$logger->pushHandler(new StreamHandler('/var/log/billing.log', Level::Debug));
return $logger;
},
];
Once such definitions exist, the ChannelManager
can resolve the channel by asking the container for the corresponding service.
Overriding Channels Via Configuration
The effective channels are determined by the logging.channels
section in your configuration (see config/base_config.php
). Each entry maps a channel name to a container id or to an inline configuration array:
return [
'logging' => [
'channels' => [
'default' => 'channels.default',
'exception' => [
'type' => 'file',
'path' => '/tmp/app-exceptions.log',
'level' => \Monolog\Level::Error,
],
],
],
];
- If the value is a string, the manager asks the container for that service id.
- If the value is an array with
type => 'file'
, the manager creates aMonolog\Logger
on the fly that writes topath
with an optionallevel
(defaults toLevel::Debug
).
This mechanism lets you temporarily override a definition—handy for local debugging or when you want a different logger in production—without touching the DI definitions themselves. Changing the mapping to reference another definition immediately redirects the channel to that logger.
Fallback Behaviour
When neither the configuration nor the container can supply a logger for a channel, the manager returns a NullLogger
. This guarantees that logging is safe even while you are still wiring channels.
With these tools you can keep the framework supplied LoggerInterface
for generic logging, add specialised channels for specific domains, and override them per environment whenever needed.