Package · logger

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:

php
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');
text
[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:

php
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:

php
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
php
// 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:

php
LoggerFactory::contextStorage()->forget('user_id');