

Laravel9-10を使用しています。

Logファサードで普段ログを出力しています。

普段日付単位でフォルダを作成してその中にログファイルを作って運用しています。

質問は、とあるログを見返すことが多い処理があるためにファイル名を動的に変更したいです。  
具体的にはその処理の場合だけログをログインしているユーザー毎に分けたファイル名にしたかった感じです。

調べて実装済みなのがカスタムチャネルを作成する方法でした。  
これで一応はやりたいことは実現出来ているのですが・・・

・いちいちconfig/logging.phpファイルへチャネルを追加していかないといけない。  
・いちいちログを取る際に「Log::info」ではなくて「Log::channel(***)->info」のようにchennelを追加で書かないと行けないので冗長になるのが嫌

この２点を解消したいです。

一応思ったのはメソッドの開始時にLogファサードのconfig部分を書き換えたらそのメソッドないでは全て変更できるかも？と思いまして

config()->set('logging.channels.single.path', storage_path("logs/" .
date("Y/m/d") . "/filename.log"));

の感じでやってみた所、Laravelで読まれているconfigは書き換わりますが実際の出力先は元のままです。  
おそらくLogファサードの初期化が終わった後の書き換えになっているから有効にならないのか？と思いました。

なにか方法等ございませんでしょうか？

よろしくお願いいたします。


CC BY-SA 4.0
2023-08-04T19:02:32.537
95849
2023-08-18T15:35:13.460
3814
question
php laravel
LaravelでLogの出力先を動的に変更したい


Linux でしか使えない方法でLaravelのみで解決したい趣旨とは異なりますのであくまで参考ですが。

加工したいログのチャンネルドライバを syslog にする。

別プロセスでデーモンとして常駐させるプロセスを立ち上げる。

stream_socket_server('unix:///dev/log') でソケット作ってlissenする。  
stream_select でポーリング。  
ログが出力されたら stream_socket_accept して fread で送信文が受信できるので、  
preg_match などでパターンマッチして該当結果で出力先を変更する。

みたいな方法があります。


CC BY-SA 4.0
2023-08-05T07:24:25.733
95853
2023-08-05T07:24:25.733
59393
95849
answer


Log ファサードだけだとそれこそかなり泥臭いことになってやりづらそうです．  
PHP で解決したいのであれば，いっそのこと Logger を作ってしまったほうが早いような気がします．

あんまりお行儀は良くないですが，ちょっと考えてみたので共有します．  
本来であれば Psr の LoggerInterface を実装するのが良いですが，指しあたってとりあえずこうやったら実現できる，を示します．

App\Logging を作り UserLogger::class を実装します．

[code]

    <?php
    
    declare(strict_types=1);
    
    namespace App\Logging;
    
    use Carbon\Carbon;
    use Illuminate\Support\Facades\Auth;
    use Monolog\Handler\StreamHandler;
    use Monolog\Level;
    use Monolog\Logger;
    
    class UserLogger
    {
        public static function log(Level $level, string $message, array $context = []): void
        {
            // ログインユーザーを取ってくる
            $userId = Auth::id();
    
            // ユーザーを使ってファイル名を作る
            $filename = sprintf("user_%s", $userId === null ? 'guest' : (string)$userId);
    
            // ログを置く path を決める
            $path = sprintf("%s/%s/%s.log", storage_path('/logs'), Carbon::now()->format('Y_m_d'), $filename);
    
            // Monolog のハンドラ作成
            $handler = new StreamHandler(
                stream: $path,
                level: $level
            );
    
            // Monolog の Logger
            $logger = new Logger(
                name: 'per_user',
                handlers: [$handler],
            );
    
            // 書き込み
            $logger->log($level, $message, $context);
        }
    
        public static function info(string $message, array $context = []): void
        {
            self::log(Level::Info, $message, $context);
        }
    
        public static function error(string $message, array $context = []): void
        {
            self::log(Level::Error, $message, $context);
        }
    }
    
    
[/code]

Monolog のラッパー的な感じですね．  
とりあえず info と error だけ作ってあります．  
ここでは， Auth ファサードを直接中で呼んでいますが，引数で取ったほうがよりよいでしょう．

試しに以下のような Console コマンドを作り，ログインユーザーの id や日付を変えてみて検証しました．

[code]

    public function handle()
    {
        Auth::shouldReceive('id')->andReturns(1);
        Carbon::setTestNow('2023-01-01');
    
        UserLogger::info('foo');
        UserLogger::info('bar', ['key' => 'value']);
        UserLogger::error('foo');
        UserLogger::error('bar', ['key' => 'value']);
    }
    
[/code]

[![生成されたログファイル群](https://i.stack.imgur.com/UUSMf.png)](https://i.stack.imgur.com/UUSMf.png)

[code]

    [2023-08-18T15:26:08.487349+00:00] per_user.INFO: foo [] []
    [2023-08-18T15:26:08.488610+00:00] per_user.INFO: bar {"key":"value"} []
    [2023-08-18T15:26:08.489011+00:00] per_user.ERROR: foo [] []
    [2023-08-18T15:26:08.489299+00:00] per_user.ERROR: bar {"key":"value"} []
    
[/code]

要件は満たせていそうです．

参考までに．


CC BY-SA 4.0
2023-08-18T15:35:13.460
95979
2023-08-18T15:35:13.460
58973
95849
answer