Laravelのログ出力ディレクトリを動的に変更する

Laravelのログ出力でdailyチャネルを利用した場合、storage/logs/laravel-2024-05-25.logといったファイルに出力されます。
.envLOG_DAILY_DAYSで1以上を設定し保存数を制限する場合は問題にならないのですが、0を設定し無制限にログファイルを残しておく場合にファイルが増えてしまいます。

ログファイルの保存ディレクトリ、ファイル名をある程度制御できるようにします。

Laravelのバージョンは11で試しています。

簡単だけどダメな方法

一見正しく動作しそうな方法を紹介します。
開発中は期待通りに動作しますが、本番環境にデプロイした際に期待通りにならなくなります。

config/logging.phppathdate('Y-m') を差し込み、月ごとのディレクトリに出力をします。

config/logging.php
'channels' => [

    'single' => [
        'driver' => 'single',
        'path' => storage_path('logs/' . date('Y-m') . '/laravel.log'),
        'level' => env('LOG_LEVEL', 'debug'),
        'replace_placeholders' => true,
    ],
]

何がダメなのか

php artisan config:cache コマンドによる設定キャッシュを行った際に、path の値がコマンド実行時点の値でキャッシュされてしまうので、出力先が固定されてしまいます。

タスクスケジュールで定期的に `php artisan config:cache` をじkk

独自チャネルを作成する

出力先のディレクトリを動的に変更するには、独自チャネルを生成し、Monologのインスタンス生成時にpathを設定する必要があります。

下記のページを参考に独自チャネルを追加します。

CreateCustomLogger クラスの作成

Monologのインスタンスを生成するクラスを作成します。

app/Logging/CreateCustomLogger.php
<?php

namespace App\Logging;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Processor\PsrLogMessageProcessor;
use Monolog\Formatter\LineFormatter;

class CreateCustomLogger
{
    /**
     * Create a custom Monolog instance.
     */
    public function __invoke(array $config): Logger
    {
        $path = str_replace(
            ['{year}', '{month}', '{day}'],
            [date('Y'), date('m'), date('d')],
            $config['path']
        );

        $handler = new StreamHandler(
            $path,
            $config['level'] ?? 'debug',
            $config['bubble'] ?? true,
            $config['permission'] ?? null,
            $config['locking'] ?? false
        );
        $handler->setFormatter(new LineFormatter(null, 'Y-m-d H:i:s', true, true, true));

        return new Logger('log', [$handler], [new PsrLogMessageProcessor()]);
    }
}

独自チャネルの設定を追加

config/logging.php に独自チャネルの設定を追加します。

config/logging.php
'channels' => [
    'example-custom-channel' => [
        'driver' => 'custom',
        'via' => App\Logging\CreateCustomLogger::class,
        'path' => storage_path('logs/{year}-{month}/laravel-{year}-{month}-{day}.log'),
        'level' => env('LOG_LEVEL', 'debug'),
    ],
],

解説と注意点

config/logging,phppath に含まれる{year}{month}{day}CreateCustomLogger クラスで変換することで動的にログファイルの出力先のディレクトリを動的に変更しています。

Monologのインスタンスが生成された時点で出力先のディレクトリが決定するため、処理が日をまたいだ場合でも、Monologのインスタンスで持っているpathの出力先に出力されます。