Laravel ログを出力する

## 環境

- PHP: 8.0.2
- Laravel: 8.34.0

## ログ関連記事

- [Laravel リクエストログを出力する](https://qiita.com/ucan-lab/items/bfd15b096a916f811468)
- [Laravel SQLの実行クエリログを出力する](https://qiita.com/ucan-lab/items/753cb9d3e4ceeb245341)
- [Laravel ConsoleとHttpのログファイルを分ける](https://qiita.com/ucan-lab/items/4dd7b5f7a3eb57a3ef1f)

## ログの基本

```php
logger('test', ['foo' => 'bar']);
```

ログの関数は、第一引数にメッセージ、第二引数にコンテキストを指定します。

```
[2021-04-01 00:00:00] local.DEBUG: test {"foo":"bar"} 
```

ログのフォーマットとして下記のように出力されます。

```
[YYYY-MM-DD HH:II:SS] 環境.ログレベル: メッセージ コンテキスト
```

### 補足: 第一引数のメッセージ

第一引数にメッセージには、[Jsonable](https://laravel.com/api/8.x/Illuminate/Contracts/Support/Jsonable.html)や[Arrayable](https://laravel.com/api/8.x/Illuminate/Contracts/Support/Arrayable.html)の実装クラスか[__toString()](https://www.php.net/manual/ja/language.oop5.magic.php#object.tostring)を実装済みのクラス、もしくは配列を受け取れます

### 補足: コンテキスト

```php
logger('test', ['file' => __FILE__, 'line' => __LINE__]);
```

コンテキストでマジック定数などを指定するとより詳しいデバッグログを出力できて便利です。

```
[2021-04-01 00:00:00] local.DEBUG: test {"file":"/work/backend/app/Http/Controllers/WelcomeController.php","line":14} 
```

- [マジック定数](https://www.php.net/manual/ja/language.constants.magic.php)
- [定義済みの定数](https://www.php.net/manual/ja/reserved.constants.php)

## ヘルパーでログ出力する

一番簡単な方法です。

```php
logger('test', ['foo' => 'bar']);
info('test', ['foo' => 'bar']);
logs()->notice('test', ['foo' => 'bar']);
logs()->warning('test', ['foo' => 'bar']);
logs()->error('test', ['foo' => 'bar']);
logs()->critical('test', ['foo' => 'bar']);
logs()->alert('test', ['foo' => 'bar']);
logs()->emergency('test', ['foo' => 'bar']);
```

ログの関数は、第一引数にログメッセージ、第二引数にコンテキストを指定します。

```
[2021-04-01 00:00:00] local.DEBUG: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.INFO: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.NOTICE: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.WARNING: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.ERROR: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.CRITICAL: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.ALERT: test {"foo":"bar"} 
[2021-04-01 00:00:00] local.EMERGENCY: test {"foo":"bar"} 
```

## ファサードでログ出力する

```php
use Illuminate\Support\Facades\Log;
use Log; // エイリアスでも可

Log::debug('test', ['foo' => 'bar']);
Log::info('test', ['foo' => 'bar']);
Log::notice('test', ['foo' => 'bar']);
Log::warning('test', ['foo' => 'bar']);
Log::error('test', ['foo' => 'bar']);
Log::critical('test', ['foo' => 'bar']);
Log::alert('test', ['foo' => 'bar']);
Log::emergency('test', ['foo' => 'bar']);
```

## DIでログ出力する

`WelcomeController.php` でロガーをDIする例をご紹介します。

```php:app/Http/Controllers/WelcomeController.php
<?php declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\View\View;
use Psr\Log\LoggerInterface;

final class WelcomeController extends Controller
{
    /**
     * @param LoggerInterface $logger
     */
    public function __construct(private LoggerInterface $logger) {
    }

    /**
     * @return View
     */
    public function __invoke(): View
    {
        $this->logger->info('test', ['foo' => 'bar']);

        return view('welcome');
    }
}
```

`$logger` には、[LogServiceProvider](https://github.com/laravel/framework/blob/8.x/src/Illuminate/Log/LogServiceProvider.php)にて登録されている[LogManager](https://github.com/laravel/framework/blob/8.x/src/Illuminate/Log/LogManager.php)インスタンスが入っています。

## 独自Loggerクラスを定義する

フレームワークに依存しないコードを書きたい場合は、こんな感じです。
(ディレクトリ構成に合わせてお使いください。)

```php:app/Logger/AppLogger.php
<?php declare(strict_types=1);

namespace App\Logger;

use Psr\Log\LoggerInterface;

final class AppLogger implements AppLoggerInterface
{
    public function __construct(private LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function info(string $message, array $context = []): void
    {
        $this->logger->info($message, $context);
    }
}
```

```php:app/Logger/AppLoggerInterface.php
<?php declare(strict_types=1);

namespace App\Logger;

interface AppLoggerInterface
{
    public function info(string $message, array $context = []): void;
}
```

```php:app/Providers/AppServiceProvider.php
<?php declare(strict_types=1);

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Logger\AppLoggerInterface;
use App\Logger\AppLogger;

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(AppLoggerInterface::class, AppLogger::class);
    }
}
```

```php:app/Http/Controllers/WelcomeController.php
<?php declare(strict_types=1);

namespace App\Http\Controllers;

use App\Logger\AppLoggerInterface;
use Illuminate\View\View;

final class WelcomeController extends Controller
{
    /**
     * @param AppLoggerInterface $logger
     */
    public function __construct(private AppLoggerInterface $logger)
    {
    }

    /**
     * @return View
     */
    public function __invoke(): View
    {
        $this->logger->info('test', ['foo' => 'bar']);

        return view('welcome');
    }
}
```

## 補足

### PSR-3 Logger Interface

- [PSR-3 Logger Interface](https://www.php-fig.org/psr/psr-3)

PSR（PHP Standards Recommendations）とは、PHP-FIG（PHP Framework Interop Group）が策定しているPHPコーディング規約を指します。

PSR-3ではロギングライブラリの一般的なインタフェースについて策定されています。
[LogManager](https://github.com/laravel/framework/blob/8.x/src/Illuminate/Log/LogManager.php)