Attach request context
Set fields like request_id or user_id once at the start of a request or job, and have
them appear in every log line automatically — no threading them through every call.
How it works
Every channel is built with a ContextInjectingProcessor
attached automatically. On each log record it merges a snapshot of the current
ContextStorage into the record’s
extra, so any field you set shows up until you clear it. No configuration needed.
Set fields at the start of work
Reach the storage through the manager or the factory, then set() your fields:
use Flytachi\Winter\Logger\LoggerFactory;
// At request start (FPM front controller / middleware):
$ctx = LoggerFactory::contextStorage();
$ctx->set('request_id', $_SERVER['HTTP_X_REQUEST_ID'] ?? uniqid('req_'));
$ctx->set('user_id', $userId);
$ctx->set('method', $_SERVER['REQUEST_METHOD']);
// Any log call from here on carries all three fields:
LoggerFactory::getLogger(self::class)->info('processing');[2024-01-01 12:00:00] [INFO ] -http- [4821] (OrderController): processing {"class":"App\\Http\\OrderController","request_id":"req_65a1...","user_id":42,"method":"POST"}Clear fields at the end of work
In a process that handles more than one unit of work, clear context between them or
fields leak into the next request. Under FPM the process dies after each request, so a
register_shutdown_function is the clean hook:
register_shutdown_function(
fn () => LoggerFactory::contextStorage()->clear()
);Long-running processes leak without clear()
FPM tears the process down for you, but a CLI daemon or Swoole worker reuses one
process. Always clear() at the end of each iteration/request there — otherwise the previous
job’s request_id rides along on the next one’s logs.
Daemon loop pattern
For a CLI worker that loops forever, set per-iteration fields and clear at the bottom:
while (true) {
$ctx = LoggerFactory::contextStorage();
$ctx->set('iteration_id', uniqid('iter_'));
processNextJob();
$ctx->clear(); // drop this iteration's fields; the process continues
}Bound context vs request context
Two different scopes — pick by lifetime:
| You want fields on… | Use | Notes |
|---|---|---|
| every logger for this request/job | contextStorage()->set() |
injected via processor into extra |
| one specific logger only | ->withContext([...]) |
returns a new logger; merged into context |
// Request-wide — every logger sees it:
LoggerFactory::contextStorage()->set('request_id', $id);
// This logger only — does not affect others:
$log = LoggerFactory::getLogger(self::class)->withContext(['order_id' => 5001]);
$log->info('charged');Remove a single field
forget() drops one key without clearing the rest:
LoggerFactory::contextStorage()->forget('user_id');Related
- Swoole coroutines — isolate context per request under Swoole
- Context isolation — why the strategy is per-runtime
- API reference → ContextStorage — the full interface