Symfony create a custom logger file and understand behind the scene


Aug 9, 2023 Symfony


Sometimes we want to log specific data on specific log files, to do so add the following lines in your monolog configuration (https://symfony.com/doc/current/logging/channels_handlers.html) :

   

monolog:
       channels:
['my_channel']
       handlers:
           myHandlerLogger:
             type:
stream
             path:
"%kernel.logs_dir%/abc_%kernel.environment%.log"
             channels:
[my_channel]
             level:
debug

Here we are creating a custom handler with a custom channel named “my_channel

Then we can use it on our controller or service like below :

private LoggerInterface $logger;

   
public function __construct(
       LoggerInterface $myChannelLogger
   ) {
       
$this->logger = $myChannelLogger;
   }

   
#[Route('/', name: 'app_index')]
   
public function index(): Response
   {
       
$this->logger->info('test');
   }

To take advantage of autowiring (available since version 3.5 of monolog bundle) the injected service must be named <custom_channel_name>Logger => myChannelLogger.

Let us try to understand where is this naming rule inside the vendors

Go to this file : vendor/symfony/monolog-bundle/DependencyInjection/Compiler/LoggerChannelPass.php on line 86

I have commented the code below so we understand what is happening :

foreach ($container->getParameter('monolog.additional_channels') as $chan) {
           
if ($chan === 'app') {
               
continue;
           }
           
//we generate an id for our new logger service
           $loggerId = sprintf(
'monolog.logger.%s', $chan);
           
//we create the new logger service
           
$this->createLogger($chan, $loggerId, $container);
           
//we set the service to public
           $container->getDefinition($loggerId)->setPublic(
true);
       }

Let us see what is happening inside the createLogger method : see comments inside the code

protected function createLogger($channel, $loggerId, ContainerBuilder $container)
   {
       
if (!in_array($channel, $this->channels)) {

        //we instantiate the logger service
           $logger =
new ChildDefinition('monolog.logger_prototype');
           
//we replace the channel name argument of the logger base class with our custom channel name
           $logger->replaceArgument(
0, $channel);
           
//we register our service with the id 'monolog.logger.my_channel'
           $container->setDefinition($loggerId, $logger);
           
$this->channels[] = $channel;
       }

       
// Allows only for Symfony 4.2+
       
if (\method_exists($container, 'registerAliasForArgument')) {
           $parameterName = $channel .
'Logger';
           
//we register an alias for argument named myChannelLogger i.e <custom_channel_name>Logger
           $container->registerAliasForArgument($loggerId, LoggerInterface::class, $parameterName);
       }
   }


symfony
php