跳到內容

記錄

簡介

為了幫助您更了解應用程式內部的運作狀況,Laravel 提供了強大的日誌記錄服務,讓您可以將訊息記錄到檔案、系統錯誤日誌,甚至可以記錄到 Slack 以通知您的整個團隊。

Laravel 日誌記錄基於「頻道」。每個頻道代表一種特定的寫入日誌資訊的方式。例如,single 頻道將日誌檔案寫入單個日誌檔案,而 slack 頻道將日誌訊息發送到 Slack。日誌訊息可能會根據其嚴重性寫入多個頻道。

在底層,Laravel 使用 Monolog 函式庫,該函式庫為各種強大的日誌處理器提供支援。Laravel 可以輕鬆設定這些處理器,讓您可以混合搭配它們來自訂應用程式的日誌處理方式。

設定

所有控制應用程式日誌記錄行為的設定選項都放在 config/logging.php 設定檔中。此檔案可讓您設定應用程式的日誌頻道,因此請務必查看每個可用的頻道及其選項。我們將在下面查看一些常見選項。

預設情況下,Laravel 將在記錄訊息時使用 stack 頻道。stack 頻道用於將多個日誌頻道聚合到單個頻道中。有關建立堆疊的更多資訊,請查看下面的文件

可用的頻道驅動程式

每個日誌頻道都由「驅動程式」提供支援。驅動程式決定日誌訊息的實際記錄方式和位置。每個 Laravel 應用程式中都提供以下日誌頻道驅動程式。大多數這些驅動程式的條目已存在於應用程式的 config/logging.php 設定檔中,因此請務必查看此檔案以熟悉其內容

名稱 描述
custom 一個驅動程式,呼叫指定的工廠來建立頻道。
daily 一個基於 RotatingFileHandler 的 Monolog 驅動程式,每天輪換。
errorlog 一個基於 ErrorLogHandler 的 Monolog 驅動程式。
monolog 一個 Monolog 工廠驅動程式,可以使用任何支援的 Monolog 處理器。
papertrail 一個基於 SyslogUdpHandler 的 Monolog 驅動程式。
single 一個基於單個檔案或路徑的日誌記錄頻道 (StreamHandler)。
slack 一個基於 SlackWebhookHandler 的 Monolog 驅動程式。
stack 一個用於方便建立「多頻道」頻道的包裝器。
syslog 一個基於 SyslogHandler 的 Monolog 驅動程式。

查看有關進階頻道自訂的文件,以了解有關 monologcustom 驅動程式的更多資訊。

設定頻道名稱

預設情況下,Monolog 會使用與當前環境(例如 productionlocal)相符的「頻道名稱」來實例化。若要變更此值,您可以將 name 選項新增至頻道的設定中

1'stack' => [
2 'driver' => 'stack',
3 'name' => 'channel-name',
4 'channels' => ['single', 'slack'],
5],

頻道先決條件

設定 Single 和 Daily 頻道

singledaily 頻道有三個可選的設定選項:bubblepermissionlocking

名稱 描述 預設值
bubble 指示訊息在被處理後是否應向上冒泡到其他頻道。 true
locking 嘗試在寫入日誌檔案之前鎖定它。 false
permission 日誌檔案的權限。 0644

此外,可以透過 LOG_DAILY_DAYS 環境變數或設定 days 設定選項來設定 daily 頻道的保留策略。

名稱 描述 預設值
days 應保留每日日誌檔案的天數。 14

設定 Papertrail 頻道

papertrail 頻道需要 hostport 設定選項。這些可以透過 PAPERTRAIL_URLPAPERTRAIL_PORT 環境變數定義。您可以從 Papertrail 取得這些值。

設定 Slack 頻道

slack 頻道需要 url 設定選項。此值可以透過 LOG_SLACK_WEBHOOK_URL 環境變數定義。此 URL 應與您為 Slack 團隊設定的傳入 Webhook 的 URL 相符。

預設情況下,Slack 只會接收 critical 級別及以上的日誌;但是,您可以使用 LOG_LEVEL 環境變數或修改 Slack 日誌頻道設定陣列中的 level 設定選項來調整此設定。

記錄棄用警告

PHP、Laravel 和其他函式庫通常會通知其使用者,它們的某些功能已被棄用,並將在未來版本中移除。如果您想要記錄這些棄用警告,可以使用 LOG_DEPRECATIONS_CHANNEL 環境變數或在應用程式的 config/logging.php 設定檔中指定您偏好的 deprecations 日誌頻道

1'deprecations' => [
2 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
3 'trace' => env('LOG_DEPRECATIONS_TRACE', false),
4],
5 
6'channels' => [
7 // ...
8]

或者,您可以定義一個名為 deprecations 的日誌頻道。如果存在具有此名稱的日誌頻道,它將始終用於記錄棄用

1'channels' => [
2 'deprecations' => [
3 'driver' => 'single',
4 'path' => storage_path('logs/php-deprecation-warnings.log'),
5 ],
6],

建立日誌堆疊

如前所述,stack 驅動程式可讓您為了方便起見將多個頻道組合到單個日誌頻道中。為了說明如何使用日誌堆疊,讓我們看一下您可能在生產應用程式中看到的範例設定

1'channels' => [
2 'stack' => [
3 'driver' => 'stack',
4 'channels' => ['syslog', 'slack'],
5 'ignore_exceptions' => false,
6 ],
7 
8 'syslog' => [
9 'driver' => 'syslog',
10 'level' => env('LOG_LEVEL', 'debug'),
11 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
12 'replace_placeholders' => true,
13 ],
14 
15 'slack' => [
16 'driver' => 'slack',
17 'url' => env('LOG_SLACK_WEBHOOK_URL'),
18 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
19 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
20 'level' => env('LOG_LEVEL', 'critical'),
21 'replace_placeholders' => true,
22 ],
23],

讓我們剖析此設定。首先,請注意我們的 stack 頻道透過其 channels 選項聚合了另外兩個頻道:syslogslack。因此,在記錄訊息時,這兩個頻道都有機會記錄訊息。但是,正如我們將在下面看到的,這些頻道是否實際記錄訊息可能取決於訊息的嚴重性/「級別」。

日誌級別

請注意上面範例中 syslogslack 頻道設定中存在的 level 設定選項。此選項決定了訊息必須處於的最低「級別」,才能被頻道記錄。為 Laravel 日誌記錄服務提供支援的 Monolog 提供了 RFC 5424 規範中定義的所有日誌級別。依嚴重性降序排列,這些日誌級別為:emergencyalertcriticalerrorwarningnoticeinfodebug

因此,假設我們使用 debug 方法記錄訊息

1Log::debug('An informational message.');

根據我們的設定,syslog 頻道會將訊息寫入系統日誌;但是,由於錯誤訊息不是 critical 或更高級別,因此不會發送到 Slack。但是,如果我們記錄 emergency 訊息,它將會發送到系統日誌和 Slack,因為 emergency 級別高於我們兩個頻道的最低級別閾值

1Log::emergency('The system is down!');

寫入日誌訊息

您可以使用 Log facade 將資訊寫入日誌。如前所述,記錄器提供 RFC 5424 規範中定義的八個日誌級別:emergencyalertcriticalerrorwarningnoticeinfodebug

1use Illuminate\Support\Facades\Log;
2 
3Log::emergency($message);
4Log::alert($message);
5Log::critical($message);
6Log::error($message);
7Log::warning($message);
8Log::notice($message);
9Log::info($message);
10Log::debug($message);

您可以呼叫這些方法中的任何一個來記錄對應級別的訊息。預設情況下,訊息將寫入您的 logging 設定檔設定的預設日誌頻道

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use App\Models\User;
7use Illuminate\Support\Facades\Log;
8use Illuminate\View\View;
9 
10class UserController extends Controller
11{
12 /**
13 * Show the profile for the given user.
14 */
15 public function show(string $id): View
16 {
17 Log::info('Showing the user profile for user: {id}', ['id' => $id]);
18 
19 return view('user.profile', [
20 'user' => User::findOrFail($id)
21 ]);
22 }
23}

上下文資訊

上下文資料陣列可以傳遞到日誌方法。此上下文資料將被格式化並與日誌訊息一起顯示

1use Illuminate\Support\Facades\Log;
2 
3Log::info('User {id} failed to login.', ['id' => $user->id]);

有時,您可能希望指定一些上下文資訊,這些資訊應包含在特定頻道中所有後續的日誌條目中。例如,您可能希望記錄與應用程式的每個傳入請求相關聯的請求 ID。若要完成此操作,您可以呼叫 Log facade 的 withContext 方法

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Log;
8use Illuminate\Support\Str;
9use Symfony\Component\HttpFoundation\Response;
10 
11class AssignRequestId
12{
13 /**
14 * Handle an incoming request.
15 *
16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
17 */
18 public function handle(Request $request, Closure $next): Response
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::withContext([
23 'request-id' => $requestId
24 ]);
25 
26 $response = $next($request);
27 
28 $response->headers->set('Request-Id', $requestId);
29 
30 return $response;
31 }
32}

如果您想要在所有日誌頻道之間共用上下文資訊,可以調用 Log::shareContext() 方法。此方法將為所有已建立的頻道以及後續建立的任何頻道提供上下文資訊

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Log;
8use Illuminate\Support\Str;
9use Symfony\Component\HttpFoundation\Response;
10 
11class AssignRequestId
12{
13 /**
14 * Handle an incoming request.
15 *
16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
17 */
18 public function handle(Request $request, Closure $next): Response
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::shareContext([
23 'request-id' => $requestId
24 ]);
25 
26 // ...
27 }
28}

如果您需要在處理排隊的工作時共用日誌上下文,可以使用工作中間件

寫入特定頻道

有時您可能希望將訊息記錄到應用程式預設頻道以外的頻道。您可以使用 Log facade 上的 channel 方法來檢索並記錄到設定檔中定義的任何頻道

1use Illuminate\Support\Facades\Log;
2 
3Log::channel('slack')->info('Something happened!');

如果您想要建立由多個頻道組成的隨需日誌堆疊,可以使用 stack 方法

1Log::stack(['single', 'slack'])->info('Something happened!');

隨需頻道

也可以透過在運行時提供設定,而無需該設定存在於應用程式的 logging 設定檔中來建立隨需頻道。若要完成此操作,您可以將設定陣列傳遞給 Log facade 的 build 方法

1use Illuminate\Support\Facades\Log;
2 
3Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6])->info('Something happened!');

您可能還希望在隨需日誌堆疊中包含隨需頻道。這可以透過將您的隨需頻道實例包含在傳遞給 stack 方法的陣列中來實現

1use Illuminate\Support\Facades\Log;
2 
3$channel = Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6]);
7 
8Log::stack(['slack', $channel])->info('Something happened!');

Monolog 頻道自訂

自訂頻道的 Monolog

有時您可能需要完全控制如何為現有頻道設定 Monolog。例如,您可能想要為 Laravel 的內建 single 頻道設定自訂 Monolog FormatterInterface 實作。

首先,在頻道的設定上定義一個 tap 陣列。tap 陣列應包含一個類別列表,這些類別在 Monolog 實例建立後應有機會自訂(或「tap into」)。沒有這些類別應放置的慣例位置,因此您可以自由地在應用程式中建立一個目錄來包含這些類別

1'single' => [
2 'driver' => 'single',
3 'tap' => [App\Logging\CustomizeFormatter::class],
4 'path' => storage_path('logs/laravel.log'),
5 'level' => env('LOG_LEVEL', 'debug'),
6 'replace_placeholders' => true,
7],

在頻道上設定 tap 選項後,您就可以定義將自訂 Monolog 實例的類別。此類別只需要一個方法:__invoke,它接收 Illuminate\Log\Logger 實例。Illuminate\Log\Logger 實例將所有方法呼叫代理到基礎 Monolog 實例

1<?php
2 
3namespace App\Logging;
4 
5use Illuminate\Log\Logger;
6use Monolog\Formatter\LineFormatter;
7 
8class CustomizeFormatter
9{
10 /**
11 * Customize the given logger instance.
12 */
13 public function __invoke(Logger $logger): void
14 {
15 foreach ($logger->getHandlers() as $handler) {
16 $handler->setFormatter(new LineFormatter(
17 '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
18 ));
19 }
20 }
21}

您的所有「tap」類別都由服務容器解析,因此它們所需的任何建構函式依賴項都將自動注入。

建立 Monolog 處理器頻道

Monolog 有各種可用的處理器,而 Laravel 並未為每個處理器都包含內建頻道。在某些情況下,您可能希望建立一個自訂頻道,該頻道僅僅是一個特定 Monolog 處理器的實例,該處理器沒有對應的 Laravel 日誌驅動程式。這些頻道可以使用 monolog 驅動程式輕鬆建立。

當使用 monolog 驅動程式時,handler 設定選項用於指定將要實例化的處理器。或者,可以使用 with 設定選項指定處理器需要的任何建構函式參數

1'logentries' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\SyslogUdpHandler::class,
4 'with' => [
5 'host' => 'my.logentries.internal.datahubhost.company.com',
6 'port' => '10000',
7 ],
8],

Monolog 格式器

當使用 monolog 驅動程式時,Monolog LineFormatter 將用作預設格式器。但是,您可以使用 formatterformatter_with 設定選項來自訂傳遞給處理器的格式器類型

1'browser' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\BrowserConsoleHandler::class,
4 'formatter' => Monolog\Formatter\HtmlFormatter::class,
5 'formatter_with' => [
6 'dateFormat' => 'Y-m-d',
7 ],
8],

如果您使用的 Monolog 處理器能夠提供自己的格式器,則可以將 formatter 設定選項的值設定為 default

1'newrelic' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\NewRelicHandler::class,
4 'formatter' => 'default',
5],

Monolog 處理器

Monolog 還可以在記錄訊息之前處理訊息。您可以建立自己的處理器或使用 Monolog 提供的現有處理器

如果您想要自訂 monolog 驅動程式的處理器,請將 processors 設定值新增至頻道的設定中

1'memory' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\StreamHandler::class,
4 'with' => [
5 'stream' => 'php://stderr',
6 ],
7 'processors' => [
8 // Simple syntax...
9 Monolog\Processor\MemoryUsageProcessor::class,
10 
11 // With options...
12 [
13 'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
14 'with' => ['removeUsedContextFields' => true],
15 ],
16 ],
17],

透過工廠建立自訂頻道

如果您想要定義一個完全自訂的頻道,在其中您可以完全控制 Monolog 的實例化和設定,則可以在 config/logging.php 設定檔中指定 custom 驅動程式類型。您的設定應包含一個 via 選項,其中包含將被調用以建立 Monolog 實例的工廠類別的名稱

1'channels' => [
2 'example-custom-channel' => [
3 'driver' => 'custom',
4 'via' => App\Logging\CreateCustomLogger::class,
5 ],
6],

設定 custom 驅動程式頻道後,您就可以定義將建立 Monolog 實例的類別。此類別只需要一個 __invoke 方法,該方法應傳回 Monolog 記錄器實例。該方法將接收頻道設定陣列作為其唯一參數

1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Logger;
6 
7class CreateCustomLogger
8{
9 /**
10 * Create a custom Monolog instance.
11 */
12 public function __invoke(array $config): Logger
13 {
14 return new Logger(/* ... */);
15 }
16}

使用 Pail 追蹤日誌訊息

通常,您可能需要即時追蹤應用程式的日誌。例如,在偵錯問題或監控應用程式日誌中特定類型的錯誤時。

Laravel Pail 是一個套件,可讓您直接從命令列輕鬆深入研究 Laravel 應用程式的日誌檔案。與標準 tail 命令不同,Pail 旨在與任何日誌驅動程式(包括 Sentry 或 Flare)一起使用。此外,Pail 提供了一組有用的篩選器,可幫助您快速找到所需的內容。

安裝

Laravel Pail 需要 PHP 8.2+PCNTL 擴充功能。

若要開始使用,請使用 Composer 套件管理器將 Pail 安裝到您的專案中

1composer require laravel/pail

用法

若要開始追蹤日誌,請執行 pail 命令

1php artisan pail

若要增加輸出的詳細程度並避免截斷 (...),請使用 -v 選項

1php artisan pail -v

若要獲得最大詳細程度並顯示例外堆疊追蹤,請使用 -vv 選項

1php artisan pail -vv

若要停止追蹤日誌,請隨時按下 Ctrl+C

過濾日誌

--filter

您可以使用 --filter 選項依其類型、檔案、訊息和堆疊追蹤內容來篩選日誌

1php artisan pail --filter="QueryException"

--message

若要僅依訊息篩選日誌,可以使用 --message 選項

1php artisan pail --message="User created"

--level

--level 選項可用於依其日誌級別篩選日誌

1php artisan pail --level=error

--user

若要僅顯示在給定使用者通過身份驗證時寫入的日誌,您可以將使用者的 ID 提供給 --user 選項

1php artisan pail --user=1