はじめに
こんにちは。今回は表題通り、Laravel5.8でのログの扱い方です。
わざわざバージョンを記載しているのは、2019年7月現在のLTSである5.5と5.6以降で大きな変更があったためです。本記事の内容は5.5以前には適用できない可能性がありますので、ご注意ください。
環境
- PHP 7.3.6
- Laravel 5.8.28
Monologについて
Laravelのログ機能はMonologのラッパーであり、これは5.8になっても同じです。変わったのはログの呼び出し方や設定方法のようです(私は5.8からLaravelを使い始めたので、5.5以前の書き方は把握していません…)。
なおMonolog自体はLaravelとは本来関係ないパッケージのため、Laravel公式のドキュメントよりはMonolog単体で調べた方が、まとまった情報を得ることができると思います。
コード上で設定する方法
では早速Laravelでの使い方を見ていきます。簡単かつMonologに不慣れな人でもパッと見で分かりやすいのは、コード上で全て設定してしまう方法です。
以下は「ユーザー登録時にLaravel標準とは別にログを出力したい」という意図のコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | use Monolog\Formatter\LineFormatter; use Monolog\Handler\RotatingFileHandler; use Monolog\Logger; protected function create(array $data) { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); $log_path = storage_path('logs/user-register.log'); $days = 90; $log_level = 'debug'; $bubble = true; $filePermission = 0777; $handler = new RotatingFileHandler($log_path, $days, $log_level, $bubble, $filePermission); $formatter = new LineFormatter(null, null, true, true); $handler->setFormatter($formatter); $logger = new Logger('user-register'); $logger->pushHandler($handler); $logger->info(sprintf('ユーザーID:%sが登録されました。', $user->id)); return $user; } |
- $log_path: ログのファイルパスです。アプリケーションのどこに設置してもいいのですが、ヘルパ関数の
storage_path()
を使ってstorage/logs
以下を指定するのが慣例です。 - $days: ログの保存日数です。この場合はログが90日間保存され、その後は古い順に削除されます。「0」を指定すると無期限で保存されます。
- $log_level: ログレベルはPHPのデファクトであるPSR-3に準拠しています。https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
- $bubble: 他のハンドラに処理続行させるかのフラグです。Laravelでは…というかMonologでは複数のログハンドラを同時に起動してログ出力ができます。イメージ的には、バリデーションでエラー検知時にバリデーションを継続させるのに近いと思います。
- $filePermission: これは読んで字の如く、ログファイルのパーミッションです。
chmod
と同じように指定できます。 - RotatingFileHandler: Monologが最初から用意しているログハンドラで、ログファイルを日毎にローテーションしてくれます。他にも標準出力にログを吐き出す
StreamHandler
、Slackに手軽にPOSTできるSlackHandler
など、様々なハンドラがあり、実体はvendor/monolog/monolog/src/Monolog/Handler
以下にあります。 - LineFormatter: ログ文面のフォーマットを指定します。何も指定しない場合は
const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
というフォーマットに沿って出力されます。
ControllerやServiceに書いてしまうのはあまりスマートではなく、コードの肥大化も招きます。trait化したり、設定値を
.env
や
config
に追い出すことで可読性を保てるのではないでしょうか。
上記のコードで
storage/logs/user-register-[YYYY-MM-DD].log
が生成され、以下のようなログが出力されます。
storage/logs/laravel-[YYYY-MM-DD].log
はLaravelデフォルトのログで、
config/logging.php
の設定に沿って出力されます(後述)。
config/logging.phpで設定する方法
5.6以降で追加された設定ファイルで、公式ドキュメントを見る限り、恐らくこちらが推奨の方法だと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 'default' => env('LOG_CHANNEL', 'stack'), (中略) 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['daily'], 'ignore_exceptions' => false, ], 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', 'days' => 14, ], (中略) 'user-register' => [ 'driver' => 'daily', 'level' => 'debug', 'path' => storage_path('logs/user-register.log'), 'days' => 90, ], ], |
stack, single, dailyはデフォルトの設定項目で、
user-register
が追記した項目です。
- driver: 前述のコードで
new Logger()
した時の引数に相当します。Monologでは複数のchannelを登録し、一度に複数の出力先にログを吐き出すことが可能です(このへんがまだ理解が浅く、正しい解釈なのか不安です…)。一番上のstack
だけは少し特殊で、複数のchannelを記述して一括登録することが可能です(channelsが配列になっているのはそのためのようです)。 - level: 前述の
$log_level
に相当します。 - path: 前述の
$log_path
に相当します。 - days: 前述の
$days
に相当します。
アプリケーションでは以下のように記述することで、最初のコードと同様に
storage/logs/user-register-[YYYY-MM-DD].log
にログが出力されます。コード量が目に見えて減りました!複雑な仕様でなければ、設定ファイルのへ記述で済ませてしまうのがベターだと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | use Illuminate\Support\Facades\Log; protected function create(array $data) { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); Log::setDefaultDriver('user-register'); Log::info(sprintf('ユーザーID:%sが登録されました。', $user->id)); return $user; } |
おまけ:ログレベルについて
上述のPSR-3で定義されているログレベルはあくまでも目安です。今回の例では「正常にユーザー登録が完了した」ときのログですのでやerrorやwarningではおかしいですが、noticeなのか、infoなのかは要件次第になるでしょう。
以下のページでログレベル一覧および一例が完結にまとまっていましたので、紹介いたします。
さいごに
「ログの出し分けをしたい」という要件から始まって、Laravelのログ出力について調査したのですが、一度理解してしまえば実に簡単です。豊富な出力先を選べるのもいいですね。