How to log your life easier in Symfony?
because everyone deserves a logger-life!

Search for a command to run...
because everyone deserves a logger-life!

No comments yet. Be the first to comment.
Smelly code, not a function or a module, but an overall design. You know what? do not start by WTFing the $!f*# developers who wrote it. There are good enough cases where developers can't be found guilty, and the "criminal" in such cases are: stakeh...

What are we trying to improve? SSH (Secure Shell Connection) is a secure way to login to a Linux server and remotely work with it. However there might be a good margin of further improvements to secure even more this. 1- Substitute password authentic...

Github Copilot.. Now this is the new beast right ? Everybody is scared or at least worried about its capabilities.. Junior developers are thinking about a career shift ? Ok, I personally only see it much like an x86 programmer in the 70's astonished ...

cover image credits to maximorlov.com

Hi coderz, how you doing? I know you are fed-up with blogposts and have tons of ToDos

So let's go for a quick blogpost and as usual I'll try to bring something useful (I hope) with as few words as I can (I hope as well).
So knowing that monolog's logger (however you configure it) is a tagged service at the end, the "only" way to "cleanly" use it is to DI/inject it to the needed service
Injecting services in Symfony can be done with multiple approaches and strategies. But in best cases you'll have at least to touch two things:
The class needing the logger by defining a private property to receive the logger service instance, and an assignement in the constructor to actually receive the service.
The services.yaml if you choose to go with setter injection rather than constructor injection (as the latter can benefit from autowiring)
Now as logging is a "nice and wanted" feature, your urge to log stuff here and there can grow over and over, and you might feel silly polluting your code with one more line of properties and one other in the constructor. Sometimes you'd only write a constructor for the sake eyes of the DI injection of the logger. Having this exact snippet copy/pasted over a dozen of classes might really make you feel unhappy.
If you use the autocomplete feature of PHPStorm, you'd notice a pair of interface/trait having the same prefix.
Psr\Log\LoggerAwareInterface
Psr\Log\LoggerAwareTrait

<?php
// src/Kernel.php
namespace App;
use DateTime;
use Psr\Log\LoggerAwareInterface;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
public function process(ContainerBuilder $container): void
{
$definitions = $container->getDefinitions();
foreach ($definitions as $definition) {
if (!$this->isAware($definition, LoggerAwareInterface::class)) {
continue;
}
$definition->addMethodCall(
'setLogger',
[$container->getDefinition('monolog.logger')]
);
}
}
private function isAware(Definition $definition, string $awarenessClass): bool
{
$serviceClass = $definition->getClass();
if ($serviceClass === null) {
return false;
}
$implementedClasses = @class_implements($serviceClass, false);
if (empty($implementedClasses)) {
return false;
}
if (\array_key_exists($awarenessClass, $implementedClasses)) {
return true;
}
return false;
}
}
Now Just use implement the interface and use the trait in your command, for example, and you are ready to go!
<?php
// src/Command/MyCommand.php
declare(strict_types=1);
namespace App\Command;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(
name: 'app:my-command',
description: 'test!',
)]
class MyCommand extends Command implements LoggerAwareInterface
{
use LoggerAwareTrait;
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->logger->info('I can log!');
return Command::SUCCESS;
}
}
Enough talk for today, hope it helped and see you soon!