psr-3
Logging is one of the most ubiquitous tasks encountered in PHP. We use logs to track error messages, record important events, and debug problems with our code. In any PHP project, the code is likely to be full of calls to a logging library which handles these actions for us.
日志记录是PHP中遇到的最普遍的任务之一。 我们使用日志来跟踪错误消息,记录重要事件以及调试代码问题。 在任何PHP项目中,该代码可能都充满了对日志库的调用,该日志库为我们处理了这些操作。
Unfortunately, having calls to a logging library scattered throughout our code makes the code dependent on the availability of that library, a clear violation of the Dependency Inversion Principle. Even if we use dependency injection to give our objects access to the logging library, the differences between logging libraries means that it can be difficult and time consuming to switch between them, requiring a major refactoring of our entire codebase.
不幸的是,在我们的代码中分散了对日志记录库的调用,使得代码依赖于该库的可用性,这明显违反了“依赖倒置原则”。 即使我们使用依赖注入使对象可以访问日志库,日志库之间的差异也意味着在它们之间进行切换可能很困难且很耗时,这需要对整个代码库进行重大重构。
To promote compatibility between logging libraries, the PHP-FIG group recently released PSR-3, a common interface for logger objects. In this article, I’ll discuss how the logger interface defined by PSR-3 allows us to write reusable code that isn’t dependent on any particular logging implementation.
为了促进日志记录库之间的兼容性,PHP-FIG小组最近发布了PSR-3 ,它是记录器对象的通用接口。 在本文中,我将讨论PSR-3定义的记录器接口如何使我们能够编写不依赖于任何特定记录实现的可重用代码。
Before we look at how PSR-3 can make our code more reusable, it is necessary to understand what PSR-3 is. If you are already familiar with PSR-3, you can skip this section.
在研究PSR-3如何使代码更可重用之前,有必要了解什么是PSR-3。 如果您已经熟悉PSR-3,则可以跳过本节。
The heart of the specification is an interface for logger objects. This interface exposes eight methods for handling messages of different severity, and a generic log() method which can accept an arbitrary severity level.
规范的核心是记录器对象的接口。 该接口公开了用于处理不同严重性的消息的八个方法,以及可以接受任意严重性级别的通用log()方法。
The eight severity levels supported by PSR-3 are based on RFC 5424, and are described below:
PSR-3支持的八个严重性级别基于RFC 5424 ,如下所述:
Emergency – the system is unusable
Emergency –系统无法使用
Alert – immediate action is required
Alert -需要立即采取行动
Critical – critical conditions
Critical –关键条件
Error – errors that do not require immediate attention but should be monitored
Error -不需要立即注意但应进行监视的错误
Warning – unusual or undesirable occurrences that are not errors
Warning –非错误的异常或不良事件
Notice – normal but significant events
Notice –正常但重要的事件
Info – interesting events
Info –有趣的事件
Debug – detailed information for debugging purposes
Debug –用于调试目的的详细信息
Each of the logging methods accepts a message, which must be a string or an object with a __toString() method. An additional argument accepts an array, which may be given to provide contextual information for the log message.
每个日志记录方法都接受一条消息,该消息必须是字符串或带有__toString()方法的对象。 附加参数接受一个数组,可以提供该数组以提供日志消息的上下文信息。
A full explanation of these methods and parameters can be found in the PSR-3 specification.
有关这些方法和参数的完整说明,请参见PSR-3规范 。
Getting the files you need to work with PSR-3 is easy – you can find them in the Psr/Log GitHub repository. You can also use Composer to get the files from Packagist. Below is a sample composer.json file to retrieve the Psr/Log files:
获得使用PSR-3所需的文件很容易–您可以在Psr / Log GitHub存储库中找到它们。 您也可以使用Composer从Packagist获取文件。 以下是用于检索Psr / Log文件的示例composer.json文件:
{ "require": { "psr/log": "dev-master" } }There are many different logging libraries for PHP, each with its own approach to collecting and recording data. While there are some common ideas among them, each library has its own unique set of logging methods. This means that switching between loggers can be challenging, often requiring code changes wherever logging is used. This works against the idea of code reuse and the SOLID principles of object-oriented design. We’re left with a situation that requires us to either declare a dependency on a specific logging library, or avoid logging entirely.
PHP有许多不同的日志记录库,每种都有自己的收集和记录数据的方法。 尽管它们之间有一些共同的想法,但是每个库都有其自己独特的日志记录方法集。 这意味着在记录器之间切换可能具有挑战性,无论在何处使用记录,通常都需要更改代码。 这与代码重用的思想和面向对象设计的SOLID原则背道而驰 。 剩下的情况是,我们要么声明对特定日志记录库的依赖关系,要么完全避免进行日志记录。
To illustrate this problem more clearly, a concrete example is needed. Let’s say we’re creating a simple Mailer object to handle sending emails. We want the Mailer to log a message whenever an email is sent, and we decide to use the excellent Monolog library to handle our logging needs.
为了更清楚地说明这个问题,需要一个具体的例子。 假设我们正在创建一个简单的Mailer对象来处理发送电子邮件。 我们希望Mailer在发送电子邮件时记录一条消息,因此我们决定使用出色的Monolog库来满足我们的记录需求。
<?php namespace Email; class Mailer { private $logger; public function __construct($logger) { $this->logger = $logger; } public function sendEmail($emailAddress) { // code to send an email... // log a message $this->logger->addInfo("Email sent to $emailAddress"); } }We can use this class with the following code:
我们可以通过以下代码使用此类:
<?php // create a Monolog object $logger = new MonologLogger("Mail"); $logger->pushHandler(new MonologHandlerStreamHandler("mail.log")); // create the mailer and send an email $mailer = new EmailMailer($logger); $mailer->sendEmail("email@example.com");Running this code will result in a new entry in the mail.log file, recording that the email was sent.
运行此代码将在mail.log文件中产生一个新条目,记录已发送电子邮件。
At this point, we might be tempted to say that we’ve written a reusable Mailer object. We’re making the logger available to the Mailer with dependency injection so we can swap out different logger configurations without having to touch our Mailer code. It appears that we’ve successfully followed the SOLID principles and avoided creating any hard dependencies.
在这一点上,我们可能会倾向于说我们已经编写了一个可重用的Mailer对象。 我们通过依赖项注入使记录器对Mailer可用,因此我们可以交换不同的记录器配置,而无需触摸我们的Mailer代码。 看来我们已成功遵循SOLID原则,并避免了创建任何硬性依赖关系。
But suppose we want to reuse our Mailer class in a different project which uses Analog to handle logging interactions. Now we run into a problem, because Analog doesn’t have an addInfo() method. To log an info level message with Analog, we call Analog::log($message, Analog::INFO).
但是假设我们想在另一个使用Analog处理日志记录交互的项目中重用Mailer类。 现在我们遇到了一个问题,因为Analog没有addInfo()方法。 要使用Analog记录信息级别的消息,我们调用Analog::log($message, Analog::INFO) 。
We could modify our Mailer class to use Analog’s methods, as seen below.
我们可以修改Mailer类以使用Analog的方法,如下所示。
<?php namespace Email; class Mailer { public function sendEmail($emailAddress) { // code to send an email... // log a message Analog::log("Email sent to $emailAddress", Analog::INFO); } }We can consume the updated Mailer class using the following code:
我们可以使用以下代码使用更新后的Mailer类:
<?php // set up Analog Analog::handler(AnalogHandlerFile::init("mail.log")); // create the mailer and send an email $mailer = new EmailMailer(); $mailer->sendEmail("email@example.com");While this will work, it is far from ideal. We’ve run into Mailer’s dependency on a specific logging implementation, which requires the class to change whenever a new logger is introduced. This makes the class less reusable and forces us to choose between being dependent on the availability of a specific logger or abandoning logging from within our class entirely.
尽管这将起作用,但远非理想。 我们遇到了Mailer对特定日志记录实现的依赖,这要求在引入新的记录器时更改类。 这使得该类的可重用性降低,并迫使我们在依赖于特定记录器的可用性或完全放弃类中的日志之间进行选择。
As Alejandro Gervasio explained in his excellent article on the subject, the Dependency Inversion Principle tells us that we should depend upon abstractions rather than concretions. In the case of logging, our problem so far has been a lack of a decent abstraction on which to depend.
正如亚历杭德罗·格瓦西奥(Alejandro Gervasio)在其关于该主题的出色文章中所解释的那样,依赖倒置原则告诉我们,我们应该依靠抽象而不是凝结。 就日志而言,到目前为止,我们的问题一直是缺乏可依赖的体面抽象。
This is where PSR-3 comes in. PSR-3 is designed to overcome the problem of incompatible logging implementations by providing a universal interface for loggers, aptly named LoggerInterface. By providing an interface which is not bound to any particular implementation, PSR-3 frees us from having to rely on a specific logger – we can instead type-hint against LoggerInterface to acquire a PSR-3 compliant logger. I have updated the Mailer class below to demonstrate this:
这就是PSR-3出现的地方。PSR-3旨在通过为记录器提供一个通用接口(恰当地命名为LoggerInterface来克服不兼容的记录实现问题。 通过提供不受任何特定实现约束的接口,PSR-3使我们不必依赖特定的记录器,而可以通过LoggerInterface键入提示以获取兼容PSR-3的记录器。 我已经更新了下面的Mailer类来证明这一点:
<?php namespace Email; class Mailer { private $logger; public function __construct(PsrLogLoggerInterface $logger) { $this->logger = $logger; } public function sendEmail($emailAddress) { // code to send an email... // log a message $this->logger->info("Email sent to $emailAddress"); } }The constructor has been modified to accept an implementor of LoggerInterface, and the sendEmail() method now calls the info() method specified in PSR-3.
构造函数已被修改为接受LoggerInterface的实现者,并且sendEmail()方法现在调用PSR-3中指定的info()方法。
Monolog is already PSR-3 compliant, and Analog supplies a wrapper object that implements LoggerInterface, so we can now use both loggers without modifying our Mailer class.
Monolog已经符合PSR-3的标准,Analog提供了一个实现LoggerInterface的包装器对象,因此我们现在可以在不修改Mailer类的情况下使用这两个记录器。
Here’s how you would call the class with Monolog:
使用Monolog调用类的方法如下:
<?php // create a Monolog object $logger = new MonologLogger("Mail"); $logger->pushHandler(new MonologHandlerStreamHandler("mail.log")); // create the mailer and send an email $mailer = new EmailMailer($logger); $mailer->sendEmail("email@example.com");And with Analog:
与模拟:
<?php // create a PSR-3 compatible Analog wrapper $logger = new AnalogLogger(); $logger->handler(AnalogHandlerFile::init("mail.log")); // create the mailer and send an email $mailer = new EmailMailer($logger); $mailer->sendEmail("email@example.com");Now we’re able to use our Mailer object with either library without having to edit the Mailer class or change the way we consume it.
现在,我们可以在两个库中使用我们的Mailer对象,而无需编辑Mailer类或更改我们使用它的方式。
So far we’ve successfully decoupled the Mailer object from any particular logging implementation by asking for an implementor of LoggerInterface. But what about loggers that have yet to add support for PSR-3? For example, the popular KLogger library has not been updated for some time and is currently not compatible with PSR-3.
到目前为止,我们已经通过请求LoggerInterface实现者成功地将Mailer对象与任何特定的日志记录实现LoggerInterface 。 但是,尚未添加对PSR-3的支持的记录器又如何呢? 例如,流行的KLogger库已有一段时间没有更新,并且当前与PSR-3不兼容。
Luckily, it’s simple for us to map the methods exposed by KLogger to those defined by LoggerInterface by harnessing the power of the Adapter Pattern. The supporting files in the Psr/Log repository make it easy to create adapter classes by providing us with an AbstractLogger class that we can extend. The abstract class simply forwards the eight level-specific logging methods defined in LoggerInterface to a generic log() method. By extending the AbstractLogger class and defining our own log() method, we can easily create PSR-3 compliant adapters for loggers that do not natively support PSR-3. I’ll demonstrate this below by creating a simple adapter for KLogger:
幸运的是,它的简单让我们通过映射暴露KLogger那些通过定义的方法LoggerInterface通过利用的功率适配器模式 。 通过为我们提供可以扩展的AbstractLogger类, Psr / Log存储库中的支持文件使创建适配器类变得容易。 抽象类仅将LoggerInterface定义的八个特定于级别的日志记录方法LoggerInterface到通用log()方法。 通过扩展AbstractLogger类并定义我们自己的log()方法,我们可以轻松地为本身不支持PSR-3的记录器创建符合PSR-3的适配器。 我将在下面通过为KLogger创建一个简单的适配器来演示这一点:
<?php namespace Logger; class KloggerAdapter extends PsrLogAbstractLogger implements PsrLogLoggerInterface { private $logger; public function __construct($logger) { $this->logger = $logger; } public function log($level, $message, array $context = array()) { // PSR-3 states that $message should be a string $message = (string)$message; // map $level to the relevant KLogger method switch ($level) { case PsrLogLogLevel::EMERGENCY: $this->logger->logEmerg($message, $context); break; case PsrLogLogLevel::ALERT: $this->logger->logAlert($message, $context); break; case PsrLogLogLevel::CRITICAL: $this->logger->logCrit($message, $context); break; case PsrLogLogLevel::ERROR: $this->logger->logError($message, $context); break; case PsrLogLogLevel::WARNING: $this->logger->logWarn($message, $context); break; case PsrLogLogLevel::NOTICE: $this->logger->logNotice($message, $context); break; case PsrLogLogLevel::INFO: $this->logger->logInfo($message, $context); break; case PsrLogLogLevel::DEBUG: // KLogger logDebug() method does not accept a second // argument $this->logger->logDebug($message); break; default: // PSR-3 states that we must throw a // PsrLogInvalidArgumentException if we don't // recognize the level throw new PsrLogInvalidArgumentException( "Unknown severity level" ); } } }The log() method simply maps the LoggerInterface methods to the respective KLogger methods, and KLogger handles the actual logging activity. By wrapping the KLogger class in this way, we’re able to use it without breaking the LoggerInterface contract.
log()方法仅将LoggerInterface方法映射到相应的KLogger方法,而KLogger处理实际的日志记录活动。 通过以这种方式包装KLogger类,我们可以使用它而不会破坏LoggerInterface合同。
We can now use the KLogger adapter to work with the Mailer class:
现在,我们可以使用KLogger适配器来处理Mailer类:
<?php // create a new KLogger object which will log to the "logs" directory $klogger = KLogger::instance("logs"); // inject KLoggger to a PSR-3 compatible KloggerAdapter $logger = new LoggerKloggerAdapter($klogger); // send an email $mailer = new EmailMailer($logger); $mailer->sendEmail("email@example.com");Using the adapter class, we’re able to use KLogger without modifying the Mailer class and still adhering to LoggerInterface.
通过使用适配器类,我们可以使用KLogger而不修改Mailer类,并且仍然坚持LoggerInterface 。
KLogger doesn’t accept a second argument for debug level messages, so it’s not technically PSR-3 compliant even with an adapter. Extending KLogger to make it fully compatible with PSR-3 would be a trivial task, but one which falls outside the scope of this article. However, it is safe to say that using our adapter class gets us very close to full PSR-3 compliance and allows us to use LoggerInterface with the KLogger class.
KLogger不接受调试级别消息的第二个参数,因此,即使使用适配器,它在技术上也不符合PSR-3。 扩展KLogger以使其与PSR-3完全兼容将是一件微不足道的任务,但这超出了本文的范围。 但是,可以肯定地说,使用我们的适配器类使我们非常接近完全符合PSR-3的要求,并允许我们将LoggerInterface与KLogger类一起使用。
In this article we’ve seen how using PSR-3 can help us to write logger-agnostic code which does not depend on a particular logging implementation. Many major PHP projects have already added support for PSR-3, including Monolog, Symfony, and Mustache.php, and other big names such as Drupal are discussing how to best integrate it. As PSR-3 makes logging less of a barrier to code reuse, we should see more libraries and frameworks making proper use of logging to provide useful information to developers.
在本文中,我们了解了使用PSR-3如何帮助我们编写与记录器无关的代码,而这些代码不依赖于特定的记录实现。 许多主要PHP项目已经增加了对PSR-3的支持,包括Monolog, Symfony和Mustache.php ,而其他大公司(如Drupal)正在讨论如何最好地集成它。 由于PSR-3减少了日志记录对代码重用的障碍,因此我们应该看到更多的库和框架可以适当地使用日志记录来为开发人员提供有用的信息。
Will PSR-3 affect how you use logging in your applications? Let us know in the comments section below.
PSR-3是否会影响您在应用程序中使用日志记录的方式? 在下面的评论部分让我们知道。
Image via Fotolia
图片来自Fotolia
翻译自: https://www.sitepoint.com/logging-with-psr-3-to-improve-reusability/
psr-3
相关资源:jdk-8u281-windows-x64.exe