How to Add extra Data to Log Messages via a Processor
Monolog allows you to process every record before logging it by adding some extra data. This is the role of a processor, which can be applied for the whole handler stack or only for a specific handler or channel.
A processor is a callable receiving the record as its first argument.
Processors are configured using the monolog.processor
DIC tag. See the
reference about it.
Adding a Session/Request Token
Sometimes it is hard to tell which entries in the log belong to which session and/or request. The following example will add a unique token for each request using a processor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// src/Logger/SessionRequestProcessor.php
namespace App\Logger;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
class SessionRequestProcessor
{
private $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
// method is called for each log record; optimize it to not hurt performance
public function __invoke(array $record)
{
try {
$session = $this->requestStack->getSession();
} catch (SessionNotFoundException $e) {
return $record;
}
if (!$session->isStarted()) {
return $record;
}
$sessionId = substr($session->getId(), 0, 8) ?: '????????';
$record['extra']['token'] = $sessionId.'-'.substr(uniqid('', true), -8);
return $record;
}
}
Next, register your class as a service, as well as a formatter that uses the extra information:
1 2 3 4 5 6 7 8 9 10
# config/services.yaml
services:
monolog.formatter.session_request:
class: Monolog\Formatter\LineFormatter
arguments:
- "[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%% %%context%% %%extra%%\n"
App\Logger\SessionRequestProcessor:
tags:
- { name: monolog.processor }
Finally, set the formatter to be used on whatever handler you want:
1 2 3 4 5 6 7 8
# config/packages/prod/monolog.yaml
monolog:
handlers:
main:
type: stream
path: '%kernel.logs_dir%/%kernel.environment%.log'
level: debug
formatter: monolog.formatter.session_request
If you use several handlers, you can also register a processor at the handler level or at the channel level instead of registering it globally (see the following sections).
When registering a new processor, instead of adding the tag manually in your
configuration files, you can use the #[AsMonologProcessor]
attribute to
apply it on the processor class:
1 2 3 4 5 6 7 8 9 10
// src/Logger/SessionRequestProcessor.php
namespace App\Logger;
use Monolog\Attribute\AsMonologProcessor;
#[AsMonologProcessor]
class SessionRequestProcessor
{
// ...
}
The #[AsMonologProcessor]
attribute takes these optional arguments:
channel
: the logging channel the processor should be pushed to;handler
: the handler the processor should be pushed to;method
: the method that processes the records (useful when applying the attribute to the entire class instead of a single method).
3.8
The #[AsMonologProcessor]
attribute was introduced in MonologBundle 3.8.
Symfony's MonologBridge provides processors that can be registered inside your application.
- DebugProcessor
- Adds additional information useful for debugging like a timestamp or an error message to the record.
- TokenProcessor
- Adds information from the current user's token to the record namely username, roles and whether the user is authenticated.
- SwitchUserTokenProcessor
-
Adds information about the user who is impersonating the logged in user, namely username, roles and whether the user is authenticated.
5.2
The
SwitchUserTokenProcessor
was introduced in Symfony 5.2. - WebProcessor
- Overrides data from the request using the data inside Symfony's request object.
- RouteProcessor
- Adds information about current route (controller, action, route parameters).
- ConsoleCommandProcessor
- Adds information about the current console command.
See also
Check out the built-in Monolog processors to learn more about how to create these processors.
Registering Processors per Handler
You can register a processor per handler using the handler
option of
the monolog.processor
tag:
1 2 3 4 5
# config/services.yaml
services:
App\Logger\SessionRequestProcessor:
tags:
- { name: monolog.processor, handler: main }
Registering Processors per Channel
By default, processors are applied to all channels. Add the channel
option
to the monolog.processor
tag to only apply a processor for the given channel:
1 2 3 4 5
# config/services.yaml
services:
App\Logger\SessionRequestProcessor:
tags:
- { name: monolog.processor, channel: 'app' }