michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cr = Components.results; michael@0: michael@0: const KEY_PROFILEDIR = "ProfD"; michael@0: const FILE_EXTENSIONS_LOG = "extensions.log"; michael@0: const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; michael@0: michael@0: const LOGGER_FILE_PERM = parseInt("666", 8); michael@0: michael@0: const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; michael@0: michael@0: Components.utils.import("resource://gre/modules/FileUtils.jsm"); michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: this.EXPORTED_SYMBOLS = [ "LogManager" ]; michael@0: michael@0: var gDebugLogEnabled = false; michael@0: michael@0: function formatLogMessage(aType, aName, aStr, aException) { michael@0: let message = aType.toUpperCase() + " " + aName + ": " + aStr; michael@0: if (aException) { michael@0: if (typeof aException == "number") michael@0: return message + ": " + Components.Exception("", aException).name; michael@0: michael@0: message = message + ": " + aException; michael@0: // instanceOf doesn't work here, let's duck type michael@0: if (aException.fileName) michael@0: message = message + " (" + aException.fileName + ":" + aException.lineNumber + ")"; michael@0: michael@0: if (aException.message == "too much recursion") michael@0: dump(message + "\n" + aException.stack + "\n"); michael@0: } michael@0: return message; michael@0: } michael@0: michael@0: function getStackDetails(aException) { michael@0: // Defensively wrap all this to ensure that failing to get the message source michael@0: // doesn't stop the message from being logged michael@0: try { michael@0: if (aException) { michael@0: if (aException instanceof Ci.nsIException) { michael@0: return { michael@0: sourceName: aException.filename, michael@0: lineNumber: aException.lineNumber michael@0: }; michael@0: } michael@0: michael@0: if (typeof aException == "object") { michael@0: return { michael@0: sourceName: aException.fileName, michael@0: lineNumber: aException.lineNumber michael@0: }; michael@0: } michael@0: } michael@0: michael@0: let stackFrame = Components.stack.caller.caller.caller; michael@0: return { michael@0: sourceName: stackFrame.filename, michael@0: lineNumber: stackFrame.lineNumber michael@0: }; michael@0: } michael@0: catch (e) { michael@0: return { michael@0: sourceName: null, michael@0: lineNumber: 0 michael@0: }; michael@0: } michael@0: } michael@0: michael@0: function AddonLogger(aName) { michael@0: this.name = aName; michael@0: } michael@0: michael@0: AddonLogger.prototype = { michael@0: name: null, michael@0: michael@0: error: function AddonLogger_error(aStr, aException) { michael@0: let message = formatLogMessage("error", this.name, aStr, aException); michael@0: michael@0: let stack = getStackDetails(aException); michael@0: michael@0: let consoleMessage = Cc["@mozilla.org/scripterror;1"]. michael@0: createInstance(Ci.nsIScriptError); michael@0: consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0, michael@0: Ci.nsIScriptError.errorFlag, "component javascript"); michael@0: Services.console.logMessage(consoleMessage); michael@0: michael@0: // Always dump errors, in case the Console Service isn't listening yet michael@0: dump("*** " + message + "\n"); michael@0: michael@0: try { michael@0: var tstamp = new Date(); michael@0: var logfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]); michael@0: var stream = Cc["@mozilla.org/network/file-output-stream;1"]. michael@0: createInstance(Ci.nsIFileOutputStream); michael@0: stream.init(logfile, 0x02 | 0x08 | 0x10, LOGGER_FILE_PERM, 0); // write, create, append michael@0: var writer = Cc["@mozilla.org/intl/converter-output-stream;1"]. michael@0: createInstance(Ci.nsIConverterOutputStream); michael@0: writer.init(stream, "UTF-8", 0, 0x0000); michael@0: writer.writeString(tstamp.toLocaleFormat("%Y-%m-%d %H:%M:%S ") + michael@0: message + " at " + stack.sourceName + ":" + michael@0: stack.lineNumber + "\n"); michael@0: writer.close(); michael@0: } michael@0: catch (e) { } michael@0: }, michael@0: michael@0: warn: function AddonLogger_warn(aStr, aException) { michael@0: let message = formatLogMessage("warn", this.name, aStr, aException); michael@0: michael@0: let stack = getStackDetails(aException); michael@0: michael@0: let consoleMessage = Cc["@mozilla.org/scripterror;1"]. michael@0: createInstance(Ci.nsIScriptError); michael@0: consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0, michael@0: Ci.nsIScriptError.warningFlag, "component javascript"); michael@0: Services.console.logMessage(consoleMessage); michael@0: michael@0: if (gDebugLogEnabled) michael@0: dump("*** " + message + "\n"); michael@0: }, michael@0: michael@0: log: function AddonLogger_log(aStr, aException) { michael@0: if (gDebugLogEnabled) { michael@0: let message = formatLogMessage("log", this.name, aStr, aException); michael@0: dump("*** " + message + "\n"); michael@0: Services.console.logStringMessage(message); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: this.LogManager = { michael@0: getLogger: function LogManager_getLogger(aName, aTarget) { michael@0: let logger = new AddonLogger(aName); michael@0: michael@0: if (aTarget) { michael@0: ["error", "warn", "log"].forEach(function(name) { michael@0: let fname = name.toUpperCase(); michael@0: delete aTarget[fname]; michael@0: aTarget[fname] = function LogManager_targetName(aStr, aException) { michael@0: logger[name](aStr, aException); michael@0: }; michael@0: }); michael@0: } michael@0: michael@0: return logger; michael@0: } michael@0: }; michael@0: michael@0: var PrefObserver = { michael@0: init: function PrefObserver_init() { michael@0: Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false); michael@0: Services.obs.addObserver(this, "xpcom-shutdown", false); michael@0: this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED); michael@0: }, michael@0: michael@0: observe: function PrefObserver_observe(aSubject, aTopic, aData) { michael@0: if (aTopic == "xpcom-shutdown") { michael@0: Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this); michael@0: Services.obs.removeObserver(this, "xpcom-shutdown"); michael@0: } michael@0: else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) { michael@0: try { michael@0: gDebugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED); michael@0: } michael@0: catch (e) { michael@0: gDebugLogEnabled = false; michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: PrefObserver.init();