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.

example.php
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.

example.php
$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.

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

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

Configuration

LogManager reads values from LoggingConfig. App-level input now lives in app/Config/Logging.yaml; the framework maps that YAML into typed LoggingConfigDefinition, then resolves the runtime DTO. The framework contract stays typed, not ad hoc string-path access.

example.php
module: logging
config:
  app:
    enabled: true
    path: app.log
    days: 7
  error:
    enabled: true
    not_found: false
    path: error.log
    days: 7
  request:
    enabled: false
    path: request.log
    days: 7
    min_status: 0
  benchmark:
    enabled: false
    path: 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 the channel retention days from LoggingConfig.

HTTP boundary

LoggingServiceProvider registers loggers and channels only. HTTP request logging is performed by RequestLoggingMiddleware, which uses LogManager::request() and the request-channel status threshold from LoggingConfig. Benchmark logging is performed by BenchmarkMiddleware, which uses LogManager::benchmark() when the benchmark channel is enabled in LoggingConfig.

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.

example.php
$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.