Source for file Logger.php

Documentation is available at Logger.php

  1. <?php
  2. // vim: foldmethod=marker
  3. /**
  4.  *  Logger.php
  5.  *
  6.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  7.  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
  8.  *  @package    Ethna
  9.  *  @version    $Id: 6f58c77fd941bd5d0cb675e883b3864786eb832b $
  10.  */
  11.  
  12. /**
  13.  *  拡張ログプロパティ: ファイル出力
  14.  */
  15. define('LOG_FILE'<< 16);
  16.  
  17. /**
  18.  *  拡張ログプロパティ: 標準出力
  19.  */
  20. define('LOG_ECHO'<< 17);
  21.  
  22. // {{{ Ethna_Logger
  23. /**
  24.  *  ログ管理クラス
  25.  *
  26.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  27.  *  @access     public
  28.  *  @package    Ethna
  29.  */
  30. {
  31.     // {{{ properties
  32.     /**#@+
  33.      *  @access private
  34.      */
  35.  
  36.     /** @protected    array   ログファシリティ一覧 */
  37.     protected $log_facility_list = array(
  38.         'auth'      => array('name' => 'LOG_AUTH'),
  39.         'cron'      => array('name' => 'LOG_CRON'),
  40.         'daemon'    => array('name' => 'LOG_DAEMON'),
  41.         'kern'      => array('name' => 'LOG_KERN'),
  42.         'lpr'       => array('name' => 'LOG_LPR'),
  43.         'mail'      => array('name' => 'LOG_MAIL'),
  44.         'news'      => array('name' => 'LOG_NEWS'),
  45.         'syslog'    => array('name' => 'LOG_SYSLOG'),
  46.         'user'      => array('name' => 'LOG_USER'),
  47.         'uucp'      => array('name' => 'LOG_UUCP'),
  48.         'file'      => array('name' => 'LOG_FILE'),
  49.         'echo'      => array('name' => 'LOG_ECHO'),
  50.     );
  51.  
  52.     /** @protected    array   ログレベル一覧 */
  53.     protected $log_level_list = array(
  54.         'emerg'     => array('name' => 'LOG_EMERG',     'value' => 7),
  55.         'alert'     => array('name' => 'LOG_ALERT',     'value' => 6),
  56.         'crit'      => array('name' => 'LOG_CRIT',      'value' => 5),
  57.         'err'       => array('name' => 'LOG_ERR',       'value' => 4),
  58.         'warning'   => array('name' => 'LOG_WARNING',   'value' => 3),
  59.         'notice'    => array('name' => 'LOG_NOTICE',    'value' => 2),
  60.         'info'      => array('name' => 'LOG_INFO',      'value' => 1),
  61.         'debug'     => array('name' => 'LOG_DEBUG',     'value' => 0),
  62.     );
  63.  
  64.     /** @protected    object  Ethna_Controller    controllerオブジェクト */
  65.     protected $controller;
  66.  
  67.     /** @protected    object  Ethna_Controller    controllerオブジェクト($controllerの省略形) */
  68.     protected $ctl;
  69.  
  70.     /** @protected    array   ログファシリティ */
  71.     protected $facility = array();
  72.  
  73.     /** @protected    array   ログレベル */
  74.     protected $level = array();
  75.  
  76.     /** @protected    array   ログオプション */
  77.     protected $option = array();
  78.  
  79.     /** @protected    array   メッセージフィルタ(出力) */
  80.     protected $message_filter_do = array();
  81.  
  82.     /** @protected    array   メッセージフィルタ(無視) */
  83.     protected $message_filter_ignore = array();
  84.  
  85.     /** @protected    int     アラートレベル */
  86.     protected $alert_level;
  87.  
  88.     /** @protected    string  アラートメールアドレス */
  89.     protected $alert_mailaddress;
  90.  
  91.     /** @protected    array   Ethna_LogWriter ログ出力オブジェクト */
  92.     protected $writer = array();
  93.  
  94.     /** @protected    bool    ログ出力開始フラグ */
  95.     protected $is_begin = false;
  96.  
  97.     /** @protected    array   ログスタック(begin()前にlog()が呼び出された場合のスタック) */
  98.     protected $log_stack = array();
  99.  
  100.     /**#@-*/
  101.     // }}}
  102.     
  103.     // {{{ Ethna_Logger
  104.     /**
  105.      *  Ethna_Loggerクラスのコンストラクタ
  106.      *
  107.      *  @access public
  108.      *  @param  object  Ethna_Controller    $controller controllerオブジェクト
  109.      */
  110.     public function __construct($controller)
  111.     {
  112.         $this->controller = $controller;
  113.         $this->ctl = $this->controller;
  114.         $config $controller->getConfig();
  115.  
  116.         // ログファシリティテーブル補完(LOCAL0〜LOCAL8)
  117.         for ($i 0$i 8$i++{
  118.             if (defined("LOG_LOCAL$i")) {
  119.                 $this->log_facility_list["local$i"array('name' => "LOG_LOCAL$i");
  120.             }
  121.         }
  122.  
  123.         $config_log $config->get('log');
  124.  
  125.         // ログファシリティ
  126.         if (is_array($config_log)) {
  127.             $this->facility = array_keys($config_log);
  128.         else {
  129.             $this->facility = $this->_parseLogFacility($config->get('log_facility'));
  130.         }
  131.  
  132.         foreach ($this->facility as $f{
  133.             // ログレベル
  134.             if (isset($config_log[$f]['level'])) {
  135.                 $this->level[$f$this->_parseLogLevel($config_log[$f]['level']);
  136.             else if (($level $config->get("log_level_$f")) !== null{
  137.                 $this->level[$f$this->_parseLogLevel($level);
  138.             else if (($level $config->get("log_level")) !== null{
  139.                 $this->level[$f$this->_parseLogLevel($level);
  140.             else {
  141.                 $this->level[$fLOG_WARNING;
  142.             }
  143.  
  144.             // メッセージフィルタ(filter_do)
  145.             if (isset($config_log[$f]['filter_do'])) {
  146.                 $this->message_filter_do[$f$config_log[$f]['filter_do'];
  147.             else if (($filter $config->get("log_filter_do_$f")) !== null{
  148.                 $this->message_filter_do[$f$filter;
  149.             else if (($filter $config->get("log_filter_do")) !== null{
  150.                 $this->message_filter_do[$f$filter;
  151.             else {
  152.                 $this->message_filter_do[$f'';
  153.             }
  154.  
  155.             // メッセージフィルタ(filter_ignore)
  156.             if (isset($config_log[$f]['filter_ignore'])) {
  157.                 $this->message_filter_ignore[$f$config_log[$f]['filter_ignore'];
  158.             else if (($filter $config->get("log_filter_ignore_$f")) !== null{
  159.                 $this->message_filter_ignore[$f$filter;
  160.             else if (($filter $config->get("log_filter_ignore")) !== null{
  161.                 $this->message_filter_ignore[$f$filter;
  162.             else {
  163.                 $this->message_filter_ignore[$f'';
  164.             }
  165.  
  166.             // そのたオプション (unsetはせずにそのまま渡す)
  167.             if (isset($config_log[$f])) {
  168.                 $this->option[$f$config_log[$f];
  169.             else {
  170.                 $this->option[$farray();
  171.             }
  172.  
  173.             // 'option' によるオプション指定 (for B.C.)
  174.             if (isset($config_log[$f]['option'])) {
  175.                 $option $this->_parseLogOption($config_log[$f]['option']);
  176.             else if (($option $config->get("log_option_$f")) !== null{
  177.                 $option $this->_parseLogOption($option);
  178.             else if (($option $config->get("log_option")) !== null{
  179.                 $option $this->_parseLogOption($option);
  180.             }
  181.             if ($option !== null{
  182.                 $this->option[$farray_merge($this->option[$f]$option);
  183.             }
  184.         }
  185.  
  186.         // アラートオプション
  187.         $this->alert_level =
  188.             $this->_parseLogLevel($config->get('log_alert_level'));
  189.         $this->alert_mailaddress
  190.             = preg_split('/\s*,\s*/'$config->get('log_alert_mailaddress'));
  191.     }
  192.     // }}}
  193.  
  194.     // {{{ getLogFacility
  195.     /**
  196.      *  ログファシリティを取得する
  197.      *
  198.      *  @access public
  199.      *  @return mixed   ログファシリティ(ファシリティが1つ以下ならscalar、
  200.      *                   2つ以上なら配列を返す for B.C.)
  201.      */
  202.     public function getLogFacility()
  203.     {
  204.         if (is_array($this->facility)) {
  205.             if (count($this->facility== 0{
  206.                 return null;
  207.             else if (count($this->facility== 1{
  208.                 return $this->facility[0];
  209.             }
  210.         }
  211.         return $this->facility;
  212.     }
  213.     // }}}
  214.  
  215.     // {{{ errorLevelToLogLevel
  216.     /**
  217.      *  PHPエラーレベルをログレベルに変換する
  218.      *
  219.      *  @access public
  220.      *  @param  int     $errno  PHPエラーレベル
  221.      *  @return array   ログレベル(LOG_NOTICE,...), エラーレベル表示名("E_NOTICE"...)
  222.      *  @static
  223.      */
  224.     public static function errorLevelToLogLevel($errno)
  225.     {
  226.         switch ($errno{
  227.         case E_ERROR:           $code "E_ERROR"$level LOG_ERRbreak;
  228.         case E_WARNING:         $code "E_WARNING"$level LOG_WARNINGbreak;
  229.         case E_PARSE:           $code "E_PARSE"$level LOG_CRITbreak;
  230.         case E_NOTICE:          $code "E_NOTICE"$level LOG_NOTICEbreak;
  231.         case E_USER_ERROR:      $code "E_USER_ERROR"$level LOG_ERRbreak;
  232.         case E_USER_WARNING:    $code "E_USER_WARNING"$level LOG_WARNINGbreak;
  233.         case E_USER_NOTICE:     $code "E_USER_NOTICE"$level LOG_NOTICEbreak;
  234.         case E_STRICT:          $code "E_STRICT"$level LOG_NOTICEreturn;
  235.         default:                $code "E_UNKNOWN"$level LOG_DEBUGbreak;
  236.         }
  237.         return array($level$code);
  238.     }
  239.     // }}}
  240.  
  241.     // {{{ begin
  242.     /**
  243.      *  ログ出力を開始する
  244.      *
  245.      *  @access public
  246.      */
  247.     public function begin()
  248.     {
  249.         // LogWriterクラスの生成
  250.         foreach ($this->facility as $f{
  251.             $this->writer[$f$this->_getLogWriter($this->option[$f]$f);
  252.             if (Ethna::isError($this->writer[$f])) {
  253.                 // use default
  254.                 $this->writer[$f$this->_getLogWriter($this->option[$f],
  255.                                                           "default");
  256.             }
  257.         }
  258.  
  259.         foreach (array_keys($this->writeras $key{
  260.             $this->writer[$key]->begin();
  261.         }
  262.         
  263.         $this->is_begin = true;
  264.  
  265.         // begin()以前のlog()を処理
  266.         if (count($this->log_stack0{
  267.             // copy and clear for recursive calls
  268.             $tmp_stack $this->log_stack;
  269.             $this->log_stack = array();
  270.  
  271.             while (count($tmp_stack0{
  272.                 $log array_shift($tmp_stack);
  273.                 $this->log($log[0]$log[1]);
  274.             }
  275.         }
  276.     }
  277.     // }}}
  278.  
  279.     // {{{ log
  280.     /**
  281.      *  ログを出力する
  282.      *
  283.      *  @access public
  284.      *  @param  int     $level      ログレベル(LOG_DEBUG, LOG_NOTICE...)
  285.      *  @param  string  $message    ログメッセージ(+引数)
  286.      */
  287.     public function log($level$message)
  288.     {
  289.         if ($this->is_begin == false{
  290.             $args func_get_args();
  291.             if (count($args2{
  292.                 array_splice($args02);
  293.                 $message vsprintf($message$args);
  294.             }
  295.             $this->log_stack[array($level$message);
  296.             return;
  297.         }
  298.  
  299.         foreach (array_keys($this->writeras $key{
  300.             // ログメッセージフィルタ(レベルフィルタに優先する)
  301.             $r $this->_evalMessageMask($this->message_filter_do[$key]$message);
  302.             if (is_null($r)) {
  303.                 $r $this->_evalMessageMask($this->message_filter_ignore[$key],
  304.                                              $message);
  305.                 if ($r{
  306.                     continue;
  307.                 }
  308.             }
  309.  
  310.             // ログレベルフィルタ
  311.             if ($this->_evalLevelMask($this->level[$key]$level)) {
  312.                 continue;
  313.             }
  314.  
  315.             // ログ出力
  316.             $args func_get_args();
  317.             if (count($args2{
  318.                 array_splice($args02);
  319.                 $message vsprintf($message$args);
  320.             }
  321.             $output $this->writer[$key]->log($level$message);
  322.         }
  323.  
  324.         // アラート処理
  325.         if ($this->_evalLevelMask($this->alert_level$level== false{
  326.             if (count($this->alert_mailaddress0{
  327.                 $this->_alert($output);
  328.             }
  329.         }
  330.     }
  331.     // }}}
  332.  
  333.     // {{{ end
  334.     /**
  335.      *  ログ出力を終了する
  336.      *
  337.      *  @access public
  338.      */
  339.     public function end()
  340.     {
  341.         foreach (array_keys($this->writeras $key{
  342.             $this->writer[$key]->end();
  343.         }
  344.  
  345.         $this->is_begin = false;
  346.     }
  347.     // }}}
  348.  
  349.     // {{{ _getLogWriter
  350.     /**
  351.      *  LogWriterオブジェクトを取得する
  352.      *
  353.      *  @access protected
  354.      *  @param  array   $option     ログオプション
  355.      *  @param  string  $facility   ログファシリティ
  356.      *  @return object  LogWriter   LogWriterオブジェクト
  357.      */
  358.     protected function _getLogWriter($option$facility null)
  359.     {
  360.         if ($facility == null{
  361.             $facility $this->getLogFacility();
  362.             if (is_array($facility)) {
  363.                 $facility $facility[0];
  364.             }
  365.         }
  366.  
  367.         if (is_null($facility)) {
  368.             $plugin "default";
  369.         else if (isset($this->log_facility_list[$facility])) {
  370.             if ($facility == "file" || $facility == "echo"{
  371.                 $plugin $facility;
  372.  
  373.             else {
  374.                 $plugin "syslog";
  375.             }
  376.         else {
  377.             $plugin $facility;
  378.         }
  379.  
  380.         $plugin_manager $this->controller->getPlugin();
  381.         $plugin_object $plugin_manager->getPlugin('Logwriter',
  382.                                                     ucfirst(strtolower($plugin)));
  383.         if (Ethna::isError($plugin_object)) {
  384.             return $plugin_object;
  385.         }
  386.  
  387.         if (isset($option['ident']== false{
  388.             $option['ident'$this->controller->getAppId();
  389.         }
  390.         if (isset($option['facility']== false{
  391.             $option['facility'$facility;
  392.         }
  393.         $plugin_object->setOption($option);
  394.  
  395.         return $plugin_object;
  396.     }
  397.     // }}}
  398.  
  399.     // {{{ _alert
  400.     /**
  401.      *  アラートメールを送信する
  402.      *
  403.      *  @access protected
  404.      *  @param  string  $message    ログメッセージ
  405.      *  @return int     0:正常終了
  406.      *  @deprecated
  407.      */
  408.     protected function _alert($message)
  409.     {
  410.         restore_error_handler();
  411.  
  412.         // ヘッダ
  413.         $header "Mime-Version: 1.0\n";
  414.         $header .= "Content-Type: text/plain; charset=ISO-2022-JP\n";
  415.         $header .= "X-Alert: " $this->controller->getAppId();
  416.         $subject sprintf("[%s] alert (%s%s)\n",
  417.                            $this->controller->getAppId(),
  418.                            substr($message012),
  419.                            strlen($message12 "..." "");
  420.         
  421.         // 本文
  422.         $mail sprintf("--- [log message] ---\n%s\n\n"$message);
  423.         if (function_exists("debug_backtrace")) {
  424.             $bt debug_backtrace();
  425.             $mail .= sprintf("--- [backtrace] ---\n%s\n",
  426.                              Ethna_Util::FormatBacktrace($bt));
  427.         }
  428.  
  429.         foreach ($this->alert_mailaddress as $mailaddress{
  430.             mail($mailaddress,
  431.                  $subject,
  432.                  mb_convert_encoding($mail"ISO-2022-JP"),
  433.                  $header);
  434.         }
  435.  
  436.         set_error_handler("ethna_error_handler");
  437.  
  438.         return 0;
  439.     }
  440.     // }}}
  441.  
  442.     // {{{ _evalMessageMask
  443.     /**
  444.      *  ログメッセージのマスクチェックを行う
  445.      *
  446.      *  @access private
  447.      *  @param  string  $filter     フィルタ
  448.      *  @param  string  $message    ログメッセージ
  449.      *  @return mixed   true:match, null:skip
  450.      */
  451.     private  function _evalMessageMask($filter$message)
  452.     {
  453.         $regexp sprintf("/%s/"$filter);
  454.  
  455.         if ($filter && preg_match($regexp$message)) {
  456.             return true;
  457.         }
  458.  
  459.         return null;
  460.     }
  461.     // }}}
  462.  
  463.     // {{{ _evalLevelMask
  464.     /**
  465.      *  ログレベルのマスクチェックを行う
  466.      *
  467.      *  @access private
  468.      *  @param  int     $src    ログレベルマスク
  469.      *  @param  int     $dst    ログレベル
  470.      *  @return bool    true:閾値以下 false:閾値以上
  471.      */
  472.     private function _evalLevelMask($src$dst)
  473.     {
  474.         static $log_level_table null;
  475.  
  476.         if (is_null($log_level_table)) {
  477.             $log_level_table array();
  478.  
  479.             // ログレベルテーブル(逆引き)作成
  480.             foreach ($this->log_level_list as $key => $def{
  481.                 if (defined($def['name']== false{
  482.                     continue;
  483.                 }
  484.                 $log_level_table[constant($def['name'])$def['value'];
  485.             }
  486.         }
  487.  
  488.         // 知らないレベルなら出力しない
  489.         if (isset($log_level_table[$src]== false
  490.             || isset($log_level_table[$dst]== false{
  491.             return true;
  492.         }
  493.  
  494.         if ($log_level_table[$dst>= $log_level_table[$src]{
  495.             return false;
  496.         }
  497.  
  498.         return true;
  499.     }
  500.     // }}}
  501.  
  502.     // {{{ _parseLogOption
  503.     /**
  504.      *  ログオプション(設定ファイル値)を解析する
  505.      *
  506.      *  @access private
  507.      *  @param  mixed   $option ログオプション(設定ファイル値)
  508.      *  @return array   解析された設定ファイル値(アラート通知メールアドレス,
  509.      *                   アラート対象ログレベル, ログオプション)
  510.      */
  511.     private function _parseLogOption($option)
  512.     {
  513.         if (is_null($option)) {
  514.             return null;
  515.         else if (is_array($option)) {
  516.             return $option;
  517.         }
  518.  
  519.         $ret array();
  520.         $elts preg_split('/\s*,\s*/'$option);
  521.         foreach ($elts as $elt{
  522.             if (preg_match('/^(.*?)\s*:\s*(.*)/'$elt$match)) {
  523.                 $ret[$match[1]] $match[2];
  524.             else {
  525.                 $ret[$elttrue;
  526.             }
  527.         }
  528.  
  529.         return $ret;
  530.     }
  531.     // }}}
  532.  
  533.     // {{{ _parseLogFacility
  534.     /**
  535.      *  ログファシリティ(設定ファイル値)を解析する
  536.      *
  537.      *  @access private
  538.      *  @param  string  $facility   ログファシリティ(設定ファイル値)
  539.      *  @return array   ログファシリティ(LOG_LOCAL0, LOG_FILE...)を格納した配列
  540.      */
  541.     private function _parseLogFacility($facility)
  542.     {
  543.         $facility_list preg_split('/\s*,\s*/'$facility-1PREG_SPLIT_NO_EMPTY);
  544.         return $facility_list;
  545.     }
  546.     // }}}
  547.  
  548.     // {{{ _parseLogLevel
  549.     /**
  550.      *  ログレベル(設定ファイル値)を解析する
  551.      *
  552.      *  @access private
  553.      *  @param  string  $level  ログレベル(設定ファイル値)
  554.      *  @return int     ログレベル(LOG_DEBUG, LOG_NOTICE...)
  555.      */
  556.     private function _parseLogLevel($level)
  557.     {
  558.         if (isset($this->log_level_list[strtolower($level)]== false{
  559.             return null;
  560.         }
  561.         $constant_name $this->log_level_list[strtolower($level)]['name'];
  562.  
  563.         return constant($constant_name);
  564.     }
  565.     // }}}
  566. }
  567. // }}}

Documentation generated on Fri, 11 Nov 2011 03:58:31 +0900 by phpDocumentor 1.4.3