📖

laravelのログをslackに送信する(production環境でのログ管理)

2025/02/10に公開

時間がなくて適当なログ設定にしてる人への記事

開発モードのログなんて適当な時しか見ないんだろうけど、インフラ屋はアプリログを制す必要あり。要するに開発時のログとproductionとしてデプロイしたもののログは性質が異なる。開発時に見落されているエラーログを素早く受けとる手法は何かしら残しておく必要がある。筆者もいくつかアプリケーションを運用してきたが、これらのログの受信はメール送信でもいいのだが今日日やはりslack送信が便利であると思ったので、ここに紹介する。そもそも、まともに色々できないで適当に設定するようなアプリはテストが甘く単純なエラーも見落とされているものだ。デプロイメントに関してもとりわけステージング環境などは雑に設定しやすいので、ある程度ログ設定には気配りして欲しい。

ディフォルトのログ設定

.env
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

おそらくこれが通常のログ設定で、ほとんど深掘りしなくても動作するのではあるが、これはdebugを含む全部のログをシングルファイルに送信している。以下にエラーレベルを記述するが

  • debug - 詳細なデバッグ情報
  • info - 一般的な情報
  • notice - 重要な通知
  • warning - 潜在的な問題
  • error - 一般的なエラー
  • critical - 致命的なエラー
  • alert - 即対応が必要なエラー
  • emergency - システム全体が使用不能

このようになっており、debugが指定されているため実質全てのログが送信されている。

自分でログを送信する以前のレベルの話

上記で諸々ログレベルを記述したが、一番重要なのはerrorというレベルであり、たとえば以下のような例


syntax error

これは、アプリケーションのコードが解析・実行される前に PHP エンジンによって検出されており、スクリプトが動作していないのでこのようなエラーは確実に拾い上げる必要がある。まあそのテストが甘いものなどはこれが隠されている事もあるので少なくともerror以上は確実にproduction環境においては拾い上げる必要があるのと、debug、infoとかをどうするという問題は多少ある。多少あるもののerror以上はかなり重大なのでslackに通知してもほとんどの場合損は無い。逆に言うと、アプリケーション作成者はその辺を意識してログレベルを決定する必要があるが、割と適当に

logger('moge'); // debugレベル

とかやりがちなのでまあその辺はerror以上に送るかどうかを気をつけて開発してもらうとして...

defaultの設定の上にslackを加える

プロダクション環境でこのディフォルトを適用したdebugかつsingleファイルがいいとは思えないが、先述の通り、その辺を雑に記録してもとりあえずerror以上を受けとれるようにしておけば割と何とかはなる、ということでdefaultの上にslackで受信する設定を加えてみよう。

config/logging.phpをみる

config/logging.php
// <snip...>
    'channels' => [

        'stack' => [
            'driver' => 'stack',
            'channels' => explode(',', env('LOG_STACK', 'single')),
            'ignore_exceptions' => false,
        ],
// <snip...>

このように、stackにおいてはLOG_STACKの設定によりカンマ区切りで複数のchannelを待ち受ける事を想定している。現在 .env を再度貼り付けると

.env
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

のようにLOG_STACK=singleなっているので何だかんだsingleログにしか記録されていないのであるが、singleかつslackにするには以下のようにセットすればいいことが理解できる。

LOG_CHANNEL=stack
LOG_STACK=single,slack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

この設定だとdebug以上がsingleとslackに記録されるので、非常にうるさいわけだが、これで適当に

routes/web.php
Route::get('/demo', function () {
    logger("test"); // debugレベルの簡易ログ送信ヘルパー
});

などして/demoにアクセスすると、slackのエラーもまとめて記録される

storage/logs/laravel.log
[2025-02-10 20:00:04] laravel.EMERGENCY: Unable to create configured logger. Using emergency logger. {"exception":"[object] (TypeError(code: 0): Monolog\\Handler\\SlackWebhookHandler::__construct(): Argument #1 ($webhookUrl) must be of type string, null given, called in /var/www/html/vendor/laravel/framework/src/Illuminate/Log/LogManager.php on line 346 at /var/www/html/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php:50)
[stacktrace]

まあこれはslackの設定を何もしてないので当然ではある。

slackの設定

https://api.slack.com/apps などで適切にアプリケーションを作成する。slackアプリケーションの作成に関しては本稿では取り上げないので、どうにかして適切にwebhookのurlを取得する事。必要なのはincoming webhookだけというのがslackの楽なところだろう。

configでslack関連の設定を確認する

config/logging.php
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
            'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
            'level' => env('LOG_LEVEL', 'critical'),
            'replace_placeholders' => true,
        ],

このようになっており、 .env に適切な値をセットすればそれが利用される。少なくとも LOG_SLACK_WEBHOOK_URLだけは絶対に必要なので、この値を .env に適切にセットする。

特に特殊な設定を施さずとも、この段階でslackにログが転送されるはずだ。

debugを記録するのは流石にうるさ過ぎる

これに関してはslackののconfig arrayのlevelを変更するといい。ただ

config/logging.php
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
            'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
            // 'level' => env('LOG_LEVEL', 'critical'),
            'level' => 'error',
            'replace_placeholders' => true,
        ],

としてもokなんだけど、

config/logging.php
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
            'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
            'level' => env('SLACK_LOG_LEVEL', 'error'),
            'replace_placeholders' => true,
        ],

のように .env のエントリーを新設するのが個人的には好みである。ただconfigに変更かかっちゃうけど、まあそれはそれとして。

テスト

この段階で

routes/web.php
Route::get('/demo', function () {
    logger("test"); // debugレベルの簡易ログ送信ヘルパー
});

としたとき、storage/logs/laravel.logのみにtestが書かれ、slackの通知はこなくなる

Route::get('/demo', function () {
    logger()->error("This is an error log via logger()!");
});

に変更すると、slackに通知されなおかつログにも記録されるはずだ。

まとめ

slackのincoming webhookはurlさえゲットできれは非常に手軽にログ送信できるので、書いておくのはおすすめ。production環境でたとえばerror以上のログのみファイルにもslackにも記録したいというような場合はconfig/logger.phpの変更すら不要で

LOG_CHANNEL=stack
LOG_STACK=single,slack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error
LOG_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/****/****

と雑に設定しておいても割とお得な事が多い。

あとがき

結構雑にデプロイしたステージングサーバーなどでも、流石にsingleファイルじゃなくてdailyにしておくといいかもしれないですね。まああとstagingであっても.envにセンシティブな設定が残ってる場合はdebug設定から露出しないように注意(かつては露出してたんだよなあ...)

Discussion