CodeIgniterの学習 43 - ログ出力をZend_Logに置き換える
今日はCodeIgniterのログ出力をZend FrameworkのZend_Logに置き換える作業をする。
興味無い人には価値のない作業。
既に学習というより拡張の段階に入ってるかも。
(好みで拡張できるのがCIの良いところだと思うけど、
幕の内弁当が大好きな人には向かないかもね。一長一短だな。)
ログ機能を置き換える理由
1)興味があるから
CodeIgniterは最低限部分のみのフレームワーク、足りない機能はZend Frameworkで補強するパタンを作りたい。
ついでにZend Frameworkも使い倒せるようになりたい。
2)ログ機能を柔軟にしたいから
CodeIgniter標準のログ機能が貧弱だから。
ALERT以上のエラーはメール送信とか、ログをsyslog化するとか拡張したい。
自力で実装よりは、log4phpなりZend_Logなりを使った方がいいよね。
log4phpは以前使ったのだけど、結構ソース量(ステップ数)も多いし、部分的に修正が必要だったりして面倒だった記憶がある。
既にZend Frameworkを使えるようにしているので、
(CodeIgniterの学習 15 - Zend Framework を CodeIgniter上で使う http://d.hatena.ne.jp/dix3/20081003/1222980188)
今回はZend_Logでいく。
3)ログ記録の閾値($config['log_threshold'])の指定方法が貧弱だから
CI標準だと、
0 = Disables logging, Error logging TURNED OFF
1 = Error Messages (including PHP errors)
2 = Debug Messages
3 = Informational Messages
4 = All Messages
しか選べない。
こうじゃなくて、
開発時は、
$config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR','WARN','NOTICE','INFO','DEBUG');
運用時は、
$config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR');
みたいに閾値を柔軟にしたい。
(似たような実装は、http://codeigniter.com/wiki/MY_Log/ でもされている。)
ソース
手順のみ書く。いつもながら無保証。手順0:前提
(CodeIgniterの学習 15 - Zend Framework を CodeIgniter上で使う http://d.hatena.ne.jp/dix3/20081003/1222980188)
とかで既にZend Framework導入済のこと。
手順1:フック部分(system/application/config/hooks.php)でのini_set( 'include_path', APPPATH . 'my_classes/' )をやめる
手順0:前提でのやり方だと、
system/application/config/hooks.phpの$hook['pre_controller']経由で、application/hooks/MyClasses.php を呼びだし、
その中で
ini_set( 'include_path', APPPATH . 'my_classes/' );
とやっているのだが、
ini_setされる順序が、CI_Logが読み込まれた後になってしまうので、CI_Logを置き換えるMY_Log側でZend_Logが読み込めない。
なので、hookのpre_controller部分はコメントアウトした。
(他のhook部分は過去の別エントリでのhookなので今回は関係ない)
system/application/config/hooks.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /* | ------------------------------------------------------------------------- | Hooks | ------------------------------------------------------------------------- | This file lets you define "hooks" to extend CI without hacking the core | files. Please see the user guide for info: | | http://codeigniter.com/user_guide/general/hooks.html | */ //for my_classes //Zend Framework、その他独自移植ライブラリ用のパスの読み込み /* $hook['pre_controller'][] = array( 'class' => 'MyClasses', 'function' => 'index', 'filename' => 'MyClasses.php', 'filepath' => 'hooks' ); */ //開発時に自動的にプロファイラを有効にする $hook['post_controller_constructor'][] = array( 'class' => 'MyClasses', 'function' => 'enable_profiler', 'filename' => 'MyClasses.php', 'filepath' => 'hooks' ); //HTTPレスポンスヘッダの調整 $hook['post_controller'][] = array( 'class' => 'MyClasses', 'function' => 'set_header', 'filename' => 'MyClasses.php', 'filepath' => 'hooks' ); //for my_classes /* End of file hooks.php */ /* Location: ./system/application/config/hooks.php */ ?>
手順2:index.php でini_set( 'include_path', APPPATH . 'my_classes/' )する
手順1:でコメントアウトした代わりに、ドキュメントルート/index.phpでinclude_pathを追加する。
ドキュメントルート/index.php
<?php //上略 //独自のインクルードパスを追加するようにする ini_set( 'include_path', APPPATH . 'my_classes/' ); /* |--------------------------------------------------------------- | LOAD THE FRONT CONTROLLER |--------------------------------------------------------------- | | And away we go... | */ require_once BASEPATH.'codeigniter/CodeIgniter'.EXT; /* End of file index.php */ /* Location: ./index.php */ ?>
てなかんじ。(index.phpの終わりに近い部分にini_setを差し込む。)
極力コアに近い部分は書き換えたくないのでhookで読み込んでいたのだが、今回は仕方がない。
手順3:application/libraries/MY_Log.php を新設する
system/libraries/Log.phpの代わりに、application/libraries/MY_Log.phpを使う事が出来る。
とりあえずZend_Logへ置き換えただけなので中身の解説はしません。
後でもっと細かく制御できるように改良するつもり。
(Zend_Logの使い方は、http://framework.zend.com/manual/ja/zend.log.html)
application/libraries/MY_Log.php
<?php if ( ! defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); // ------------------------------------------------------------------------ /** * Logging Class */ class MY_Log extends CI_Log { var $log_path; /** * CI標準のロガーの区分けと機能が貧弱なので Zend_Logを使って拡張する * http://framework.zend.com/manual/ja/zend.log.html * EMERG = 0 ; // 緊急事態 (Emergency): システムが使用不可能です * ALERT = 1 ; // 警報 (Alert): 至急対応が必要です * CRIT = 2 ; // 危機 (Critical): 危機的な状況です * ERR = 3 ; // エラー (Error): エラーが発生しました (CI標準の'ERROR'も同義として扱う) * WARN = 4 ; // 警告 (Warning): 警告が発生しました * NOTICE = 5 ; // 注意 (Notice): 通常動作ですが、注意すべき状況です * INFO = 6 ; // 情報 (Informational): 情報メッセージ (CI標準の'INFO'も同義として扱う) * DEBUG = 7 ; // デバッグ (Debug): デバッグメッセージ (CI標準の'DEBUG'も同義として扱う) * (CI標準のALLは、上記の全組み合わせ、またlog_message('level', 'message')の引数levelがALLで呼び出された時は、INFO扱いで常に記録する) */ var $_threshold = array( 'EMERG', 'ALERT', 'CRIT', 'ERR', 'ERROR', 'WARN', 'NOTICE', 'INFO', 'DEBUG'); //var $_date_fmt = 'Y-m-d H:i:s'; var $_enabled = TRUE; var $logger; /** * Constructor * * @access public */ function MY_Log() { $config = &get_config(); $this -> log_path = ( $config['log_path'] != '' ) ? $config['log_path'] : BASEPATH . 'logs/'; if ( ! is_dir( $this -> log_path ) OR ! is_really_writable( $this -> log_path ) ) { $this -> _enabled = FALSE; } //if ( $config['log_date_format'] != '' ) { //$this -> _date_fmt = $config['log_date_format']; //} if ( $config['log_threshold'] != '' ) { $this -> _threshold = $config['log_threshold']; } if ( $this -> _enabled ) { // Zend_Logを使うようにする require_once( 'Zend/Log.php' ); require_once( 'Zend/Log/Writer/Stream.php' ); // TODO:ログレベルによってログファイルを分けられるにする $filepath = $this -> log_path . 'log-' . date( 'Y-m-d' ) . EXT; $writer = new Zend_Log_Writer_Stream( $filepath ); $this -> logger = new Zend_Log( $writer ); } } // -------------------------------------------------------------------- /** * Write Log File * * Generally this function will be called using the global log_message() function * * @access public * @param string $ the error level * @param string $ the error message * @param bool $ whether the error is a native PHP error * @return bool */ function write_log( $level = 'err', $msg, $php_error = FALSE )//$php_errorは使われていないけど引数はそのままにしておく { if ( $this -> _enabled === FALSE ) { return FALSE; } $level = strtoupper( $level ); if ( 'ERROR' === $level ) {//ERRORはERRと同義 $level = 'ERR'; } //ALL以外は、$config['log_threshold']を見てログを記録するか判定 if ( 'ALL' !== $level && !in_array($level , $this -> _threshold) ) { return FALSE; } if ( 'ALL' === $level ) {//ALLはINFOとして常に記録する $level = 'INFO'; } $this -> logger -> log( $msg, constant("Zend_Log::$level") ); return TRUE; } } // END Log Class /* End of file MY_Log.php */ /* Location: ./application/libraries/MY_Log.php */ ?>
手順4:system/application/config/config.phpの$config['log_threshold']を変更する
$config['log_threshold'] = 4;
とかしか書けないところを、
$config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR','WARN','NOTICE','INFO','DEBUG');
とか、
$config['log_threshold'] = array('EMERG','ALERT','CRIT');
とか書けるようにした。
log_thresholdの書き方が変わっても既存ソースの互換性を考えて、
log_message('error','えらーだよ'); → log_message('ERR','えらーだよ'); log_message('info','いんふぉだよ'); → log_message('INFO','いんふぉだよ'); log_message('debug','でばっぐだよ'); → log_message('DEBUG','でばっぐだよ'); log_message('all','おーるだよ'); → log_message('ALL','おーるだよ');
として扱えるように、手順3のソース内で吸収しているつもり。
ちなみにlog_message('all','おーるだよ');の場合は、
log_thresholdの設定に関わらず、INFO扱いでログを吐くようにしている。
($config['log_threshold'] =''の時を除く、$config['log_threshold'] =array();の時は吐かれる。)
system/application/config/config.php
<?php //上略 /* |-------------------------------------------------------------------------- | Error Logging Threshold |-------------------------------------------------------------------------- | | If you have enabled error logging, you can set an error threshold to | determine what gets logged. Threshold options are: | You can enable error logging by setting a threshold over zero. The | threshold determines what gets logged. Threshold options are: | | 0 = Disables logging, Error logging TURNED OFF | 1 = Error Messages (including PHP errors) | 2 = Debug Messages | 3 = Informational Messages | 4 = All Messages | | For a live site you'll usually only enable Errors (1) to be logged otherwise | your log files will fill up very fast. | */ //$config['log_threshold'] = 4; /** * CI標準のロガーの区分けと機能が貧弱なので Zend_Logを使って拡張する * http://framework.zend.com/manual/ja/zend.log.html * EMERG = 0 ; // 緊急事態 (Emergency): システムが使用不可能です * ALERT = 1 ; // 警報 (Alert): 至急対応が必要です * CRIT = 2 ; // 危機 (Critical): 危機的な状況です * ERR = 3 ; // エラー (Error): エラーが発生しました (CI標準の'ERROR'も同義として扱う) * WARN = 4 ; // 警告 (Warning): 警告が発生しました * NOTICE = 5 ; // 注意 (Notice): 通常動作ですが、注意すべき状況です * INFO = 6 ; // 情報 (Informational): 情報メッセージ (CI標準の'INFO'も同義として扱う) * DEBUG = 7 ; // デバッグ (Debug): デバッグメッセージ (CI標準の'DEBUG'も同義として扱う) * (CI標準のALLは、上記の全組み合わせ、またlog_message('level', 'message')の引数levelがALLで呼び出された時は、INFO扱いで常に記録する) */ $config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR','WARN','NOTICE','INFO','DEBUG');//数値指定ではなく、出したいログの種類を指定する(数値では指定しない事) //下略 ?>
動作確認
適当なコントローラとかで、
log_message('emerg','emergだよ'); log_message('alert','alertだよ'); log_message('crit','critだよ'); log_message('err','errだよ1'); log_message('error','errだよ2'); log_message('warn','warnだよ'); log_message('notice','noticeだよ'); log_message('info','infoだよ'); log_message('debug','debugだよ'); log_message('all','allだよ');
を貼って、ログを吐いてみる。
なおログファイルは、今のところはCI標準のログファイルと同じ、system/logs/log-yyyy-mm-dd.php に保存している。
1)$config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR','WARN');の時
こんなかんじ。
2)$config['log_threshold'] = array('EMERG','ALERT','CRIT','ERR','WARN','NOTICE','INFO','DEBUG');の時
こんなかんじ。
差し替えてもパフォーマンスへの影響は誤差の範囲位に感じる。
今日はここまで。今後さらに拡張していきたい。