Core

Logging Service Provider

Logger channels, PSR logger bindings and writable log file path resolution.

Responsibility

Registers framework logging infrastructure: LogFilePathResolver, LogManager, alias log, alias log.app, default Psr\Log\LoggerInterface binding and alias log.benchmark. Channel creation and disabled-channel fallback are owned by LogManager.

Bindings

LoggerInterface resolves to the app logger channel via alias log.app. Alias log resolves to LogManager. Alias log.benchmark resolves directly to the benchmark logger. There are no log.error or log.request aliases; use LogManager for those channels.

use Lemonade\Framework\Core\Logging\LogManager;
use Psr\Log\LoggerInterface;

$logger = $container->get(Psr\Log\LoggerInterface::class);

$appLogger = $container->get('log.app');
$benchmarkLogger = $container->get('log.benchmark');

$logs = $container->get(LogManager::class);
$errorLogger = $logs->error();
$requestLogger = $logs->request();

Logger channels

LogManager exposes four framework channels: app(), error(), request() and benchmark(). Disabled channels return Psr\Log\NullLogger, so callers can log safely without checking channel state.

$logs = $container->get(Lemonade\Framework\Core\Logging\LogManager::class);

$logs->app()->info('Application message');
$logs->error()->error('Error message');
$logs->request()->info('Request message');
$logs->benchmark()->info('Benchmark message');

Log file path convention

Log file config values are resolved through ApplicationContext::resolveLogPath(). Relative filenames are stored under the framework writable log directory, conventionally storage/writable/logs; absolute paths are preserved by the context path resolver.

$resolver = $container->get(Lemonade\Framework\Core\Logging\LogFilePathResolver::class);

$path = $resolver->resolve('app.log', 'app.log');

Configuration

LogManager reads {channel}.log.enabled, {channel}.log.file and {channel}.log.days for each channel. App config may source these values from environment variables, but the framework contract is the config structure, not the env var names.

'app' => [
    'log' => [
        'enabled' => true,
        'file' => 'app.log',
        'days' => 7,
    ],
],

'error' => [
    'log' => [
        'enabled' => true,
        'not_found' => false,
        'file' => 'error.log',
        'days' => 7,
    ],
],

'request' => [
    'log' => [
        'enabled' => false,
        'file' => 'request.log',
        'days' => 7,
        'min_status' => 0,
    ],
],

'benchmark' => [
    'log' => [
        'enabled' => false,
        'file' => 'benchmark.log',
        'days' => 7,
    ],
],

Rotation format

RotatingFileLogger writes JSON lines to dated files. A configured app.log writes records to files like app-YYYY-MM-DD.log; old files matching the channel filename pattern are removed according to {channel}.log.days.

HTTP boundary

LoggingServiceProvider registers loggers and channels only. HTTP request logging is performed by RequestLoggingMiddleware, which uses LogManager::request() and request.log.min_status. Benchmark logging is performed by BenchmarkMiddleware, which uses LogManager::benchmark() when benchmark.log.enabled is true.

Benchmark channel

Benchmark logging can be resolved through alias log.benchmark or LogManager::benchmark(). The benchmark channel can be disabled independently from the main application logger.

$benchmark = $container->get('log.benchmark');

$benchmark->info('Benchmark finished', [
    'duration_ms' => 12.4,
]);

Provider boundary

LoggingServiceProvider does not define application log messages, route logging policy or benchmark collection. It only supplies PSR-compatible logger services; middleware and application code decide what to log.