Laravelのファイルログ書込時のロック設定
こんにちは、ナカエです。
今回はLaravelのログについての記事です。
ファイルログへの同時書き込み
ある程度アクセス数の多い環境でLaravelのファイルログ('daily'または'single'チャンネルドライバ)を利用しているとログへの書き込み内容の一部が消失することがあります。 これはLaravelがデフォルトではログファイルへの書き込み時にロックをかけていないためです。
ロックを設定する
Laravel >= 5.6
Laravel 5.6から、ログの設定ファイルがconfig/app.phpからconfig/logging.phpへと分離されて設定可能内容が増えています。 single, dailyドライバでbubble, permission, lockingが利用できるようになりました。
lockingをtrueに設定すればロックが有効になります。
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 7,
'locking' => true, // ロック設定
],
],
];
Laravel < 5.6
Laravel5.5以前はこの設定項目がありませんでした。 Laravelが利用しているMonologにはファイルロックの設定がありますが、Laravelのconfigだけでそれを利用することはできません。 ServiceProviderとReflectionで無理やりLogの設定を修正します。
class FileLogLockingServiceProvider extends ServiceProvider
{
/**
* @inheritdoc
*/
public function register()
{
$this->app->afterResolving('log', function (Log $writer) {
if (!$writer instanceof Writer) {
return;
}
$handlers = $writer->getMonolog()->getHandlers();
$refClass = new \ReflectionClass(StreamHandler::class);
$refProp = $refClass->getProperty('useLocking');
$refProp->setAccessible(true);
foreach ($handlers as $handler) {
if ($handler instanceof StreamHandler) {
$refProp->setValue($handler, true);
}
}
});
}
}
ここまでやるくらいならMonologをそのまま使った方が楽なのではという気さえしますが、Laravel5.5で運用中のプロジェクトで必要だったので大きい変更は断念しました。
まとめ
- Laravel >= 5.6ではconfigだけでロックの設定ができる
- Laravel < 5.6 では小細工が必要