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:
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);