Hiding sensitive data from logging #61215
|
How to replace passwords, tokens, etc in log context with We can append custom message processor only to custom monolog logger, but what about |
Replies: 2 comments
|
return $this->channels[$name] ?? with($this->resolve($name, $config), function ($logger) use ($name) {
$loggerWithContext = $this->tap(
$name,
new Logger($logger, $this->app['events'])
)->withContext($this->sharedContext);and foreach ($this->configurationFor($name)['tap'] ?? [] as $tap) {
[$class, $arguments] = $this->parseTap($tap);
$this->app->make($class)->__invoke($logger, ...explode(',', $arguments));
}So nothing there cares which driver produced the logger. Add the key to your existing channel in 'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'tap' => [App\Logging\RedactSensitive::class],
// ...
],and have the tap push a Monolog processor: namespace App\Logging;
use Monolog\LogRecord;
class RedactSensitive
{
public function __invoke($logger): void
{
foreach ($logger->getLogger()->getHandlers() as $handler) {
$handler->pushProcessor(function (LogRecord $record) {
return $record->with(context: $this->redact($record->context));
});
}
}
}Note the Since |
|
// config/logging.php
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
'tap' => [App\Logging\RedactSensitiveData::class],
],
],The tap class receives the namespace App\Logging;
class RedactSensitiveData
{
public function __invoke($logger): void
{
foreach ($logger->getHandlers() as $handler) {
$handler->pushProcessor(new SensitiveDataProcessor);
}
}
}Then the processor itself does the actual redaction, walking the context array and masking anything matching your sensitive keys: namespace App\Logging;
use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;
class SensitiveDataProcessor implements ProcessorInterface
{
protected array $keys = ['password', 'token', 'secret', 'api_key', 'authorization'];
public function __invoke(LogRecord $record): LogRecord
{
$record['context'] = $this->redact($record['context']);
return $record;
}
protected function redact(array $context): array
{
foreach ($context as $key => $value) {
if (is_array($value)) {
$context[$key] = $this->redact($value);
} elseif (in_array(strtolower((string) $key), $this->keys, true)) {
$context[$key] = '***';
}
}
return $context;
}
}Attach the same One thing worth double-checking regardless of this: Laravel's exception handler already redacts common sensitive input keys ( |
tapisn't limited to a custom driver — it works on any channel inconfig/logging.php, including the built-insingleanddailyones, since under the hood they're all just Monolog-backed loggers Laravel assembles from config. You don't need a separate custom channel just to attach a processor.The tap class receives the
Illuminate\Log\Loggerinstance and pushes a Monolog processor onto it: