# 日志
為了幫助你更多的了解應用程序中到底發生了什么,Laravel 提供了強大的日志服務,允許你將日志消息、系統錯誤日志記錄到文件,甚至使用 Slack 通知到你的整個團隊。
在 Laravel 框架中,Laravel 使用[Monolog](https://github.com/Seldaek/monolog)庫,它為各種強大的日志處理提供支持。Laravel 使配置這些處理程序變得簡單,允許你混合并匹配它們自定義的應用程序日志處理。
## 配置
所有的應用程序日志系統配置都位于`config/logging.php`配置文件中。這個文件允許你配置你的應用程序日志通道,所以務必查看每個可用的通道及它們的選項。當然,我們將在下面回顧一些常用的選項。
默認情況下,Laravel 將使用`stack`去記錄日志消息。stack 通道被用來將多個日志通道聚合到一個單一的通道中。關于堆棧的更多信息,查看[以下文檔](https://laravel-china.org/docs/laravel/5.7/logging/2264#building-log-stacks)。
#### 配置通道名稱
默認情況下,Monolog 使用與當前環境匹配的『通道名稱』進行實例化,比如`production`或者`local`。要改變這個值,需添加一個`name`選項到你的通道配置中:
~~~php
'stack' => [
'driver' => 'stack',
'name' => 'channel-name',
'channels' => ['single', 'slack'],
],
~~~
#### 可用的通道驅動
| 名稱 | 描述 |
| --- | --- |
| `single` | 一個便于創建『多通道』通道的包裝器 |
| `daily` | 單個文件或者基于日志通道的路徑 (`StreamHandler`) |
| `slack` | 一個每天輪換的基于 Monolog 驅動的`RotatingFileHandler` |
| `syslog` | 一個基于 Monolog 驅動的`SyslogHandler` |
| `errorlog` | 一個基于 Monolog 驅動的`ErrorLogHandler` |
| `monolog` | 一個可以使用任何支持 Monolog 處理程序的 Monolog 工廠驅動程序 |
| `custom` | 一個調用指定工廠創建通道的驅動程序 |
> {tip} 有關`monolog`和`custom`驅動,查看[高級通道自定義](https://laravel-china.org/docs/laravel/5.7/logging/2264#advanced-monolog-channel-customization)
#### 配置 Slack 通道
`slack`通道需要`url`配置選項。這個 URL 應當與你為 Slack 團隊配置的一個[incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks)相匹配。
### 構建日志棧
如前所述,`stack`驅動允許你將多個通道合并到一個單一日志通道中。為了說明如何使用日志棧,讓我們看一個你可能在生產應用配置中看到的實例配置:
~~~php
'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`配置選項。這個選項決定了一個消息必須被通道記錄的最小『level』。Monolog 為 Laravel 的日志服務提供了[RFC 5424 規范](https://tools.ietf.org/html/rfc5424)中定義的所有日志級別:**emergency**,**alert**,**critical**,**error**,**warning**,**notice**,**info**和**debug**。
因此,假設我們記錄一個日志消息使用`debug`方法:
~~~php
Log::debug('An informational message.');
~~~
根據我們的配置,`syslog`通道將寫消息到系統日志;然而,由于錯誤消息不是`critical`或者這個級別之上,它將不被發送到 Slack。但是,如果我們記錄一個`emergency`的日志消息,它將被同時發送到系統日志和 Slack,因為`emergency`級別高于我們對兩個通道最低級別的閾值:
~~~php
Log::emergency('The system is down!');
~~~
## 記錄日志消息
你可以通過`Log`外觀類將信息寫入到日志。如前所述,日志器提供在[RFC 5424 規范](https://tools.ietf.org/html/rfc5424)中定義的八個日志級別:**emergency**,**alert**,**critical**,**error**,**warning**,**notice**,**info**和**debug**:
~~~php
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
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
Log::info('Showing user profile for user: '.$id);
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
~~~
#### 上下文信息
還可以將一組上下文數組數據傳遞給日志方法。這個上下文數據將被格式化并用日志消息顯示:
~~~php
Log::info('User failed to login.', ['id' => $user->id]);
~~~
### 記錄日志到指定通道
有時候你可能希望將日志記錄到非默認通道。你可以使用?`Log`?Facade 中的?`channel`?方法,將日志記錄到應用配置中存在的任何渠道:
~~~php
Log::channel('slack')->info('Something happened!');
~~~
如果你想按需創建多個渠道的日志堆棧,你可以使用?`stack`?方法:
~~~php
Log::stack(['single', 'slack'])->info('Something happened!');
~~~
## 先進的 Monolog 日志通道定制
### 自定義 Monolog 日志通道
有時你可能需要完全配置 Monolog 現有的通道。例如:你想要為現有通道自定義一個 Monolog?`FormatterInterface`?實現。
首先,在頻道配置文件中定義一個?`tap`?數組。`tap`?數組應該包含所需的類列表,這些類就是 Monolog 實例創建后需要自定義(或開發)的類:
~~~php
'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
<?php
namespace App\Logging;
class CustomizeFormatter
{
/**
* 自定義日志實例
*
* @param \Illuminate\Log\Logger $logger
* @return void
*/
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(...);
}
}
}
~~~
> {tip} 所有的「tap」類可以通過?[服務容器](https://laravel-china.org/docs/laravel/5.7/container)?解析,所以它們所需的任何構造函數依賴項都會被自動注入。
### 創建自定義通道
Monolog 有各種[可用的處理程序](https://github.com/Seldaek/monolog/tree/master/src/Monolog/Handler)。 在某些情況下,你希望創建的記錄器類型僅僅是具有特定處理程序實例的 Monolog 驅動程序。 可以使用`monolog`驅動程序創建這些通道。
當使用`monolog`驅動程序時,`handler`配置選項用于指定將實例化哪個處理程序。 可選地,可以使用`handler_with`配置選項指定處理程序所需的任何構造函數參數:
~~~php
'logentries' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\SyslogUdpHandler::class,
'handler_with' => [
'host' => 'my.logentries.internal.datahubhost.company.com',
'port' => '10000',
],
],
~~~
#### Monolog 格式化
當使用`monolog`驅動程序時,Monolog`LineFormatter`將用作默認格式化程序。 但是,你可以使用`formatter`和`formatter_with`配置選項自定義傳遞給處理程序的格式化程序的類型:
~~~php
'browser' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\BrowserConsoleHandler::class,
'formatter' => Monolog\Formatter\HtmlFormatter::class,
'formatter_with' => [
'dateFormat' => 'Y-m-d',
],
],
~~~
如果你使用能夠提供自我格式化程序的 Monolog 處理程序,你可以將`formatter`配置選項的值設置為`default`:
~~~php
'newrelic' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\NewRelicHandler::class,
'formatter' => 'default',
],
~~~
### 通過工廠創建渠道
如果你想定義一個完全自定義的通道,你可以完全控制 Monolog 的實例化和配置,你可以在`config / logging.php`配置文件中指定`custom`驅動程序類型。 你的配置應該包含一個`via`選項,指向將被調用以創建 Monolog 實例的工廠類:
~~~php
'channels' => [
'custom' => [
'driver' => 'custom',
'via' => App\Logging\CreateCustomLogger::class,
],
],
~~~
一旦配置了`custom`通道,就可以定義創建 Monolog 實例的類。 這個類只需要一個方法:`__ invoke`,它就可以返回 Monolog 實例:
~~~php
<?php
namespace App\Logging;
use Monolog\Logger;
class CreateCustomLogger
{
/**
* 創建一個 Monolog 實例
*
* @param array $config
* @return \Monolog\Logger
*/
public function __invoke(array $config)
{
return new Logger(...);
}
}
~~~