Bug 989137 - Part 3: Log.jsm API to get a Logger that prefixes messages; r=bsmedberg

A common pattern for logging is to have multiple loggers for multiple
underlying object instances. You often want to have each instance attach
some identifying metdata contained in each logged message. This patch
provides an API to facilitate that.

--HG--
extra : rebase_source : 5816e0671c78f55cca45bdd1aed52c85695945c4
This commit is contained in:
Gregory Szorc 2014-03-28 11:36:37 -07:00
Родитель f3005fef10
Коммит 5b230fc390
3 изменённых файлов: 83 добавлений и 24 удалений

Просмотреть файл

@ -69,6 +69,7 @@ this.Log = {
Formatter: Formatter,
BasicFormatter: BasicFormatter,
MessageOnlyFormatter: MessageOnlyFormatter,
StructuredFormatter: StructuredFormatter,
Appender: Appender,
@ -359,13 +360,58 @@ LoggerRepository.prototype = {
}
},
getLogger: function LogRep_getLogger(name) {
/**
* Obtain a named Logger.
*
* The returned Logger instance for a particular name is shared among
* all callers. In other words, if two consumers call getLogger("foo"),
* they will both have a reference to the same object.
*
* @return Logger
*/
getLogger: function (name) {
if (name in this._loggers)
return this._loggers[name];
this._loggers[name] = new Logger(name, this);
this._updateParents(name);
return this._loggers[name];
}
},
/**
* Obtain a Logger that logs all string messages with a prefix.
*
* A common pattern is to have separate Logger instances for each instance
* of an object. But, you still want to distinguish between each instance.
* Since Log.repository.getLogger() returns shared Logger objects,
* monkeypatching one Logger modifies them all.
*
* This function returns a new object with a prototype chain that chains
* up to the original Logger instance. The new prototype has log functions
* that prefix content to each message.
*
* @param name
* (string) The Logger to retrieve.
* @param prefix
* (string) The string to prefix each logged message with.
*/
getLoggerWithMessagePrefix: function (name, prefix) {
let log = this.getLogger(name);
let proxy = {__proto__: log};
for (let level in Log.Level) {
if (level == "Desc") {
continue;
}
let lc = level.toLowerCase();
proxy[lc] = function (msg, ...args) {
return log[lc].apply(log, [prefix + msg, ...args]);
};
}
return proxy;
},
};
/*
@ -396,6 +442,19 @@ BasicFormatter.prototype = {
}
};
/**
* A formatter that only formats the string message component.
*/
function MessageOnlyFormatter() {
}
MessageOnlyFormatter.prototype = Object.freeze({
__proto__: Formatter.prototype,
format: function (message) {
return message.message + "\n";
},
});
// Structured formatter that outputs JSON based on message data.
// This formatter will format unstructured messages by supplying
// default values.

Просмотреть файл

@ -173,28 +173,8 @@ function openConnection(options) {
* `openConnection`.
*/
function OpenedConnection(connection, basename, number, options) {
let log = Log.repository.getLogger("Sqlite.Connection." + basename);
// getLogger() returns a shared object. We can't modify the functions on this
// object since they would have effect on all instances and last write would
// win. So, we create a "proxy" object with our custom functions. Everything
// else is proxied back to the shared logger instance via prototype
// inheritance.
let logProxy = {__proto__: log};
// Automatically prefix all log messages with the identifier.
for (let level in Log.Level) {
if (level == "Desc") {
continue;
}
let lc = level.toLowerCase();
logProxy[lc] = function (msg) {
return log[lc].call(log, "Conn #" + number + ": " + msg);
};
}
this._log = logProxy;
this._log = Log.repository.getLoggerWithMessagePrefix("Sqlite.Connection." + basename,
"Conn #" + number + ": ");
this._log.info("Opened");

Просмотреть файл

@ -72,6 +72,26 @@ add_test(function test_Logger_parent() {
run_next_test();
});
add_test(function test_LoggerWithMessagePrefix() {
let log = Log.repository.getLogger("test.logger.prefix");
let appender = new MockAppender(new Log.MessageOnlyFormatter());
log.addAppender(appender);
let prefixed = Log.repository.getLoggerWithMessagePrefix(
"test.logger.prefix", "prefix: ");
log.warn("no prefix");
prefixed.warn("with prefix");
Assert.equal(appender.messages.length, 2, "2 messages were logged.");
Assert.deepEqual(appender.messages, [
"no prefix\n",
"prefix: with prefix\n",
], "Prefix logger works.");
run_next_test();
});
// A utility method for checking object equivalence.
// Fields with a reqular expression value in expected will be tested
// against the corresponding value in actual. Otherwise objects