From a02e83d8971568617a1723a2bd2b60b79c78faa8 Mon Sep 17 00:00:00 2001 From: xue <> Date: Sun, 8 Jan 2006 20:10:27 +0000 Subject: Added documentation. --- framework/Log/TLogRouter.php | 357 ++++++++++++++++++++++++++++++++++++++----- framework/Log/TLogger.php | 73 +++++++++ 2 files changed, 395 insertions(+), 35 deletions(-) (limited to 'framework/Log') diff --git a/framework/Log/TLogRouter.php b/framework/Log/TLogRouter.php index 98bded1c..7a09c758 100644 --- a/framework/Log/TLogRouter.php +++ b/framework/Log/TLogRouter.php @@ -1,11 +1,59 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Log + */ + +/** + * TLogRouter class. + * + * TLogRouter manages routes that record log messages in different media different ways. + * For example, a file log route {@link TFileLogRoute} records log messages + * in log files. An email log route {@link TEmailLogRoute} sends log messages + * to email addresses. + * + * Log routes may be configured in application or page folder configuration files + * or an external configuration file specified by {@link setConfigFile ConfigFile}. + * The format is as follows, + * + * <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" /> + * <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" /> + * + * You can specify multiple routes with different filtering conditions and different + * targets, even if the routes are of the same type. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Log + * @since 3.0 + */ class TLogRouter extends TModule { + /** + * File extension of external configuration file + */ const CONFIG_FILE_EXT='.xml'; + /** + * @var array list of routes available + */ private $_routes=array(); + /** + * @var string external configuration file + */ private $_configFile=null; + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ public function init($config) { if($this->_configFile!==null) @@ -23,6 +71,11 @@ class TLogRouter extends TModule $this->getApplication()->attachEventHandler('EndRequest',array($this,'collectLogs')); } + /** + * Loads configuration from an XML element + * @param TXmlElement configuration node + * @throws TConfigurationException if log route class or type is not specified + */ private function loadConfig($xml) { foreach($xml->getElementsByTagName('route') as $routeConfig) @@ -32,7 +85,7 @@ class TLogRouter extends TModule throw new TConfigurationException('logrouter_routeclass_required'); $route=Prado::createComponent($class); if(!($route instanceof TLogRoute)) - throw new TConfigurationException('logrouter_routetype_required'); + throw new TConfigurationException('logrouter_routetype_invalid'); foreach($properties as $name=>$value) $route->setSubproperty($name,$value); $this->_routes[]=$route; @@ -40,27 +93,65 @@ class TLogRouter extends TModule } } + /** + * @return string external configuration file. Defaults to null. + */ public function getConfigFile() { return $this->_configFile; } + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ public function setConfigFile($value) { if(($this->_configFile=Prado::getPathOfNamespace($value,self::LOG_FILE_EXT))===null) throw new TConfigurationException('logrouter_configfile_invalid',$value); } + /** + * Collects log messages from a logger. + * This method is an event handler to application's EndRequest event. + * @param mixed event parameter + */ public function collectLogs($param) { + $logger=Prado::getLogger(); foreach($this->_routes as $route) - $route->collectLogs(); + $route->collectLogs($logger); } } - +/** + * TLogRoute class. + * + * TLogRoute is the base class for all log route classes. + * A log route object retrieves log messages from a logger and sends it + * somewhere, such as files, emails. + * The messages being retrieved may be filtered first before being sent + * to the destination. The filters include log level filter and log category filter. + * + * To specify level filter, set {@link setLevels Levels} property, + * which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). + * To specify category filter, set {@link setCategories Categories} property, + * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). + * + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Log + * @since 3.0 + */ abstract class TLogRoute extends TComponent { + /** + * @var array lookup table for level names + */ private static $_levelNames=array( TLogger::ERROR=>'Error', TLogger::DEBUG=>'Debug', @@ -71,6 +162,9 @@ abstract class TLogRoute extends TComponent TLogger::ALERT=>'Alert', TLogger::FATAL=>'Fatal' ); + /** + * @var array lookup table for level values + */ private static $_levelValues=array( 'error'=>TLogger::ERROR, 'debug'=>TLogger::DEBUG, @@ -81,115 +175,231 @@ abstract class TLogRoute extends TComponent 'alert'=>TLogger::ALERT, 'fatal'=>TLogger::FATAL ); + /** + * @var integer log level filter (bits) + */ private $_levels=null; + /** + * @var array log category filter + */ private $_categories=null; + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + */ public function init($config) { } + /** + * @return integer log level filter + */ public function getLevels() { return $this->_levels; } + /** + * @param integer|string integer log level filter (in bits). If the value is + * a string, it is assumed to be comma-separated level names. Valid level names + * include 'Error', 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'. + */ public function setLevels($levels) { - $this->_levels=null; - $levels=strtolower($levels); - foreach(explode(',',$levels) as $level) + if(is_integer($levels)) + $this->_levels=$levels; + else { - $level=trim($level); - if(isset(self::$_levelValues[$level])) - $this->_levels|=self::$_levelValues[$level]; + $this->_levels=null; + $levels=strtolower($levels); + foreach(explode(',',$levels) as $level) + { + $level=trim($level); + if(isset(self::$_levelValues[$level])) + $this->_levels|=self::$_levelValues[$level]; + } } } + /** + * @return array list of categories to be looked for + */ public function getCategories() { return $this->_categories; } + /** + * @param array|string list of categories to be looked for. If the value is a string, + * it is assumed to be comma-separated category names. + */ public function setCategories($categories) { - $this->_categories=null; - foreach(explode(',',$categories) as $category) + if(is_array($categories)) + $this->_categories=$categories; + else { - if(($category=trim($category))!=='') - $this->_categories[]=$category; + $this->_categories=null; + foreach(explode(',',$categories) as $category) + { + if(($category=trim($category))!=='') + $this->_categories[]=$category; + } } } + /** + * @param integer level value + * @return string level name + */ protected function getLevelName($level) { return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown'; } + /** + * @param string level name + * @return integer level value + */ + protected function getLevelValue($level) + { + return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0; + } + + /** + * Formats a log message given different fields. + * @param string message content + * @param integer message level + * @param string message category + * @param integer timestamp + * @return string formatted message + */ protected function formatLogMessage($message,$level,$category,$time) { return @date('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n"; } - public function collectLogs() + /** + * Retrieves log messages from logger to log route specific destination. + * @param TLogger logger instance + */ + public function collectLogs(TLogger $logger) { - $logs=Prado::getLogger()->getLogs($this->getLevels(),$this->getCategories()); + $logs=$logger->getLogs($this->getLevels(),$this->getCategories()); $this->processLogs($logs); } + /** + * Processes log messages and sends them to specific destination. + * Derived child classes must implement this method. + * @param array list of messages. Each array element is a (formatted) message string. + */ abstract protected function processLogs($logs); } +/** + * TFileLogRoute class. + * + * TFileLogRoute records log messages in files. + * The log files are stored under {@link setLogPath LogPath} and the file name + * is specified by {@link setLogFile LogFile}. If the size of the log file is + * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation + * is performed, which renames the current log file by suffixing the file name + * with '.1'. All existing log files are moved backwards one place, i.e., '.2' + * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles} + * specifies how many files to be kept. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Log + * @since 3.0 + */ class TFileLogRoute extends TLogRoute { + /** + * @var integer maximum log file size + */ private $_maxFileSize=1024; // in KB + /** + * @var integer number of log files used for rotation + */ private $_maxLogFiles=2; + /** + * @var string directory storing log files + */ private $_logPath=null; + /** + * @var string log file name + */ private $_logFile='prado.log'; - public function init($config) + /** + * @return string directory storing log files. Defaults to application runtime path. + */ + public function getLogPath() { if($this->_logPath===null) $this->_logPath=$this->getApplication()->getRuntimePath(); - } - - public function getLogPath() - { return $this->_logPath; } + /** + * @param string directory (in namespace format) storing log files. + * @throws TConfigurationException if log path is invalid + */ public function setLogPath($value) { - if(($this->_logPath=Prado::getPathOfNamespace($value))===null) + if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath)) throw new TConfigurationException('filelogroute_logpath_invalid',$value); } + /** + * @return string log file name. Defaults to 'prado.log'. + */ public function getLogFile() { return $this->_logFile; } + /** + * @param string log file name + */ public function setLogFile($value) { $this->_logFile=$value; } + /** + * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). + */ public function getMaxFileSize() { return $this->_maxFileSize; } + /** + * @param integer maximum log file size in kilo-bytes (KB). + * @throws TInvalidDataValueException if the value is smaller than 1. + */ public function setMaxFileSize($value) { $this->_maxFileSize=TPropertyValue::ensureInteger($value); - if($this->_maxFileSize<0) + if($this->_maxFileSize<=0) throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid'); } + /** + * @return integer number of files used for rotation. Defaults to 2. + */ public function getMaxLogFiles() { return $this->_maxLogFiles; } + /** + * @param integer number of files used for rotation. + */ public function setMaxLogFiles($value) { $this->_maxLogFiles=TPropertyValue::ensureInteger($value); @@ -197,18 +407,25 @@ class TFileLogRoute extends TLogRoute throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid'); } + /** + * Saves log messages in files. + * @param array list of log messages + */ protected function processLogs($logs) { - $logFile=$this->_logPath.'/'.$this->_logFile; + $logFile=$this->getLogPath().'/'.$this->getLogFile(); if(@filesize($logFile)>$this->_maxFileSize*1024) $this->rotateFiles(); foreach($logs as $log) error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile); } + /** + * Rotates log files. + */ protected function rotateFiles() { - $file=$this->_logPath.'/'.$this->_logFile; + $file=$this->getLogPath().'/'.$this->getLogFile(); for($i=$this->_maxLogFiles;$i>0;--$i) { $rotateFile=$file.'.'.$i; @@ -225,59 +442,129 @@ class TFileLogRoute extends TLogRoute } } +/** + * TEmailLogRoute class. + * + * TEmailLogRoute sends selected log messages to email addresses. + * The target email addresses may be specified via {@link setEmails Emails} property. + * Optionally, you may set the email {@link setSubject Subject} and the + * {@link setSentFrom SentFrom} address. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Log + * @since 3.0 + */ class TEmailLogRoute extends TLogRoute { + /** + * Regex pattern for email address. + */ const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/'; + /** + * Default email subject. + */ const DEFAULT_SUBJECT='Prado Application Log'; + /** + * @var array list of destination email addresses. + */ private $_emails=array(); + /** + * @var string email subject + */ private $_subject=''; + /** + * @var string email sent from address + */ private $_from=''; + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and + * 'sendmail_from' in php.ini is also empty. + */ + public function init($config) + { + if($this->_from==='') + $this->_from=ini_get('sendmail_from'); + if($this->_from==='') + throw new TConfigurationException('emaillogroute_sentfrom_required'); + } + + /** + * Sends log messages to specified email addresses. + * @param array list of log messages + */ protected function processLogs($logs) { $message=''; - $headers=($this->_from==='') ? '' : "From:{$this->_from}\r\n"; - $subject=$this->_subject===''?self::DEFAULT_SUBJECT:$this->_subject; foreach($logs as $log) $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]); $message=wordwrap($message,70); foreach($this->_emails as $email) - mail($email,$subject,$message,$headers); + mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n"); } + /** + * @return array list of destination email addresses + */ public function getEmails() { return $this->_emails; } + /** + * @return array|string list of destination email addresses. If the value is + * a string, it is assumed to be comma-separated email addresses. + */ public function setEmails($emails) { - $this->_emails=array(); - foreach(explode(',',$emails) as $email) + if(is_array($emails)) + $this->_emails=$emails; + else { - $email=trim($email); - if(preg_match(self::EMAIL_PATTERN,$email)) - $this->_emails[]=$email; + $this->_emails=array(); + foreach(explode(',',$emails) as $email) + { + $email=trim($email); + if(preg_match(self::EMAIL_PATTERN,$email)) + $this->_emails[]=$email; + } } } + /** + * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT + */ public function getSubject() { + if($this->_subject===null) + $this->_subject=self::DEFAULT_SUBJECT; return $this->_subject; } + /** + * @param string email subject. + */ public function setSubject($value) { $this->_subject=$value; } - public function getFrom() + /** + * @return string send from address of the email + */ + public function getSentFrom() { return $this->_from; } - public function setFrom($value) + /** + * @param string send from address of the email + */ + public function setSentFrom($value) { $this->_from=$value; } diff --git a/framework/Log/TLogger.php b/framework/Log/TLogger.php index f9b9b47a..fbe56409 100644 --- a/framework/Log/TLogger.php +++ b/framework/Log/TLogger.php @@ -1,7 +1,32 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System.Log + */ +/** + * TLogger class. + * + * TLogger records log messages in memory and implements the methods to + * retrieve the messages with filter conditions, including log levels and + * log categories. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Log + * @since 3.0 + */ class TLogger extends TComponent { + /** + * Log levels. + */ const DEBUG=0x01; const INFO=0x02; const NOTICE=0x04; @@ -9,15 +34,55 @@ class TLogger extends TComponent const ERROR=0x10; const ALERT=0x20; const FATAL=0x40; + /** + * @var array log messages + */ private $_logs=array(); + /** + * @var integer log levels (bits) to be filtered + */ private $_levels; + /** + * @var array list of categories to be filtered + */ private $_categories; + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link getLogs}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + */ public function log($message,$level,$category='Uncategorized') { $this->_logs[]=array($message,$level,$category,time()); } + /** + * Retrieves log messages. + * Messages may be filtered by log levels and/or categories. + * A level filter is specified by an integer, whose bits indicate the levels interested. + * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels. + * A category filter is specified by concatenating interested category names + * with commas. A message whose category name starts with any filtering category + * will be returned. For example, a category filter 'System.Web, System.IO' + * will return messages under categories such as 'System.Web', 'System.IO', + * 'System.Web.UI', 'System.Web.UI.WebControls', etc. + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * @param integer level filter + * @param string category filter + * @param array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message + * [1] => level + * [2] => category + * [3] => timestamp); + */ public function getLogs($levels=null,$categories=null) { $this->_levels=$levels; @@ -35,6 +100,10 @@ class TLogger extends TComponent } } + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ private function filterByCategories($value) { foreach($this->_categories as $category) @@ -45,6 +114,10 @@ class TLogger extends TComponent return false; } + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ private function filterByLevels($value) { if($value[1] & $this->_levels) -- cgit v1.2.3