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

This commit is contained in:
Dan Stillman 2009-08-10 16:44:15 +00:00
parent 75fd0b4448
commit 5f9e39f959
7 changed files with 402 additions and 46 deletions

View file

@ -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');

View file

@ -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>
@ -756,6 +758,30 @@ To add a new preference:
</hbox>
</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;"/>

View file

@ -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 = [];
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -40,6 +40,7 @@ var xpcomFiles = [
'data/tag',
'data/tags',
'db',
'debug',
'duplicate',
'enstyle',
'error',

View file

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