ログを日別に作成する

 Laravelでログファイルを日別に分けて出力する方法を紹介します。

 Laravelでは、ログファイルはデフォルトで`storage/logs`ディレクトリに`laravel.log`という名前で保存されます。

 ログを日別に分けることで、特に大規模なアプリケーションや、長期間にわたる運用を行う際に役立ちます。

 以下のように年月日でログ管理を行えるようになります。

 ログファイルが日付ごとに分けられていると、問題を特定しやすくなります。
 また、ログの量が膨大になると、一つの大きなファイルに全てを保存するよりも、日付ごとに分けて保存した方が管理しやすくなります。

 それでは実装方法を紹介しますね。

1.カスタムログチャネルを追加する

 サービスプロバイダで拡張しているチャネルを`config/logging.php``channels`に追加します。また、エラー時の出力先を追加したチャネルに変更します。
 ここでは`custom`というチャネル名で作成しています。

config/logging.php

コピー用↓

<?php

use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;

return [

  // 省略

  'channels' => [
    'stack' => [
      'driver' => 'stack',
      'channels' => ['custom'],
      'ignore_exceptions' => false,
    ],

   // 省略

   'custom' => [
     'driver' => 'custom',
     'via' => App\Providers\LoggingServiceProvider::class,
     'level' => 'debug',
     'name' => 'custom',
   ],
  ],

];

2.サービスプロバイダを作成する

 カスタムログ出力を作成するために、サービスプロバイダを作成します。
 `boot`メソッドでログ出力を拡張し、日付ごとに異なるファイルパスに保存するようにします。

 Laravelではサービスプロバイダを作成するコマンドが用意されています。
 今回は`LoggingServiceProvider `という名前で作成します。

Php artisan make:provider LoggingServiceProvider

App/Providers/ LoggingServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\File;
use Monolog\Logger as Monolog;
use Monolog\Handler\StreamHandler;

class LoggingServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        app(‘log’)->extend(‘custom’, function($app, $config){
            // ログファイルの出力先を変更します
            $logFilePath = storage_path(‘logs/’.now()->format(‘Y/m’).’/aravel_’.now()
->format(‘Ymd’).’.log’);
            $logFileDirectory = dirname($logFilePath);

            // ディレクトリが存在しない場合は作成します
            if(!File::exists($logFileDirectory)){
                File::makeDirectory($logFileDirectory, 0775, true, true);
            }

            // 識別名$config[‘name’]のチャネルを作成します
            $monolog = new Monolog($config[‘name’]);

// ハンドラーを設定します
// 第一引数でログを書き込むファイルパスを指定し、
// 第二引数でログレベルを指定しています
            $handler = new StreamHandler(
                $logFilePath,
                $this->level($config)
            );

            $monolog->pushHandler($handler);

            return new \Illuminate\Log\Logger($monolog, $app[‘events’]);
        });
    }

    /**
     * Parse the string level into a Monolog constant.
     *
     * @param  array  $config
     * @return int
     */

// ログレベルを指定する関数です
    protected function level(array $config)
    {
    // ログレベルが$config[‘level’]以上のときに
// $config[‘level’]のメッセージをログに書き込みます
        $level = $config[‘level’] ?? ‘debug’;

        return Monolog::toMonologLevel($level);
    }
}

3.サービスプロバイダを登録する

 作成したサービスプロバイダを`config/app.php``providers`に追加します。
 
ここに追加することでアプリケーションへのリクエスト時に自動でロードされるようになります。

config/app/php

コピー用↓

<?php

return [

    // 省略

    'providers' => [

        // 省略

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        App\Providers\LoggingServiceProvider::class,
    ],




    // 省略

];

 これでログファイルが年、月、日別にフォルダ分けされて作成されるようになります。
 しかし、このままではエラー内容しか出力されず、デフォルトの出力時にあるようなスタックトレースが保存されません。
 デフォルトの出力時と同じ内容が保存されるようにさらにカスタマイズしていきます。

4.カスタムフォーマッタを作成する

 ログ出力にスタックトレースを追加するためにカスタムフォーマッタを作成します。
 ここでは`app/Logging``CustomFormatter.php`という名前で作成しています。

app/Logging/ CustomFormatter.php

<?php

namespace App\Logging;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;

class CustomFormatter extends LineFormatter implements FormatterInterface
{
    public function format(array $record): string
    {
        // 継承元のformatメソッドを呼び出す
        $output = parent::format($record);

        // スタックとレースを出力する
        if (isset($record['context']['exception']) &&
$record['context']['exception'] instanceof \Throwable) {
       $output .= "[stacktrace]\n".$record['context']['exception']
->getTraceAsString()."\n\n";
        }
        return $output;
    }
}

5.フォーマッタを設定する

 最後に、作成したフォーマッタをサービスプロバイダで設定します。

 1で作成したサービスプロバイダに以下を追記します。

コピー用↓

<?php

namespace App\Providers;

use Illuminate\Log\LogManager;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\File;
use Monolog\Logger as Monolog;
use Monolog\Handler\StreamHandler;
use App\Logging\CustomFormatter;

class LoggingServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @param LogManager $manager
     * @return void
     */
    public function boot()
    {
        app('log')->extend('custom', function($app, $config){
            // ログファイルの出力先を設定
            $logFilePath = storage_path('logs/'.now()
->format('Y/m').'/laravel_'.now
->format('Ymd').'.log');
            $logFileDirectory = dirname($logFilePath);

            // ディレクトリが存在しない場合は作成する
            if(!File::exists($logFileDirectory)){
                File::makeDirectory($logFileDirectory, 0775, true, true);
            }

            $monolog = new Monolog($config['name']);

            $handler = new StreamHandler(
                $logFilePath,
                $this->level($config)
            );

            // フォーマッタの設定
            $formatter = new CustomFormatter(null, 'Y-m-d H:i:s', true, true);
            $handler->setFormatter($formatter);

            $monolog->pushHandler($handler);

            return new \Illuminate\Log\Logger($monolog, $app['events']);
        });
    }

    /**
     * Parse the string level into a Monolog constant.
     *
     * @param  array  $config
     * @return int
     */
    protected function level(array $config)
    {
        $level = $config['level'] ?? 'debug';

        return Monolog::toMonologLevel($level);
    }
}

 これでログファイルを内容はデフォルトと同じまま日別にフォルダ分けして出力することができるようになります。

 役に立ったでしょうか? もし役にたったのであれば、いいね! シェアなどをして頂けると嬉しいです♪

 最後までどうもありがとうございました。

 

お問い合わせはこちら