LoginSignup
26

More than 5 years have passed since last update.

Laravelのログ出力先とフォーマットの変更

Last updated at Posted at 2018-07-12

概要

Laravelのアプリケーションログの出力先を.envで変更できるようにして、さらにフォーマットも変更したかったので調べた内容をメモしておく。

ログのフォーマットについて

いろいろとありそうですが、LTSV形式が使いやすそうだったので使ってみる。
自分でMonologのFormatterを作るのも面倒なのでGoogle先生に聞いたところ良さそうのが見つかる。

■ hikaeme/monolog-ltsv-formatter
https://packagist.org/packages/hikaeme/monolog-ltsv-formatter

上記ライブラリを利用することにします。

# インストール
$ composer require hikaeme/monolog-ltsv-formatter

カスタマイズ方法について検討

Laravelのログ出力のカスタマイズを調べてみるとマニュアルには、

configureMonologUsingメソッドでと紹介されているが書いてみると・・・イマイチ微妙。

bootstrap/app.php
$app->configureMonologUsing(function($monolog) {  
    $level = \Monolog\Logger::DEBUG;  
    $format = "%datetime%,%level_name%,%message%,%context%,%extra%\n";

    $monolog->pushProcessor(new \Monolog\Processor\IntrospectionProcessor(
        $level, ['Monolog\\', 'Illuminate\\',]
    ));

    $monolog->pushHandler(
        $handler = new \Monolog\Handler\StreamHandler(
            storage_path('logs/laravel.log'), 
            $level
        )
    );
    $handler->setFormatter(new Monolog\Formatter\LineFormatter($format));
});

<微妙と思う点について>

  • config/app.phpにログの設定があるが、上記方法でカスタマイズすると設定が反映されない
  • ログレベルなどの設定も.envやconfigの設定が反映されない(できなくもないが・・・)

他の方法を探すと「LogServiceProvider」の差し替えで行けそう。

■Laravel・Lumenでログ出力先を変更する
https://k2ss.info/archives/1629

こちらの方法を採用してみる。

LogServiceProviderの実装

Illuminate\Log\Writerの「 parseLevel 」メソッドが、protectedで利用できず、仕方がなく移植。

app/Providers/LogServiceProvider.php
<?php

namespace App\Providers;

use InvalidArgumentException;
use Illuminate\Log\LogServiceProvider AS BaseLogServiceProvider;
use Illuminate\Log\Writer;
use Monolog\Logger AS MonologLogger;
use Monolog\Processor\IntrospectionProcessor;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Hikaeme\Monolog\Formatter\LtsvFormatter;

class LogServiceProvider extends BaseLogServiceProvider
{
    /**
     * The Log levels.
     * ※「Illuminate\Log\Writer」から移植
     *
     * @var array
     */
    protected $levels = [
        'debug'     => MonologLogger::DEBUG,
        'info'      => MonologLogger::INFO,
        'notice'    => MonologLogger::NOTICE,
        'warning'   => MonologLogger::WARNING,
        'error'     => MonologLogger::ERROR,
        'critical'  => MonologLogger::CRITICAL,
        'alert'     => MonologLogger::ALERT,
        'emergency' => MonologLogger::EMERGENCY,
    ];

    /**
     * singleモードの出力
     *
     * @param  \Illuminate\Log\Writer $log
     * @return void
     * @throws \Exception
     */
    protected function configureSingleHandler(Writer $log)
    {
        // コメントアウト (フォーマットが変更できない)
        #$log->useFiles(
        #    $this->app->storagePath().'/logs/laravel.log',
        #    $this->logLevel()
        #);

        $monolog = $log->getMonolog();

        // フォーマット変更 (LTSV形式)
        $monolog->pushHandler(
            $handler = new StreamHandler(
                $this->path().$this->file(),
                $this->parseLevel($this->logLevel())
            )
        );
        $handler->setFormatter(new LtsvFormatter());

        // ログ追加 (ファイル名、行番号、クラス名、メソッド名)
        $monolog->pushProcessor(
            new IntrospectionProcessor(MonologLogger::DEBUG, ['Monolog\\', 'Illuminate\\',])
        );
    }

    /**
     * 日付ローテートモードでの出力
     *
     * @param  \Illuminate\Log\Writer $log
     * @return void
     * @throws \Exception
     */
    protected function configureDailyHandler(Writer $log)
    {
        // コメントアウト (フォーマットが変更できない)
        #$log->useDailyFiles(
        #    $this->app->storagePath().'/logs/laravel.log', $this->maxFiles(),
        #    $this->logLevel()
        #);

        $monolog = $log->getMonolog();

        // フォーマット変更 (LTSV形式)
        $monolog->pushHandler(
            $handler = new RotatingFileHandler(
                $this->path().$this->file(),
                $this->maxFiles(),
                $this->parseLevel($this->logLevel())
            )
        );
        $handler->setFormatter(new LtsvFormatter());

        // ログ追加 (ファイル名、行番号、クラス名、メソッド名)
        $monolog->pushProcessor(
            new IntrospectionProcessor(MonologLogger::DEBUG, ['Monolog\\', 'Illuminate\\',])
        );
    }

    /**
     * ログ出力先
     *
     * @return string
     */
    private function path(): string
    {
        return env('APP_LOG_DIR', $this->app->storagePath().'/logs/');
    }

    /**
     * ファイル名
     *
     * @return string
     */
    private function file(): string
    {
        return 'laravel.log';
    }

    /**
     * Parse the string level into a Monolog constant.
     * ※「Illuminate\Log\Writer」から移植
     *
     * @param  string  $level
     * @return int
     *
     * @throws \InvalidArgumentException
     */
    protected function parseLevel($level): int
    {
        if (isset($this->levels[$level])) {
            return $this->levels[$level];
        }

        throw new InvalidArgumentException('Invalid log level.');
    }
}

独自のApplicationクラス

LogServiceProviderを差し替えるために、独自クラスを作成する。

app/Application.php
<?php

namespace App;

use App\Providers\LogServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;

/**
 * アプリケーションクラス
 * ※ ログサービスプロバイダを変更したいため、overwrite
 *
 * @package app.Providers
 */
class Application extends \Illuminate\Foundation\Application
{
    /**
     * Register all of the base service providers.
     *
     * @return void
     */
    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));

        $this->register(new LogServiceProvider($this));

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

bootstrap/app.php

上記で作成した独自Applicationクラスに差し替えるため、修正。

bootstrap/app.php
<?php

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/

// Applicationクラスを差し替え
// $app = new Illuminate\Foundation\Application(
$app = new \App\Application(
    realpath(__DIR__.'/../')
);

試してみる

クエリの実行ログを出力してみる。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // アプリケーションで実行される各SQLクエリのログ出力
        DB::listen(function ($query) {
            // 改行と連続スペースを取り除く
            $sql = trim(preg_replace('/\s+/', ' ', str_replace(["\r\n", "\r", "\n"], '', $query->sql)));
            \Log::debug($sql);
            \Log::debug($query->bindings);
            \Log::debug($query->time);
        });
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

実行してみると、下記のようにLTSV形式のログが出力される。

storage/logs/laravel.php
time:2018-07-12 12:13:48    level:DEBUG message:SELECT id, name FROM users WHERE id = ? ;   file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:21 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}
time:2018-07-12 12:13:48    level:DEBUG message:array (\n  0 => '1',\n) file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:22 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}
time:2018-07-12 12:13:48    level:DEBUG message:16.79   file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:23 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}

ログの出力場所を変えてみる。
パーミッションの関係で他のところに出力し難かったので、とりあえず、storageフォルダ直下に出力してみる。

.envファイルで出力先のパスを指定する。

.env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:*********************************=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_LOG_DIR=/var/www/www.example.com/storage/     <- ここに追加
APP_URL=http://localhost

実行してみると、下記のようにLTSV形式のログが出力される。

storage/laravel.php
time:2018-07-12 12:13:48    level:DEBUG message:SELECT id, name FROM users WHERE id = ? ;   file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:21 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}
time:2018-07-12 12:13:48    level:DEBUG message:array (\n  0 => '1',\n) file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:22 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}
time:2018-07-12 12:13:48    level:DEBUG message:16.79   file:/var/www/www.example.com/app/Providers/AppServiceProvider.php  line:23 class:App\Providers\AppServiceProvider  function:App\Providers\{closure}

以上

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26