Symonony autowiring monolog channels

By following this documentation , I can create many channels that will create services with the following namemonolog.logger.<channel_name>

How can I inject these services into my service using DI injection and auto-wiring?

class FooService
{
    public function __construct(LoggerInterface $loggerInterface) {  }
}

      

YAML

#existing
foo_service:
    class: AppBundle\Services\FooService
    arguments: ["@monolog.logger.barchannel"]
# what I want to do
foo_service:
    autowire: true # how to inject @monolog.logger.barchannel ? 

      

+8


source to share


6 answers


I wrote a (maybe more complex) method. I don't want to tag my auto messaging services to tell Symfony which channel to use. Using Symfony 4 with PHP 7.1.

I created a LoggerFactory with all additional channels defined in monolog.channels.

My factory is bundled, so in Bundle.php add

$container->addCompilerPass(
    new LoggerFactoryPass(), 
    PassConfig::TYPE_BEFORE_OPTIMIZATION, 
    1
); // -1 call before monolog

      

It is important to call this compiler pass before monolog.bundle because monolog after the pass removes the options from the container.

Now LoggerFactoryPass



namespace Bundle\DependencyInjection\Compiler;


use Bundle\Service\LoggerFactory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class LoggerFactoryPass implements CompilerPassInterface
{

    /**
     * You can modify the container here before it is dumped to PHP code.
     * @param ContainerBuilder $container
     * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
     */
    public function process(ContainerBuilder $container): void
    {
        if (!$container->has(LoggerFactory::class) || !$container->hasDefinition('monolog.logger')) {
            return;
        }

        $definition = $container->findDefinition(LoggerFactory::class);
        foreach ($container->getParameter('monolog.additional_channels') as $channel) {
            $loggerId = sprintf('monolog.logger.%s', $channel);
            $definition->addMethodCall('addChannel', [
                $channel,
                new Reference($loggerId)
            ]);
        }
    }
}

      

and LoggerFactory

namespace Bundle\Service;

use Psr\Log\LoggerInterface;

class LoggerFactory
{
    protected $channels = [];

    public function addChannel($name, $loggerObject): void
    {
        $this->channels[$name] = $loggerObject;
    }

    /**
     * @param string $channel
     * @return LoggerInterface
     * @throws \InvalidArgumentException
     */
    public function getLogger(string $channel): LoggerInterface
    {
        if (!array_key_exists($channel, $this->channels)) {
            throw new \InvalidArgumentException('You are trying to reach not defined logger channel');
        }

        return $this->channels[$channel];
    }
}

      

So now you can add LoggerFactory and select your channel

public function acmeAction(LoggerFactory $factory)
{
    $logger = $factory->getLogger('my_channel');
    $logger->log('this is awesome!');
}

      

+8


source


After some searching, I found some kind of workaround using tags and manually entered a few parameters into the auto connect service.

My answer is similar to @ Thomas-Landauer's. The difference is that I don't have to manually create the registration service as the compiler goes from the monologue package for me.



services:
    _defaults:
        autowire: true
        autoconfigure: true
    AppBundle\Services\FooService:
        arguments:
            $loggerInterface: '@logger'
        tags:
            - { name: monolog.logger, channel: barchannel }

      

+7


source


I have not found a way to auto-prepare the registration channel itself. However, I found a way to use autowire

in principle and only enter the log manually. With help, class FooService

it might look like services.yml

(Symfony 3.3):

# services.yml

services:
    _defaults:
        autowire: true
        autoconfigure: true
    AppBundle\Services\FooService:
        arguments:
            $loggerInterface: '@monolog.logger.barchannel'

      

So the "trick" is to explicitly introduce the registration channel, but all other dependencies of that service are injected through the autodevice.

+4


source


You can use the bind parameter :

services:
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        public: true
        bind:
            $loggerMyApi: '@monolog.logger.my_api'

      

Then you can use it in your service constructor:

use Psr\Log\LoggerInterface;
...
public function __construct(LoggerInterface $loggerMyApi)
{
...
}

      

+2


source


Basically, you have two options:

First, the labeling service:

services:
App\Log\FooLogger:
    arguments: ['@logger']
    tags:
        - { name: monolog.logger, channel: foo }

      

Then you can use yours CustomLogger

as a dependency elsewhere

Second, you can rely on Monolog to automatically register loggers for each configurable channel in the configuration:

# config/packages/prod/monolog.yaml
monolog:
    channels: ['foo', 'bar']

      

Then the following services will be available to you:, monolog.logger.foo

'monolog.logger.bar'

Then you can retrieve them from the service container or connect them manually, for example:

services:
App\Lib\MyService:
    $fooLogger: ['@monolog.logger.foo']

      

You can read more here and here .

0


source


I recently implemented single point access to all registered MonologBundle loggers. And also I tried to find the best solution - and made the automatically generated logger decorators. Each class decorates one object of one registered monologue channel.

Link to kit adrenaline / monologue-car rental-kit

0


source







All Articles