跳到主要內容

日誌記錄

📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本

日誌記錄

簡介

為了幫助您了解更多關於應用程式內部發生的事情,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 驅動程式。

[!NOTE] 查看進階管道自訂文件以了解更多關於 monologcustom 驅動程式的資訊。

配置管道名稱

預設情況下,Monolog 使用與當前環境匹配的「管道名稱」實例化,例如 productionlocal。要更改此值,您可以在管道的配置中新增 name 選項:

'stack' => [
    'driver' => 'stack',
    'name' => 'channel-name',
    'channels' => ['single', 'slack'],
],

管道先決條件

配置 Single 和 Daily 管道

singledaily 管道有三個可選的配置選項:bubblepermissionlocking

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

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

名稱描述預設值
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 日誌管道:

'deprecations' => [
    'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
    'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],

'channels' => [
    // ...
]

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

'channels' => [
    'deprecations' => [
        'driver' => 'single',
        'path' => storage_path('logs/php-deprecation-warnings.log'),
    ],
],

建構日誌堆疊

如前所述,stack 驅動程式允許您為了方便將多個管道組合成單一日誌管道。為了說明如何使用日誌堆疊,讓我們看看您可能在生產應用程式中看到的範例配置:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['syslog', 'slack'], // [tl! add]
        'ignore_exceptions' => false,
    ],

    'syslog' => [
        'driver' => 'syslog',
        'level' => env('LOG_LEVEL', 'debug'),
        'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
        'replace_placeholders' => true,
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
        'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
        'level' => env('LOG_LEVEL', 'critical'),
        'replace_placeholders' => true,
    ],
],

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

日誌等級

注意上面範例中 syslogslack 管道配置中存在的 level 配置選項。此選項決定了訊息被管道記錄所需的最低「等級」。驅動 Laravel 日誌記錄服務的 Monolog 提供了 RFC 5424 規範中定義的所有日誌等級。按嚴重性降序排列,這些日誌等級為:emergencyalertcriticalerrorwarningnoticeinfodebug

假設我們使用 debug 方法記錄一條訊息:

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

根據我們的配置,syslog 管道將把訊息寫入系統日誌;但是,由於錯誤訊息不是 critical 或以上,它不會被發送到 Slack。但是,如果我們記錄一條 emergency 訊息,它將被發送到系統日誌和 Slack,因為 emergency 等級高於兩個管道的最低等級閾值:

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

寫入日誌訊息

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

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);

您可以呼叫這些方法中的任何一個來記錄相應等級的訊息。預設情況下,訊息將寫入由 logging 配置檔配置的預設日誌管道:

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     */
    public function show(string $id): View
    {
        Log::info('Showing the user profile for user: {id}', ['id' => $id]);

        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}

上下文資訊

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

use Illuminate\Support\Facades\Log;

Log::info('User {id} failed to login.', ['id' => $user->id]);

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

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;

class AssignRequestId
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $requestId = (string) Str::uuid();

        Log::withContext([
            'request-id' => $requestId
        ]);

        $response = $next($request);

        $response->headers->set('Request-Id', $requestId);

        return $response;
    }
}

如果您想在所有日誌管道之間共享上下文資訊,可以呼叫 Log::shareContext() 方法。此方法將向所有已建立的管道和隨後建立的任何管道提供上下文資訊:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;

class AssignRequestId
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $requestId = (string) Str::uuid();

        Log::shareContext([
            'request-id' => $requestId
        ]);

        // ...
    }
}

[!NOTE] 如果您需要在處理佇列任務時共享日誌上下文,可以使用任務中介層

寫入特定管道

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

use Illuminate\Support\Facades\Log;

Log::channel('slack')->info('Something happened!');

如果您想建立一個由多個管道組成的按需日誌堆疊,可以使用 stack 方法:

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

按需管道

也可以透過在執行時提供配置來建立按需管道,而該配置無需存在於應用程式的 logging 配置檔中。要實現此目的,您可以向 Log facade 的 build 方法傳遞一個配置陣列:

use Illuminate\Support\Facades\Log;

Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
])->info('Something happened!');

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

use Illuminate\Support\Facades\Log;

$channel = Log::build([
  'driver' => 'single',
  'path' => storage_path('logs/custom.log'),
]);

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

Monolog 管道自訂

為管道自訂 Monolog

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

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

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

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

<?php

namespace App\Logging;

use Illuminate\Log\Logger;
use Monolog\Formatter\LineFormatter;

class CustomizeFormatter
{
    /**
     * Customize the given logger instance.
     */
    public function __invoke(Logger $logger): void
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(new LineFormatter(
                '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
            ));
        }
    }
}

[!NOTE] 所有「tap」類別都由服務容器解析,因此它們需要的任何構造函數依賴項都將自動注入。

建立 Monolog 處理器管道

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

使用 monolog 驅動程式時,handler 配置選項用於指定將實例化哪個處理器。或者,可以使用 handler_with 配置選項指定處理器需要的任何構造函數參數:

'logentries' => [
    'driver'  => 'monolog',
    'handler' => Monolog\Handler\SyslogUdpHandler::class,
    'handler_with' => [
        'host' => 'my.logentries.internal.datahubhost.company.com',
        'port' => '10000',
    ],
],

Monolog 格式化器

使用 monolog 驅動程式時,Monolog LineFormatter 將用作預設格式化器。但是,您可以使用 formatterformatter_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 處理器

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

如果您想自訂 monolog 驅動程式的處理器,請在管道的配置中新增 processors 配置值:

'memory' => [
    'driver' => 'monolog',
    'handler' => Monolog\Handler\StreamHandler::class,
    'handler_with' => [
        'stream' => 'php://stderr',
    ],
    'processors' => [
        // Simple syntax...
        Monolog\Processor\MemoryUsageProcessor::class,

        // With options...
        [
            'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
            'with' => ['removeUsedContextFields' => true],
        ],
    ],
],

透過工廠建立自訂管道

如果您想定義一個完全自訂的管道,您可以在其中完全控制 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.
     */
    public function __invoke(array $config): Logger
    {
        return new Logger(/* ... */);
    }
}

使用 Pail 追蹤日誌訊息

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

Laravel Pail 是一個套件,允許您直接從命令列輕鬆深入了解 Laravel 應用程式的日誌檔案。與標準 tail 命令不同,Pail 設計為與任何日誌驅動程式配合使用,包括 Sentry 或 Flare。此外,Pail 提供了一組有用的篩選器來幫助您快速找到所需內容。

安裝

[!WARNING] Laravel Pail 需要 PCNTL PHP 擴充功能。

首先,使用 Composer 套件管理器將 Pail 安裝到您的專案中:

composer require --dev laravel/pail

用法

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

php artisan pail

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

php artisan pail -v

要獲得最大的詳細程度並顯示異常堆疊追蹤,請使用 -vv 選項:

php artisan pail -vv

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

篩選日誌

--filter

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

php artisan pail --filter="QueryException"

--message

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

php artisan pail --message="User created"

--level

--level 選項可用於按日誌等級篩選日誌:

php artisan pail --level=error

--user

要僅顯示在給定使用者已認證時寫入的日誌,可以將使用者的 ID 傳遞給 --user 選項:

php artisan pail --user=1