Lumenでログを出力する

投稿日:

LumenでちょっとAPIを書いてみようと思い、とりあえずログにでも吐き出すかなぁとLog::info('Hello Lumen!!');と書いてみたら見事に怒られました。
まぁいちおう対応をしたわけですが、その際にLumenでログを吐き出すまでに行ったことをメモとして残しておきます。
(ちょっと素人の匂いが漂う内容かもしれません。。)

bootstrap/app.php$app->withFacades();を有効にするパターンと、コメントアウトのままログを吐き出すパターンを書いてみます。※Monologを直接使うパターンも

なお、Lumenのバージョンは

Lumen (5.3.2) (Laravel Components 5.3.*)

です。

発端

Lumenを使ってみようと思っていつもの様に

Controllers/SampleController.php

<?php

namespace App\Http\Controllers;

use Log;

class SampleController extends Controller
{
    public function index()
    {
        Log::info('Hello Lumen!!');
    }
}

こんな感じで書いたら、

Class ‘Log’ not found

こうやって怒られました。わぉ。
ググッてみると、Errors & Logging – Lumen – PHP Micro-Framework By Laravel がHIT(最初に見ろ)。

Note: Before using the Log facade, be sure you have uncommented the $app->withFacades() method call in your bootstrap/app.php file.

Log Facade を使う前にbootstrap/app.php$app->withFacades();のコメントを外しなさい。
と書いてありました。

修正

では早速、bootstrap/app.phpを修正します。

// ~ 略 ~

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/

$app = new Laravel\Lumen\Application(
    realpath(__DIR__.'/../')
);

$app->withFacades(); // コメントを外す

// ~ 略 ~

もう一度ログを仕込んだ画面を開いてみると、エラーとならず storage/logs/lumen.log にログが書き出されている事が確認できます。

基本的にこれだけで単純なログ出力に関しては問題無いと思いますが、Monologを使っていることを知っている人にとってみれば他の各種Handlerでもログを出力したいというのが自然の摂理だと思います。
というわけで、そのへんの簡単なサンプルも試してみます。

ローテーション用のログを残しつつメールでも送信する

日付別にログを残しつつ、ついでにメールを送るというユースケースを考えます。
ここでも、bootstrap/app.phpを修正すれば事足ります。

<?php

require_once __DIR__.'/../vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Formatter\LineFormatter;

// ~ 略 ~

/*
 |--------------------------------------------------------------------------
 | Define a callback to be used to configure Monolog.
 |--------------------------------------------------------------------------
 */
$app->configureMonologUsing(function($monolog) {

    $handlers[] = (
        new RotatingFileHandler(
            storage_path('logs/lumen.log')
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    /*
    // ローテート無しのログを残したい場合は
    // use Monolog\Handler\StreamHandler; を書いて
    // StreamHandlerを使います
    $handlers[] = (
        new StreamHandler(
            storage_path('logs/lumen.log')
        )
    )->setFormatter(new LineFormatter(null, null, true, true));
    */

    $handlers[] = (
        new NativeMailerHandler(
            'to@mailaddress',
            'メールのテスト',
            'from@mailaddress',
            Logger::INFO // 便宜上Logger::INFOにしていますが本来であればLogger::ERROR以上とかがいいです
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    $monolog->setHandlers($handlers);

    return $monolog;
});

return $app;

もう一度ログを仕込んだ画面を開いてみると、storage/logs/lumen-{Y-m-d}.log にログが出力されつつメールが送信される事が確認できます。
また、この方法を取るもう一つの利点として、$app->configureMonologUsing()内で、env('APP_ENV')の値を見て利用するHandlerやログレベルのコントロールが可能になる事が考えられます(あと、ログファイル名の変更も)。

Facadeを使わない場合

と、ここまでは$app->withFacades();を有効にしてLogを使ってみましたが、中にはデフォルトでコメントされているのに何でLogを出す為にコメントを外すんだぜ?という人もいるかもしれません(まさに自分がそう)。
そこで少し調べてみたのですが、$app->withFacades();を呼ばなくてもLogger(Monolog)は使えるようです。

はい、ここでも、bootstrap/app.phpを修正すれば事足ります。
※上記で修正した部分($app->configureMonologUsing()の追加など)は戻しておきます

<?php

require_once __DIR__.'/../vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Formatter\LineFormatter;

// ~ 略 ~

// $app->withFacades(); // コメントした状態に戻す

// $app->withEloquent();

/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

// コンテナー名は任意でおk
// 'log' にすると元々logにバインドされている Laravel\Lumen\Application->registerLogBindings()
// の中身をここで定義した内容ですげ替えるようです
$app->singleton('AppLogger', function() {
    $handlers[] = (
        new RotatingFileHandler(
            storage_path('logs/lumen.log')
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    $handlers[] = (
        new NativeMailerHandler(
            'to@mailaddress',
            'メールのテスト',
            'from@mailaddress',
            Logger::INFO // 便宜上Logger::INFOにしていますが本来であればLogger::ERROR以上とかがいいです
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    // デフォルトに合わせて 'lumen' にしていますがなんでもいいです
    return new Logger('lumen', $handlers);
});

// ~ 略 ~

return $app;

で、使う側はこうします。

Controllers/SampleController.php

<?php

namespace App\Http\Controllers;

class SampleController extends Controller
{
    public function index()
    {
        $log = app('AppLogger'); // Monolog\Logger です
        $log->info('Hello Lumen!!'); // storage/logs/lumen-{Y-m-d}.log が出力されます && メールも送信されます

        // Monolog\Handler\StreamHandler を使う場合
        // $log = app('log');
        // $log->info('Hello Lumen!!'); // storage/logs/lumen.log が出力されます
    }
}

こんな感じで$app->withFacades();を使わずとも、Logger(Monolog)を使えることが確認できました。
使う時、app();で利用するコンテナーを呼び出すのが手間かもしれませんが、そこは自己責任でチョイスして頂ければと。
※Facadeも結局 use Log; 書かなきゃならんし

このあたりが参考になるかもしれません。

tips

上記にも書きましたが、$app->singletonでLoggerを登録する際、コンテナー名を ‘log’ にすると元々logにバインドされている Laravel\Lumen\Application->registerLogBindings()の中身をここで定義した内容ですげ替えるようです。

bootstrap/app.php

// コンテナー名を log にする
$app->singleton('log', function() {
    $handlers[] = (
        new RotatingFileHandler(
            storage_path('logs/app.log') // ログファイル名を変えてみる
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    $handlers[] = (
        new NativeMailerHandler(
            'to@mailaddress',
            'メールのテスト',
            'from@mailaddress',
            Logger::INFO // 便宜上Logger::INFOにしていますが本来であればLogger::ERROR以上とかがいいです
        )
    )->setFormatter(new LineFormatter(null, null, true, true));

    // ログのchannel名 を app にしてみる
    return new Logger('app', $handlers);
});

Controllers/SampleController.php

<?php

namespace App\Http\Controllers;

class SampleController extends Controller
{
    public function index()
    {
        // storage/logs/app-{Y-m-d}.log が出力されます && メールも送信されます
        $log = app('log');
        $log->info('Hello Lumen!!');
    }
}

Monologを直接使う

というか、単純にLog出力だけを考えるならこれでもいいわけです。

<?php

namespace App\Http\Controllers;

use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;

class SampleController extends Controller
{
    public function index()
    {
        $handler = new RotatingFileHandler(storage_path('logs/app.log'));
        $handler->setFormatter(new LineFormatter(null, null, true, true));

        $log = new Logger('app');
        $log->pushHandler($handler);
        $log->info('Hello Lumen!!');
    }
}

まとめ

こんな感じでLumenでログを出力することが出来ました。
LumenでFacadeを使ったほうがいいのか、否、なるべく使わないほうがいいのか沼に入りそうなのでこれ以上突っ込むのはやめておきます。

参考

作成者: shimabox

Web系のプログラマをやっています。 なるべく楽しく生きていきたい。

1件のコメント

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください