CodeIgniterのログをMonologで出力する

CodeIgniterは、ログ機能が貧弱ですが、Monologを使うことで解決ができます。
Monologを使えば、簡単に付加情報を出力したり、Syslogに出力したりできます。

# 概要
- アプリケーションログを、Monologで出力する
- CodeIgniter自身のログも、Monologで出力する

# 前提
- CodeIgniter 3.1.6
- Monolog 1.23.0
- PHP 7.2.1

# composerでMonologをインストール
applicationディレクトリで、composerを実行します。
すると、application配下にvendorが作成され、vendor内にMonologとautoload.phpが作成されます。

```sh
$ cd application
$ composer require monolog/monolog
```

これは、CodeIgniterのcomposer_autoloadを有効にするだけで、自動的にロードさせるためです。
CodeIgniterのautoloadは、標準でapplication/vendor/autoload.phpをロードします。

# CodeIgniterの設定
CodeIgniterのautoloadを有効にします。

```php:application/config/config.php
$config['composer_autoload'] = TRUE;
```

# ログ出力
アプリケーションログをMonologで出力させるだけであれば、必要な個所でLoggerを生成するだけです。


```php:application/controllers/Welcome.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

// Monologをuseします
// Processorsなど必要であれば、useを追加します
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

class Welcome extends CI_Controller {

	public function index()
	{
		// Monologのオブジェクトを生成します
		$log = new Logger('Sample');
		// Syslogなどに出力する場合は、Handlerを変更することで可能です
		$stream = new StreamHandler('./path/log.log');
		$stream->setFormatter(new LineFormatter("%message%"));
		$log->pushHandler($stream);
		// ログを出力します
		$log->info('test');
		
		$this->load->view('welcome_message');
	}
}

```

しかし、これだけではCodeIgniter自身で出力されるログが、Monologで出力されません。

# CodeIgnierのログ出力
CodeIgniterでは、log_message()によってログが出力されます。
つまり、log_message()の処理を、Monologでの出力に変更する必要があります。

log_message()は、system/core/Common.phpでwrite_log()を呼び出していることがわかります。
そして、write_log()はsystem/core/Log.phpで定義されています。

```php:system/core/Common.php
$_log[0]->write_log($level, $message);
```

```php:system/core/Log.php
public function write_log($level, $msg)
```

Log.phpのwrite_log()をオーバーライドすれば、すべてのログ出力をMonologに変更できることがわかります。
CodeIgniterには、coreクラスを自作のcoreクラスに置き換えることが簡単にできる仕組みがあります。

# Monolog出力用のログクラスを作成
coreのログクラス(CI_Log)を継承して、Monolog用のログクラス(MY_Log)を作成します。
write_log()をオーバーライドすることで、log_message()を変更します。

coreクラスを作成する際のプレフィックスは、configに設定されています。

```php:application/config/config.php
$config['subclass_prefix'] = 'MY_';
```

自作のログクラスです。

```php:application/core/MY_Log.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

// Monologをuseします
// Processorsなど必要であれば、useを追加します
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

class MY_Log extends CI_Log {

	protected $log;

	public function __construct()
	{
		// Monologのオブジェクトを生成します
		$log = new Logger('Sample');
		// Syslogなどに出力する場合は、Handlerを変更することで可能です
		$stream = new StreamHandler('./path/log.log');
		$stream->setFormatter(new LineFormatter("%message%"));
		$log->pushHandler($stream);
	}

	public function write_log($level, $msg)
	{
		$log->{$level}($msg);
	}
}
```

ログ出力は、通常と同じくlog_message()を呼び出すだけです。

```php:application/controllers/Welcome.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

	public function index()
	{
		// ログを出力します
		log_message('info', 'test');
		
		$this->load->view('welcome_message');
	}
}
```

# まとめ
- CodeIgniterのlog_message()は、CI_Logのwrite_log()である
- CI_Logを継承したログクラスで、write_log()をオーバーライドする