~ubuntu-branches/ubuntu/oneiric/weave/oneiric

« back to all changes in this revision

Viewing changes to source/modules/log4moz.js

  • Committer: Bazaar Package Importer
  • Author(s): Micah Gersten
  • Date: 2010-08-11 00:35:15 UTC
  • mfrom: (3.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100811003515-o3jbh826bnd1syjv
Tags: 1.4.3-1ubuntu1
* Add -fshort-wchar to CXXFLAGS to fix FTBFS in Ubuntu
  - update debian/rules 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* ***** BEGIN LICENSE BLOCK *****
2
 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 
 *
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/
8
 
 *
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
12
 
 * License.
13
 
 *
14
 
 * The Original Code is log4moz
15
 
 *
16
 
 * The Initial Developer of the Original Code is
17
 
 * Michael Johnston
18
 
 * Portions created by the Initial Developer are Copyright (C) 2006
19
 
 * the Initial Developer. All Rights Reserved.
20
 
 *
21
 
 * Contributor(s):
22
 
 * Michael Johnston <special.michael@gmail.com>
23
 
 * Dan Mills <thunder@mozilla.com>
24
 
 *
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.
36
 
 *
37
 
 * ***** END LICENSE BLOCK ***** */
38
 
 
39
 
const EXPORTED_SYMBOLS = ['Log4Moz'];
40
 
 
41
 
const Cc = Components.classes;
42
 
const Ci = Components.interfaces;
43
 
const Cr = Components.results;
44
 
const Cu = Components.utils;
45
 
 
46
 
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
47
 
 
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;
53
 
 
54
 
const PERMS_FILE      = 0644;
55
 
const PERMS_DIRECTORY = 0755;
56
 
 
57
 
const ONE_BYTE = 1;
58
 
const ONE_KILOBYTE = 1024 * ONE_BYTE;
59
 
const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
60
 
 
61
 
let Log4Moz = {
62
 
  Level: {
63
 
    Fatal:  70,
64
 
    Error:  60,
65
 
    Warn:   50,
66
 
    Info:   40,
67
 
    Config: 30,
68
 
    Debug:  20,
69
 
    Trace:  10,
70
 
    All:    0,
71
 
    Desc: {
72
 
      70: "FATAL",
73
 
      60: "ERROR",
74
 
      50: "WARN",
75
 
      40: "INFO",
76
 
      30: "CONFIG",
77
 
      20: "DEBUG",
78
 
      10: "TRACE",
79
 
      0:  "ALL"
80
 
    }
81
 
  },
82
 
 
83
 
  get repository() {
84
 
    delete Log4Moz.repository;
85
 
    Log4Moz.repository = new LoggerRepository();
86
 
    return Log4Moz.repository;
87
 
  },
88
 
  set repository(value) {
89
 
    delete Log4Moz.repository;
90
 
    Log4Moz.repository = value;
91
 
  },
92
 
 
93
 
  get LogMessage() { return LogMessage; },
94
 
  get Logger() { return Logger; },
95
 
  get LoggerRepository() { return LoggerRepository; },
96
 
 
97
 
  get Formatter() { return Formatter; },
98
 
  get BasicFormatter() { return BasicFormatter; },
99
 
 
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; },
105
 
 
106
 
  // Logging helper:
107
 
  // let logger = Log4Moz.repository.getLogger("foo");
108
 
  // logger.info(Log4Moz.enumerateInterfaces(someObject).join(","));
109
 
  enumerateInterfaces: function Log4Moz_enumerateInterfaces(aObject) {
110
 
    let interfaces = [];
111
 
 
112
 
    for (i in Ci) {
113
 
      try {
114
 
        aObject.QueryInterface(Ci[i]);
115
 
        interfaces.push(i);
116
 
      }
117
 
      catch(ex) {}
118
 
    }
119
 
 
120
 
    return interfaces;
121
 
  },
122
 
 
123
 
  // Logging helper:
124
 
  // let logger = Log4Moz.repository.getLogger("foo");
125
 
  // logger.info(Log4Moz.enumerateProperties(someObject).join(","));
126
 
  enumerateProperties: function Log4Moz_enumerateProps(aObject,
127
 
                                                       aExcludeComplexTypes) {
128
 
    let properties = [];
129
 
 
130
 
    for (p in aObject) {
131
 
      try {
132
 
        if (aExcludeComplexTypes &&
133
 
            (typeof aObject[p] == "object" || typeof aObject[p] == "function"))
134
 
          continue;
135
 
        properties.push(p + " = " + aObject[p]);
136
 
      }
137
 
      catch(ex) {
138
 
        properties.push(p + " = " + ex);
139
 
      }
140
 
    }
141
 
 
142
 
    return properties;
143
 
  }
144
 
};
145
 
 
146
 
 
147
 
/*
148
 
 * LogMessage
149
 
 * Encapsulates a single log event's data
150
 
 */
151
 
function LogMessage(loggerName, level, message){
152
 
  this.loggerName = loggerName;
153
 
  this.message = message;
154
 
  this.level = level;
155
 
  this.time = Date.now();
156
 
}
157
 
LogMessage.prototype = {
158
 
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
159
 
 
160
 
  get levelDesc() {
161
 
    if (this.level in Log4Moz.Level.Desc)
162
 
      return Log4Moz.Level.Desc[this.level];
163
 
    return "UNKNOWN";
164
 
  },
165
 
 
166
 
  toString: function LogMsg_toString(){
167
 
    return "LogMessage [" + this.time + " " + this.level + " " +
168
 
      this.message + "]";
169
 
  }
170
 
};
171
 
 
172
 
/*
173
 
 * Logger
174
 
 * Hierarchical version.  Logs to all appenders, assigned or inherited
175
 
 */
176
 
 
177
 
function Logger(name, repository) {
178
 
  if (!repository)
179
 
    repository = Log4Moz.repository;
180
 
  this._name = name;
181
 
  this._appenders = [];
182
 
  this._repository = repository;
183
 
}
184
 
Logger.prototype = {
185
 
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
186
 
 
187
 
  parent: null,
188
 
 
189
 
  get name() {
190
 
    return this._name;
191
 
  },
192
 
 
193
 
  _level: null,
194
 
  get level() {
195
 
    if (this._level != null)
196
 
      return this._level;
197
 
    if (this.parent)
198
 
      return this.parent.level;
199
 
    dump("log4moz warning: root logger configuration error: no level defined\n");
200
 
    return Log4Moz.Level.All;
201
 
  },
202
 
  set level(level) {
203
 
    this._level = level;
204
 
  },
205
 
 
206
 
  _appenders: null,
207
 
  get appenders() {
208
 
    if (!this.parent)
209
 
      return this._appenders;
210
 
    return this._appenders.concat(this.parent.appenders);
211
 
  },
212
 
 
213
 
  addAppender: function Logger_addAppender(appender) {
214
 
    for (let i = 0; i < this._appenders.length; i++) {
215
 
      if (this._appenders[i] == appender)
216
 
        return;
217
 
    }
218
 
    this._appenders.push(appender);
219
 
  },
220
 
 
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]);
226
 
    }
227
 
    this._appenders = newAppenders;
228
 
  },
229
 
 
230
 
  log: function Logger_log(message) {
231
 
    if (this.level > message.level)
232
 
      return;
233
 
    let appenders = this.appenders;
234
 
    for (let i = 0; i < appenders.length; i++){
235
 
      appenders[i].append(message);
236
 
    }
237
 
  },
238
 
 
239
 
  fatal: function Logger_fatal(string) {
240
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Fatal, string));
241
 
  },
242
 
  error: function Logger_error(string) {
243
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Error, string));
244
 
  },
245
 
  warn: function Logger_warn(string) {
246
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Warn, string));
247
 
  },
248
 
  info: function Logger_info(string) {
249
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Info, string));
250
 
  },
251
 
  config: function Logger_config(string) {
252
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Config, string));
253
 
  },
254
 
  debug: function Logger_debug(string) {
255
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Debug, string));
256
 
  },
257
 
  trace: function Logger_trace(string) {
258
 
    this.log(new LogMessage(this._name, Log4Moz.Level.Trace, string));
259
 
  }
260
 
};
261
 
 
262
 
/*
263
 
 * LoggerRepository
264
 
 * Implements a hierarchy of Loggers
265
 
 */
266
 
 
267
 
function LoggerRepository() {}
268
 
LoggerRepository.prototype = {
269
 
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
270
 
 
271
 
  _loggers: {},
272
 
 
273
 
  _rootLogger: null,
274
 
  get rootLogger() {
275
 
    if (!this._rootLogger) {
276
 
      this._rootLogger = new Logger("root", this);
277
 
      this._rootLogger.level = Log4Moz.Level.All;
278
 
    }
279
 
    return this._rootLogger;
280
 
  },
281
 
  // FIXME: need to update all parent values if we do this
282
 
  //set rootLogger(logger) {
283
 
  //  this._rootLogger = logger;
284
 
  //},
285
 
 
286
 
  _updateParents: function LogRep__updateParents(name) {
287
 
    let pieces = name.split('.');
288
 
    let cur, parent;
289
 
 
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++) {
294
 
      if (cur)
295
 
        cur += '.' + pieces[i];
296
 
      else
297
 
        cur = pieces[i];
298
 
      if (cur in this._loggers)
299
 
        parent = cur;
300
 
    }
301
 
 
302
 
    // if we didn't assign a parent above, there is no parent
303
 
    if (!parent)
304
 
      this._loggers[name].parent = this.rootLogger;
305
 
    else
306
 
      this._loggers[name].parent = this._loggers[parent];
307
 
 
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);
312
 
    }
313
 
  },
314
 
 
315
 
  getLogger: function LogRep_getLogger(name) {
316
 
    if (!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];
323
 
  }
324
 
};
325
 
 
326
 
/*
327
 
 * Formatters
328
 
 * These massage a LogMessage into whatever output is desired
329
 
 * Only the BasicFormatter is currently implemented
330
 
 */
331
 
 
332
 
// Abstract formatter
333
 
function Formatter() {}
334
 
Formatter.prototype = {
335
 
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
336
 
  format: function Formatter_format(message) {}
337
 
};
338
 
 
339
 
// FIXME: should allow for formatting the whole string, not just the date
340
 
function BasicFormatter(dateFormat) {
341
 
  if (dateFormat)
342
 
    this.dateFormat = dateFormat;
343
 
}
344
 
BasicFormatter.prototype = {
345
 
  __proto__: Formatter.prototype,
346
 
 
347
 
  _dateFormat: null,
348
 
 
349
 
  get dateFormat() {
350
 
    if (!this._dateFormat)
351
 
      this._dateFormat = "%Y-%m-%d %H:%M:%S";
352
 
    return this._dateFormat;
353
 
  },
354
 
 
355
 
  set dateFormat(format) {
356
 
    this._dateFormat = format;
357
 
  },
358
 
 
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 || " ");
363
 
 
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("");
370
 
 
371
 
    return dateStr + "\t" + pad(message.loggerName) + " " + message.levelDesc +
372
 
      "\t" + message.message + "\n";
373
 
  }
374
 
};
375
 
 
376
 
/*
377
 
 * Appenders
378
 
 * These can be attached to Loggers to log to different places
379
 
 * Simply subclass and override doAppend to implement a new one
380
 
 */
381
 
 
382
 
function Appender(formatter) {
383
 
  this._name = "Appender";
384
 
  this._formatter = formatter? formatter : new BasicFormatter();
385
 
}
386
 
Appender.prototype = {
387
 
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
388
 
 
389
 
  _level: Log4Moz.Level.All,
390
 
  get level() { return this._level; },
391
 
  set level(level) { this._level = level; },
392
 
 
393
 
  append: function App_append(message) {
394
 
    if(this._level <= message.level)
395
 
      this.doAppend(this._formatter.format(message));
396
 
  },
397
 
  toString: function App_toString() {
398
 
    return this._name + " [level=" + this._level +
399
 
      ", formatter=" + this._formatter + "]";
400
 
  },
401
 
  doAppend: function App_doAppend(message) {}
402
 
};
403
 
 
404
 
/*
405
 
 * DumpAppender
406
 
 * Logs to standard out
407
 
 */
408
 
 
409
 
function DumpAppender(formatter) {
410
 
  this._name = "DumpAppender";
411
 
  this._formatter = formatter? formatter : new BasicFormatter();
412
 
}
413
 
DumpAppender.prototype = {
414
 
  __proto__: Appender.prototype,
415
 
 
416
 
  doAppend: function DApp_doAppend(message) {
417
 
    dump(message);
418
 
  }
419
 
};
420
 
 
421
 
/*
422
 
 * ConsoleAppender
423
 
 * Logs to the javascript console
424
 
 */
425
 
 
426
 
function ConsoleAppender(formatter) {
427
 
  this._name = "ConsoleAppender";
428
 
  this._formatter = formatter;
429
 
}
430
 
ConsoleAppender.prototype = {
431
 
  __proto__: Appender.prototype,
432
 
 
433
 
  doAppend: function CApp_doAppend(message) {
434
 
    if (message.level > Log4Moz.Level.Warn) {
435
 
      Cu.reportError(message);
436
 
      return;
437
 
    }
438
 
    Cc["@mozilla.org/consoleservice;1"].
439
 
      getService(Ci.nsIConsoleService).logStringMessage(message);
440
 
  }
441
 
};
442
 
 
443
 
/*
444
 
 * FileAppender
445
 
 * Logs to a file
446
 
 */
447
 
 
448
 
function FileAppender(file, formatter) {
449
 
  this._name = "FileAppender";
450
 
  this._file = file; // nsIFile
451
 
  this._formatter = formatter? formatter : new BasicFormatter();
452
 
}
453
 
FileAppender.prototype = {
454
 
  __proto__: Appender.prototype,
455
 
  __fos: null,
456
 
  get _fos() {
457
 
    if (!this.__fos)
458
 
      this.openStream();
459
 
    return this.__fos;
460
 
  },
461
 
 
462
 
  openStream: function FApp_openStream() {
463
 
    try {
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);
468
 
 
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);
473
 
    } catch(e) {
474
 
      dump("Error opening stream:\n" + e);
475
 
    }
476
 
  },
477
 
 
478
 
  closeStream: function FApp_closeStream() {
479
 
    if (!this.__fos)
480
 
      return;
481
 
    try {
482
 
      this.__fos.close();
483
 
      this.__fos = null;
484
 
    } catch(e) {
485
 
      dump("Failed to close file output stream\n" + e);
486
 
    }
487
 
  },
488
 
 
489
 
  doAppend: function FApp_doAppend(message) {
490
 
    if (message === null || message.length <= 0)
491
 
      return;
492
 
    try {
493
 
      this._fos.writeString(message);
494
 
    } catch(e) {
495
 
      dump("Error writing file:\n" + e);
496
 
    }
497
 
  },
498
 
 
499
 
  clear: function FApp_clear() {
500
 
    this.closeStream();
501
 
    try {
502
 
      this._file.remove(false);
503
 
    } catch (e) {
504
 
      // XXX do something?
505
 
    }
506
 
  }
507
 
};
508
 
 
509
 
/*
510
 
 * RotatingFileAppender
511
 
 * Similar to FileAppender, but rotates logs when they become too large
512
 
 */
513
 
 
514
 
function RotatingFileAppender(file, formatter, maxSize, maxBackups) {
515
 
  if (maxSize === undefined)
516
 
    maxSize = ONE_MEGABYTE * 2;
517
 
 
518
 
  if (maxBackups === undefined)
519
 
    maxBackups = 0;
520
 
 
521
 
  this._name = "RotatingFileAppender";
522
 
  this._file = file; // nsIFile
523
 
  this._formatter = formatter? formatter : new BasicFormatter();
524
 
  this._maxSize = maxSize;
525
 
  this._maxBackups = maxBackups;
526
 
}
527
 
RotatingFileAppender.prototype = {
528
 
  __proto__: FileAppender.prototype,
529
 
 
530
 
  doAppend: function RFApp_doAppend(message) {
531
 
    if (message === null || message.length <= 0)
532
 
      return;
533
 
    try {
534
 
      this.rotateLogs();
535
 
      FileAppender.prototype.doAppend.call(this, message);
536
 
    } catch(e) {
537
 
      dump("Error writing file:" + e + "\n");
538
 
    }
539
 
  },
540
 
 
541
 
  rotateLogs: function RFApp_rotateLogs() {
542
 
    if(this._file.exists() &&
543
 
       this._file.fileSize < this._maxSize)
544
 
      return;
545
 
 
546
 
    this.closeStream();
547
 
 
548
 
    for (let i = this.maxBackups - 1; i > 0; i--){
549
 
      let backup = this._file.parent.clone();
550
 
      backup.append(this._file.leafName + "." + i);
551
 
      if (backup.exists())
552
 
        backup.moveTo(this._file.parent, this._file.leafName + "." + (i + 1));
553
 
    }
554
 
 
555
 
    let cur = this._file.clone();
556
 
    if (cur.exists())
557
 
      cur.moveTo(cur.parent, cur.leafName + ".1");
558
 
 
559
 
    // Note: this._file still points to the same file
560
 
  }
561
 
};