Laravel5.4 ログ周りの設定 1/3 開始・終了ログを残す

ログ周りの設定レシピ、３品。今回はその1品目。

- Laravel5.4 ログ周りの設定 1/3 開始・終了ログを残す（本記事）
- [Laravel5.4 ログ周りの設定 2/3 クエリログを別ファイルで](http://qiita.com/n-oota/items/d2a05e967fa01743f103)
- [Laravel5.4 ログ周りの設定 3/3 ミリ秒に出力場所を添えて](http://qiita.com/n-oota/items/82520a06defef8fc00c6)

#やりたいこと
アプリケーションの開始・終了ログを残すようにします。

- 開始ログ：リクエストURLやバッチコマンドを出力
- 終了ログ：処理時間・使用メモリのピーク値を出力

```text:laravel.log
[2017-06-17 10:16:11] local.INFO: -- Startup {"method":"GET","uri":"/log/sample?a=1&b=2"} 
[2017-06-17 10:16:11] local.INFO: -- Shutdown {"time":"153.086[ms]","memory":"2048[kb]"} 
[2017-06-17 10:16:19] local.INFO: -- Startup {"command":"php artisan list"} 
[2017-06-17 10:16:19] local.INFO: -- Shutdown {"time":"383.189[ms]","memory":"6144[kb]"} 
```

#環境
Laravel 5.4.25
PHP 7.1.3

#実装手順

1. アプリケーションの開始・終了ログを残すクラスを作成
2. Kernelクラスのbootstrappersプロパティに作ったクラスを登録します。（登録するクラスにはbootstrapメソッドが必要です。）

以下、サンプル。
一応動作確認はしていますが、サンプルをそのまま使う場合は自己責任で。

##開始・終了ログを残すクラス
```php:app\Bootstrap\ApplicationLog.php
<?php

namespace App\Bootstrap;

use Illuminate\Contracts\Foundation\Application;

/**
 * include時の時間を取得しておく。
 */
if (!defined('APP_START_TIME')) {
    define('APP_START_TIME', microtime(true));
}

/**
 * アプリケーションログ設定クラス
 *
 * @package app.Bootstrap
 */
class ApplicationLog
{
    /**
     * 警告閾値：利用メモリ（MB）
     */
    const THRESHOLD_WARNING_MEMORY = 100;

    /**
     * The application instance.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $this->app = $app;

        // テスト時はアプリケーションログなし
        if (!$app->environment('testing')) {
            // シャットダウンハンドラ追加：シャットダウンログ
            register_shutdown_function([$this, 'handleShutdownLog']);

            // 起動ログ
            $this->startupLog();
        }
    }

    /**
     * 起動ログ
     */
    public function startupLog()
    {
        if ($this->app->runningInConsole()) {
            $command = sprintf('php %s', implode(' ', (array)array_get($GLOBALS, 'argv')));
            $params = compact('command');
        } else {
            $method = $_SERVER['REQUEST_METHOD'];
            $uri = $_SERVER['REQUEST_URI'];
            $params = compact('method', 'uri');
        }
        app('log')->info('-- Startup', $params);
    }

    /**
     * シャットダウンログ
     *
     * @return void
     */
    public function handleShutdownLog()
    {
        // シャットダウンログを出力
        $time = $this->getProcessingTime();
        $memory = $this->getPeekMemory();
        if ($this->isOverUsingMemory(self::THRESHOLD_WARNING_MEMORY)) {
            app('log')->warning('using a large amount of memory.', compact('memory'));
        }
        app('log')->info('-- Shutdown', compact('time', 'memory'));
    }

    /**
     * 単位をつけて見やすくした時間を返す
     *
     * @param float $time マイクロ秒数
     * @return string
     */
    protected function formatTime($time)
    {
        if ($time < 1) {
            return sprintf('%0.3f[ms]', $time * 1000);
        } else if ($time < (1 / 1000)) {
            return sprintf('%0.3f[μs]', $time * 1000 * 1000);
        }
        return sprintf('%0.3f[s]', $time);
    }

    /**
     * 単位をつけて見やすくしたメモリを返す
     *
     * @param float $value
     * @return string
     */
    protected function formatMemory($value)
    {
        if ($value < 1024 * 10) {
            // 10KBまではB表示
            return sprintf('%s[b]', $value);
        } else if ($value < (1024 * 1024 * 10)) {
            // 10MBまではKB表示
            return sprintf('%s[kb]', round($value / 1024, 1));
        } else {
            return sprintf('%s[mb]', round($value / (1024 * 1024), 2));
        }
    }

    /**
     * @param int $threshold 閾値（MB）
     * @return bool
     */
    protected function isOverUsingMemory($threshold)
    {
        return round(memory_get_peak_usage(1) / 1024 / 1024) > $threshold;
    }

    /**
     * 処理時間を取得
     *
     * @return string
     */
    protected function getProcessingTime()
    {
        return $this->formatTime(microtime(true) - APP_START_TIME);
    }

    /**
     * メモリ使用量のピーク値を取得
     *
     * @return float
     */
    protected function getPeekMemory()
    {
        return $this->formatMemory(memory_get_peak_usage(true));
    }

}
```

##Kernelクラスに登録
```php:app\Http\Kernel.php

/**
 * 親クラスからコピー
 */
protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
    
    // この一行を追加
    \App\Bootstrap\ApplicationLog::class,
];
```
```php:app\Console\Kernel.php

/**
 * 親クラスからコピー
 */
protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
    \Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
    \Illuminate\Foundation\Bootstrap\BootProviders::class,
    
    // この一行を追加
    \App\Bootstrap\ApplicationLog::class,
];
```

以上です。