打開 Monolog 的資料夾,會發現除了昨天提到的 Logger 與 Handler 之外,還有幾個沒提到的角色,如 Formatter
或是 Processor
。
今天我們就來看看 Formatter
裡面做了什麼事。
首先當然先看看它如何使用!我們可以從先從 Handler\HandlerInterface
找到相關的實作:
interface HandlerInterface
{
public function setFormatter(FormatterInterface $formatter);
}
這裡可以注意到,它的設計是「抽象依賴抽象」,這符合依賴反轉原則;並且它的行為設計上,是做成抽象與抽象之間的一對一關係(因為它用 set
,而不是 push
),所以它實際上是實作了 Bridge Pattern 。關係圖如下:
@startuml
Interface Handler\HandlerInterface {
+ setFormatter(f: FormatterInterface)
}
Interface Formatter\FormatterInterface
Class Handler\ConcreteHandler
Class Formatter\ConcreteFormatter
Handler\HandlerInterface -> Formatter\FormatterInterface
Handler\HandlerInterface <|-- Handler\ConcreteHandler
Formatter\FormatterInterface <|-- Formatter\ConcreteFormatter
@enduml
它們的實作之間並沒有直接耦合,而是透過抽象介面耦合;換句話說,只要物件有實作抽象介面的話,它就能正常的介接使用。
我們先來看昨天使用的 Handler\StreamHandler
裡面是使用了哪種 Formatter 。追一下程式碼,會發現在 Handler\AbstractHandler
裡面:
abstract class AbstractHandler implements HandlerInterface
{
public function getFormatter()
{
if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter();
}
return $this->formatter;
}
protected function getDefaultFormatter()
{
return new LineFormatter();
}
}
因此預設會是 LineFormatter
,當然不是發 Log 到 Line ,而是指單行的 log 。
再來我們來寫一個測試程式如下:
$logger = new \Monolog\Logger('name');
$handler = new \Monolog\Handler\StreamHandler('php://stdout', \Monolog\Logger::DEBUG);
$logger->pushHandler($handler);
$logger->warning('test');
這樣輸出的結果應該會如下:
[2018-01-15 18:10:12] name.WARNING: test [] []
剛有提到,我們應該能自由地抽換 Formatter 才對,我們來換成 JsonFormatter
試看看:
$handler->setFormatter(new \Monolog\Formatter\JsonFormatter());
輸出就會變成 JSON 了:
{"message":"test","context":[],"level":300,"level_name":"WARNING","channel":"name","datetime":{"date":"2018-01-15 18:16:28.438148","timezone_type":3,"timezone":"UTC"},"extra":[]}
相信大家都去過黃色拱門, Formatter 與 Handler 之間的關係事實上就跟銅板輕鬆點很像,可以依需求任意搭配兩區的餐點,而店員都可以接受並結帳。
我們也可以使用 Slack 配 Json 或是 Mail 配 Html 等等,完全自由配。