www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

debug.js (8162B)


      1 /*
      2     ***** BEGIN LICENSE BLOCK *****
      3     
      4     Copyright © 2009 Center for History and New Media
      5                      George Mason University, Fairfax, Virginia, USA
      6                      http://zotero.org
      7     
      8     This file is part of Zotero.
      9     
     10     Zotero is free software: you can redistribute it and/or modify
     11     it under the terms of the GNU Affero General Public License as published by
     12     the Free Software Foundation, either version 3 of the License, or
     13     (at your option) any later version.
     14     
     15     Zotero is distributed in the hope that it will be useful,
     16     but WITHOUT ANY WARRANTY; without even the implied warranty of
     17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18     GNU Affero General Public License for more details.
     19     
     20     You should have received a copy of the GNU Affero General Public License
     21     along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
     22     
     23     ***** END LICENSE BLOCK *****
     24 */
     25 
     26 
     27 Zotero.Debug = new function () {
     28 	var _console, _stackTrace, _store, _level, _lastTime, _output = [];
     29 	var _slowTime = false;
     30 	var _colorOutput = false;
     31 	var _consoleViewer = false;
     32 	var _consoleViewerQueue = [];
     33 	var _consoleViewerListener;
     34 	
     35 	/**
     36 	 * Initialize debug logging
     37 	 *
     38 	 * Debug logging can be set in several different ways:
     39 	 *
     40 	 *   - via the debug.log pref in the client or connector
     41 	 *   - by enabling debug output logging from the Help menu
     42 	 *   - by passing -ZoteroDebug or -ZoteroDebugText on the command line
     43 	 *
     44 	 * In the client, debug.log and -ZoteroDebugText enable logging via the terminal, while -ZoteroDebug
     45 	 * enables logging via an in-app HTML-based window.
     46 	 *
     47 	 * @param {Integer} [forceDebugLog = 0] - Force output even if pref disabled
     48 	 *    2: window (-ZoteroDebug)
     49 	 *    1: text console (-ZoteroDebugText)
     50 	 *    0: disabled
     51 	 */
     52 	this.init = function (forceDebugLog = 0) {
     53 		_console = Zotero.Prefs.get('debug.log') || forceDebugLog == 1;
     54 		_consoleViewer = forceDebugLog == 2;
     55 		// When logging to the text console from the client on Mac/Linux, colorize output
     56 		if (_console && Zotero.isFx && !Zotero.isBookmarklet) {
     57 			_colorOutput = true;
     58 			
     59 			// Time threshold in ms above which intervals should be colored red in terminal output
     60 			_slowTime = Zotero.Prefs.get('debug.log.slowTime');
     61 		}
     62 		_store = Zotero.Prefs.get('debug.store');
     63 		if (_store) {
     64 			Zotero.Prefs.set('debug.store', false);
     65 		}
     66 		_level = Zotero.Prefs.get('debug.level');
     67 		_stackTrace = Zotero.Prefs.get('debug.stackTrace');
     68 		
     69 		this.storing = _store;
     70 		this.updateEnabled();
     71 		
     72 		if (Zotero.isStandalone) {
     73 			// Enable dump() from window (non-XPCOM) scopes when terminal or viewer logging is enabled.
     74 			// (These will always go to the terminal, even in viewer mode.)
     75 			Zotero.Prefs.set('browser.dom.window.dump.enabled', _console || _consoleViewer, true);
     76 			
     77 			if (_consoleViewer) {
     78 				setTimeout(function () {
     79 					Zotero.openInViewer("chrome://zotero/content/debugViewer.html");
     80 				}, 1000);
     81 			}
     82 		}
     83 	}
     84 	
     85 	this.log = function (message, level, maxDepth, stack) {
     86 		if (!this.enabled) {
     87 			return;
     88 		}
     89 		
     90 		if (typeof message != 'string') {
     91 			message = Zotero.Utilities.varDump(message, 0, maxDepth);
     92 		}
     93 		
     94 		if (!level) {
     95 			level = 3;
     96 		}
     97 		
     98 		// If level above debug.level value, don't display
     99 		if (level > _level) {
    100 			return;
    101 		}
    102 		
    103 		var deltaStr = '';
    104 		var deltaStrStore = '';
    105 		var delta = 0;
    106 		var d = new Date();
    107 		if (_lastTime) {
    108 			delta = d - _lastTime;
    109 		}
    110 		_lastTime = d;
    111 		var slowPrefix = "";
    112 		var slowSuffix = "";
    113 		if (_slowTime && delta > _slowTime) {
    114 			slowPrefix = "\x1b[31;40m";
    115 			slowSuffix = "\x1b[0m";
    116 		}
    117 		
    118 		delta = ("" + delta).padStart(7, "0")
    119 		
    120 		deltaStr = "(" + slowPrefix + "+" + delta + slowSuffix + ")";
    121 		if (_store) {
    122 			deltaStrStore = "(+" + delta + ")";
    123 		}
    124 		
    125 		if (stack === true) {
    126 			// Display stack starting from where this was called
    127 			stack = Components.stack.caller;
    128 		} else if (stack >= 0) {
    129 			let i = stack;
    130 			stack = Components.stack.caller;
    131 			while(stack && i--) {
    132 				stack = stack.caller;
    133 			}
    134 		} else if (_stackTrace) {
    135 			// Stack trace enabled globally
    136 			stack = Components.stack.caller;
    137 		} else {
    138 			stack = undefined;
    139 		}
    140 		
    141 		if (stack) {
    142 			message += '\n' + this.stackToString(stack);
    143 		}
    144 		
    145 		if (_console || _consoleViewer) {
    146 			var output = '(' + level + ')' + deltaStr + ': ' + message;
    147 			if (Zotero.isFx && !Zotero.isBookmarklet) {
    148 				// Text console
    149 				if (_console) {
    150 					dump("zotero" + output + "\n\n");
    151 				}
    152 				// Console window
    153 				if (_consoleViewer) {
    154 					// Remove ANSI color codes. We could replace this with HTML, but it's probably
    155 					// unnecessarily distracting/alarming to show the red in the viewer. Devs who care
    156 					// about times should just use a terminal.
    157 					if (slowPrefix) {
    158 						output = output.replace(slowPrefix, '').replace(slowSuffix, '');
    159 					}
    160 					
    161 					// If there's a listener, pass line immediately
    162 					if (_consoleViewerListener) {
    163 						_consoleViewerListener(output);
    164 					}
    165 					// Otherwise add to queue
    166 					else {
    167 						_consoleViewerQueue.push(output);
    168 					}
    169 				}
    170 			} else if(window.console) {
    171 				window.console.log(output);
    172 			}
    173 		}
    174 		if (_store) {
    175 			if (Math.random() < 1/1000) {
    176 				// Remove initial lines if over limit
    177 				var overage = this.count() - Zotero.Prefs.get('debug.store.limit');
    178 				if (overage > 0) {
    179 					_output.splice(0, Math.abs(overage));
    180 				}
    181 			}
    182 			_output.push('(' + level + ')' + deltaStrStore + ': ' + message);
    183 		}
    184 	}
    185 	
    186 	
    187 	this.get = Zotero.Promise.method(function(maxChars, maxLineLength) {
    188 		var output = _output;
    189 		var total = output.length;
    190 		
    191 		if (total == 0) {
    192 			return "";
    193 		}
    194 		
    195 		if (maxLineLength) {
    196 			for (var i=0, len=output.length; i<len; i++) {
    197 				if (output[i].length > maxLineLength) {
    198 					output[i] = Zotero.Utilities.ellipsize(output[i], maxLineLength, false, true);
    199 				}
    200 			}
    201 		}
    202 		
    203 		output = output.join('\n\n');
    204 		
    205 		if (maxChars) {
    206 			output = output.substr(maxChars * -1);
    207 			// Cut at two newlines
    208 			let matches = output.match(/^[\n]*\n\n/);
    209 			if (matches) {
    210 				output = output.substr(matches[0].length);
    211 			}
    212 		}
    213 
    214 		return Zotero.getSystemInfo().then(function(sysInfo) {
    215 			if (Zotero.isConnector) {
    216 				return Zotero.Errors.getErrors().then(function(errors) {
    217 					return errors.join('\n\n') +
    218 						"\n\n" + sysInfo + "\n\n" +
    219 						"=========================================================\n\n" +
    220 						output;	
    221 				});
    222 			}
    223 			else {
    224 				return Zotero.getErrors(true).join('\n\n') +
    225 					"\n\n" + sysInfo + "\n\n" +
    226 					"=========================================================\n\n" +
    227 					output;
    228 			}
    229 		});
    230 	});
    231 	
    232 	
    233 	this.getConsoleViewerOutput = function () {
    234 		var queue = _output.concat(_consoleViewerQueue);
    235 		_consoleViewerQueue = [];
    236 		return queue;
    237 	}
    238 	
    239 	
    240 	this.addConsoleViewerListener = function (listener) {
    241 		this.enabled = _consoleViewer = true;
    242 		_consoleViewerListener = listener;
    243 	};
    244 	
    245 	
    246 	this.removeConsoleViewerListener = function () {
    247 		_consoleViewerListener = null;
    248 		// At least for now, stop logging once console viewer is closed
    249 		_consoleViewer = false;
    250 		this.updateEnabled();
    251 	};
    252 	
    253 	
    254 	this.setStore = function (enable) {
    255 		if (enable) {
    256 			this.clear();
    257 		}
    258 		_store = enable;
    259 		this.updateEnabled();
    260 		this.storing = _store;
    261 	}
    262 	
    263 	
    264 	this.updateEnabled = function () {
    265 		this.enabled = _console || _consoleViewer || _store;
    266 	};
    267 	
    268 	
    269 	this.count = function () {
    270 		return _output.length;
    271 	}
    272 	
    273 	
    274 	this.clear = function () {
    275 		_output = [];
    276 	}
    277 	
    278 	/**
    279 	 * Format a stack trace for output in the same way that Error.stack does
    280 	 * @param {Components.stack} stack
    281 	 * @param {Integer} [lines=5] Number of lines to format
    282 	 */
    283 	this.stackToString = function (stack, lines) {
    284 		if (!lines) lines = 5;
    285 		var str = '';
    286 		while(stack && lines--) {
    287 			str += '\n  ' + (stack.name || '') + '@' + stack.filename
    288 				+ ':' + stack.lineNumber;
    289 			stack = stack.caller;
    290 		}
    291 		return this.filterStack(str).substr(1);
    292 	};
    293 	
    294 	
    295 	/**
    296 	 * Strip Bluebird lines from a stack trace
    297 	 *
    298 	 * @param {String} stack
    299 	 */
    300 	this.filterStack = function (stack) {
    301 		return stack.split(/\n/).filter(line => line.indexOf('zotero/bluebird') == -1).join('\n');
    302 	}
    303 }