核心要點
log()
方法,可以接收任意嚴重級別。其設計目的是為了解決日誌實現不兼容的問題。 AbstractLogger
類來創建符合PSR-3的適配器。 在PHP開發中,日誌記錄是最常見的任務之一。我們使用日誌來跟踪錯誤消息、記錄重要事件和調試代碼問題。在任何PHP項目中,代碼中都可能充滿了對日誌庫的調用,這些庫為我們處理這些操作。不幸的是,在代碼中散佈著對日誌庫的調用,這使得代碼依賴於該庫的可用性,這明顯違反了依賴倒置原則。即使我們使用依賴注入讓我們的對象訪問日誌庫,日誌庫之間的差異也意味著在它們之間切換可能很困難且費時,需要對整個代碼庫進行重大重構。為了提高日誌庫之間的兼容性,PHP-FIG小組最近發布了PSR-3,這是一個通用的日誌對象接口。在本文中,我將討論PSR-3定義的日誌接口如何允許我們編寫不依賴於任何特定日誌實現的可重用代碼。
PSR-3快速入門
在我們了解PSR-3如何使我們的代碼更可重用之前,有必要了解PSR-3是什麼。如果您已經熟悉PSR-3,可以跳過本節。規範的核心是日誌對象的接口。此接口公開了八種方法來處理不同嚴重級別的消息,以及一個通用的log()
方法,可以接受任意嚴重級別。 PSR-3支持的八個嚴重級別基於RFC 5424,如下所述:
emergency
– 系統無法使用alert
– 需要立即採取行動critical
– 嚴重狀況error
– 不需要立即關注但應監控的錯誤warning
– 不尋常或不希望發生的事件,但並非錯誤notice
– 正常但重要的事件info
– 有趣的事件debug
– 用於調試的詳細信息每個日誌方法都接受一個消息,該消息必須是字符串或具有__toString()
方法的對象。附加參數接受一個數組,可以提供日誌消息的上下文信息。可以在PSR-3規範中找到這些方法和參數的完整說明。
獲取PSR-3文件
獲取使用PSR-3所需的文件很容易——您可以在Psr/Log GitHub存儲庫中找到它們。您也可以使用Composer從Packagist獲取這些文件。下面是一個用於檢索Psr/Log文件的示例composer.json
文件:
<code class="language-json">{ "require": { "psr/log": "dev-master" } }</code>
日誌記錄如何限制代碼重用
PHP有很多不同的日誌庫,每個庫都有自己收集和記錄數據的方法。雖然它們之間有一些共同點,但每個庫都有自己獨特的一套日誌方法。這意味著在日誌之間切換可能具有挑戰性,通常需要更改任何使用日誌記錄的地方的代碼。這與代碼重用和麵向對象設計的SOLID原則背道而馳。我們面臨的局面是:要么聲明對特定日誌庫的依賴,要么完全避免日誌記錄。為了更清楚地說明這個問題,需要一個具體的例子。假設我們正在創建一個簡單的Mailer對象來處理髮送電子郵件。我們希望Mailer在每次發送電子郵件時記錄一條消息,並且我們決定使用優秀的Monolog庫來處理我們的日誌記錄需求。
<code class="language-php"><?php namespace Email; class Mailer { private $logger; public function __construct($logger) { $this->logger = $logger; } public function sendEmail($emailAddress) { // 发送电子邮件的代码... // 记录消息 $this->logger->addInfo("Email sent to $emailAddress"); } }</code>
我們可以使用以下代碼使用此類:
<code class="language-php"><?php // 创建一个Monolog对象 $logger = new Monolog\Logger("Mail"); $logger->pushHandler(new Monolog\Handler\StreamHandler("mail.log")); // 创建邮件发送器并发送电子邮件 $mailer = new Email\Mailer($logger); $mailer->sendEmail("email@example.com");</code>
運行此代碼將在mail.log
文件中創建一個新條目,記錄已發送電子郵件。此時,我們可能會認為我們已經編寫了一個可重用的Mailer對象。我們使用依賴注入使日誌記錄器可用於Mailer,因此我們可以交換不同的日誌記錄器配置,而無需觸及我們的Mailer代碼。看起來我們已經成功遵循了SOLID原則並避免了創建任何硬依賴。但是,假設我們想在使用Analog處理日誌記錄交互的不同項目中重用Mailer類。現在我們遇到了問題,因為Analog沒有addInfo()
方法。要使用Analog記錄信息級別消息,我們調用Analog::log($message, Analog::INFO)
。我們可以修改Mailer類以使用Analog的方法,如下所示。
<code class="language-php"><?php namespace Email; class Mailer { public function sendEmail($emailAddress) { // 发送电子邮件的代码... // 记录消息 Analog::log("Email sent to $emailAddress", Analog::INFO); } }</code>
我們可以使用以下代碼使用更新後的Mailer類:
<code class="language-json">{ "require": { "psr/log": "dev-master" } }</code>
雖然這會起作用,但這遠非理想。我們遇到了Mailer對特定日誌記錄實現的依賴,這要求在引入新的日誌記錄器時更改類。這使得類不太可重用,並迫使我們必須在依賴於特定日誌記錄器的可用性或完全放棄類中的日誌記錄之間做出選擇。
使用PSR-3避免日誌記錄器依賴
正如Alejandro Gervasio在他關於該主題的優秀文章中解釋的那樣,依賴倒置原則告訴我們,我們應該依賴抽象而不是具體實現。在日誌記錄的情況下,我們目前的問題一直是缺乏一個可以依賴的合適的抽象。這就是PSR-3發揮作用的地方。 PSR-3旨在通過為日誌記錄器提供一個通用接口(恰當地命名為LoggerInterface
)來克服日誌記錄實現不兼容的問題。通過提供一個不綁定到任何特定實現的接口,PSR-3使我們無需依賴特定的日誌記錄器——我們可以改為對LoggerInterface
進行類型提示以獲取符合PSR-3的日誌記錄器。我已經更新了下面的Mailer類來演示這一點:
<code class="language-php"><?php namespace Email; class Mailer { private $logger; public function __construct($logger) { $this->logger = $logger; } public function sendEmail($emailAddress) { // 发送电子邮件的代码... // 记录消息 $this->logger->addInfo("Email sent to $emailAddress"); } }</code>
構造函數已修改為接受LoggerInterface
的實現者,並且sendEmail()
方法現在調用PSR-3中指定的info()
方法。 Monolog已經符合PSR-3,並且Analog提供了一個實現LoggerInterface
的包裝器對象,因此我們現在可以使用這兩個日誌記錄器而無需修改Mailer類。以下是如何使用Monolog調用該類的:
<code class="language-php"><?php // 创建一个Monolog对象 $logger = new Monolog\Logger("Mail"); $logger->pushHandler(new Monolog\Handler\StreamHandler("mail.log")); // 创建邮件发送器并发送电子邮件 $mailer = new Email\Mailer($logger); $mailer->sendEmail("email@example.com");</code>
以及使用Analog:
<code class="language-php"><?php namespace Email; class Mailer { public function sendEmail($emailAddress) { // 发送电子邮件的代码... // 记录消息 Analog::log("Email sent to $emailAddress", Analog::INFO); } }</code>
現在,我們能夠使用我們的Mailer對象與任何庫一起使用,而無需編輯Mailer類或更改我們使用它的方式。
為不支持PSR-3的日誌記錄器使用適配器模式
到目前為止,我們已經通過請求LoggerInterface
的實現者成功地將Mailer對象與任何特定的日誌記錄實現解耦。但是,那些尚未添加對PSR-3支持的日誌記錄器呢?例如,流行的KLogger庫已經有一段時間沒有更新了,目前與PSR-3不兼容。幸運的是,我們可以通過利用適配器模式輕鬆地將KLogger公開的方法映射到LoggerInterface
中定義的方法。 Psr/Log存儲庫中的支持文件使我們能夠通過提供一個我們可以擴展的AbstractLogger
類來輕鬆創建適配器類。抽像類只是將LoggerInterface
中定義的八個特定於級別的日誌方法轉發到一個通用的log()
方法。通過擴展AbstractLogger
類並定義我們自己的log()
方法,我們可以輕鬆地為不原生支持PSR-3的日誌記錄器創建符合PSR-3的適配器。我將在下面通過為KLogger創建一個簡單的適配器來演示這一點:
<code class="language-json">{ "require": { "psr/log": "dev-master" } }</code>
log()
方法只是將LoggerInterface
方法映射到各自的KLogger方法,而KLogger處理實際的日誌記錄活動。通過這種方式包裝KLogger類,我們能夠在不破壞LoggerInterface
契約的情況下使用它。我們現在可以使用KLogger適配器與Mailer類一起使用:
<code class="language-php"><?php namespace Email; class Mailer { private $logger; public function __construct($logger) { $this->logger = $logger; } public function sendEmail($emailAddress) { // 发送电子邮件的代码... // 记录消息 $this->logger->addInfo("Email sent to $emailAddress"); } }</code>
使用適配器類,我們能夠在不修改Mailer類的情況下使用KLogger,並且仍然遵守LoggerInterface
。 KLogger不接受調試級別消息的第二個參數,因此即使使用適配器,它也不完全符合PSR-3。擴展KLogger以使其完全與PSR-3兼容將是一項微不足道的任務,但這超出了本文的範圍。但是,可以肯定地說,使用我們的適配器類使我們非常接近完全符合PSR-3,並允許我們使用LoggerInterface
與KLogger類一起使用。
結論
在本文中,我們已經了解瞭如何使用PSR-3來幫助我們編寫與日誌記錄器無關的代碼,這些代碼不依賴於特定的日誌記錄實現。許多主要的PHP項目已經添加了對PSR-3的支持,包括Monolog、Symfony和Mustache.php,以及Drupal等其他知名項目正在討論如何最好地集成它。由於PSR-3降低了代碼重用的障礙,我們應該看到更多庫和框架正確使用日誌記錄,為開發者提供有用的信息。 PSR-3會影響您在應用程序中使用日誌記錄的方式嗎?請在下面的評論部分告訴我們。
(圖片來自Fotolia)
(PSR-3日誌記錄的常見問題解答部分,由於篇幅限制,此處省略。 可以根據需要添加。)
以上是PHP主|使用PSR-3登錄以提高可重用性的詳細內容。更多資訊請關注PHP中文網其他相關文章!