0

I'd like to be able to differentiate the channel used by Monolog based on if a service is called by a Symfony command or by the application server.

For example:

class A {
    private $logger;

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

class B {
    private $logger;

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

class SomeCommand extends Symfony\Component\Console\Command\Command {
    private $logger;

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

    protected function configure() {
        $this->setName('my-app:command');
    }

    protected function execute(InputInterface $input, OutputInterface $output) {
        // execute something from class A
        // execute something from class B
    }
}

If i run php bin\console my-app:command I'd like Symfony to write everything to the commands channel (file commands-{environment}.log), while if the class A and B are used by a request from a Controller, they write everything to another channel.

At the moment the config I have is:

monolog:
    channels: ['commands']
    handlers:
        main:
            type: rotating_file
            path: '%kernel.logs_dir%/%kernel.environment%.log'
            level: debug
            channels: [!commands]
        commands_only:
            type: rotating_file
            path: '%kernel.logs_dir%/commands-%kernel.environment%.log'
            level: debug
            channels: [commands]

Injecting the logger to the Command service only doesn't give me the result I'd want: that writes only log from the Command class to the file commands-{environment}.log, while the other classes keep logging to the file {envinronment}.log.

Thank you in advance for any help.

Dario Vogogna
  • 618
  • 6
  • 7

1 Answers1

1

You can use tagged Strategy Pattern for that and here.

monolog

Assuming that you created two custom monolog handlers. handler_x and handler_y

config

service:
    # CONSUMER
    App\Service\StrategyService:
        arguments: [!tagged mytag]

    App\Service\ServiceA:
            - @monolog.logger.handler_x
        tags:
            - { name: mytag }

    App\Service\ServiceB:
        arguments:
            - @monolog.logger.handler_y
        tags:
            - { name: mytag }

StrategyService

declare(strict_types=1);

namespace App\Service;

use Traversable;

class StrategyService
{
    private $services;

    public function __construct(Traversable $services)
    {
        $this->services = $services;
    }

    public function process(string $name, string $message): bool
    {
        /** @var ServiceInterface $service */
        foreach ($this->services as $service) {
            if ($service->canProcess($name)) {
                $service->process($message);

                break;
            }
        }
    }
}

ServiceInterface

declare(strict_types=1);

namespace App\Service;

interface ServiceInterface
{
    public function canProcess(string $name): bool;

    public function process(string $message): void;
}

ServiceA

declare(strict_types=1);

namespace App\Service;

use Psr\Log\LoggerInterface;

class ServiceA implements ServiceInterface
{
    private $logger;

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

    public function canProcess(string $name): bool
    {
        return $name === 'handler_x';
    }

    public function process(string $message): void
    {
        // Do something with your service

        $this->logger->error($message);
    }
}

ServiceB

declare(strict_types=1);

namespace App\Service;

use Psr\Log\LoggerInterface;

class ServiceB implements ServiceInterface
{
    private $logger;

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

    public function canProcess(string $name): bool
    {
        return $name === 'handler_y';
    }

    public function process(string $message): void
    {
        // Do something with your service

        $this->logger->error($message);
    }
}

Now all you have to do is, injecting StrategyService to command and call process method.

$this->strategyService->process('handler_x', 'This goes to ServiceA logger.');
$this->strategyService->process('handler_y', 'This goes to ServiceB logger.');
BentCoder
  • 12,257
  • 22
  • 93
  • 165
  • Thank you for your answer. I was hoping there could be an easier way, without changing code. I would have loved to be able to achieve this only with configuration files. – Dario Vogogna Sep 11 '18 at 13:43