/js/yii/logging/CLogger.js
JavaScript | 339 lines | 200 code | 6 blank | 133 comment | 52 complexity | 04c6c3ae30c3ae412449de058faeacc0 MD5 | raw file
1/*global Yii, php, $, jQuery, alert, clearInterval, clearTimeout, document, event, frames, history, Image, location, name, navigator, Option, parent, screen, setInterval, setTimeout, window, XMLHttpRequest */ 2/** 3 * CLogger records log messages in memory. 4 * 5 * CLogger implements the methods to retrieve the messages with 6 * various filter conditions, including log levels and log categories. 7 * 8 * @originalAuthor Qiang Xue <qiang.xue@gmail.com> 9 * @version $Id: CLogger.php 3137 2011-03-28 11:08:06Z mdomba $ 10 * @package system.logging 11 * @since 1.0 12 * @author Charles Pick 13 * @class 14 * @extends Yii.CComponent 15 */ 16Yii.CLogger = function() { 17}; 18Yii.CLogger.prototype = new Yii.CComponent(); 19Yii.CLogger.prototype.constructor = Yii.CLogger; 20/** 21 * @const 22 */ 23Yii.CLogger.prototype.LEVEL_TRACE = 'trace'; 24/** 25 * @const 26 */ 27Yii.CLogger.prototype.LEVEL_WARNING = 'warning'; 28/** 29 * @const 30 */ 31Yii.CLogger.prototype.LEVEL_ERROR = 'error'; 32/** 33 * @const 34 */ 35Yii.CLogger.prototype.LEVEL_INFO = 'info'; 36/** 37 * @const 38 */ 39Yii.CLogger.prototype.LEVEL_PROFILE = 'profile'; 40/** 41 * @var {Integer} how many messages should be logged before they are flushed to destinations. 42 * Defaults to 10,000, meaning for every 10,000 messages, the {@link flush} method will be 43 * automatically invoked once. If this is 0, it means messages will never be flushed automatically. 44 * @since 1.1.0 45 */ 46Yii.CLogger.prototype.autoFlush = 10000; 47/** 48 * @var {Array} log messages 49 */ 50Yii.CLogger.prototype._logs = []; 51/** 52 * @var {Integer} number of log messages 53 */ 54Yii.CLogger.prototype._logCount = 0; 55/** 56 * @var {Array} log levels for filtering (used when filtering) 57 */ 58Yii.CLogger.prototype._levels = null; 59/** 60 * @var {Array} log categories for filtering (used when filtering) 61 */ 62Yii.CLogger.prototype._categories = null; 63/** 64 * @var {Array} the profiling results (category, token => time in seconds) 65 * @since 1.0.6 66 */ 67Yii.CLogger.prototype._timings = null; 68/** 69 * Logs a message. 70 * Messages logged by this method may be retrieved back via {@link getLogs}. 71 * @param {String} message message to be logged 72 * @param {String} level level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive. 73 * @param {String} category category of the message (e.g. 'system.web'). It is case-insensitive. 74 * @see getLogs 75 */ 76Yii.CLogger.prototype.log = function (message, level, category) { 77 if (level === undefined) { 78 level = 'info'; 79 } 80 if (category === undefined) { 81 category = 'application'; 82 } 83 this._logs.push([message,level,category,php.microtime(true)]); 84 this._logCount++; 85 if(this.autoFlush>0 && this._logCount>=this.autoFlush) { 86 this.flush(); 87 } 88 }; 89/** 90 * Retrieves log messages. 91 * 92 * Messages may be filtered by log levels and/or categories. 93 * A level filter is specified by a list of levels separated by comma or space 94 * (e.g. 'trace, error'). A category filter is similar to level filter 95 * (e.g. 'system, system.web'). A difference is that in category filter 96 * you can use pattern like 'system.*' to indicate all categories starting 97 * with 'system'. 98 * 99 * If you do not specify level filter, it will bring back logs at all levels. 100 * The same applies to category filter. 101 * 102 * Level filter and category filter are combinational, i.e., only messages 103 * satisfying both filter conditions will be returned. 104 * 105 * @param {String} levels level filter 106 * @param {String} categories category filter 107 * @returns {Array} list of messages. Each array elements represents one message 108 * with the following structure: 109 * array( 110 * [0] => message (string) 111 * [1] => level (string) 112 * [2] => category (string) 113 * [3] => timestamp (float, obtained by microtime(true)); 114 */ 115Yii.CLogger.prototype.getLogs = function (levels, categories) { 116 var ret, self; 117 if (levels === undefined) { 118 levels = ''; 119 this._levels = []; 120 } 121 else { 122 this._levels=levels.toLowerCase().split(/[\s,]+/); 123 } 124 if (categories === undefined) { 125 categories = ''; 126 this._categories = []; 127 } 128 else { 129 this._categories=categories.toLowerCase().split(/[\s,]+/); 130 } 131 132 133 self = this; 134 if(php.empty(levels) && php.empty(categories)) { 135 return this._logs; 136 } 137 else if(php.empty(levels)) { 138 139 return Yii.filter(this._logs,function(value, k, arr) { 140 var matched = false, cat = value[2].toLowerCase(), c; 141 Yii.forEach(self._categories, function(i, category) { 142 if(cat===category || ((c=php.rtrim(category,'.*'))!==category && php.strpos(cat,c)===0)) { 143 matched = true; 144 return false; 145 } 146 }); 147 return matched ? value : false; 148 }); 149 150 } 151 else if(php.empty(categories)) { 152 return Yii.filter(this._logs,function(value, k, arr) { 153 var matched = false, matchLevel = value[1].toLowerCase(); 154 Yii.forEach(self._levels, function(i, level) { 155 if (level === matchLevel) { 156 matched = true; 157 return false; 158 } 159 }); 160 return matched ? value : false; 161 }); 162 } 163 else { 164 return Yii.filter(Yii.filter(this._logs,function(value, k, arr) { 165 var matched = false, cat = value[2].toLowerCase(), c; 166 Yii.forEach(self._categories, function(i, category) { 167 if(cat===category || ((c=php.rtrim(category,'.*'))!==category && php.strpos(cat,c)===0)) { 168 matched = true; 169 return false; 170 } 171 }); 172 return matched ? value : false; 173 }), function(value, k, arr) { 174 var matched = false, matchLevel = value[1].toLowerCase(); 175 Yii.forEach(self._levels, function(i, level) { 176 if (level === matchLevel) { 177 matched = true; 178 return false; 179 } 180 }); 181 return matched ? value : false; 182 }); 183 184 } 185 }; 186/** 187 * Filter function used by {@link getLogs} 188 * @param {Array} value element to be filtered 189 * @returns {Array} valid log, false if not. 190 */ 191Yii.CLogger.prototype.filterByCategory = function (value) { 192 var i, cat, category, c; 193 for (i in this._categories) { 194 if (this._categories.hasOwnProperty(i)) { 195 category = this._categories[i]; 196 cat=value[2].toLowerCase(); 197 if(cat===category || ((c=php.rtrim(category,'.*'))!==category && php.strpos(cat,c)===0)) { 198 return value; 199 } 200 } 201 } 202 return false; 203 }; 204/** 205 * Filter function used by {@link getLogs} 206 * @param {Array} value element to be filtered 207 * @returns {Array} valid log, false if not. 208 */ 209Yii.CLogger.prototype.filterByLevel = function (value) { 210 var matched = false, matchLevel = value[1].toLowerCase(); 211 212 Yii.forEach(this._levels, function(i, level) { 213 console.log(level); 214 if (level === matchLevel) { 215 matched = true; 216 return false; 217 } 218 }); 219 return matched ? value : false; 220 }; 221/** 222 * Returns the total time for serving the current request. 223 * This method calculates the difference between now and the timestamp 224 * defined by constant YII_BEGIN_TIME. 225 * To estimate the execution time more accurately, the constant should 226 * be defined as early as possible (best at the beginning of the entry script.) 227 * @returns {Float} the total time for serving the current request. 228 */ 229Yii.CLogger.prototype.getExecutionTime = function () { 230 return php.microtime(true)-YII_BEGIN_TIME; 231 }; 232/** 233 * Not Available in JavaScript, always returns 0 234 * @returns {Integer} memory usage of the application (in bytes). 235 */ 236Yii.CLogger.prototype.getMemoryUsage = function () { 237 return 0; 238 }; 239/** 240 * Returns the profiling results. 241 * The results may be filtered by token and/or category. 242 * If no filter is specified, the returned results would be an array with each element 243 * being array($token,$category,$time). 244 * If a filter is specified, the results would be an array of timings. 245 * @param {String} token token filter. Defaults to null, meaning not filtered by token. 246 * @param {String} category category filter. Defaults to null, meaning not filtered by category. 247 * @param {Boolean} refresh whether to refresh the internal timing calculations. If false, 248 * only the first time calling this method will the timings be calculated internally. 249 * @returns {Array} the profiling results. 250 * @since 1.0.6 251 */ 252Yii.CLogger.prototype.getProfilingResults = function (token, category, refresh) { 253 var results, i, timing; 254 if (token === undefined) { 255 token = null; 256 } 257 if (category === undefined) { 258 category = null; 259 } 260 if (refresh === undefined) { 261 refresh = false; 262 } 263 if(this._timings===null || refresh) { 264 this.calculateTimings(); 265 } 266 if(token===null && category===null) { 267 return this._timings; 268 } 269 results=[]; 270 for (i in this._timings) { 271 if (this._timings.hasOwnProperty(i)) { 272 timing = this._timings[i]; 273 if((category===null || timing[1]===category) && (token===null || timing[0]===token)) { 274 results.push(timing[2]); 275 } 276 } 277 } 278 return results; 279 }; 280Yii.CLogger.prototype.calculateTimings = function () { 281 var stack, i, log, message, level, category, timestamp, token, last, delta, now; 282 this._timings=[]; 283 stack=[]; 284 for (i in this._logs) { 285 if (this._logs.hasOwnProperty(i)) { 286 log = this._logs[i]; 287 if(log[1]!==Yii.CLogger.prototype.LEVEL_PROFILE) { 288 continue; 289 } 290 message = log[0]; 291 level = log[1]; 292 category = log[2]; 293 timestamp = log[3]; 294 if(!php.strncasecmp(message,'begin:',6)) { 295 log[0]=message.slice(6); 296 stack.push(log); 297 } 298 else if(!php.strncasecmp(message,'end:',4)) { 299 token=message.slice(4); 300 if((last=php.array_pop(stack))!==null && last[0]===token) { 301 delta=log[3]-last[3]; 302 this._timings.push([message,category,delta]); 303 } 304 else { 305 throw new Yii.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 {'{token}':token})); 307 } 308 } 309 } 310 } 311 now=php.microtime(true); 312 while((last=php.array_pop(stack))!==null) { 313 delta=now-last[3]; 314 this._timings.push([last[0],last[2],delta]); 315 } 316 }; 317/** 318 * Removes all recorded messages from the memory. 319 * This method will raise an {@link onFlush} event. 320 * The attached event handlers can process the log messages before they are removed. 321 * @param {Boolean} dumpLogs whether to process the logs 322 * @since 1.1.0 323 */ 324Yii.CLogger.prototype.flush = function (dumpLogs) { 325 if (dumpLogs === undefined) { 326 dumpLogs = false; 327 } 328 this.onFlush(new Yii.CEvent(this, {'dumpLogs':dumpLogs})); 329 this._logs=[]; 330 this._logCount=0; 331 }; 332/** 333 * Raises an <code>onFlush</code> event. 334 * @param {Yii.CEvent} event the event parameter 335 * @since 1.1.0 336 */ 337Yii.CLogger.prototype.onFlush = function (event) { 338 this.raiseEvent('onFlush', event); 339 }