Documentation menu
On this page
Components · v1.x
Logger
PSR-3 compatible logger (same methods and signatures), without dependency. Messages are written in one file per channel, with a minimum level, rotation by size or period, and optional zip / gz archives of the rotated files.
Usage
LoggerInterface writes in the default channel. LoggerManagerInterface (or LoggerManager) gives access to every channel.
<?php
declare(strict_types=1);
namespace App\Controller;
use NeoPHP\Component\Controller\Contract\AbstractController;
use NeoPHP\Component\Http\Response\Response;
use NeoPHP\Component\Logger\Contract\LoggerInterface;
use NeoPHP\Component\Logger\Contract\LoggerManagerInterface;
use RuntimeException;
class PaymentController extends AbstractController
{
public function __construct(
protected LoggerInterface $logger,
protected LoggerManagerInterface $loggers,
) {
}
public function pay(): Response
{
$this->logger->info('User {user} logged in', ['user' => 'neo']);
$this->logger->error('Payment failed', ['exception' => new RuntimeException('Card declined')]);
$this->loggers->channel('framework')->warning('Cache cleared');
return $this->render('payment/done');
}
}
Methods: emergency(), alert(), critical(), error(), warning(), notice(), info(), debug(), and log($level, $message, $context). Each one takes a string|Stringable message and a context array.
Uncaught errors (HTTP 500) are written in the framework channel when it exists.
Levels
NeoPHP\Component\Logger\LogLevel holds the level constants, from the lowest to the highest severity:
| Constant | Value | Severity |
|---|---|---|
LogLevel::DEBUG |
debug |
100 |
LogLevel::INFO |
info |
200 |
LogLevel::NOTICE |
notice |
250 |
LogLevel::WARNING |
warning |
300 |
LogLevel::ERROR |
error |
400 |
LogLevel::CRITICAL |
critical |
500 |
LogLevel::ALERT |
alert |
550 |
LogLevel::EMERGENCY |
emergency |
600 |
LogLevel::normalize(mixed $level): string lowercases a level and throws a LoggerException for an unknown one; LogLevel::severity(string $level): int returns its severity. A channel writes only the messages whose level is at least its minimum level.
Message format
{key} placeholders in the message are replaced by the context values (scalars, null, booleans, Stringable and dates). The whole context is written as JSON; exceptions are converted into their class, message, code, file and line.
[2026-09-24 14:05:12] app.INFO User neo logged in {"user":"neo"}
The line is built from settings.format_message:
| Placeholder | Value |
|---|---|
%datetime% |
date, formatted with settings.date_format |
%channel% |
channel name |
%type% / %level% |
level, uppercase |
%message% |
interpolated message |
%context% |
context as JSON (empty without context) |
Line breaks are replaced by spaces: one message is always one line.
Configuration
config/framework/logger.yaml
channels:
app:
enabled: true
extension: log
framework:
enabled: true
extension: log
minimum_level: warning
rotation:
enabled: true
max_files: 30
when:
filesize: 10M
every: day
archive:
enabled: true
extension: zip
settings:
path: '%kernel.root_path%/var/log'
format_message: '[%datetime%] %channel%.%type% %message% %context%'
date_format: 'Y-m-d H:i:s'
timezone: Europe/Paris
minimum_level: debug
default_channel: app
| Option | Description |
|---|---|
channels.<name>.enabled |
writes the channel into <path>/<name>.<extension> (default true) |
channels.<name>.extension |
file extension (log, txt...), default log |
channels.<name>.minimum_level |
overrides settings.minimum_level for the channel |
channels.<name>.path |
overrides settings.path for the channel |
rotation.enabled |
enables the rotation |
rotation.when.filesize |
rotates when the file exceeds a size (500K, 10M, 1G, bytes, ~ = never) |
rotation.when.every |
rotates every minute, hour, day, week, month, year (~ = never) |
rotation.max_files |
keeps only the N most recent rotated files |
archive.enabled / archive.extension |
compresses rotated files as zip (PHP zip extension) or gz (PHP zlib extension) |
settings.path |
directory of the log files (default var/log) |
settings.format_message |
line format (default [%datetime%] %channel%.%level% %message% %context%) |
settings.date_format |
PHP date format of %datetime% (default Y-m-d H:i:s) |
settings.timezone |
timezone of the dates (~ = PHP date.timezone) |
settings.minimum_level |
lowest level written (default debug) |
settings.default_channel |
channel used by LoggerInterface (default: first channel) |
Without channels, a single app channel is created. A disabled channel exists but writes nothing, so channel('name') keeps working.
Rotation and archives
Rotated files are named after the period (app-2026-09-23.log) or the rotation time (app-2026-09-24_14-05-12.log). With archive.enabled, each rotated file is compressed (app-2026-09-23.log.zip) and the original is removed. rotation.max_files removes the oldest rotated files.
API
LoggerManagerInterface
NeoPHP\Component\Logger\Contract\LoggerManagerInterface extends LoggerInterface; its log methods write in the default channel.
| Method | Description |
|---|---|
channel(string $name): LoggerInterface |
a channel; throws a LoggerException for an unknown channel |
hasChannel(string $name): bool |
whether the channel is defined |
getChannels(): array |
name => Channel |
getDefaultChannel(): string |
name of the default channel |
LoggerManager also provides addChannel(Channel $channel): static and LoggerManager::fromConfig(array $config, string $defaultPath): static, which builds the manager from the configuration above.
Container services: LoggerManagerInterface, with the aliases LoggerManager and LoggerInterface.
Channel
NeoPHP\Component\Logger\Channel\Channel is a LoggerInterface writing in one file:
| Method | Description |
|---|---|
getName(): string |
channel name |
isEnabled(): bool |
enabled and with a writer |
getFile(): ?string |
log file |
isHandling(string $level): bool |
whether a message of this level is written |
Custom logger
AbstractLogger implements the eight level methods on top of log():
<?php
declare(strict_types=1);
namespace App\Logger;
use NeoPHP\Component\Logger\Contract\AbstractLogger;
use Stringable;
class MemoryLogger extends AbstractLogger
{
public array $records = [];
public function log(mixed $level, string|Stringable $message, array $context = []): void
{
$this->records[] = [$level, (string) $message, $context];
}
}
Building blocks
| Class | Description |
|---|---|
Formatter\LineFormatter |
__construct(string $format, string $dateFormat), format($channel, $level, $message, $context, $datetime): string, interpolate(string $message, array $context): string |
Writer\FileWriter |
__construct($file, $rotation, $maxSize, $every, $maxFiles, $archiver), write(), rotateIfNeeded(), rotate(), getFile(), FileWriter::parseSize(int|string|null): ?int, PERIODS |
Writer\Archiver |
__construct(string $format = 'zip'), archive(string $file): string, getFormat(), FORMATS |
Exceptions
NeoPHP\Component\Logger\Exception\LoggerException extends FrameworkException. It is thrown for an unknown level, channel, rotation period, archive extension or timezone, an empty channels list, a missing PHP extension, or a file that cannot be written.
Changelog
- v1.1.0 — PSR-3 compatible logger, channels, rotation by size or period, zip / gz archives,
config/framework/logger.yaml.