--- tags: laravel --- # 日誌 Logging ![](https://i.imgur.com/iBayl0U.jpg) ## 簡介 為了幫助你更多的了解應用中到底發生了什麼,Laravel 提供了強大的日誌服務,允許你將日誌消息、系統錯誤日誌記錄到檔案中,甚至使用 Slack 通知到你的整個團隊 Laravel的日誌是以頻道來做為基底,每一個頻道代表一種特定的方式來記錄日誌資訊。例如 single 頻道寫日誌訊息到單一的日誌檔案,而 slack 頻道發送日誌訊息到 slack。而日誌訊息可以根據它們的需要寫入到多個頻道中 在 Laravel 框架中,Laravel 使用 Monolog 函式庫,它為各種強大的日誌處理提供支持。Laravel 使設定這些處理程序變得簡單,允許你混合並匹配它們自定義的應用日誌處理 ## 設定 所有的應用日誌系統配置都位於 config/logging.php 設定文件中。這個檔案允許你設定你的應用日誌頻道,所以務必查看每個可用的頻道及它們的選項。我們將在下面介紹一些常用的選項 預設情況下,Laravel 將使用 stack 頻道去記錄日誌消息。stack 頻道被用來將多個日誌頻道聚合到一個單一的頻道中。關於堆疊的更多訊息,查看下面更多的內容 ### 設定頻道名稱 預設情況下,Monolog 使用與當前環境匹配的『頻道名稱』進行實例化,比如 production 或者 local。要改變這個值,需添加一個 name 選項到你的頻道配置中: ``` 'stack' => [ 'driver' => 'stack', 'name' => 'channel-name', 'channels' => ['single', 'slack'], ], ``` ### 可用的頻道驅動 每一個日誌頻道是利用驅動來得到能力。該驅動決定日誌訊息是如何又是在哪裡被記錄下來。下面列出的每一個頻道驅動將能夠被每個 Laravel 應用使用。大多數的這些驅動已經列在應用的 config/logging.php 裏頭,所以盡量多去看看這個檔案並熟悉裏頭的內容 |名稱 |描述| |---|---| |custom |一個便於創建『多頻道』頻道的包裝器| |daily |一個每天輪換的基於 Monolog 驅動的 RotatingFileHandler| |errorlog |一個基於 Monolog 驅動的 ErrorLogHandle| |monolog |一個 Monolog 工廠驅動能使用任何有支持的 Monolog handler | |null |一個驅動放棄所有的日誌訊息| |papertrail |一個基於 Monolog 驅動的 SyslogUdpHandler| |single |單個檔案或者基於日誌頻道的路徑 (StreamHandler)| |slack |一個基於 Monolog 驅動的 SlackWebhookHandler| |stack |一個便於創建『多頻道』頻道的包裝器| |syslog |一個基於 Monolog 驅動的 SyslogHandler| 確認進階頻道自定義文件來了解更多關於 monolog 和自定義驅動 ### 頻道配置 #### 設定 Single 和 Daily 頻道 single 和 daily 頻道包含三個可選配置項:bubble、permission 和 locking |名稱|描述|預設值| |---|---|---| |bubble |消息處理後,指示消息是否推送到其他頻道| true| |permission |日誌文件權限 |0644| |locking |寫入之前嘗試鎖定日誌文件 |false| #### 設定 Papertrail 頻道 papertrail 頻道需要 url 和 port 配置選項,你可以從 Papertrail 獲取這些值 #### 設定 Slack 頻道 slack 頻道需要 url 配置選項。這個 URL 應當與你為 Slack 團隊配置的一個 incoming webhook 相匹配。預設情況下,Slack 只會接收 critical 和更高等級的日誌,你可以在 logging 設定文件中對其進行修改。 ## 建立日誌堆疊 前面說過,stack 驅動允許你在單一日誌頻道中整合多個頻道。讓我們通過一個產品級應用的配置實例來看看如何使用日誌堆疊: ``` 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['syslog', 'slack'], ], 'syslog' => [ 'driver' => 'syslog', 'level' => 'debug', ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'emoji' => ':boom:', 'level' => 'critical', ], ], ``` 我們來分析這個配置。首先要注意的是 stack 通過藉助它的 channels 選項聚合了另外兩個頻道:syslog 和 slack。因此,在記錄日誌消息時,這兩個頻道都有機會完成日誌消息記錄。然而這些頻道是否真能記錄這些訊息將要看訊息的階層/等級 ### 日誌階層 請留意上面例子中 syslog 和 slack 中的 level 配置。這個選項決定了需要被該頻道記錄的日誌的最低 「級別」。Monolog (一個功能強勁的 Laravel 日誌服務)接受定義在 RFC 5424 specification 中的全部級別: emergency、alert、critical、error、warning、notice、info 和 debug 假設我們使用 debug 方法記錄日誌消息: `Log::debug('An informational message.');` 根據我們的配置,syslog 頻道將把該消息記錄到系統日誌;不過因為錯誤消息不是 critical 或更高級別,它將不會被發送到 Slack。如果我們記錄一條 emergency 消息的話,由於 emergency 的級別高於兩個頻道的最低級別限制,它將被發送給系統日誌和 Slack: `Log::emergency('The system is down!');` ## 撰寫日誌訊息 你可以使用 Log Facade 來將訊息寫入日誌。如前所述,日誌提供定義在 RFC 5424 specification 中的可用日誌級別: emergency、alert、critical、error、warning、notice、info 和 debug: ``` use Illuminate\Support\Facades\Log; Log::emergency($message); Log::alert($message); Log::critical($message); Log::error($message); Log::warning($message); Log::notice($message); Log::info($message); Log::debug($message); ``` 因此,你可以呼叫這些方法中的任一方法記錄相應級別的日誌。預設情況下,消息被寫入到在 config/logging.php 設定文件中定義的預設日誌頻道: ``` <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Support\Facades\Log; class UserController extends Controller { /** * Show the profile for the given user. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { Log::info('Showing the user profile for user: '.$id); return view('user.profile', [ 'user' => User::findOrFail($id) ]); } } ``` ### 上下文訊息 可以將上下文訊息陣列傳遞給日誌方法。這些訊息將被格式化,並與日誌消息一起顯示: `Log::info('使用者登入失敗.', ['id' => $user->id]);` ### 寫到指定通道 有時候你可能希望將消息寫入到應用預設頻道之外的頻道中。可以使用 Log Facade 的 channel() 獲取定義在配置文件中的任一頻道並將訊息寫入其中: ``` use Illuminate\Support\Facades\Log; Log::channel('slack')->info('Something happened!'); ``` 如果想要建立一個由多頻道構成的按需記錄的堆疊,可以使用 stack() : `Log::stack(['single', 'slack'])->info('Something happened!');` ## 自定義 Monolog 通道 ### 為通道自定義 Monolog 有時需要完全控制已存在頻道的 Monolog。比如,你可能想要為指定頻道的日誌處理配置自定義的 Monolog FormatterInterface 實作 先在通道配置中定義一個 tap 陣列。 tap 陣列包含一個在頻道創建後有機會用於自定義 Monolog 實例的類別列表,類別所在資料夾沒有特別限定,你可以自行規劃 ``` 'single' => [ 'driver' => 'single', 'tap' => [App\Logging\CustomizeFormatter::class], 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', ], ``` 一旦在頻道中有了 tap 選項配置,就要準備用於自定義 Monolog 實例的類別。這種類別需要一個方法: __invoke,它接受一個 Illuminate\Log\Logger 實例作為其參數。 Illuminate\Log\Logger 實例將所有方法呼叫代理到基礎的 Monolog 實例: ``` <?php namespace App\Logging; use Monolog\Formatter\LineFormatter; class CustomizeFormatter { /** * Customize the given logger instance. * * @param \Illuminate\Log\Logger $logger * @return void */ public function __invoke($logger) { foreach ($logger->getHandlers() as $handler) { $handler->setFormatter(new LineFormatter( '[%datetime%] %channel%.%level_name%: %message% %context% %extra%' )); } } } ``` > 技巧: > > 所有的 「tap」 類別都是由服務容器解析的,因此任何依賴它們的建構子都會自動被注入 ### 自定義 Monolog 頻道 ### 創建 Monolog 處理器頻道 Monolog 有多種可用處理器。在某些情況下,你會希望僅建立一個帶有指定處理器的 Monolog 驅動的日誌類型。這些頻道可以使用 monolog 驅動建立 在使用 monolog 驅動時, handler 配置項用於指定被實例化的處理器。如果該處理器的建構子需要參數,可以使用可選的 with 配置項來指定: ``` 'logentries' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\SyslogUdpHandler::class, 'with' => [ 'host' => 'my.logentries.internal.datahubhost.company.com', 'port' => '10000', ], ], ``` #### Monolog 格式化 使用 monolog 驅動時,Monolog 的 LineFormatter 用於預設的格式化處理器。當然你也可以使用 formatter 和 formatter_with 配置項自定義格式化處理器類型: ``` 'browser' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\BrowserConsoleHandler::class, 'formatter' => Monolog\Formatter\HtmlFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y-m-d', ], ], ``` 如果所用的 Monolog 處理器能夠提供自帶的格式代處理器,可以將 formatter 配置項指定為 default: ``` 'newrelic' => [ 'driver' => 'monolog', 'handler' => Monolog\Handler\NewRelicHandler::class, 'formatter' => 'default', ], ``` ### 通過工廠創建自定義頻道 如果你想定義一個完全自定義的頻道,你可以完全控制 Monolog 的實例化和配置,你可以在 config/logging.php 配置文件中指定 custom 驅動程序類型。 你的配置應該包含一個 via 選項,指向將被呼叫以建立 Monolog 實例的工廠類別: ``` 'channels' => [ 'example-custom-channel' => [ 'driver' => 'custom', 'via' => App\Logging\CreateCustomLogger::class, ], ], ``` 一旦配置了 custom 頻道,就可以定義創建 Monolog 實例的類別。 這個類別只需要一個方法: __invoke,它就可以回傳 Monolog 實例,該方法將接收頻道設定陣列做為單一參數: ``` <?php namespace App\Logging; use Monolog\Logger; class CreateCustomLogger { /** * Create a custom Monolog instance. * * @param array $config * @return \Monolog\Logger */ public function __invoke(array $config) { return new Logger(...); } } ```