123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- <?php
- /**
- * CLogger class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.yiiframework.com/
- * @copyright 2008-2013 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- /**
- * CLogger records log messages in memory.
- *
- * CLogger implements the methods to retrieve the messages with
- * various filter conditions, including log levels and log categories.
- *
- * @property array $logs List of messages. Each array element represents one message
- * with the following structure:
- * array(
- * [0] => message (string)
- * [1] => level (string)
- * [2] => category (string)
- * [3] => timestamp (float, obtained by microtime(true));.
- * @property float $executionTime The total time for serving the current request.
- * @property integer $memoryUsage Memory usage of the application (in bytes).
- * @property array $profilingResults The profiling results.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @package system.logging
- * @since 1.0
- */
- class CLogger extends CComponent
- {
- const LEVEL_TRACE='trace';
- const LEVEL_WARNING='warning';
- const LEVEL_ERROR='error';
- const LEVEL_INFO='info';
- const LEVEL_PROFILE='profile';
- /**
- * @var integer how many messages should be logged before they are flushed to destinations.
- * Defaults to 10,000, meaning for every 10,000 messages, the {@link flush} method will be
- * automatically invoked once. If this is 0, it means messages will never be flushed automatically.
- * @since 1.1.0
- */
- public $autoFlush=10000;
- /**
- * @var boolean this property will be passed as the parameter to {@link flush()} when it is
- * called in {@link log()} due to the limit of {@link autoFlush} being reached.
- * By default, this property is false, meaning the filtered messages are still kept in the memory
- * by each log route after calling {@link flush()}. If this is true, the filtered messages
- * will be written to the actual medium each time {@link flush()} is called within {@link log()}.
- * @since 1.1.8
- */
- public $autoDump=false;
- /**
- * @var array log messages
- */
- private $_logs=array();
- /**
- * @var integer number of log messages
- */
- private $_logCount=0;
- /**
- * @var array log levels for filtering (used when filtering)
- */
- private $_levels;
- /**
- * @var array log categories for filtering (used when filtering)
- */
- private $_categories;
- /**
- * @var array log categories for excluding from filtering (used when filtering)
- */
- private $_except=array();
- /**
- * @var array the profiling results (category, token => time in seconds)
- */
- private $_timings;
- /**
- * @var boolean if we are processing the log or still accepting new log messages
- * @since 1.1.9
- */
- private $_processing=false;
- /**
- * Logs a message.
- * Messages logged by this method may be retrieved back via {@link getLogs}.
- * @param string $message message to be logged
- * @param string $level level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive.
- * @param string $category category of the message (e.g. 'system.web'). It is case-insensitive.
- * @see getLogs
- */
- public function log($message,$level='info',$category='application')
- {
- $this->_logs[]=array($message,$level,$category,microtime(true));
- $this->_logCount++;
- if($this->autoFlush>0 && $this->_logCount>=$this->autoFlush && !$this->_processing)
- {
- $this->_processing=true;
- $this->flush($this->autoDump);
- $this->_processing=false;
- }
- }
- /**
- * Retrieves log messages.
- *
- * Messages may be filtered by log levels and/or categories.
- * A level filter is specified by a list of levels separated by comma or space
- * (e.g. 'trace, error'). A category filter is similar to level filter
- * (e.g. 'system, system.web'). A difference is that in category filter
- * you can use pattern like 'system.*' to indicate all categories starting
- * with 'system'.
- *
- * If you do not specify level filter, it will bring back logs at all levels.
- * The same applies to category filter.
- *
- * Level filter and category filter are combinational, i.e., only messages
- * satisfying both filter conditions will be returned.
- *
- * @param string $levels level filter
- * @param array|string $categories category filter
- * @param array|string $except list of log categories to ignore
- * @return array list of messages. Each array element represents one message
- * with the following structure:
- * array(
- * [0] => message (string)
- * [1] => level (string)
- * [2] => category (string)
- * [3] => timestamp (float, obtained by microtime(true));
- */
- public function getLogs($levels='',$categories=array(), $except=array())
- {
- $this->_levels=preg_split('/[\s,]+/',strtolower($levels),-1,PREG_SPLIT_NO_EMPTY);
- if (is_string($categories))
- $this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
- else
- $this->_categories=array_filter(array_map('strtolower',$categories));
- if (is_string($except))
- $this->_except=preg_split('/[\s,]+/',strtolower($except),-1,PREG_SPLIT_NO_EMPTY);
- else
- $this->_except=array_filter(array_map('strtolower',$except));
- $ret=$this->_logs;
- if(!empty($levels))
- $ret=array_values(array_filter($ret,array($this,'filterByLevel')));
- if(!empty($this->_categories) || !empty($this->_except))
- $ret=array_values(array_filter($ret,array($this,'filterByCategory')));
- return $ret;
- }
- /**
- * Filter function used by {@link getLogs}
- * @param array $value element to be filtered
- * @return boolean true if valid log, false if not.
- */
- private function filterByCategory($value)
- {
- return $this->filterAllCategories($value, 2);
- }
- /**
- * Filter function used by {@link getProfilingResults}
- * @param array $value element to be filtered
- * @return boolean true if valid timing entry, false if not.
- */
- private function filterTimingByCategory($value)
- {
- return $this->filterAllCategories($value, 1);
- }
- /**
- * Filter function used to filter included and excluded categories
- * @param array $value element to be filtered
- * @param integer $index index of the values array to be used for check
- * @return boolean true if valid timing entry, false if not.
- */
- private function filterAllCategories($value, $index)
- {
- $cat=strtolower($value[$index]);
- $ret=empty($this->_categories);
- foreach($this->_categories as $category)
- {
- if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
- $ret=true;
- }
- if($ret)
- {
- foreach($this->_except as $category)
- {
- if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
- $ret=false;
- }
- }
- return $ret;
- }
- /**
- * Filter function used by {@link getLogs}
- * @param array $value element to be filtered
- * @return boolean true if valid log, false if not.
- */
- private function filterByLevel($value)
- {
- return in_array(strtolower($value[1]),$this->_levels);
- }
- /**
- * Returns the total time for serving the current request.
- * This method calculates the difference between now and the timestamp
- * defined by constant YII_BEGIN_TIME.
- * To estimate the execution time more accurately, the constant should
- * be defined as early as possible (best at the beginning of the entry script.)
- * @return float the total time for serving the current request.
- */
- public function getExecutionTime()
- {
- return microtime(true)-YII_BEGIN_TIME;
- }
- /**
- * Returns the memory usage of the current application.
- * This method relies on the PHP function memory_get_usage().
- * If it is not available, the method will attempt to use OS programs
- * to determine the memory usage. A value 0 will be returned if the
- * memory usage can still not be determined.
- * @return integer memory usage of the application (in bytes).
- */
- public function getMemoryUsage()
- {
- if(function_exists('memory_get_usage'))
- return memory_get_usage();
- else
- {
- $output=array();
- if(strncmp(PHP_OS,'WIN',3)===0)
- {
- exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST',$output);
- return isset($output[5])?preg_replace('/[\D]/','',$output[5])*1024 : 0;
- }
- else
- {
- $pid=getmypid();
- exec("ps -eo%mem,rss,pid | grep $pid", $output);
- $output=explode(" ",$output[0]);
- return isset($output[1]) ? $output[1]*1024 : 0;
- }
- }
- }
- /**
- * Returns the profiling results.
- * The results may be filtered by token and/or category.
- * If no filter is specified, the returned results would be an array with each element
- * being array($token,$category,$time).
- * If a filter is specified, the results would be an array of timings.
- *
- * Since 1.1.11, filtering results by category supports the same format used for filtering logs in
- * {@link getLogs}, and similarly supports filtering by multiple categories and wildcard.
- * @param string $token token filter. Defaults to null, meaning not filtered by token.
- * @param string $categories category filter. Defaults to null, meaning not filtered by category.
- * @param boolean $refresh whether to refresh the internal timing calculations. If false,
- * only the first time calling this method will the timings be calculated internally.
- * @return array the profiling results.
- */
- public function getProfilingResults($token=null,$categories=null,$refresh=false)
- {
- if($this->_timings===null || $refresh)
- $this->calculateTimings();
- if($token===null && $categories===null)
- return $this->_timings;
- $timings = $this->_timings;
- if($categories!==null) {
- $this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
- $timings=array_filter($timings,array($this,'filterTimingByCategory'));
- }
- $results=array();
- foreach($timings as $timing)
- {
- if($token===null || $timing[0]===$token)
- $results[]=$timing[2];
- }
- return $results;
- }
- private function calculateTimings()
- {
- $this->_timings=array();
- $stack=array();
- foreach($this->_logs as $log)
- {
- if($log[1]!==CLogger::LEVEL_PROFILE)
- continue;
- list($message,$level,$category,$timestamp)=$log;
- if(!strncasecmp($message,'begin:',6))
- {
- $log[0]=substr($message,6);
- $stack[]=$log;
- }
- elseif(!strncasecmp($message,'end:',4))
- {
- $token=substr($message,4);
- if(($last=array_pop($stack))!==null && $last[0]===$token)
- {
- $delta=$log[3]-$last[3];
- $this->_timings[]=array($message,$category,$delta);
- }
- else
- throw new CException(Yii::t('yii','CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
- array('{token}'=>$token)));
- }
- }
- $now=microtime(true);
- while(($last=array_pop($stack))!==null)
- {
- $delta=$now-$last[3];
- $this->_timings[]=array($last[0],$last[2],$delta);
- }
- }
- /**
- * Removes all recorded messages from the memory.
- * This method will raise an {@link onFlush} event.
- * The attached event handlers can process the log messages before they are removed.
- * @param boolean $dumpLogs whether to process the logs immediately as they are passed to log route
- * @since 1.1.0
- */
- public function flush($dumpLogs=false)
- {
- $this->onFlush(new CEvent($this, array('dumpLogs'=>$dumpLogs)));
- $this->_logs=array();
- $this->_logCount=0;
- }
- /**
- * Raises an <code>onFlush</code> event.
- * @param CEvent $event the event parameter
- * @since 1.1.0
- */
- public function onFlush($event)
- {
- $this->raiseEvent('onFlush', $event);
- }
- }
|