ロギング
イントロダクション
アプリケーション内で何が起こっているかをより深く知るために、Laravelは堅牢なロギングサービスを提供しています。これにより、ファイル、システムエラーログ、さらにはSlackにメッセージをログとして記録し、チーム全体に通知できます。
Laravelのロギングは「チャネル」に基づいています。各チャネルはログ情報を書き込む特定の方法を表します。例えば、singleチャネルはログファイルを単一のログファイルに書き込み、slackチャネルはログメッセージをSlackに送信します。ログメッセージは、その重要度に基づいて複数のチャネルに書き込まれる場合があります。
内部的に、LaravelはMonologライブラリを利用しています。このライブラリは、さまざまな強力なログハンドラをサポートしています。Laravelはこれらのハンドラの設定を簡単に行えるようにしており、アプリケーションのログ処理をカスタマイズするために、それらを組み合わせることができます。
設定
アプリケーションのロギング動作を制御するすべての設定オプションは、config/logging.php設定ファイルに格納されています。このファイルでアプリケーションのログチャネルを設定できるため、利用可能な各チャネルとそのオプションを確認してください。以下で、いくつかの一般的なオプションについて説明します。
デフォルトで、Laravelはメッセージをログに記録する際にstackチャネルを使用します。stackチャネルは、複数のログチャネルを1つのチャネルに集約するために使用します。スタックの構築に関する詳細は、以下のドキュメントを確認してください。
利用可能なチャネルドライバ
各ログチャネルは「ドライバ」によって動作します。ドライバは、ログメッセージが実際にどのように、どこに記録されるかを決定します。すべての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ドライバ。 |
monologおよびcustomドライバの詳細については、高度なチャネルカスタマイズのドキュメントを確認してください。
チャネル名の設定
デフォルトで、Monologは現在の環境(productionやlocalなど)に一致する「チャネル名」でインスタンス化されます。この値を変更するには、チャネルの設定にnameオプションを追加します。
1'stack' => [2 'driver' => 'stack',3 'name' => 'channel-name',4 'channels' => ['single', 'slack'],5],
チャネルの前提条件
SingleチャネルとDailyチャネルの設定
singleチャネルとdailyチャネルには、bubble、permission、lockingの3つのオプション設定があります。
| 名前 | 説明 | デフォルト |
|---|---|---|
bubble |
メッセージが処理された後、他のチャネルにバブルアップ(伝播)するかどうかを示します。 | true |
locking |
ログファイルに書き込む前にロックを試みます。 | false |
permission |
ログファイルのパーミッション。 | 0644 |
さらに、dailyチャネルの保持ポリシーは、LOG_DAILY_DAYS環境変数またはdays設定オプションで設定できます。
| 名前 | 説明 | デフォルト |
|---|---|---|
days |
日次ログファイルを保持する日数。 | 14 |
Papertrailチャネルの設定
papertrailチャネルには、hostとportの設定オプションが必要です。これらはPAPERTRAIL_URLとPAPERTRAIL_PORT環境変数で定義できます。これらの値はPapertrailから取得できます。
Slackチャネルの設定
slackチャネルにはurl設定オプションが必要です。この値はLOG_SLACK_WEBHOOK_URL環境変数で定義できます。このURLは、Slackチーム用に設定したIncoming 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つのログチャネルにまとめて利便性を高めることができます。ログスタックの使用方法を説明するために、本番アプリケーションで見られる可能性のある設定例を見てみましょう。
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オプションを介してsyslogとslackの2つの他のチャネルを集約していることに注目してください。したがって、メッセージをログに記録すると、これら両方のチャネルがメッセージをログに記録する機会を得ます。ただし、後述するように、これらのチャネルが実際にメッセージをログに記録するかどうかは、メッセージの重要度/「レベル」によって決定される場合があります。
ログレベル
上記の例で、syslogとslackチャネルの設定にlevel設定オプションが存在することに注意してください。このオプションは、チャネルによってログに記録されるためにメッセージが必要とする最小「レベル」を決定します。Laravelのロギングサービスを支えるMonologは、RFC 5424仕様で定義されているすべてのログレベルを提供しています。重要度の高い順に、これらのログレベルは次のとおりです: emergency、alert、critical、error、warning、notice、info、debug。
では、debugメソッドを使用してメッセージをログに記録するとします。
1Log::debug('An informational message.');
この設定では、syslogチャネルはメッセージをシステムログに書き込みます。ただし、エラーメッセージはcritical以上ではないため、Slackには送信されません。しかし、emergencyメッセージをログに記録すると、emergencyレベルは両方のチャネルの最小レベルのしきい値を超えているため、システムログとSlackの両方に送信されます。
1Log::emergency('The system is down!');
ログメッセージの書き込み
Logファサードを使用して、ログに情報を書き込むことができます。前述のように、ロガーはRFC 5424仕様で定義されている8つのログレベルを提供しています: emergency、alert、critical、error、warning、notice、info、debug。
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 Controller11{12 /**13 * Show the profile for the given user.14 */15 public function show(string $id): View16 {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ファサードの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 AssignRequestId12{13 /**14 * Handle an incoming request.15 *16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next17 */18 public function handle(Request $request, Closure $next): Response19 {20 $requestId = (string) Str::uuid();21 22 Log::withContext([23 'request-id' => $requestId24 ]);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 AssignRequestId12{13 /**14 * Handle an incoming request.15 *16 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next17 */18 public function handle(Request $request, Closure $next): Response19 {20 $requestId = (string) Str::uuid();21 22 Log::shareContext([23 'request-id' => $requestId24 ]);25 26 // ...27 }28}
キュー投入されたジョブの処理中にログコンテキストを共有する必要がある場合は、ジョブミドルウェアを利用できます。
特定のチャネルへの書き込み
アプリケーションのデフォルトチャネル以外のチャネルにメッセージをログとして記録したい場合があります。Logファサードのchannelメソッドを使用して、設定ファイルで定義されている任意のチャネルを取得し、ログを記録できます。
1use Illuminate\Support\Facades\Log;2 3Log::channel('slack')->info('Something happened!');
複数のチャネルで構成されるオンデマンドのロギングスタックを作成したい場合は、stackメソッドを使用できます。
1Log::stack(['single', 'slack'])->info('Something happened!');
オンデマンドチャネル
アプリケーションのlogging設定ファイルにその設定が存在しなくても、実行時に設定を提供することでオンデマンドチャネルを作成することも可能です。これを実現するには、Logファサードの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インスタンスが作成された後にそれをカスタマイズ(または「タップ」)する機会を持つべきクラスのリストを含める必要があります。これらのクラスを配置する決まった場所はないため、アプリケーション内にこれらのクラスを格納するためのディレクトリを自由に作成できます。
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インスタンスをカスタマイズするクラスを定義する準備が整いました。このクラスには、Illuminate\Log\Loggerインスタンスを受け取る__invokeという1つのメソッドだけが必要です。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): void14 {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はそれぞれのハンドラに対応する組み込みチャネルを含んでいるわけではありません。場合によっては、対応するLaravelログドライバを持たない特定のMonologハンドラのインスタンスであるカスタムチャネルを作成したいことがあるでしょう。これらのチャネルは、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がデフォルトのフォーマッタとして使用されます。ただし、formatterおよびformatter_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ドライバタイプを指定できます。設定には、Monologインスタンスを作成するために呼び出されるファクトリクラスの名前を含むviaオプションを含める必要があります。
1'channels' => [2 'example-custom-channel' => [3 'driver' => 'custom',4 'via' => App\Logging\CreateCustomLogger::class,5 ],6],
customドライバチャネルを設定したら、Monologインスタンスを作成するクラスを定義する準備が整いました。このクラスには、Monologロガーインスタンスを返す必要がある__invokeメソッドが1つだけ必要です。このメソッドは、唯一の引数としてチャネルの設定配列を受け取ります。
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): Logger13 {14 return new Logger(/* ... */);15 }16}
Pailを使用したログメッセージの追跡
アプリケーションのログをリアルタイムで追跡する必要がしばしばあります。例えば、問題をデバッグしているときや、特定のエラータイプについてアプリケーションのログを監視しているときなどです。
Laravel Pailは、コマンドラインから直接Laravelアプリケーションのログファイルに簡単にアクセスできるパッケージです。標準のtailコマンドとは異なり、PailはSentryやFlareを含むあらゆるログドライバで動作するように設計されています。さらに、Pailは探しているものを素早く見つけるのに役立つ便利なフィルタのセットを提供します。
インストール
始めるには、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
特定のユーザーが認証されている間に書き込まれたログのみを表示するには、--userオプションにユーザーのIDを指定します。
1php artisan pail --user=1