The last couple of years I’ve written a lot of code that synchronizes data from and to a Symfony store or application. One important thing I learned is that storing extensive logs can avoid a lot of discussions and help debugging big time.
Logging in Symfony is as easy as this:
use Psr\Log\LoggerInterface;
class MyClass
{
public function __construct(
private LoggerInterface $logger
) {}
public function method()
{
$this->logger->info('Something has happened');
}
}
By default, whenever you add an argument type hinted with Psr\Log\LoggerInterface
the default logger is injected. This one stores everything in a [env].log
.
It goes without saying this file would become pretty useless if you saved all your custom logs there. So what I tend to do is create a new logging channel for every piece of code responsible for a certain set of actions:
monolog:
channels:
- product_import
handlers:
product_import_handler:
type: rotating_file
path: '%kernel.logs_dir%/import/products/log.log'
max_files: 30
channels: product_import
From now on a new Logger service is available under the monolog.logger.product_import
alias. Everything I log using this service is stored in var/logs/import/products/
. What’s even cooler is that I don’t have to clean up my logs since Monolog automatically creates a new file every day, with a max of 30 days.
Now, there are a couple of ways to inject the Logger into your services, you could do it manually:
services:
App\Command\ImportProductsCommand:
arguments:
$logger: '@monolog.logger.product_import'
But then you’d have to do this for every service that needs that specific logger, binding it to a variable will make things a lot easier:
services:
_defaults:
bind:
$myCustomProductImportLogger: '@monolog.logger.product_import'
This way you can inject it anywhere by adding a $productImportLogger
argument:
class MyClass
{
public function __construct(LoggerInterface $myCustomProductImportLogger)
{
// ....
}
}
But there’s even an easier way that I discovered only recently. Since MonologBundle 3.5 you don’t even have to configure anything! Each channel can be injected by simply adding a [channelName]Logger
argument:
class MyClass
{
public function __construct(LoggerInterface $productImportLogger)
{
// ....
}
}
So there you go, no more packed, messy production.log
file you need to dig in.