---
tags: laravel
---
# 日誌 Logging

## 簡介
為了幫助你更多的了解應用中到底發生了什麼,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(...);
}
}
```