分析 Log
Laravel 的 Log 套件在 5.5 版之前,是使用 Writer 包裝 Monolog,成為一個 proxy pattern,被代理的類別則是寫死 Monolog。在 5.6 版開始,設計改採用 Logger 包裝 PSR-3 的 LoggerInterface,一樣是 proxy pattern,但被代理的類別只要是符合 PSR-3 的介面,就能使用。
後面一樣只會討論 5.7.6 版的原始碼。
Service provider 定義非常簡單,就直接初始化 LogManager
:
$this->app->singleton('log', function () { |
LogManager 實作了 LoggerInterface。它跟之前提到的 Day 10 有異區同工之妙,方法定義也非常接近,只差 LogManager 並沒有繼承 Manager 而已。
只要 logging.php 有設定好,基本上就可以直接 make 出來用:
app()->make('log')->info('something'); |
來看看 info()
裡面的實作:
public function info($message, array $context = []) |
非常簡單,取得 LoggerInterface driver 後,馬上再把 log 資訊轉交給 driver。這也是為何會說它是 proxy pattern 的主因--因為 LogManager 本身並沒有做跟 log 有關的事。
接著,看看 driver()
做了哪些事才能取得實例:
public function driver($driver = null) |
getDefaultDriver()
是從 logging.php
裡取得設定,檔案註解裡有提到有下列幾種 driver:
- single
- daily
- slack
- syslog
- errorlog
- monolog
- custom
- stack
我們接著來看 single 與 stack 兩種 driver 怎麼透過 get()
產生吧:
protected function get($name) |
這裡的 channels 指的是屬於 LogManager 的 channel,與 Monolog 無關。
with()
函式的等價程式碼如下:
$callable = function ($logger) { |
Laravel 會有這些簡單的函式,最主要的用途是為了 function chain。比方說 with()->blah();
就有辦法實現。
不過事實上,PHP 7.0 開始也支援下面的寫法,也是可以參考的:
return (function($logger) { |
產生實例後,會使用 tap()
方法(跟之前提到的 tap()
函式是不同的)設定實例。
protected function tap($name, Logger $logger) |
從以上原始碼可以得知,tap 設定與 middleware 設定有點雷同,如:
class SomeTap |
簡單來說,這是一種用類別 __invoke()
來取代 tap()
功能的另解法。
tap()
裡面基本上是對 $logger
做一些初始化設定,比方說 Monolog 設定 processor
等。
再回頭過來看 resolve()
如何做:
protected function resolve($name) |
從上面的程式碼來看,single 與 stack 會對應到的建置方法為:
- createSingleDriver
- createStackDriver
protected function createSingleDriver(array $config) |
Stack 本身代表的意義是把所有 Laravel Logger 所定義的 channels(也就是 driver)整合成一個懶人包,只要對 stack 推送 log,所有在設定裡的 channel 都會跟著推送 log。
如果想針對單一 driver 推 log 也很簡單,使用 driver()
取得對應的 driver 即可。
雖然 Monolog 已經有內建 Registry,是類似概念的做法,但比起 Laravel 的 LogManager 還是缺了少了幾個元素,一個是設定轉成建實例的方法;另一個則是單元測試的易用性。
談到測試,Laravel 還是大勝許多套件,剩下沒幾天時間,希望有機會能談到測試。