カテゴリー: BackEnd

[Laravel] ログの扱い方 [5.8]

はじめに

こんにちは。今回は表題通り、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標準とは別にログを出力したい」という意図のコードです。

   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化したり、設定値を.envconfigに追い出すことで可読性を保てるのではないでしょうか。
上記のコードでstorage/logs/user-register-[YYYY-MM-DD].logが生成され、以下のようなログが出力されます。
storage/logs/laravel-[YYYY-MM-DD].logはLaravelデフォルトのログで、config/logging.phpの設定に沿って出力されます(後述)。

config/logging.phpで設定する方法

5.6以降で追加された設定ファイルで、公式ドキュメントを見る限り、恐らくこちらが推奨の方法だと思います。

    '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にログが出力されます。コード量が目に見えて減りました!複雑な仕様でなければ、設定ファイルのへ記述で済ませてしまうのがベターだと思います。

    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のログ出力について調査したのですが、一度理解してしまえば実に簡単です。豊富な出力先を選べるのもいいですね。

おすすめ書籍

nomura

シェア
執筆者:
nomura

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前