coisa / logger
Twelve-Factory PSR-3 logger on top of Monolog
Requires
- php: ^8.4
- alexandre-daubois/monolog-processor-collection: ^1.3
- fast-forward/config: ^1.5
- monolog/monolog: ^3.0
- psr/container: ^1.1 || ^2.0
- psr/log: ^2.0 || ^3.0
- symfony/polyfill-mbstring: ^1.31
- symfony/stopwatch: ^7.2 || ^8.0
Requires (Dev)
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-14 21:05:19 UTC
README
coisa/logger is a PSR-3 Logger implementation that uses the Monolog to create a Twelve-Factor Logger.
Installation
Using composer
$ composer require coisa/logger
Factory configuration
LoggerConfigFactory optionally resolves FastForward\Config\ConfigInterface from
the PSR-11 container. Register that service with a nested logger section:
use FastForward\Config\ArrayConfig; use FastForward\Config\ConfigInterface; $config = new ArrayConfig([ 'logger' => [ 'level' => 'debug', 'filename' => '/var/log/my-application/debug.log', 'timezone' => 'UTC', ], ]); // Bind $config as ConfigInterface::class in your application's container.
All keys are optional. Without a configuration service or section, behavior
remains debug, php://stdout, and the container's DateTimeZone service (or
the process default timezone when that service is absent). Missing and null
settings use the same defaults: debug for level,
LoggerConfig::DEFAULT_FILENAME for filename, and the lazily resolved
container/process fallback for timezone. A non-null timezone name or
DateTimeZone instance takes precedence over the container for enabled logging.
See Optional logging and record configuration
for disabled logging, formatters and processors.
LoggerConfig is a readonly snapshot constructed from ConfigInterface. Its
public level, filename and timezone properties contain validated values;
level uses the backed CoiSA\Logger\Enum\LogLevel enum.
LoggerConfig::DEFAULT_FILENAME owns the php://stdout default shared by the
configuration and logger creation APIs.
Non-null configured levels accept only the eight canonical lowercase PSR-3 names. Invalid types,
unknown levels/timezones, empty paths and paths containing NUL bytes fail during
factory resolution with InvalidArgumentException. The factory does not open
the stream until a record passes the level filter; file/permission failures are
reported when writing. Configuration must be application-owned and trusted.
The configured coding standard uses mb_trim() for filename validation;
symfony/polyfill-mbstring supplies it when native mbstring functions are absent,
without making the extension a platform requirement.
Destinations and their permissions are administered by the application/operator;
the library does not impose a filesystem policy or redact credentials.
The Laminas ConfigProvider, container-interop provider and Laravel adapter
register LoggerConfigFactory for LoggerConfig, alongside the logger factory
and existing aliases. LoggerFactory consumes that registered configuration
service; custom containers must provide it too. LoggerConfigFactory resolves
optional ConfigInterface or supplies empty configuration for defaults.
Direct LoggerFactory::createLogger() calls accept
a legacy string (including existing uppercase forms) or the package's LogLevel
enum; the enum is converted to its string value at the Monolog boundary. These
calls do not resolve configuration. The original first three arguments remain
unchanged; optional trailing processors, formatter and enabled arguments
share the same construction path used by the factory. Applications may
route diagnostic files independently of console verbosity; avoid logging
secrets or complete request/event objects.
Optional logging and record configuration
The provider can supply a disabled logger or configure Monolog's standard
StreamHandler. These options are additive: omitting them keeps
the 2.0 stream/processor defaults and the static creation API unchanged.
'logger' => [ 'enabled' => true, 'filename' => '/var/log/my-application/debug.log', 'level' => 'debug', 'timezone' => 'UTC', 'formatter' => 'json', 'processors' => [], ],
Set enabled: false to return a CoiSA\Logger\Logger backed by Monolog's
NullHandler. It discards records without opening a destination, constructing
processors or consulting the container's timezone fallback. Configured and static
creation use the same createLogger() path with the enabled flag. The concrete
entry, LoggerInterface and logger aliases share one instance in both modes;
constructor injection and withLevel() remain safe and cannot enable a disabled
logger's output.
Enabled logging uses StreamHandler directly. File paths and PHP stream wrappers
are supported; missing/null filenames retain the php://stdout default, and
"0" is a valid relative filename. File creation, appending and write failures
follow Monolog's behavior. Provision appropriate directory access and file
permissions outside this library. No strict mode-bit, symlink, inode or locking
policy is promised.
processors accepts a list of classes or existing objects implementing
Monolog\Processor\ProcessorInterface, including application-owned custom
processors. Configuration rejects classes that are not publicly instantiable
without constructor arguments; optional constructor parameters are accepted.
Already constructed objects may have constructor dependencies and are reused
directly, without cloning or constructing them again. Distinct instances of the
same class are allowed; repeating the same instance is rejected.
Class names are normalized to their declared spelling before duplicate detection.
CoiSA\Logger\Enum\Processor cases are also accepted as a convenience and
normalize to their class-name values; the enum is not an extension allowlist.
An explicit empty list disables all processors. Missing/null selections keep
legacy defaults directly from Processor::cases() in declaration order. For example:
'processors' => [CoiSA\Logger\Enum\Processor::PSR_MESSAGE], // Equivalent: [Monolog\Processor\PsrLogMessageProcessor::class]
Available cases are PSR_MESSAGE, SAPI_NAME, PROCESS_ID, UID, UUID,
PROTOCOL_VERSION, SESSION_ID, REQUEST_SIZE, CLIENT_IP, IS_HTTPS, WEB,
BACKTRACE, MEMORY_USAGE, MEMORY_PEAK_USAGE, LOAD_AVERAGE and
STOPWATCH_TIMER. The factory instantiates each selected case's class value,
without a duplicated match table.
Select metadata-producing processors only when their content is appropriate.
formatter accepts text (default) or json (one JSON record per line,
without automatic exception stack traces). Formatter output still contains
caller-supplied messages/context; never pass credentials or entire private
event/request objects. Non-implementing/duplicate processor entries, non-constructible
classes and invalid option types fail during configuration construction.
The additional readonly properties are enabled, processors
(normalized processor class/instance list, or null for legacy defaults), and formatter
(Formatter enum). Configuration remains registered by all three existing
service-provider adapters; no extra application service registration is needed.
The selection is readonly, not deeply immutable: supplied processor objects keep
their identity and may maintain application-owned state.
Symfony Stopwatch 7.2 and 8.x are supported. To exercise both dependency lines:
composer update 'symfony/stopwatch:^7.2' --with-all-dependencies composer test composer update 'symfony/stopwatch:^8.0' --with-all-dependencies composer test
Credits
License
Please see License File for more information.