Every project acquires a logger and every one of them is shaped slightly differently — log($message, $level) here, error($message) there, a static call in the third. PSR-3 fixes the shape, which is what lets a class depend on logging without depending on your particular logger.
use PsrLogLoggerInterface;
use PsrLogNullLogger;
class ProductImporter
{
private $catalogue;
private $log;
public function __construct(Catalogue $catalogue, LoggerInterface $log = null)
{
$this->catalogue = $catalogue;
$this->log = $log ?: new NullLogger();
}
public function import(array $rows)
{
foreach ($rows as $line => $row) {
if (!$this->catalogue->has($row['sku'])) {
$this->log->warning('sku {sku} is not in the catalogue, row skipped', array(
'sku' => $row['sku'],
'line' => $line,
));
}
}
}
}
Eight levels taken from syslog, and one convention that earns the standard its keep: the message stays a constant string carrying {placeholder} markers, and the values go into the context array. That is what lets anything reading the log group ten thousand lines under one message rather than treating each as unique, and it is the reason not to build the string with concatenation. NullLogger removes every if (null !== $this->log) from the class. The cost is honest and small — composer require psr/log pulls in a package containing nothing but interfaces, in exchange for being able to hand the class Monolog today and something else in two years without opening it.