www

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

commit 5f9e39f959c2ee1c527b752b871049a46b194537
parent 75fd0b44486dd6b9907623e120899628524f33b2
Author: Dan Stillman <dstillman@zotero.org>
Date:   Mon, 10 Aug 2009 16:44:15 +0000

In-memory debug logging, configurable in Advanced pane of preferences, with ability to send output to zotero.org


Diffstat:
Mchrome/content/zotero/preferences/preferences.js | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/preferences/preferences.xul | 30++++++++++++++++++++++++++++--
Achrome/content/zotero/xpcom/debug.js | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/xpcom/zotero.js | 44++------------------------------------------
Mcomponents/zotero-protocol-handler.js | 38++++++++++++++++++++++++++++++++++++--
Mcomponents/zotero-service.js | 1+
Mdefaults/preferences/zotero.js | 2++
7 files changed, 402 insertions(+), 46 deletions(-)

diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js @@ -26,6 +26,14 @@ var proxies; var charsets; var _io = {}; + +var Zotero_Preferences = { + + onClose: function () { + Zotero_Preferences.DebugOutput.onClose(); + } +} + function init() { // Display the appropriate modifier keys for the platform @@ -40,6 +48,7 @@ function init() populateQuickCopyList(); updateQuickCopyInstructions(); initSearchPane(); + Zotero_Preferences.Debug_Output.init(); var charsetMenu = document.getElementById("zotero-import-charsetMenu"); var charsetMap = Zotero_Charset_Menu.populate(charsetMenu, false); @@ -1206,6 +1215,239 @@ function resetStyles() { } +Zotero_Preferences.Debug_Output = { + _timer: null, + + init: function () { + var storing = Zotero.Debug.storing; + this._updateButton(); + this.updateLines(); + if (storing) { + this._initTimer(); + } + }, + + + toggleStore: function () { + var storing = Zotero.Debug.storing; + Zotero.Debug.setStore(!storing); + if (!storing) { + this._initTimer(); + } + else { + if (this._timerID) { + this._timer.cancel(); + this._timerID = null; + } + } + this._updateButton(); + this.updateLines(); + }, + + + view: function () { + var uri = "zotero://debug/"; + var features = "menubar=yes,toolbar=no,location=no,scrollbars,centerscreen,resizable"; + + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var win = wm.getMostRecentWindow("navigator:browser"); + if (win) { + win.open(uri, null, features); + } + else { + var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Components.interfaces.nsIWindowWatcher); + var win = ww.openWindow(null, uri, null, features + ",width=775,height=575", null); + } + }, + + + // TODO: localize + submit: function () { + document.getElementById('debug-output-submit').disabled = true; + document.getElementById('debug-output-submit-progress').hidden = false; + + var url = "http://www.zotero.org/utils/dev/repo/report?debug=1"; + var output = Zotero.Debug.get(); + + var uploadCallback = function (xmlhttp) { + document.getElementById('debug-output-submit').disabled = false; + document.getElementById('debug-output-submit-progress').hidden = true; + + Zotero.debug(xmlhttp.responseText); + + var pr = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + + if (!xmlhttp.responseXML) { + pr.alert( + Zotero.getString('general.error'), + 'Invalid response from server' + ); + return; + } + var reported = xmlhttp.responseXML.getElementsByTagName('reported'); + if (reported.length != 1) { + pr.alert( + Zotero.getString('general.error'), + 'The server returned an error. Please try again.' + ); + return; + } + + var reportID = reported[0].getAttribute('reportID'); + pr.alert( + "Submitted", + "Debug output has been sent to the Zotero server.\n\n" + + "The Report ID is D" + reportID + "." + ); + } + + var bufferUploader = function (data) { + var pr = Components.classes["@mozilla.org/network/default-prompt;1"] + .getService(Components.interfaces.nsIPrompt); + + var oldLen = output.length; + var newLen = data.length; + var savings = Math.round(((oldLen - newLen) / oldLen) * 100) + Zotero.debug("HTTP POST " + newLen + " bytes to " + url + + " (gzipped from " + oldLen + " bytes; " + + savings + "% savings)"); + + if (Zotero.Utilities.HTTP.browserIsOffline()) { + pr.alert( + Zotero.getString( + 'general.error', + Zotero.appName + " is in offline mode." + ) + ); + return false; + } + + var req = + Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(); + req.open('POST', url, true); + req.setRequestHeader('Content-Type', "application/octet-stream"); + req.setRequestHeader('Content-Encoding', 'gzip'); + + req.channel.notificationCallbacks = { + onProgress: function (request, context, progress, progressMax) { + var pm = document.getElementById('debug-output-submit-progress'); + pm.mode = 'determined' + pm.value = progress; + pm.max = progressMax; + }, + + // nsIInterfaceRequestor + getInterface: function (iid) { + try { + return this.QueryInterface(iid); + } + catch (e) { + throw Components.results.NS_NOINTERFACE; + } + }, + + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsIInterfaceRequestor) || + iid.equals(Components.interfaces.nsIProgressEventSink)) { + return this; + } + throw Components.results.NS_NOINTERFACE; + }, + + } + req.onreadystatechange = function () { + if (req.readyState == 4) { + uploadCallback(req); + } + }; + try { + req.sendAsBinary(data); + } + catch (e) { + pr.alert( + Zotero.getString('general.error'), + "An error occurred sending debug output." + ); + } + } + + // Get input stream from debug output data + var unicodeConverter = + Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + unicodeConverter.charset = "UTF-8"; + var bodyStream = unicodeConverter.convertToInputStream(output); + + // Get listener for when compression is done + var listener = new Zotero.BufferedInputListener(bufferUploader); + + // Initialize stream converter + var converter = + Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"] + .createInstance(Components.interfaces.nsIStreamConverter); + converter.asyncConvertData("uncompressed", "gzip", listener, null); + + // Send input stream to stream converter + var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]. + createInstance(Components.interfaces.nsIInputStreamPump); + pump.init(bodyStream, -1, -1, 0, 0, true); + pump.asyncRead(converter, null); + }, + + + clear: function () { + Zotero.Debug.clear(); + this.updateLines(); + }, + + + updateLines: function () { + var lines = Zotero.Debug.count(); + document.getElementById('debug-output-lines').value = lines; + var empty = lines == 0; + document.getElementById('debug-output-view').disabled = empty; + document.getElementById('debug-output-clear').disabled = empty; + document.getElementById('debug-output-submit').disabled = empty; + }, + + + _initTimer: function () { + this._timer = Components.classes["@mozilla.org/timer;1"]. + createInstance(Components.interfaces.nsITimer); + this._timer.initWithCallback({ + notify: function() { + Zotero_Preferences.Debug_Output.updateLines(); + } + }, 10000, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); + }, + + + _updateButton: function () { + var storing = Zotero.Debug.storing + + var button = document.getElementById('debug-output-enable'); + // TODO: localize + if (storing) { + button.label = "Disable"; + } + else { + button.label = "Enable"; + } + }, + + + onClose: function () { + if (this._timer) { + this._timer.cancel(); + } + } +} + function onOpenURLSelected() { var openURLMenu = document.getElementById('openURLMenu'); diff --git a/chrome/content/zotero/preferences/preferences.xul b/chrome/content/zotero/preferences/preferences.xul @@ -37,8 +37,9 @@ To add a new preference: in Zotero.Prefs.observe() --> -<prefwindow id="zotero-prefs" title="&zotero.preferences.title;" onload="moveToAlertPosition(); init()" - windowtype="zotero:pref" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<prefwindow id="zotero-prefs" title="&zotero.preferences.title;" onload="moveToAlertPosition(); init()" onclose="Zotero_Preferences.onClose()" + windowtype="zotero:pref" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + > <prefpane id="zotero-prefpane-general" label="&zotero.preferences.prefpane.general;" @@ -724,6 +725,7 @@ To add a new preference: <preference id="pref-useDataDir" name="extensions.zotero.useDataDir" type="bool"/> <preference id="pref-dataDir" name="extensions.zotero.dataDir" type="string"/> <preference id="pref-export-displayCharsetOption" name="extensions.zotero.export.displayCharsetOption" type="bool"/> + <preference id="pref-debug-output-enableAfterRestart" name="extensions.zotero.debug.store" type="bool"/> <preference id="pref-import-charset" name="extensions.zotero.import.charset" type="string"/> </preferences> @@ -757,6 +759,30 @@ To add a new preference: </groupbox> <groupbox> + <!-- TODO: localize --> + <caption label="Debug Output Logging"/> + + <!-- This doesn't wrap without an explicit width --> + <vbox> + <description width="45em">Debug output can help Zotero developers diagnose problems in Zotero. Debug logging will slow down Zotero, so you should generally leave it disabled unless a Zotero developer requests debug output.</description> + </vbox> + + <hbox align="center"> + <button id="debug-output-enable" oncommand="Zotero_Preferences.Debug_Output.toggleStore()"/> + <label id="debug-output-lines" style="margin-right: 0"/> + <label value="lines logged"/> + <checkbox preference="pref-debug-output-enableAfterRestart" label="Enable after restart" style="margin-left: 1.5em"/> + </hbox> + + <hbox align="center"> + <button id="debug-output-view" label="View Output" oncommand="Zotero_Preferences.Debug_Output.view()"/> + <button id="debug-output-clear" label="Clear Output" oncommand="Zotero_Preferences.Debug_Output.clear()"/> + <button id="debug-output-submit" label="Submit to Zotero Server" oncommand="Zotero_Preferences.Debug_Output.submit()"/> + <progressmeter id="debug-output-submit-progress" mode="undetermined" hidden="true"/> + </hbox> + </groupbox> + + <groupbox> <caption label="&zotero.preferences.charset;"/> <checkbox id="zotero-export-displayCharsetOption" label="&zotero.preferences.charset.displayExportOption;" diff --git a/chrome/content/zotero/xpcom/debug.js b/chrome/content/zotero/xpcom/debug.js @@ -0,0 +1,91 @@ +// TODO: license + +Zotero.Debug = new function () { + this.__defineGetter__('storing', function () _store); + + var _console; + var _store; + var _level; + var _time; + var _lastTime; + var _output = []; + + + this.init = function () { + _console = Zotero.Prefs.get('debug.log'); + _store = Zotero.Prefs.get('debug.store'); + _level = Zotero.Prefs.get('debug.level'); + _time = Zotero.Prefs.get('debug.time'); + } + + this.log = function (message, level) { + if (!_console && !_store) { + return; + } + + if (typeof message != 'string') { + message = Zotero.varDump(message); + } + + if (!level) { + level = 3; + } + + // If level above debug.level value, don't display + if (level > _level) { + return; + } + + var deltaStr = ''; + if (_time || _store) { + var delta = 0; + var d = new Date(); + if (_lastTime) { + delta = d - _lastTime; + } + _lastTime = d; + + while (("" + delta).length < 7) { + delta = '0' + delta; + } + + deltaStr = '(+' + delta + ')'; + } + + if (_console) { + dump('zotero(' + level + ')' + (_time ? deltaStr : '') + ': ' + message + "\n\n"); + } + if (_store) { + if (Zotero.Utilities.prototype.probability(1000)) { + // Remove initial lines if over limit + var overage = this.count() - Zotero.Prefs.get('debug.store.limit'); + if (overage > 0) { + _output.splice(0, Math.abs(overage)); + } + } + _output.push('(' + level + ')' + deltaStr + ': ' + message); + } + } + + this.get = function () { + return _output.join('\n\n'); + } + + + this.setStore = function (enable) { + if (enable) { + this.clear(); + } + _store = enable; + } + + + this.count = function () { + return _output.length; + } + + + this.clear = function () { + _output = []; + } +} diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js @@ -135,10 +135,6 @@ var Zotero = new function(){ var _startupError; var _startupErrorHandler; var _zoteroDirectory = false; - var _debugLogging; - var _debugLevel; - var _debugTime; - var _debugLastTime; var _localizedStringBundle; var _localUserKey; var _waiting; @@ -167,9 +163,7 @@ var Zotero = new function(){ // Load in the preferences branch for the extension Zotero.Prefs.init(); - _debugLogging = Zotero.Prefs.get('debug.log'); - _debugLevel = Zotero.Prefs.get('debug.level'); - _debugTime = Zotero.Prefs.get('debug.time'); + Zotero.Debug.init(); this.mainThread = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread; @@ -606,41 +600,7 @@ var Zotero = new function(){ * Defaults to log level 3 if level not provided */ function debug(message, level) { - if (!_debugLogging){ - return false; - } - - if (typeof message!='string'){ - message = Zotero.varDump(message); - } - - if (!level){ - level = 3; - } - - // If level above debug.level value, don't display - if (level > _debugLevel){ - return false; - } - - var deltaStr = ''; - if (_debugTime) { - var delta = 0; - var d = new Date(); - if (_debugLastTime) { - delta = d - _debugLastTime; - } - _debugLastTime = d; - - while (("" + delta).length < 7) { - delta = '0' + delta; - } - - deltaStr = '(+' + delta + ')'; - } - - dump('zotero(' + level + ')' + deltaStr + ': ' + message + "\n\n"); - return true; + Zotero.Debug.log(message, level); } diff --git a/components/zotero-protocol-handler.js b/components/zotero-protocol-handler.js @@ -808,11 +808,42 @@ function ChromeExtensionHandler() { } } }; - + + + /* + zotero://debug/ + */ + var DebugExtension = new function() { + this.newChannel = newChannel; + + this.__defineGetter__('loadAsChrome', function () { return false; }); + + function newChannel(uri) { + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + var Zotero = Components.classes["@zotero.org/Zotero;1"] + .getService(Components.interfaces.nsISupports) + .wrappedJSObject; + + try { + var output = Zotero.Debug.get(); + + var uriStr = 'data:text/plain,' + encodeURIComponent(output); + var extURI = ioService.newURI(uriStr, null, null); + return ioService.newChannelFromURI(extURI); + } + catch (e) { + Zotero.debug(e); + throw (e); + } + } + }; + var ReportExtensionSpec = ZOTERO_SCHEME + "://report" this._extensions[ReportExtensionSpec] = ReportExtension; - + var TimelineExtensionSpec = ZOTERO_SCHEME + "://timeline" this._extensions[TimelineExtensionSpec] = TimelineExtension; @@ -824,6 +855,9 @@ function ChromeExtensionHandler() { var FullscreenExtensionSpec = ZOTERO_SCHEME + "://fullscreen" this._extensions[FullscreenExtensionSpec] = FullscreenExtension; + + var DebugExtensionSpec = ZOTERO_SCHEME + "://debug" + this._extensions[DebugExtensionSpec] = DebugExtension; } diff --git a/components/zotero-service.js b/components/zotero-service.js @@ -40,6 +40,7 @@ var xpcomFiles = [ 'data/tag', 'data/tags', 'db', + 'debug', 'duplicate', 'enstyle', 'error', diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js @@ -11,6 +11,8 @@ pref("extensions.zotero.dataDir", ''); pref("extensions.zotero.lastDataDir", ''); pref("extensions.zotero.dbLockExclusive", true); pref("extensions.zotero.debug.log",false); +pref("extensions.zotero.debug.store",false); +pref("extensions.zotero.debug.store.limit",750000); pref("extensions.zotero.debug.level",5); pref("extensions.zotero.debug.time", false); pref("extensions.zotero.automaticScraperUpdates",true);