CodeIgniterにはログファイルを簡単に作成してくれる関数が用意されています。
エラー処理-log_message
http://codeigniter.jp/user_guide/3/general/errors.html?highlight=%E3%83%AD%E3%82%B0#log_message
この関数、CodeIgniterが読み込まれている範囲であればどこでも、
log_message('debug', 'デバックメッセージ');
と書けば、application/logs
の中にlog-2018-10-05.php
のような形でファイルが作成され、中身に
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
DEBUG - 2018-10-05 17:33:35 --> デバックメッセージ
というようなログが溜まっていきます(ただし、config/config.php
の設定やapplication/logs
へApacheの書き込み権限設定が必要)。
しかしこのログ、自分で仕込んだlog_message()
以外にもCodeIgniterのコア部分で多用されていることもあり、CodeIgniterが起動する度に結構なメッセージが書き込まれていきます。
時系列に沿って書き込まれていくとはいえ、欲を言えばカテゴリー別にログが取れればもっと便利にログを使えそうです。
なので作りました。
※対象はCodeIgniterの3系です。
Logクラスを拡張する
だいたいのCodeIgniterのコアクラスは自作のクラスで代替可能です。
ただし公式マニュアルが口酸っぱく警告しているとおり、ほとんどの場合はクラスを継承してメソッドをオーバーライドしていくほうが簡単かつ安全でしょう。
今回の拡張もLog.php
を継承したMY_Log.php
を作成することにします。
以下がクラスの全貌です。
class MY_Log extends CI_Log {
private function _write_log($level, $msg)
{
$category = isset($this->_levels[$level]) ? '' : $level.'-';
$filepath = $this->_log_path.'log-'.$category.date('Y-m-d').'.'.$this->_file_ext;
$message = '';
if ( ! file_exists($filepath))
{
$newfile = TRUE;
// Only add protection to php files
if ($this->_file_ext === 'php')
{
$message .= "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\n";
}
}
if ( ! $fp = @fopen($filepath, 'ab'))
{
return FALSE;
}
flock($fp, LOCK_EX);
// Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
if (strpos($this->_date_fmt, 'u') !== FALSE)
{
$microtime_full = microtime(TRUE);
$microtime_short = sprintf("%06d", ($microtime_full - floor($microtime_full)) * 1000000);
$date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full));
$date = $date->format($this->_date_fmt);
}
else
{
$date = date($this->_date_fmt);
}
$message .= $this->_format_line($level, $date, $msg);
for ($written = 0, $length = self::strlen($message); $written < $length; $written += $result)
{
if (($result = fwrite($fp, self::substr($message, $written))) === FALSE)
{
break;
}
}
flock($fp, LOCK_UN);
fclose($fp);
if (isset($newfile) && $newfile === TRUE)
{
chmod($filepath, $this->_file_permissions);
}
return is_int($result);
}
/**
* Write Log File
*
* Generally this function will be called using the global log_message() function
*
* @param string $level The error level: 'error', 'debug' or 'info'
* @param string $msg The error message
* @return bool
*/
public function write_log($level, $msg)
{
if ($this->_enabled === FALSE)
{
return FALSE;
}
$level = strtoupper($level);
if ( ! isset($this->_levels[$level]))
{
return $this->_write_log($level, $msg);
}
if (
isset($this->_levels[$level])
&& (($this->_levels[$level] > $this->_threshold))
&& ! isset($this->_threshold_array[$this->_levels[$level]])
)
{
return FALSE;
}
return $this->_write_log($level, $msg);
}
}
このコードをapplication/core/MY_Log.php
の位置に配置すると拡張処理が動作します。
どんなものが出力されるのか
先述のように、
log_message('debug', 'デバックメッセージ');
と書いても拡張前と書き出されるログは変わりません。
第一引数が「error」「debug」「info」の場合は変わらず、設定ファイルに設定できるしきい値に応じてデフォルトのログファイルに出力されたりされなかったりします。
今回の拡張が役立つのは第一引数にそれら以外の文字列を設定した場合です。
log_message('custome', 'カスタムメッセージ');
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
DEBUG - 2018-10-05 17:36:15 --> カスタムメッセージ
上述のように第一引数をcustome
と設定すると、デフォルトのログファイル名中、__log__と__日付__の間に第一引数と同じ文字列が挿入されたファイルが作成され、以後同じ第一引数が設定されたlog_message()
関数はそのファイルへログを追記します。
こうすることでログの種類によってログファイルをカテゴリー分けできるのです。