Package · logger

Mask sensitive data

Secrets slip into logs easily — a request body with a password, a header with an authorization token. SensitiveMaskingProcessor redacts them before they reach any handler, so they never hit disk or your aggregator.

Add the processor to a channel

The processor is optional — add it after building the manager. Push it onto the channel’s underlying Monolog instance:

php
use Flytachi\Winter\Logger\Processor\SensitiveMaskingProcessor;

$manager->channel('http')->monolog()->pushProcessor(new SensitiveMaskingProcessor());

Now any matching key is replaced with *** in both context and extra:

php
LoggerFactory::getLogger(self::class)->info('login attempt', [
  'username' => 'alice',
  'password' => 'hunter2',      // ← masked
  'metadata' => [
      'token' => 'secret-jwt',  // ← nested, also masked
  ],
]);
text
[2024-01-01 12:00:00] [INFO ] -http- [4821] (AuthService): login attempt {"username":"alice","password":"***","metadata":{"token":"***"}}

What it matches

  • Case-insensitive on keys — Password, PASSWORD, and password all match.
  • Recursive — nested arrays are traversed to any depth.
  • Applies to both the per-call context and the injected extra.

The default masked keys (all replaced with ***):

text
password  passwd  secret  token  access_token  refresh_token
api_key  apikey  authorization  auth  cookie  set-cookie
credit_card  card_number  cvv  ssn  pin

The authoritative list lives in Log format reference.

Add your own keys

Pass extra keys to the constructor — they’re merged with the defaults:

php
$processor = new SensitiveMaskingProcessor(['patient_id', 'insurance_number']);

$manager->channel('http')->monolog()->pushProcessor($processor);

Apply it to every channel

There’s no global switch — add the processor to each channel you want masked. A small loop at bootstrap covers them all:

php
foreach (['http', 'cli', 'sys'] as $name) {
  if ($manager->hasChannel($name)) {
      $manager->channel($name)->monolog()->pushProcessor(new SensitiveMaskingProcessor());
  }
}

Values are matched by key, not by content

The processor redacts based on the field name, not the value. A secret logged under an unlisted key (e.g. data) or interpolated into the message string won’t be caught — keep secrets in well-named context keys, and never put them in the message text.