Package · logger

Quickstart

From install to a live, request-scoped log line in five minutes: you’ll build a manager, register it once, set a request id, and watch it appear in every line automatically.

Time ~5 minLevel BeginnerPrereqs PHP 8.3+, Monolog

What you’ll build

A tiny script that logs from a service class and carries a request_id on every line — the same loop you’ll use in a real app: configure → register → set context → log.

Before you start

Install the package with Monolog so you get real output:

bash
composer require flytachi/winter-logger monolog/monolog

Step 1 — Build a LoggerManager

The manager takes a ready-made config. In a real app the framework resolves the output target and context storage for you; here you pass them directly. Use ProcessContext for FPM/CLI and write to stdout so you can see it in the terminal.

log.php
<?php

require 'vendor/autoload.php';

use Monolog\Level;
use Flytachi\Winter\Logger\LoggerManager;
use Flytachi\Winter\Logger\Context\ProcessContext;

$manager = new LoggerManager(
  contextStorage: new ProcessContext(),
  channels: [
      'http' => [
          'level'        => Level::Debug,
          'format'       => 'line',     // 'line' | 'json'
          'output'       => 'stdout',   // 'stdout' | 'stderr' | 'syslog' | 'file' | 'null'
          'file_path'    => null,
          'file_max'     => 30,
          'syslog_ident' => 'winter',
      ],
  ],
);

Every config key is documented in the Channel config reference.

Step 2 — Register it with LoggerFactory

Do this once at bootstrap. After it, any code can log without touching the manager. Set the default channel so getLogger() and Log::*() know where to write.

php
use Flytachi\Winter\Logger\LoggerFactory;

LoggerFactory::setManager($manager);
LoggerFactory::setDefaultChannel('http');

Step 3 — Set request context once

Set request_id at the start of the request. The ContextInjectingProcessor — added to every channel automatically — merges it into every subsequent line.

php
LoggerFactory::contextStorage()->set('request_id', 'req-abc-123');

Step 4 — Log from anywhere

Use a per-class logger. The short class name shows up as (ClassName), and the full FQCN is stored in context for aggregator queries.

php
// Per-class logger — Java-style:
LoggerFactory::getLogger('App\\Service\\UserService')->info('user created', ['id' => 42]);

// One-line shortcut to the default channel:
use Flytachi\Winter\Logger\Log;

Log::warning('rate limit hit', ['ip' => '10.0.0.1']);

Run it:

bash
php log.php

You’ll see the request_id on both lines, even though you set it only once:

text
[2024-01-01 12:00:00] [INFO ] -http- [4821] (UserService): user created {"id":42,"class":"App\\Service\\UserService","request_id":"req-abc-123"}
[2024-01-01 12:00:00] [WARN ] -http- [4821]: rate limit hit {"ip":"10.0.0.1","request_id":"req-abc-123"}

Step 5 — Bind context to a logger

To attach fields to one logger only (not the whole request), use withContext(). It returns a new logger; the original is untouched.

php
$log = LoggerFactory::getLogger('App\\Service\\PaymentService')
  ->withContext(['order_id' => 5001]);

$log->info('payment started');  // carries order_id
$log->info('payment done');     // carries order_id

The whole thing

log.php
<?php

require 'vendor/autoload.php';

use Monolog\Level;
use Flytachi\Winter\Logger\LoggerManager;
use Flytachi\Winter\Logger\LoggerFactory;
use Flytachi\Winter\Logger\Context\ProcessContext;

$manager = new LoggerManager(
  contextStorage: new ProcessContext(),
  channels: [
      'http' => [
          'level'        => Level::Debug,
          'format'       => 'line',
          'output'       => 'stdout',
          'file_path'    => null,
          'file_max'     => 30,
          'syslog_ident' => 'winter',
      ],
  ],
);

LoggerFactory::setManager($manager);
LoggerFactory::setDefaultChannel('http');

LoggerFactory::contextStorage()->set('request_id', 'req-abc-123');

LoggerFactory::getLogger('App\\Service\\UserService')->info('user created', ['id' => 42]);

What just happened

  • The manager built one Monolog channel (http) lazily and cached it.
  • ContextInjectingProcessor (automatic) merged request_id into every line’s context.
  • The per-class logger rendered (UserService) and stored the FQCN under class.
  • SpringLineFormatter produced the readable line — datetime, level, channel, PID.

Next steps