waffle-commons / log
Log component for Waffle framework.
Requires
- php: ^8.5
- psr/log: ^3.0
- waffle-commons/contracts: 0.1.0-beta2.1
Requires (Dev)
- carthage-software/mago: ^1.29
- cyclonedx/cyclonedx-php-composer: ^6.2
- php-mock/php-mock-phpunit: ^2.15
- phpunit/phpunit: ^12.5
- vimeo/psalm: ^6.16
This package is auto-updated.
Last update: 2026-05-30 19:47:09 UTC
README
Waffle Log Component
Release:
v0.1.0-beta2ย |ยCHANGELOG.mdPSR Compliance: PSR-3 (Psr\Log\AbstractLogger)
A strict, container-native logger that emits one JSON line per record onto a stream. Designed for Docker/Kubernetes deployments where stdout/stderr are the log sinks โ no buffering, no per-process state, safe across FrankenPHP worker requests.
๐ฆ Installation
composer require waffle-commons/log
๐งฑ Surface
| Class | Role |
|---|---|
Waffle\Commons\Log\StreamLogger |
PSR-3 AbstractLogger writing JSON-formatted records to any PHP stream (php://stderr, php://stdout, file paths). |
Waffle\Commons\Log\Channel\LogChannel |
Typed-constant container for canonical log-channel names (APP, CORE, HTTP, SECURITY, AUDIT). |
๐ Usage
StreamLogger exposes its configuration via PHP 8.5 asymmetric visibility (public private(set)) and readonly promoted properties:
use Waffle\Commons\Log\StreamLogger; use Waffle\Commons\Log\Channel\LogChannel; $logger = new StreamLogger( streamPath: 'php://stderr', channel: LogChannel::HTTP, permissions: 0o644, ); $logger->info('Request handled', ['route' => '/users', 'status' => 200]);
The signature, verbatim from src/StreamLogger.php:
public function __construct( private(set) readonly string $streamPath = 'php://stderr', private(set) readonly string $channel = LogChannel::APP, private(set) readonly int $permissions = 0o644, ) { /* opens the stream, chmods if it's a regular file */ }
๐ชต Output format
Every record is a single JSON object emitted with a trailing \n, suitable for Docker's JSON-file driver and for jq/grep pipelines:
{"time":"2026-05-16T10:42:01.123+00:00","level":"info","channel":"http","message":"Request handled","context":{"route":"/users","status":200}}
RFC 5424 / Monolog level constants (DEBUG=100 โฆ EMERGENCY=600) are interpolated from Psr\Log\LogLevel โ no custom level vocabulary.
๐งท Channels
LogChannel is a typed-constant container (intentionally not a backed enum, so it can be used as a property default):
final class LogChannel { public const string APP = 'app'; public const string CORE = 'core'; public const string HTTP = 'http'; public const string SECURITY = 'sec'; public const string AUDIT = 'audit'; }
๐ก๏ธ Worker-mode safety
- Stream is opened in the constructor and closed in
__destruct(), preventing file-descriptor leaks across long-running workers. - No per-call buffering; each
log()call writes immediately. - No static state. Multiple
StreamLoggerinstances can co-exist (one per channel, for example).
๐งญ Architectural boundary (mago guard)
An active dependency perimeter is enforced on every CI run by vendor/bin/mago guard (bundled into composer mago; zero baselines). The rules live in mago.toml under [guard.perimeter] โ a forbidden use statement fails the build, not a reviewer.
Production code under Waffle\Commons\Log may depend only on:
Waffle\Commons\Log\**โ itselfWaffle\Commons\Contracts\**โ the shared contracts package, the only Waffle dependency permittedPsr\**โ PSR interfaces (PSR-3)@global+Psl\**โ PHP core and the PHP Standard Library
Test code under WaffleTests\Commons\Log is unrestricted (@all). Structural rules are guarded too: interfaces must be named *Interface, Exception\** classes must end in *Exception, and any Enum\** namespace may hold only enum declarations.
Contract-first, component-agnostic by construction: components compose through waffle-commons/contracts, never directly through one another.
๐งช Testing
docker exec -w /waffle-commons/log waffle-dev composer tests
๐ License
MIT โ see LICENSE.md.