CakeLogをスマートにカスタマイズする方法

CakePHPのログ出力処理は結構簡易なもので今ひとつ使いにくいです。また、「内部的に flock してるからアクセスが多いと遅いので実運用では使わない方が良いよ!」という話も聞いていたので、うちではカスタマイズしたものを使っています。

ROOT/cake/libs/cake_log.phpを書き換えてしまえばそれでも良いのですが、COREのコードを書き換えずにカスタマイズする方法を見つけたのでまとめました。

ログ出力処理は、objectクラスのメソッドとして実装されていて実際のコードは以下のようになっています。

<?php
// cake/libs/cake_log.php
...
	function log($msg, $type = LOG_ERROR) {
		if (!class_exists('CakeLog')) {
			uses('cake_log');
		}
		if (is_null($this->_log)) {
			$this->_log = new CakeLog();
		}
		if (!is_string($msg)) {
			$msg = print_r($msg, true);
		}
		return $this->_log->write($type, $msg);
	}
...

上記のコードの通り、このコードが実行される前に CakeLog というクラスを定義してしまえば差し替えが出来そうです。

で、どこにその設定を書けば良いか探るために起動時にどんな順番で処理が遷移するか主なファイルにログを仕込んでみました。
取得した結果は以下のとおり。

1. ROOT/cake/bootstrap.php
2. ROOT/app/config/core.php
3. ROOT/cake/libs/cake_log.php
4. ROOT/cake/config/config.php
5. ROOT/app/config/bootstrap.php
6. ROOT/cake/dispatcher.php

ということで、cake_log.php より前に呼ばれているAPP側のファイルは core.phpだけなのでここになにか仕込めば良さそうです。

実際のログ出力は、LOG4PHPを使うなりsyslogを使うなり適宜実装し、APP/app_log.phpに以下のように CakeLogクラスを定義します。

# LOG4PHPを使う方法は以下がとでも参考になりました。

<?php
// app/app_log.php
class CakeLog {
  function CakeLog() {
    $args = func_get_args();
    if (method_exists($this, '__destruct')) {
      register_shutdown_function (array(&$this, '__destruct'));
    }
  }
  
  function __destruct() {
...
  }
  
  function write($type, $msg) {
...
  }

で、APP/config/core.phpの一番頭に以下のように記述すれば、上記クラスが使用されます。

<?php
require_once(dirname(dirname(__FILE__)).DS.'app_log.php') ;
...

COREに手を入れずにカスタマイズできてなかなか良いのではないかと思います。一度試してみてはいかがでしょうか?