Laravel・Lumenでログ出力先を変更する

最近勢いがあるPHPフレームワークの1つであるLaravelですが、ログファイルのデフォルト出力先は以下になっています。

  • storage/logs/laravel.log

普段、私は自分が作成した複数アプリ(CakePHP・Rails・Django・バッチ処理など)で同じログディレクトリを利用しています(管理しやすいので)。
また、デプロイ時にはシェル側でリンクを切り替えて対応しています。
そのため、プロジェクトディレクトリ以下に出力されると結構不便なんですよね。

Laravelでは、他フレームワークのように「configファイルをいじるだけでディレクトリ・ファイル名を操作する」というのができませんでした。
※ディレクトリの変更自体はApplicationクラスのuseStoragePath()メソッドで可能です。
なので、今回はこれをカスタマイズした方法を紹介します。
といっても、他言語や他フレームワークでやるような継承を使うだけです。

1. 独自のLogServiceProviderを作成

まずはじめに、\Illuminate\Log\LogServiceProviderを継承した独自プロバイダを生成します。

useFiles(
            $this->path().$this->getFileName(),
            $this->logLevel()
        );
    }

    /**
     * 日付ローテートモードでの出力
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureDailyHandler(Writer $log)
    {
        // `configureSingleHandler`と同様
        $log->useDailyFiles(
            $this->path().$this->getFileName(), $this->maxFiles(),
            $this->logLevel()
        );
    }

    /**
     * デフォルトのログ出力パスを取得します。
     *
     * @return string ログのパス
     */
    private function path(): string
    {
        // 環境変数からログディレクトリを取得したい場合は以下のようにする
        // この場合、環境変数`LOG_DIR`が設定されていなければ、通常の`storage/logs`が使われる
        return env('LOG_DIR', $this->app->storagePath().'/logs/');
    }

    /**
     * ログファイル名を取得します。
     *
     * @return string ログファイル名
     */
    private function getFileName(): string
    {
        // ファイル名を自由に決める
        // 必要に応じて場合分けも対応可能
        return 'myapp.log';
    }
}

2. 独自のApplicationクラスを作成

次に、独自のApplicationクラスを生成します。
ここで、上記のMyLogServiceProviderを利用するように変更します。

register(new EventServiceProvider($this));

        // 今回追加したログサービスプロバイダ
        $this->register(new MyLogServiceProvider($this));

        $this->register(new RoutingServiceProvider($this));
    }
}

3. 利用するApplicationクラスの変更

最後に、利用するApplicationクラスをMyApplicationに変更します。

// bootstrap/app.php
// Illuminate\Foundation\Application`から変更
$app = new App\MyApplication(
    realpath(__DIR__.'/../')
);

これでログ出力をカスタマイズできます。

ちなみに、Laravelの軽量版であるLumenでは、Laravelの手順1が不要で、手順2の独自アプリケーションクラスでgetMonologHandler()を継承するだけで良いです。

<?php
// MyApplication.php

namespace App;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;
use Laravel\Lumen\Application;

/**
 * カスタムアプリケーションクラス
 * 継承元は`\Laravel\Lumen\Application`になる。
 */
class MyApplication extends Application
{
    /**
     * LumenではMonologの設定を直接変更する。
     */
    protected function getMonologHandler()
    {
        $log = env('LOG_DIR', storage_path('logs/')).'myapp.log';
        return (new StreamHandler($log, Logger::DEBUG))
                            ->setFormatter(new LineFormatter(null, null, true, true));
    }
}
※2018/3/13追記
こちらにて続編を書きました。