CakePHP3.xのログをlog4phpを利用して出力する


CakePHPでlog4phpを利用しようと思ったところ、
3.x以降に対応したドキュメントがなく苦戦しました。

CakePHP本体のインストールについては[こちら](http://book.cakephp.org/3.0/ja/installation.html)を参照してください

# 動作環境
- CakePHP 3.3
- PHP 7.0.11
- log4php 2.3

# インストール
CakePHP3からコンポーザを利用してインストールするようになったので、
log4phpのインストールでもコンポーザを利用します。

```json:composer.json
 "require": {
     "apache/log4php": "2.3.0"
 },
```

コンポーザを利用すればクラスマップにも自動的に反映されるため、
requireを意識しなくてもlog4phpを利用できるようになります。

# log4phpの設定ファイルを作成する
configディレクトリに設定ファイルを作成します。
logsディレクトリにYYYYMMDD.log形式のログファイルが作成されます。

```xml:config/log4php.xml
<configuration xmlns="http://logging.apache.org/log4php/">
    <appender name="myFileAppender" class="LoggerAppenderDailyFile">
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d [%p] %m %n" />
        </layout>
        <param name="file" value="../logs/%s.log" />
    </appender>
    <logger name="C1">
        <level value="DEBUG" />
        <appender_ref ref="myFileAppender" />
    </logger>
</configuration>
```

# 動作確認
ここで一旦、log4phpが利用できるかどうか確認しておきましょう。

```php
// 設定ファイルの読み込み
\Logger::configure(CONFIG.'log4php.xml');
$logger = \Logger::getLogger('C1');
$logger->info( 'ログ確認。' );

/* 出力例
2016-10-30T10:20:30+09:00 [INFO] ログ確認。
*/
```

# カスタムロガーの作成
CakePHPのロガーとlog4phpを繋ぐためのロガーを作成します。
logメソッドを上書きして、CakePHPのエラーレベルをlog4phpのレベルに置き換えて呼び出します。

```php:src/CustomLogger.php
<?php
namespace App;

use Cake\Log\Engine\BaseLog;

class CustomLogger extends BaseLog {

    /**
     * ログ出力
     */
    public function log( $level, $message, array $context = array() ) {

        // メッセージを成型
        $message = $this->_format( $message, $context );

        // エラーレベルをlog4phpのものに置換
        switch ( $level ) {
            case LOG_EMERG:
            case LOG_ALERT:
            case LOG_CRIT:
                $level = \LoggerLevel::getLevelFatal();
                break;
            case LOG_ERR:
                $level = \LoggerLevel::getLevelError();
                break;
            case LOG_WARNING:
            case LOG_NOTICE:
                $level = \LoggerLevel::getLevelWarn();
                break;
            case LOG_INFO:
                $level = \LoggerLevel::getLevelInfo();
                break;
            case LOG_DEBUG:
                $level = \LoggerLevel::getLevelDebug();
                break;
            default:
                $level = \LoggerLevel::getLevelInfo();
                break;
        }

        // ログ出力
        $logger = \Logger::getLogger('C1');
        $logger->log( $level, $message );
    }
}
```
Cake\Log\Engine\BaseLogを継承しないと「Psr\Log\LoggerInterfaceを実装しろ」というエラーが出ます。
素直にPsr\Log\LoggerInterfaceを実装するよりもCake\Log\Engine\BaseLogを継承したほうが実装の手間が少ないので、logメソッドだけを上書きするところに落ち着きました。

# ロガーの置き換え
```php:config/bootstrap.php
// 設定ファイルの読み込み
\Logger::configure(CONFIG.'log4php.xml');

// 既存の設定をコメントアウト
// Log::config(Configure::consume('Log'));

// カスタムロガーを設定
require_once (APP.'CostomLogger.php');
Log::config('log4php', ['className' => '\App\CostomLogger']);

// 既存のロガーの設定を削除
Log::drop('debug');
Log::drop('error');
```

# 再度動作確認
今度はCakePHPのログ出力メソッドでログが出力できるか確認します。

```php
// Controller, Component, View等
$this->log( 'ログ確認。', 'info' );

/* 出力例
2016-10-30T15:30:45+09:00 [INFO] ログ確認。
*/
```