# 日志
關于日志接口,PSR規范中給出了相當好的說明和定義,并且有多種細分的日記級別。

## 簡化版的日記接口
雖然PSR規范中詳盡定義了日志接口,然而在用使用開源框架或內部框架進行項目開發過程中,實際上日記的分類并沒有使用得那么豐富,通常只是頻繁集中在某幾類。為了減少不必要的復雜性,PhalApi特地將此規范的日志接口精簡為三種,只有:
+ **error**: 系統異常類日記
+ **info**: 業務紀錄類日記
+ **debug**: 開發調試類日記
### error 系統異常類日記
系統異常類日志用于紀錄**在后端不應該發生卻發生的事情**,即通常所說的系統異常。例如:調用第三方、的接口失敗了,此時需要紀錄一下當時的場景,以便復查和定位出錯的原因。又如:寫入一條紀錄到數據紀錄卻失敗了,此時需要紀錄一下,以便進一步排查。
紀錄系統異常日志,用法很簡單。可以使用[PhalApi\Logger::error($msg, $data)](https://github.com/phalapi/kernal/blob/master/src/Logger.php#L111)接口,第一個參數$msg用于描述日志信息,第二個可選參數為上下文場景的信息。下面是一些使用示例。
```php
// 只有描述
\PhalApi\DI()->logger->error('fail to insert DB');
// 描述 + 簡單的信息
\PhalApi\DI()->logger->error('fail to insert DB', 'try to register user dogstar');
// 描述 + 當時的上下文數據
$data = array('name' => 'dogstar', 'password' => '123456');
\PhalApi\DI()->logger->error('fail to insert DB', $data);
```
上面三條紀錄,會在日記文件中生成類似以下的日志內容。
```bash
$ tailf ./runtime/log/201502/20150207.log
2015-02-07 20:37:55|ERROR|fail to insert DB
2015-02-07 20:37:55|ERROR|fail to insert DB|try to register user dogstar
2015-02-07 20:37:55|ERROR|fail to insert DB|{"name":"dogstar","password":"123456"}
```
### info 業務紀錄類日記
業務紀錄日志,是指紀錄業務上關鍵流程環節的操作,以便發生系統問題后進行回滾處理、問題排查以及數據統計。如在有緩存的情況下,可能數據沒及時寫入數據庫而導致數據丟失或者回檔,這里可以通過日記簡單查看是否可以恢復。以及說明一下操作發生的背景或原由,如通常游戲中用戶的經驗值添加:
```php
// 假設:10 + 2 = 12
\PhalApi\DI()->logger->info('add user exp', array('name' => 'dogstar', 'before' => 10, 'addExp' => 2, 'after' => 12, 'reason' => 'help one more phper'));
```
對應的日記為:
```
2015-02-07 20:48:51|INFO|add user exp|{"name":"dogstar","before":10,"addExp":2,"after":12,"reason":"help one more phper"}
```
### debug 開發調試類日記
開發調試類日記,主要用于開發過程中的調試。用法如上,這里不再贅述。以下是一些簡單的示例。
```php
// 只有描述
\PhalApi\DI()->logger->debug('just for test');
// 描述 + 簡單的信息
\PhalApi\DI()->logger->debug('just for test', '一些其他的描述 ...');
// 描述 + 當時的上下文數據
\PhalApi\DI()->logger->debug('just for test', array('name' => 'dogstar', 'password' => '******'));
```
## 更靈活的日志分類
若上面的error、info、debug都不能滿足項目的需求時,可以使用[PhalApi\Logger::log($type, $msg, $data)](https://github.com/phalapi/kernal/blob/master/src/Logger.php#L75)接口進行更靈活的日記紀錄。
```php
\PhalApi\DI()->logger->log('demo', 'add user exp', array('name' => 'dogstar', 'after' => 12));
\PhalApi\DI()->logger->log('test', 'add user exp', array('name' => 'dogstar', 'after' => 12));
```
對應的日記為:
```
2015-02-07 21:13:27|DEMO|add user exp|{"name":"dogstar","after":12}
2015-02-07 21:15:39|TEST|add user exp|{"name":"dogstar","after":12}
```
注意到,第一個參數為日記分類的名稱,在寫入日記時會自動轉換為大寫。其接口函數簽名為:
```php
/**
* 日記紀錄
*
* 可根據不同需要,將日記寫入不同的媒介
*
* @param string $type 日記類型,如:info/debug/error, etc
* @param string $msg 日記關鍵描述
* @param string/array $data 場景上下文信息
* @return NULL
*/
abstract public function log($type, $msg, $data);
```
## 指定日志級別
在使用日志紀錄前,在注冊日志```\PhalApi\DI()->logger```服務時須指定開啟的日志級別,以便允許指定級別的日志得以紀錄,從而達到選擇性保存所需要的日志的目的。
通過[PhalApi\Logger](https://github.com/phalapi/kernal/blob/master/src/Logger.php)的構造函數的參數,可以指定日志級別。多個日記級別使用或運算進行組合。
```php
// 日記紀錄
$di->logger = new FileLogger(API_ROOT . '/runtime', Logger::LOG_LEVEL_DEBUG | Logger::LOG_LEVEL_INFO | Logger::LOG_LEVEL_ERROR);
```
上面的三類日記分別對應的標識如下。
日志類型|日志級別標識
---|---
error 系統異常類|PhalApi\Logger::LOG_LEVEL_ERROR
info 業務紀錄類|PhalApi\Logger::LOG_LEVEL_INFO
debug 開發調試類|PhalApi\Logger::LOG_LEVEL_DEBUG
## 擴展:定制你的日志
普遍情況下,我們認為將日記存放在文件是比較合理的,因為便于查看、管理和統計。當然,如果你的項目需要將日記紀錄保存在其他存儲媒介中,也可以快速擴展實現的。例如實現數據庫的存儲思路。
```php
<?php
namespace App\Common\Logger;
use PhalApi\Logger;
class DBLogger extends Logger {
public function log($type, $msg, $data) {
// TODO 數據庫的日記寫入 ...
}
}
```
隨后,重新注冊```\PhalApiDI()->logger```服務即可。
```php
$di->logger = new App\Common\Logger\DBLogger(API_ROOT . '/runtime', Logger::LOG_LEVEL_DEBUG | Logger::LOG_LEVEL_INFO | Logger::LOG_LEVEL_ERROR);
```