PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/craft/app/framework/logging/CLogger.php

https://gitlab.com/madebycloud/derekman
PHP | 361 lines | 166 code | 23 blank | 172 comment | 28 complexity | 46cab0b32be7c2957bff3129aea3b3d6 MD5 | raw file
  1. <?php
  2. /**
  3. * CLogger class file
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CLogger records log messages in memory.
  12. *
  13. * CLogger implements the methods to retrieve the messages with
  14. * various filter conditions, including log levels and log categories.
  15. *
  16. * @property array $logs List of messages. Each array element represents one message
  17. * with the following structure:
  18. * array(
  19. * [0] => message (string)
  20. * [1] => level (string)
  21. * [2] => category (string)
  22. * [3] => timestamp (float, obtained by microtime(true));.
  23. * @property float $executionTime The total time for serving the current request.
  24. * @property integer $memoryUsage Memory usage of the application (in bytes).
  25. * @property array $profilingResults The profiling results.
  26. *
  27. * @author Qiang Xue <qiang.xue@gmail.com>
  28. * @package system.logging
  29. * @since 1.0
  30. */
  31. class CLogger extends CComponent
  32. {
  33. const LEVEL_TRACE='trace';
  34. const LEVEL_WARNING='warning';
  35. const LEVEL_ERROR='error';
  36. const LEVEL_INFO='info';
  37. const LEVEL_PROFILE='profile';
  38. /**
  39. * @var integer how many messages should be logged before they are flushed to destinations.
  40. * Defaults to 10,000, meaning for every 10,000 messages, the {@link flush} method will be
  41. * automatically invoked once. If this is 0, it means messages will never be flushed automatically.
  42. * @since 1.1.0
  43. */
  44. public $autoFlush=10000;
  45. /**
  46. * @var boolean this property will be passed as the parameter to {@link flush()} when it is
  47. * called in {@link log()} due to the limit of {@link autoFlush} being reached.
  48. * By default, this property is false, meaning the filtered messages are still kept in the memory
  49. * by each log route after calling {@link flush()}. If this is true, the filtered messages
  50. * will be written to the actual medium each time {@link flush()} is called within {@link log()}.
  51. * @since 1.1.8
  52. */
  53. public $autoDump=false;
  54. /* CORE HACK BEGIN */
  55. /**
  56. * @var array log messages
  57. */
  58. protected $_logs=array();
  59. /**
  60. * @var integer number of log messages
  61. */
  62. protected $_logCount=0;
  63. /**
  64. * @var array log levels for filtering (used when filtering)
  65. */
  66. protected $_levels;
  67. /**
  68. * @var array log categories for filtering (used when filtering)
  69. */
  70. protected $_categories;
  71. /**
  72. * @var array log categories for excluding from filtering (used when filtering)
  73. */
  74. protected $_except=array();
  75. /**
  76. * @var array the profiling results (category, token => time in seconds)
  77. */
  78. protected $_timings;
  79. /**
  80. * @var boolean if we are processing the log or still accepting new log messages
  81. * @since 1.1.9
  82. */
  83. protected $_processing=false;
  84. /* CORE HACK END */
  85. /**
  86. * Logs a message.
  87. * Messages logged by this method may be retrieved back via {@link getLogs}.
  88. * @param string $message message to be logged
  89. * @param string $level level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive.
  90. * @param string $category category of the message (e.g. 'system.web'). It is case-insensitive.
  91. * @see getLogs
  92. */
  93. public function log($message,$level='info',$category='application')
  94. {
  95. $this->_logs[]=array($message,$level,$category,microtime(true));
  96. $this->_logCount++;
  97. if($this->autoFlush>0 && $this->_logCount>=$this->autoFlush && !$this->_processing)
  98. {
  99. $this->_processing=true;
  100. $this->flush($this->autoDump);
  101. $this->_processing=false;
  102. }
  103. }
  104. /**
  105. * Retrieves log messages.
  106. *
  107. * Messages may be filtered by log levels and/or categories.
  108. * A level filter is specified by a list of levels separated by comma or space
  109. * (e.g. 'trace, error'). A category filter is similar to level filter
  110. * (e.g. 'system, system.web'). A difference is that in category filter
  111. * you can use pattern like 'system.*' to indicate all categories starting
  112. * with 'system'.
  113. *
  114. * If you do not specify level filter, it will bring back logs at all levels.
  115. * The same applies to category filter.
  116. *
  117. * Level filter and category filter are combinational, i.e., only messages
  118. * satisfying both filter conditions will be returned.
  119. *
  120. * @param string $levels level filter
  121. * @param array|string $categories category filter
  122. * @param array|string $except list of log categories to ignore
  123. * @return array list of messages. Each array element represents one message
  124. * with the following structure:
  125. * array(
  126. * [0] => message (string)
  127. * [1] => level (string)
  128. * [2] => category (string)
  129. * [3] => timestamp (float, obtained by microtime(true));
  130. */
  131. public function getLogs($levels='',$categories=array(), $except=array())
  132. {
  133. $this->_levels=preg_split('/[\s,]+/',strtolower($levels),-1,PREG_SPLIT_NO_EMPTY);
  134. if (is_string($categories))
  135. $this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
  136. else
  137. $this->_categories=array_filter(array_map('strtolower',$categories));
  138. if (is_string($except))
  139. $this->_except=preg_split('/[\s,]+/',strtolower($except),-1,PREG_SPLIT_NO_EMPTY);
  140. else
  141. $this->_except=array_filter(array_map('strtolower',$except));
  142. $ret=$this->_logs;
  143. if(!empty($levels))
  144. $ret=array_values(array_filter($ret,array($this,'filterByLevel')));
  145. if(!empty($this->_categories) || !empty($this->_except))
  146. $ret=array_values(array_filter($ret,array($this,'filterByCategory')));
  147. return $ret;
  148. }
  149. /* CORE HACK */
  150. /**
  151. * Filter function used by {@link getLogs}
  152. * @param array $value element to be filtered
  153. * @return boolean true if valid log, false if not.
  154. */
  155. protected function filterByCategory($value)
  156. {
  157. return $this->filterAllCategories($value, 2);
  158. }
  159. /* END CORE HACK */
  160. /**
  161. * Filter function used by {@link getProfilingResults}
  162. * @param array $value element to be filtered
  163. * @return boolean true if valid timing entry, false if not.
  164. */
  165. private function filterTimingByCategory($value)
  166. {
  167. return $this->filterAllCategories($value, 1);
  168. }
  169. /* CORE HACK */
  170. /**
  171. * Filter function used to filter included and excluded categories
  172. * @param array $value element to be filtered
  173. * @param integer $index index of the values array to be used for check
  174. * @return boolean true if valid timing entry, false if not.
  175. */
  176. protected function filterAllCategories($value, $index)
  177. {
  178. $cat=strtolower($value[$index]);
  179. $ret=empty($this->_categories);
  180. foreach($this->_categories as $category)
  181. {
  182. if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
  183. $ret=true;
  184. }
  185. if($ret)
  186. {
  187. foreach($this->_except as $category)
  188. {
  189. if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
  190. $ret=false;
  191. }
  192. }
  193. return $ret;
  194. }
  195. /* END CORE HACK */
  196. /* CORE HACK */
  197. /**
  198. * Filter function used by {@link getLogs}
  199. * @param array $value element to be filtered
  200. * @return boolean true if valid log, false if not.
  201. */
  202. protected function filterByLevel($value)
  203. {
  204. return in_array(strtolower($value[1]),$this->_levels);
  205. }
  206. /* END CORE HACK */
  207. /**
  208. * Returns the total time for serving the current request.
  209. * This method calculates the difference between now and the timestamp
  210. * defined by constant YII_BEGIN_TIME.
  211. * To estimate the execution time more accurately, the constant should
  212. * be defined as early as possible (best at the beginning of the entry script.)
  213. * @return float the total time for serving the current request.
  214. */
  215. public function getExecutionTime()
  216. {
  217. return microtime(true)-YII_BEGIN_TIME;
  218. }
  219. /**
  220. * Returns the memory usage of the current application.
  221. * This method relies on the PHP function memory_get_usage().
  222. * If it is not available, the method will attempt to use OS programs
  223. * to determine the memory usage. A value 0 will be returned if the
  224. * memory usage can still not be determined.
  225. * @return integer memory usage of the application (in bytes).
  226. */
  227. public function getMemoryUsage()
  228. {
  229. if(function_exists('memory_get_usage'))
  230. return memory_get_usage();
  231. else
  232. {
  233. $output=array();
  234. if(strncmp(PHP_OS,'WIN',3)===0)
  235. {
  236. exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST',$output);
  237. return isset($output[5])?preg_replace('/[\D]/','',$output[5])*1024 : 0;
  238. }
  239. else
  240. {
  241. $pid=getmypid();
  242. exec("ps -eo%mem,rss,pid | grep $pid", $output);
  243. $output=explode(" ",$output[0]);
  244. return isset($output[1]) ? $output[1]*1024 : 0;
  245. }
  246. }
  247. }
  248. /**
  249. * Returns the profiling results.
  250. * The results may be filtered by token and/or category.
  251. * If no filter is specified, the returned results would be an array with each element
  252. * being array($token,$category,$time).
  253. * If a filter is specified, the results would be an array of timings.
  254. *
  255. * Since 1.1.11, filtering results by category supports the same format used for filtering logs in
  256. * {@link getLogs}, and similarly supports filtering by multiple categories and wildcard.
  257. * @param string $token token filter. Defaults to null, meaning not filtered by token.
  258. * @param string $categories category filter. Defaults to null, meaning not filtered by category.
  259. * @param boolean $refresh whether to refresh the internal timing calculations. If false,
  260. * only the first time calling this method will the timings be calculated internally.
  261. * @return array the profiling results.
  262. */
  263. public function getProfilingResults($token=null,$categories=null,$refresh=false)
  264. {
  265. if($this->_timings===null || $refresh)
  266. $this->calculateTimings();
  267. if($token===null && $categories===null)
  268. return $this->_timings;
  269. $timings = $this->_timings;
  270. if($categories!==null) {
  271. $this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
  272. $timings=array_filter($timings,array($this,'filterTimingByCategory'));
  273. }
  274. $results=array();
  275. foreach($timings as $timing)
  276. {
  277. if($token===null || $timing[0]===$token)
  278. $results[]=$timing[2];
  279. }
  280. return $results;
  281. }
  282. private function calculateTimings()
  283. {
  284. $this->_timings=array();
  285. $stack=array();
  286. foreach($this->_logs as $log)
  287. {
  288. if($log[1]!==CLogger::LEVEL_PROFILE)
  289. continue;
  290. list($message,$level,$category,$timestamp)=$log;
  291. if(!strncasecmp($message,'begin:',6))
  292. {
  293. $log[0]=substr($message,6);
  294. $stack[]=$log;
  295. }
  296. elseif(!strncasecmp($message,'end:',4))
  297. {
  298. $token=substr($message,4);
  299. if(($last=array_pop($stack))!==null && $last[0]===$token)
  300. {
  301. $delta=$log[3]-$last[3];
  302. $this->_timings[]=array($message,$category,$delta);
  303. }
  304. else
  305. 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.',
  306. array('{token}'=>$token)));
  307. }
  308. }
  309. $now=microtime(true);
  310. while(($last=array_pop($stack))!==null)
  311. {
  312. $delta=$now-$last[3];
  313. $this->_timings[]=array($last[0],$last[2],$delta);
  314. }
  315. }
  316. /**
  317. * Removes all recorded messages from the memory.
  318. * This method will raise an {@link onFlush} event.
  319. * The attached event handlers can process the log messages before they are removed.
  320. * @param boolean $dumpLogs whether to process the logs immediately as they are passed to log route
  321. * @since 1.1.0
  322. */
  323. public function flush($dumpLogs=false)
  324. {
  325. $this->onFlush(new CEvent($this, array('dumpLogs'=>$dumpLogs)));
  326. $this->_logs=array();
  327. $this->_logCount=0;
  328. }
  329. /**
  330. * Raises an <code>onFlush</code> event.
  331. * @param CEvent $event the event parameter
  332. * @since 1.1.0
  333. */
  334. public function onFlush($event)
  335. {
  336. $this->raiseEvent('onFlush', $event);
  337. }
  338. }