1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is log4moz
16
* The Initial Developer of the Original Code is
18
* Portions created by the Initial Developer are Copyright (C) 2006
19
* the Initial Developer. All Rights Reserved.
22
* Michael Johnston <special.michael@gmail.com>
23
* Dan Mills <thunder@mozilla.com>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
39
const EXPORTED_SYMBOLS = ['Log4Moz'];
41
const Cc = Components.classes;
42
const Ci = Components.interfaces;
43
const Cr = Components.results;
44
const Cu = Components.utils;
46
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
48
const MODE_RDONLY = 0x01;
49
const MODE_WRONLY = 0x02;
50
const MODE_CREATE = 0x08;
51
const MODE_APPEND = 0x10;
52
const MODE_TRUNCATE = 0x20;
54
const PERMS_FILE = 0644;
55
const PERMS_DIRECTORY = 0755;
58
const ONE_KILOBYTE = 1024 * ONE_BYTE;
59
const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
84
delete Log4Moz.repository;
85
Log4Moz.repository = new LoggerRepository();
86
return Log4Moz.repository;
88
set repository(value) {
89
delete Log4Moz.repository;
90
Log4Moz.repository = value;
93
get LogMessage() { return LogMessage; },
94
get Logger() { return Logger; },
95
get LoggerRepository() { return LoggerRepository; },
97
get Formatter() { return Formatter; },
98
get BasicFormatter() { return BasicFormatter; },
100
get Appender() { return Appender; },
101
get DumpAppender() { return DumpAppender; },
102
get ConsoleAppender() { return ConsoleAppender; },
103
get FileAppender() { return FileAppender; },
104
get RotatingFileAppender() { return RotatingFileAppender; },
107
// let logger = Log4Moz.repository.getLogger("foo");
108
// logger.info(Log4Moz.enumerateInterfaces(someObject).join(","));
109
enumerateInterfaces: function Log4Moz_enumerateInterfaces(aObject) {
114
aObject.QueryInterface(Ci[i]);
124
// let logger = Log4Moz.repository.getLogger("foo");
125
// logger.info(Log4Moz.enumerateProperties(someObject).join(","));
126
enumerateProperties: function Log4Moz_enumerateProps(aObject,
127
aExcludeComplexTypes) {
132
if (aExcludeComplexTypes &&
133
(typeof aObject[p] == "object" || typeof aObject[p] == "function"))
135
properties.push(p + " = " + aObject[p]);
138
properties.push(p + " = " + ex);
149
* Encapsulates a single log event's data
151
function LogMessage(loggerName, level, message){
152
this.loggerName = loggerName;
153
this.message = message;
155
this.time = Date.now();
157
LogMessage.prototype = {
158
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
161
if (this.level in Log4Moz.Level.Desc)
162
return Log4Moz.Level.Desc[this.level];
166
toString: function LogMsg_toString(){
167
return "LogMessage [" + this.time + " " + this.level + " " +
174
* Hierarchical version. Logs to all appenders, assigned or inherited
177
function Logger(name, repository) {
179
repository = Log4Moz.repository;
181
this._appenders = [];
182
this._repository = repository;
185
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
195
if (this._level != null)
198
return this.parent.level;
199
dump("log4moz warning: root logger configuration error: no level defined\n");
200
return Log4Moz.Level.All;
209
return this._appenders;
210
return this._appenders.concat(this.parent.appenders);
213
addAppender: function Logger_addAppender(appender) {
214
for (let i = 0; i < this._appenders.length; i++) {
215
if (this._appenders[i] == appender)
218
this._appenders.push(appender);
221
removeAppender: function Logger_removeAppender(appender) {
222
let newAppenders = [];
223
for (let i = 0; i < this._appenders.length; i++) {
224
if (this._appenders[i] != appender)
225
newAppenders.push(this._appenders[i]);
227
this._appenders = newAppenders;
230
log: function Logger_log(message) {
231
if (this.level > message.level)
233
let appenders = this.appenders;
234
for (let i = 0; i < appenders.length; i++){
235
appenders[i].append(message);
239
fatal: function Logger_fatal(string) {
240
this.log(new LogMessage(this._name, Log4Moz.Level.Fatal, string));
242
error: function Logger_error(string) {
243
this.log(new LogMessage(this._name, Log4Moz.Level.Error, string));
245
warn: function Logger_warn(string) {
246
this.log(new LogMessage(this._name, Log4Moz.Level.Warn, string));
248
info: function Logger_info(string) {
249
this.log(new LogMessage(this._name, Log4Moz.Level.Info, string));
251
config: function Logger_config(string) {
252
this.log(new LogMessage(this._name, Log4Moz.Level.Config, string));
254
debug: function Logger_debug(string) {
255
this.log(new LogMessage(this._name, Log4Moz.Level.Debug, string));
257
trace: function Logger_trace(string) {
258
this.log(new LogMessage(this._name, Log4Moz.Level.Trace, string));
264
* Implements a hierarchy of Loggers
267
function LoggerRepository() {}
268
LoggerRepository.prototype = {
269
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
275
if (!this._rootLogger) {
276
this._rootLogger = new Logger("root", this);
277
this._rootLogger.level = Log4Moz.Level.All;
279
return this._rootLogger;
281
// FIXME: need to update all parent values if we do this
282
//set rootLogger(logger) {
283
// this._rootLogger = logger;
286
_updateParents: function LogRep__updateParents(name) {
287
let pieces = name.split('.');
290
// find the closest parent
291
// don't test for the logger name itself, as there's a chance it's already
292
// there in this._loggers
293
for (let i = 0; i < pieces.length - 1; i++) {
295
cur += '.' + pieces[i];
298
if (cur in this._loggers)
302
// if we didn't assign a parent above, there is no parent
304
this._loggers[name].parent = this.rootLogger;
306
this._loggers[name].parent = this._loggers[parent];
308
// trigger updates for any possible descendants of this logger
309
for (let logger in this._loggers) {
310
if (logger != name && logger.indexOf(name) == 0)
311
this._updateParents(logger);
315
getLogger: function LogRep_getLogger(name) {
317
name = this.getLogger.caller.name;
318
if (name in this._loggers)
319
return this._loggers[name];
320
this._loggers[name] = new Logger(name, this);
321
this._updateParents(name);
322
return this._loggers[name];
328
* These massage a LogMessage into whatever output is desired
329
* Only the BasicFormatter is currently implemented
332
// Abstract formatter
333
function Formatter() {}
334
Formatter.prototype = {
335
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
336
format: function Formatter_format(message) {}
339
// FIXME: should allow for formatting the whole string, not just the date
340
function BasicFormatter(dateFormat) {
342
this.dateFormat = dateFormat;
344
BasicFormatter.prototype = {
345
__proto__: Formatter.prototype,
350
if (!this._dateFormat)
351
this._dateFormat = "%Y-%m-%d %H:%M:%S";
352
return this._dateFormat;
355
set dateFormat(format) {
356
this._dateFormat = format;
359
format: function BF_format(message) {
360
// Pad a string to a certain length (20) with a character (space)
361
let pad = function BF__pad(str, len, chr) str +
362
new Array(Math.max((len || 20) - str.length + 1, 0)).join(chr || " ");
364
// Generate a date string because toLocaleString doesn't work XXX 514803
365
let z = function(n) n < 10 ? "0" + n : n;
366
let d = new Date(message.time);
367
let dateStr = [d.getFullYear(), "-", z(d.getMonth() + 1), "-",
368
z(d.getDate()), " ", z(d.getHours()), ":", z(d.getMinutes()), ":",
369
z(d.getSeconds())].join("");
371
return dateStr + "\t" + pad(message.loggerName) + " " + message.levelDesc +
372
"\t" + message.message + "\n";
378
* These can be attached to Loggers to log to different places
379
* Simply subclass and override doAppend to implement a new one
382
function Appender(formatter) {
383
this._name = "Appender";
384
this._formatter = formatter? formatter : new BasicFormatter();
386
Appender.prototype = {
387
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
389
_level: Log4Moz.Level.All,
390
get level() { return this._level; },
391
set level(level) { this._level = level; },
393
append: function App_append(message) {
394
if(this._level <= message.level)
395
this.doAppend(this._formatter.format(message));
397
toString: function App_toString() {
398
return this._name + " [level=" + this._level +
399
", formatter=" + this._formatter + "]";
401
doAppend: function App_doAppend(message) {}
406
* Logs to standard out
409
function DumpAppender(formatter) {
410
this._name = "DumpAppender";
411
this._formatter = formatter? formatter : new BasicFormatter();
413
DumpAppender.prototype = {
414
__proto__: Appender.prototype,
416
doAppend: function DApp_doAppend(message) {
423
* Logs to the javascript console
426
function ConsoleAppender(formatter) {
427
this._name = "ConsoleAppender";
428
this._formatter = formatter;
430
ConsoleAppender.prototype = {
431
__proto__: Appender.prototype,
433
doAppend: function CApp_doAppend(message) {
434
if (message.level > Log4Moz.Level.Warn) {
435
Cu.reportError(message);
438
Cc["@mozilla.org/consoleservice;1"].
439
getService(Ci.nsIConsoleService).logStringMessage(message);
448
function FileAppender(file, formatter) {
449
this._name = "FileAppender";
450
this._file = file; // nsIFile
451
this._formatter = formatter? formatter : new BasicFormatter();
453
FileAppender.prototype = {
454
__proto__: Appender.prototype,
462
openStream: function FApp_openStream() {
464
let __fos = Cc["@mozilla.org/network/file-output-stream;1"].
465
createInstance(Ci.nsIFileOutputStream);
466
let flags = MODE_WRONLY | MODE_CREATE | MODE_APPEND;
467
__fos.init(this._file, flags, PERMS_FILE, 0);
469
this.__fos = Cc["@mozilla.org/intl/converter-output-stream;1"]
470
.createInstance(Ci.nsIConverterOutputStream);
471
this.__fos.init(__fos, "UTF-8", 4096,
472
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
474
dump("Error opening stream:\n" + e);
478
closeStream: function FApp_closeStream() {
485
dump("Failed to close file output stream\n" + e);
489
doAppend: function FApp_doAppend(message) {
490
if (message === null || message.length <= 0)
493
this._fos.writeString(message);
495
dump("Error writing file:\n" + e);
499
clear: function FApp_clear() {
502
this._file.remove(false);
510
* RotatingFileAppender
511
* Similar to FileAppender, but rotates logs when they become too large
514
function RotatingFileAppender(file, formatter, maxSize, maxBackups) {
515
if (maxSize === undefined)
516
maxSize = ONE_MEGABYTE * 2;
518
if (maxBackups === undefined)
521
this._name = "RotatingFileAppender";
522
this._file = file; // nsIFile
523
this._formatter = formatter? formatter : new BasicFormatter();
524
this._maxSize = maxSize;
525
this._maxBackups = maxBackups;
527
RotatingFileAppender.prototype = {
528
__proto__: FileAppender.prototype,
530
doAppend: function RFApp_doAppend(message) {
531
if (message === null || message.length <= 0)
535
FileAppender.prototype.doAppend.call(this, message);
537
dump("Error writing file:" + e + "\n");
541
rotateLogs: function RFApp_rotateLogs() {
542
if(this._file.exists() &&
543
this._file.fileSize < this._maxSize)
548
for (let i = this.maxBackups - 1; i > 0; i--){
549
let backup = this._file.parent.clone();
550
backup.append(this._file.leafName + "." + i);
552
backup.moveTo(this._file.parent, this._file.leafName + "." + (i + 1));
555
let cur = this._file.clone();
557
cur.moveTo(cur.parent, cur.leafName + ".1");
559
// Note: this._file still points to the same file