Author: Stefan Kientzler
Updated on: 2020-08-08
Posted on: 2020-08-08
Package: XLogger PHP PSR Logger
Introduction
For the purpose of logging, appropriate code for logging is usually included in every PHP project, in most cases using an appropriate library that takes over this task.
Depending on the actual purpose of the respective logging and especially on the phase in which the current project is located, it makes sense to define the output target and format as well as the scope of the data to be logged differently.
For example, what is helpful for troubleshooting during the development phase can be superfluous or even undesirable in the delivered product (because this could result in the publication of internal technical information).
Why Use PSR-3?
The use of PSR-3 ensures the highest possible independence of the actual source code from the logging library ultimately used, since the interface is clearly defined. One advantage is that you can use a different library at any time without having to make any changes in your own source code. Just as important is the possibility that any external modules or components that implement PSR-3-compliant logging can be easily integrated into your own project and their logging is thus automatically integrated into the system's own logging. Conversely, a self-created component based on PSR-3 logging can be integrated into third-party projects much faster.
PSR-3 and the dependency inversion principle
However, using a PSR-3 compatible logging library alone does not guarantee the independence of your own source code. As a further important step, no instance of a logger should be created within its own classes, but each class should implement a property holding an instance of LoggerInterface
and a method to set this property. The Logger should be passed from outside, so there is the posibility to choose any PSR-3 compliant logger and configure it for the current environment or situation. The easiest way to do this is to either implement your own classes using the LoggerAwareInterface
, which is also included in the PSR-3 specification, or, if this is not possible (if the own class already extends any other class or implements any other interface), simply integrate the LoggerAwareTrait
by an use statement.
Another common pattern is to initialize the internal logger property in the constructor of your own class with an instance of the NullLogger
class also contained in the PSR-3 specification. This class implements the Logger interface, but actually does nothing at all. The purpose of this class is that the logger can be called inside the class without need to validate the internal logger each time.
If the loger property is not initialized with a NullLogger, the logger should be included as an argument in the class constructor to ensure that a valid logger is available.
The PSR-3 LoggerInterface
In order to log PSR3 compatible, it is necessary to examine the structure and methods of the logger interface more closely.
The interface provides 8 methods for creating log entries with the appropriate weighting. In addition, a general method is defined that receives the weighting of the entry to be created as the first parameter.
PSR3 defines the following levels based on RFC 5424 (defined in class LogLevel):
- Emergency – the system is unusable
- Alert – immediate action is required
- Critical – critical conditions
- Error – errors that do not require immediate attention but should be monitored
- Warning – unusual or undesirable occurrences that are not errors
- Notice – normal but significant events
- Info – interesting events
- Debug – detailed information for debugging purposes
All methods expect the message to be logged as the first argument. This must either be a string or an object that implements the __toString()
method. In addition to plain text, the message can also contain placeholders in curly braces ({key}
), which are replaced by the corresponding values from the context data, which can be passed as a second argument.
The context data in the second (optional) argument are passed as an associative array. Any additional information that is not necessarily available as a string can be transferred via this array. The presentation of this context data is in the responsibility of the implementation of the logger, whereby the context data should be treated by the logger as tolerantly as possible. The content of the context data will under no circumstances lead to any exception, error or warning.
Exceptions are a special case when transferring the context data. An exception can also be passed directly in the message argument (this is permitted since all exceptions implement the __toString()
method), but it is better to provide this in the context array in the key 'exception'
. The implementation of the logger have thus the option to include additional, more extensive information (such as the stack trace) in the log, provided the respective output medium allows it.
Example how to implement PSR-3 logging
Below you see an example, how to implement an own class using PSR-3 logging. It shows the use of the context argument for some data and how to set an exception.
use Psr\Log\NullLogger;
/**
* Testclass to demonstrate the integration of any PSR-3 logger into
* own classes. Either implement LoggerAwareInterface or integrate
* LoggerAwareTrait by use statement (like i do here).
*/
class TestClass
{
use LoggerAwareTrait;
/**
* it makes sense to initialize the $logger property with an
* instance of the PSR-3 NullLogger() so nowhere in the code have
* to be tested, if any logger is set.
*/
public function __construct()
{
$this->logger = new NullLogger();
}
public function doSomething()
{
$this->logger->info(
'Start {class}::doSomething()',
['class' => get_class($this)]
);
for ($i = 1; $i < 10; $i++) {
// run a loop
$this->logger->debug('Run loop ({loop})', ['loop' => $i]);
}
$this->logger->info(
'{class}::doSomething() finished',
['class' => get_class($this)]
);
}
public function causeException()
{
try {
$this->throwException();
} catch (Exception $e) {
$this->logger->error('Catch Exception', ['exception' => $e]);
}
}
protected function throwException()
{
throw new Exception('this is an Exception');
}
}
// just work without logging...
$oTest->doSomething();
// work with our own PSR-3 logger $myLogger = new MyPSR3Logger(LogLevel::WARNING);
$oTest->setLogger($myLogger);
$oTest->doSomething();
// ... or use i.e. a Monolog logger
$logger = new MonologLogger('Test');
$logger->pushHandler(new MonologHandlerStreamHandler('test.log'));
$oTest->setLogger($logger);
$oTest->doSomething();
Conclusion
This post shows how using PSR-3 can help us write code that is not
dependent on a particular logging implementation. Support
for PSR-3 is already included in many well-known PHP projects (Monolog, Zend
framework, Symfony, ...) or there are
corresponding plug-ins (Wordpress, Drupal, ...).
The use of PSR-3 compliant logging considerably simplifies the integration of different modules and libraries and makes the abilities of logging much more flexible.
You need to be a registered user or login to post a comment
1,562,090 PHP developers registered to the PHP Classes site.
Be One of Us!
Login Immediately with your account on:
Comments:
No comments were submitted yet.