#はじめに
アプリケーション内で何が起きているかを把握しやすくするために、Laravelはファイルやシステムエラーログ、さらにはSlackへメッセージを送信してチーム全体に通知できる強力なロギングサービスを提供しています。
Laravelのロギングは「チャネル」をベースにしています。各チャネルはログ情報を書き込む特定の方法を表します。例えば、singleチャネルは単一のログファイルに書き込み、slackチャネルはSlackにログメッセージを送信します。ログメッセージは重要度に応じて複数のチャネルに書き込むことも可能です。
内部的には、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設定ファイルにすでにエントリがあるため、内容を確認しておくとよいでしょう:
| Name | 説明 |
|---|---|
custom |
指定したファクトリーを呼び出してチャネルを作成するドライバー |
daily |
日単位でローテーションするRotatingFileHandlerベースのMonologドライバー |
errorlog |
ErrorLogHandlerベースのMonologドライバー |
monolog |
任意のMonologハンドラーを使えるMonologファクトリードライバー |
papertrail |
SyslogUdpHandlerベースのMonologドライバー |
single |
単一ファイルまたはパスベースのロガーチャネル(StreamHandler) |
slack |
SlackWebhookHandlerベースのMonologドライバー |
stack |
複数チャネルをまとめるためのラッパー |
syslog |
SyslogHandlerベースのMonologドライバー |
monologとcustomドライバーについて詳しくは高度なチャネルカスタマイズのドキュメントを参照してください。
#チャネルの前提条件
#Single と Daily チャンネルの設定
singleとdailyチャネルには、bubble、permission、lockingの3つのオプションがあります(いずれも任意)。
| Name | 説明 | デフォルト |
|---|---|---|
bubble |
メッセージ処理後に他のチャネルへバブルアップするかどうか | true |
locking |
書き込み前にログファイルをロックするかどうか | false |
permission |
ログファイルのパーミッション | 0644 |
さらに、dailyチャネルの保持期間はdaysオプションで設定できます:
| Name | 説明 | デフォルト |
|---|---|---|
days |
日単位のログファイル保持期間 | 7 |
#Papertrail チャネルの設定
papertrailチャネルはhostとportの設定が必要です。これらの値はPapertrailから取得できます。
#Slack チャンネルの設定
slackチャネルはurlオプションが必要です。このURLはSlackチーム用に設定したincoming webhookのURLと一致させてください。
デフォルトでは、Slackはcriticalレベル以上のログのみ受信しますが、config/logging.phpのSlackチャネル設定内のlevelオプションを変更することで調整可能です。
#非推奨警告のロギング
PHP、Laravel、その他のライブラリは、機能の非推奨や将来のバージョンでの削除を通知することがあります。これらの非推奨警告をログに記録したい場合は、config/logging.phpで好みのdeprecationsログチャネルを指定できます:
'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'channels' => [
...
]
または、deprecationsという名前のログチャネルを定義できます。この名前のチャネルが存在する場合、非推奨警告のログに常に使用されます:
'channels' => [
'deprecations' => [
'driver' => 'single',
'path' => storage_path('logs/php-deprecation-warnings.log'),
],
],
#ログスタックの構築
前述の通り、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の2つのチャネルをまとめています。つまり、ログメッセージが記録される際、両方のチャネルがメッセージを処理する機会を持ちます。ただし、以下で説明するように、実際にログが記録されるかはメッセージの重要度(レベル)によって決まります。
#ログレベル
上記の例のsyslogとslackチャネル設定にあるlevelオプションに注目してください。このオプションは、そのチャネルがログを記録するために必要な最低レベルを指定します。Laravelのロギングを支えるMonologは、RFC 5424仕様で定義されたすべてのログレベルをサポートしています。重要度の高い順に、emergency、alert、critical、error、warning、notice、info、debugです。
例えば、debugメソッドでメッセージをログに記録するとします:
Log::debug('An informational message.');
この設定では、syslogチャネルはシステムログにメッセージを書き込みますが、エラーメッセージがcritical以上でないためSlackには送信されません。一方、emergencyレベルのメッセージをログに記録すると、両方のチャネルに送信されます。なぜならemergencyは両チャネルの最低レベルを超えているからです:
Log::emergency('The system is down!');
#ログメッセージの書き込み
Logファサードを使ってログに情報を書き込めます。前述の通り、ロガーはRFC 5424仕様で定義された8つのログレベルを提供しています: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);
これらのメソッドのいずれかを呼び出して、対応するレベルのメッセージをログに記録できます。デフォルトでは、メッセージはlogging設定ファイルで設定されたデフォルトのログチャネルに書き込まれます:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* 指定されたユーザーのプロフィールを表示します。
*/
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ファサードの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
{
/**
* 受信したリクエストを処理します。
*
* @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
{
/**
* 受信したリクエストを処理します。
*
* @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
]);
// ...
}
}
キュー処理中にログコンテキストを共有する必要がある場合は、ジョブミドルウェアを利用できます。
#特定のチャンネルへの書き込み
アプリケーションのデフォルトチャンネル以外にログメッセージを記録したい場合があります。Logファサードのchannelメソッドを使うと、設定ファイルで定義された任意のチャンネルにログを記録できます。
use Illuminate\Support\Facades\Log;
Log::channel('slack')->info('Something happened!');
複数のチャンネルからなるオンデマンドのログスタックを作成したい場合は、stackメソッドを使えます。
Log::stack(['single', 'slack'])->info('Something happened!');
#オンデマンドチャンネル
アプリケーションのlogging設定ファイルに存在しない設定を実行時に渡してオンデマンドチャンネルを作成することも可能です。これには、設定配列をLogファサードの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インスタンス生成後にカスタマイズ("tap")できるクラスのリストを含めます。これらのクラスの配置場所に決まりはないため、アプリケーション内に自由にディレクトリを作成して管理できます。
'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 Illuminate\Log\Logger;
use Monolog\Formatter\LineFormatter;
class CustomizeFormatter
{
/**
* ロガーインスタンスをカスタマイズします。
*/
public function __invoke(Logger $logger): void
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(new LineFormatter(
'[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
));
}
}
}
すべての"tap"クラスはサービスコンテナによって解決されるため、必要なコンストラクタ依存は自動的に注入されます。
#Monolog ハンドラーチャンネルの作成
Monologには多くの利用可能なハンドラーがあり、Laravelはそれぞれに対応する組み込みチャンネルを持ちません。特定の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 プロセッサ
Monologはログ記録前にメッセージを処理できます。独自のプロセッサを作成するか、Monologが提供する既存のプロセッサを利用できます。
monologドライバーのプロセッサをカスタマイズしたい場合は、チャンネル設定にprocessorsを追加します。
'memory' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'with' => [
'stream' => 'php://stderr',
],
'processors' => [
// シンプルな構文...
Monolog\Processor\MemoryUsageProcessor::class,
// オプション付き...
[
'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
'with' => ['removeUsedContextFields' => true],
],
],
],
#ファクトリーを使ったカスタムチャンネルの作成
Monologのインスタンス化と設定を完全に制御できるカスタムチャンネルを定義したい場合は、config/logging.phpの設定でcustomドライバータイプを指定します。設定にはMonologインスタンスを生成するファクトリークラス名を含むviaオプションを含めます。
'channels' => [
'example-custom-channel' => [
'driver' => 'custom',
'via' => App\Logging\CreateCustomLogger::class,
],
],
customドライバーチャンネルを設定したら、Monologインスタンスを生成するクラスを定義します。このクラスは__invokeメソッドだけを持ち、Monologロガーインスタンスを返します。メソッドはチャンネル設定配列を引数に受け取ります。
<?php
namespace App\Logging;
use Monolog\Logger;
class CreateCustomLogger
{
/**
* カスタムMonologインスタンスを作成します。
*/
public function __invoke(array $config): Logger
{
return new Logger(/* ... */);
}
}
#Pailを使ったログメッセージのテイル
アプリケーションのログをリアルタイムでテイルしたいことがよくあります。例えば、問題のデバッグや特定のエラーを監視する場合です。
Laravel Pailは、コマンドラインからLaravelアプリケーションのログファイルに簡単にアクセスできるパッケージです。標準のtailコマンドとは異なり、PailはSentryやFlareなど任意のログドライバーに対応しています。さらに、目的のログを素早く見つけるための便利なフィルターも提供します。
#インストール
はじめに、Composerパッケージマネージャーを使ってPailをプロジェクトにインストールします。
composer require 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