Skip to main content

Simple Logging Facade for PSR-3 loggers

This blog post might be outdated!
This blog post was published more than one year ago and might be outdated!
· 2 min read
Stephan Hochdörfer
Head of IT Business Operations

Lately I have seen more and more libraries picking up PSR-3 when it comes to logging. What a lot of libaries do wrong is that they depend on a concrete implementation of PSR-3, e.g. Mongolog instead of relying on the PSR-3 interface. From what I have seen this is because loggers get instantiated directly within the class. This is not a bad thing but it couples your code to a concrete implementation of PSR-3 which in turn means that there's no interoperability.

The Java community solved the problem by creating a Simple Logging Facade library (SLF4) which I "ported" to PHP last week. You can find the package bitexpert/slf4psrlog on packagist.org. The package is in fact just a simple Factory combined with a simple delegation callback. Easy but powerful. To use it you have to configure it first with the callback you want to execute. For Monolog the code would look like this:

\Monolog\Registry::addLogger(new \Monolog\Logger('mychannel'));
\bitExpert\Slf4PsrLog\LoggerFactory::registerFactoryCallback(
array(\Monolog\Registry::class, 'getInstance')
);

You can register any kind of callback function, the only defined requirement is that it needs to return an object implementing the \Psr\Log\LoggerInterface. In your classes you simply call the \bitExpert\Slf4PsrLog\LoggerFactory when requesting a logger instance:

$logger = \bitExpert\Slf4PsrLog\LoggerFactory::getLogger('mychannel');

Now you might think this is a stupid idea because ideally we have an Dependency Injection container in place and maybe some magic involved to automatically recognize classes implementing the \Psr\Log\LoggerAwareInterface and injecting an logger without any additional configuration. And it even exists an \Psr\Log\LoggerAwareTrait so that you do not need to implement the setter method yourself. To be honest in general this works quite fine. However I do not like the approach of injecting a logger via a setter method. It means I have to manually create a NullLogger instance in my constructor just to make sure that the logger property is defined when the setter method is not called. Or as an alternative implement a bunch of null checks all over the place when trying to call the logger which also is quite annoying.