206 lines
6.5 KiB
JavaScript
206 lines
6.5 KiB
JavaScript
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* Update logging lives in its own module for several reasons. First, it avoids
|
|
* having some very similar logging code duplicated in several places. And
|
|
* second, we only want to open the update messages file once. Opening it
|
|
* multiple times from multiple files does not result in all those messages
|
|
* ending up in the same log file.
|
|
*
|
|
* Note that simply importing this module can cause the value of the
|
|
* `app.update.log.file` pref to change. This may seem a bit weird, but we are
|
|
* going to consider it to be acceptable because any other module that wants to
|
|
* interact with that pref really ought to be doing it by interacting with this
|
|
* module instead, making the pref more of an internal implementation detail.
|
|
* This option was chosen over doing this work more lazily (say, on a log
|
|
* message) because this initialization step has user-visible consequences
|
|
* (the log file moves) and we'd prefer that those happen close to startup so
|
|
* that the user doesn't observe it happening at a seemingly random time.
|
|
*/
|
|
|
|
const PREF_APP_UPDATE_LOG = "app.update.log";
|
|
const PREF_APP_UPDATE_LOG_FILE = "app.update.log.file";
|
|
|
|
const FILE_UPDATE_MESSAGES = "update_messages.log";
|
|
const FILE_BACKUP_MESSAGES = "update_messages_old.log";
|
|
|
|
const KEY_PROFILE_DIR = "ProfD";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(
|
|
lazy,
|
|
"BinaryOutputStream",
|
|
function ul_GetBinaryStream() {
|
|
return Components.Constructor(
|
|
"@mozilla.org/binaryoutputstream;1",
|
|
"nsIBinaryOutputStream",
|
|
"setOutputStream"
|
|
);
|
|
}
|
|
);
|
|
|
|
let gLogEnabled;
|
|
let gLogFileEnabled;
|
|
let gLogFileOutputStream;
|
|
let gLogFileBinaryStream;
|
|
|
|
let gChangeListeners = [];
|
|
|
|
function updateLogEnabledVars() {
|
|
let prevLogEnabled = gLogEnabled;
|
|
let prefLogFileEnabled = gLogFileEnabled;
|
|
|
|
gLogFileEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG_FILE, false);
|
|
if (gLogFileEnabled) {
|
|
gLogEnabled = true;
|
|
} else {
|
|
gLogEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG, false);
|
|
}
|
|
|
|
if (gLogEnabled != prevLogEnabled || gLogFileEnabled != prefLogFileEnabled) {
|
|
// Since we use this function during initialization, technically any present
|
|
// listeners will be fired at that point. But it's not really possible for
|
|
// any listeners to have been added yet.
|
|
for (const listener of gChangeListeners) {
|
|
try {
|
|
listener();
|
|
} catch (ex) {
|
|
LOG(`Listener error: ${ex}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
updateLogEnabledVars();
|
|
|
|
function shutdown() {
|
|
Services.prefs.removeObserver(PREF_APP_UPDATE_LOG, updateLogEnabledVars);
|
|
Services.obs.removeObserver(shutdown, "quit-application");
|
|
|
|
// Release references to listeners.
|
|
gChangeListeners = [];
|
|
|
|
if (gLogFileOutputStream) {
|
|
gLogFileOutputStream.QueryInterface(Ci.nsISafeOutputStream);
|
|
gLogFileOutputStream.finish();
|
|
}
|
|
}
|
|
|
|
Services.obs.addObserver(shutdown, "quit-application");
|
|
// This one call observes PREF_APP_UPDATE_LOG and PREF_APP_UPDATE_LOG_FILE
|
|
Services.prefs.addObserver(PREF_APP_UPDATE_LOG, updateLogEnabledVars);
|
|
|
|
function logPrefixedString(prefix, message) {
|
|
message = message.toString();
|
|
|
|
if (gLogEnabled) {
|
|
dump("*** " + prefix + " " + message + "\n");
|
|
if (!Cu.isInAutomation) {
|
|
Services.console.logStringMessage(prefix + " " + message);
|
|
}
|
|
|
|
if (gLogFileEnabled) {
|
|
if (!gLogFileOutputStream) {
|
|
let logfile = Services.dirsvc.get(KEY_PROFILE_DIR, Ci.nsIFile);
|
|
logfile.append(FILE_UPDATE_MESSAGES);
|
|
gLogFileOutputStream =
|
|
lazy.FileUtils.openAtomicFileOutputStream(logfile);
|
|
}
|
|
if (!gLogFileBinaryStream) {
|
|
gLogFileBinaryStream = new lazy.BinaryOutputStream(
|
|
gLogFileOutputStream
|
|
);
|
|
}
|
|
|
|
try {
|
|
let encoded = new TextEncoder().encode(prefix + " " + message + "\n");
|
|
gLogFileBinaryStream.writeByteArray(encoded);
|
|
gLogFileOutputStream.flush();
|
|
} catch (e) {
|
|
dump(
|
|
"*** " + prefix + " Unable to write to messages file: " + e + "\n"
|
|
);
|
|
Services.console.logStringMessage(
|
|
prefix + " Unable to write to messages file: " + e
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prevent file logging from persisting for more than a session by disabling
|
|
// it on startup.
|
|
function deactivateUpdateLogFile() {
|
|
if (!Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG_FILE, false)) {
|
|
return;
|
|
}
|
|
Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG_FILE, false);
|
|
LOG("Application update file logging being automatically turned off");
|
|
let logFile = Services.dirsvc.get(KEY_PROFILE_DIR, Ci.nsIFile);
|
|
logFile.append(FILE_UPDATE_MESSAGES);
|
|
|
|
try {
|
|
logFile.moveTo(null, FILE_BACKUP_MESSAGES);
|
|
} catch (e) {
|
|
LOG(
|
|
"Failed to backup update messages log (" +
|
|
e +
|
|
"). Attempting to " +
|
|
"remove it."
|
|
);
|
|
try {
|
|
logFile.remove(false);
|
|
} catch (e) {
|
|
LOG("Also failed to remove the update messages log: " + e);
|
|
}
|
|
}
|
|
}
|
|
// This can potentially cause I/O at startup, which isn't great. But it's pretty
|
|
// rare to have this option turned on, especially since we automatically turn it
|
|
// off.
|
|
deactivateUpdateLogFile();
|
|
|
|
/**
|
|
* Logs a string to the error console.
|
|
* @param string
|
|
* The string to write to the error console.
|
|
*/
|
|
function LOG(string) {
|
|
logPrefixedString("AUS:LOG", string);
|
|
}
|
|
|
|
export const UpdateLog = {
|
|
logPrefixedString,
|
|
get enabled() {
|
|
return gLogEnabled;
|
|
},
|
|
get logFileEnabled() {
|
|
return gLogFileEnabled;
|
|
},
|
|
|
|
/**
|
|
* Adds a callback function to be called when `UpdateLog.enabled` or
|
|
* `UpdateLog.logFileEnabled` change values.
|
|
*
|
|
* Adding listeners here is preferable to adding pref listeners to the
|
|
* underlying prefs both because it keeps callers out of the implementation
|
|
* details and because this file also uses those listeners. Since it's hard to
|
|
* guarantee what order the listeners run in, the actual logging behavior may
|
|
* not have changed yet when another pref listener is invoked.
|
|
*
|
|
* @param listener
|
|
* The callback function that will be called when the configuration
|
|
* changes. It will be called with no arguments.
|
|
*/
|
|
addConfigChangeListener(listener) {
|
|
gChangeListeners.push(listener);
|
|
},
|
|
};
|