5
* @author Qiang Xue <qiang.xue@gmail.com>
6
* @link http://www.yiiframework.com/
7
* @copyright Copyright © 2008-2011 Yii Software LLC
8
* @license http://www.yiiframework.com/license/
12
* CLogger records log messages in memory.
14
* CLogger implements the methods to retrieve the messages with
15
* various filter conditions, including log levels and log categories.
17
* @author Qiang Xue <qiang.xue@gmail.com>
18
* @version $Id: CLogger.php 2799 2011-01-01 19:31:13Z qiang.xue $
19
* @package system.logging
22
class CLogger extends CComponent
24
const LEVEL_TRACE='trace';
25
const LEVEL_WARNING='warning';
26
const LEVEL_ERROR='error';
27
const LEVEL_INFO='info';
28
const LEVEL_PROFILE='profile';
31
* @var integer how many messages should be logged before they are flushed to destinations.
32
* Defaults to 10,000, meaning for every 10,000 messages, the {@link flush} method will be
33
* automatically invoked once. If this is 0, it means messages will never be flushed automatically.
36
public $autoFlush=10000;
38
* @var array log messages
40
private $_logs=array();
42
* @var integer number of log messages
46
* @var array log levels for filtering (used when filtering)
50
* @var array log categories for filtering (used when filtering)
54
* @var array the profiling results (category, token => time in seconds)
61
* Messages logged by this method may be retrieved back via {@link getLogs}.
62
* @param string $message message to be logged
63
* @param string $level level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive.
64
* @param string $category category of the message (e.g. 'system.web'). It is case-insensitive.
67
public function log($message,$level='info',$category='application')
69
$this->_logs[]=array($message,$level,$category,microtime(true));
71
if($this->autoFlush>0 && $this->_logCount>=$this->autoFlush)
76
* Retrieves log messages.
78
* Messages may be filtered by log levels and/or categories.
79
* A level filter is specified by a list of levels separated by comma or space
80
* (e.g. 'trace, error'). A category filter is similar to level filter
81
* (e.g. 'system, system.web'). A difference is that in category filter
82
* you can use pattern like 'system.*' to indicate all categories starting
85
* If you do not specify level filter, it will bring back logs at all levels.
86
* The same applies to category filter.
88
* Level filter and category filter are combinational, i.e., only messages
89
* satisfying both filter conditions will be returned.
91
* @param string $levels level filter
92
* @param string $categories category filter
93
* @return array list of messages. Each array elements represents one message
94
* with the following structure:
96
* [0] => message (string)
97
* [1] => level (string)
98
* [2] => category (string)
99
* [3] => timestamp (float, obtained by microtime(true));
101
public function getLogs($levels='',$categories='')
103
$this->_levels=preg_split('/[\s,]+/',strtolower($levels),-1,PREG_SPLIT_NO_EMPTY);
104
$this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
105
if(empty($levels) && empty($categories))
107
else if(empty($levels))
108
return array_values(array_filter(array_filter($this->_logs,array($this,'filterByCategory'))));
109
else if(empty($categories))
110
return array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevel'))));
113
$ret=array_values(array_filter(array_filter($this->_logs,array($this,'filterByLevel'))));
114
return array_values(array_filter(array_filter($ret,array($this,'filterByCategory'))));
119
* Filter function used by {@link getLogs}
120
* @param array $value element to be filtered
121
* @return array valid log, false if not.
123
private function filterByCategory($value)
125
foreach($this->_categories as $category)
127
$cat=strtolower($value[2]);
128
if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
135
* Filter function used by {@link getLogs}
136
* @param array $value element to be filtered
137
* @return array valid log, false if not.
139
private function filterByLevel($value)
141
return in_array(strtolower($value[1]),$this->_levels)?$value:false;
145
* Returns the total time for serving the current request.
146
* This method calculates the difference between now and the timestamp
147
* defined by constant YII_BEGIN_TIME.
148
* To estimate the execution time more accurately, the constant should
149
* be defined as early as possible (best at the beginning of the entry script.)
150
* @return float the total time for serving the current request.
152
public function getExecutionTime()
154
return microtime(true)-YII_BEGIN_TIME;
158
* Returns the memory usage of the current application.
159
* This method relies on the PHP function memory_get_usage().
160
* If it is not available, the method will attempt to use OS programs
161
* to determine the memory usage. A value 0 will be returned if the
162
* memory usage can still not be determined.
163
* @return integer memory usage of the application (in bytes).
165
public function getMemoryUsage()
167
if(function_exists('memory_get_usage'))
168
return memory_get_usage();
172
if(strncmp(PHP_OS,'WIN',3)===0)
174
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST',$output);
175
return isset($output[5])?preg_replace('/[\D]/','',$output[5])*1024 : 0;
180
exec("ps -eo%mem,rss,pid | grep $pid", $output);
181
$output=explode(" ",$output[0]);
182
return isset($output[1]) ? $output[1]*1024 : 0;
188
* Returns the profiling results.
189
* The results may be filtered by token and/or category.
190
* If no filter is specified, the returned results would be an array with each element
191
* being array($token,$category,$time).
192
* If a filter is specified, the results would be an array of timings.
193
* @param string $token token filter. Defaults to null, meaning not filtered by token.
194
* @param string $category category filter. Defaults to null, meaning not filtered by category.
195
* @param boolean $refresh whether to refresh the internal timing calculations. If false,
196
* only the first time calling this method will the timings be calculated internally.
197
* @return array the profiling results.
200
public function getProfilingResults($token=null,$category=null,$refresh=false)
202
if($this->_timings===null || $refresh)
203
$this->calculateTimings();
204
if($token===null && $category===null)
205
return $this->_timings;
207
foreach($this->_timings as $timing)
209
if(($category===null || $timing[1]===$category) && ($token===null || $timing[0]===$token))
210
$results[]=$timing[2];
215
private function calculateTimings()
217
$this->_timings=array();
220
foreach($this->_logs as $log)
222
if($log[1]!==CLogger::LEVEL_PROFILE)
224
list($message,$level,$category,$timestamp)=$log;
225
if(!strncasecmp($message,'begin:',6))
227
$log[0]=substr($message,6);
230
else if(!strncasecmp($message,'end:',4))
232
$token=substr($message,4);
233
if(($last=array_pop($stack))!==null && $last[0]===$token)
235
$delta=$log[3]-$last[3];
236
$this->_timings[]=array($message,$category,$delta);
239
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.',
240
array('{token}'=>$token)));
244
$now=microtime(true);
245
while(($last=array_pop($stack))!==null)
247
$delta=$now-$last[3];
248
$this->_timings[]=array($last[0],$last[2],$delta);
253
* Removes all recorded messages from the memory.
254
* This method will raise an {@link onFlush} event.
255
* The attached event handlers can process the log messages before they are removed.
258
public function flush()
260
$this->onFlush(new CEvent($this));
261
$this->_logs=array();
266
* Raises an <code>onFlush</code> event.
267
* @param CEvent $event the event parameter
270
public function onFlush($event)
272
$this->raiseEvent('onFlush', $event);