view modules/log4moz.js @ 382:8460f746207e default tip

remove extraneous log statement
author Myk Melez <myk@mozilla.org>
date Sat, 08 Nov 2008 10:38:57 -0800
parents d11c788c254f
children
line wrap: on
line source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is log4moz
 *
 * The Initial Developer of the Original Code is
 * Michael Johnston
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Michael Johnston <special.michael@gmail.com>
 * Dan Mills <thunder@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

const EXPORTED_SYMBOLS = ['Log4Moz'];

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

const MODE_RDONLY   = 0x01;
const MODE_WRONLY   = 0x02;
const MODE_CREATE   = 0x08;
const MODE_APPEND   = 0x10;
const MODE_TRUNCATE = 0x20;

const PERMS_FILE      = 0644;
const PERMS_DIRECTORY = 0755;

const ONE_BYTE = 1;
const ONE_KILOBYTE = 1024 * ONE_BYTE;
const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;

const Log4Moz = {};
Log4Moz.Level = {};
Log4Moz.Level.Fatal  = 70;
Log4Moz.Level.Error  = 60;
Log4Moz.Level.Warn   = 50;
Log4Moz.Level.Info   = 40;
Log4Moz.Level.Config = 30;
Log4Moz.Level.Debug  = 20;
Log4Moz.Level.Trace  = 10;
Log4Moz.Level.All    = 0;

Log4Moz.Level.Desc = {
  70: "FATAL",
  60: "ERROR",
  50: "WARN",
  40: "INFO",
  30: "CONFIG",
  20: "DEBUG",
  10: "TRACE",
  0:  "ALL"
};

/*
 * LogMessage
 * Encapsulates a single log event's data
 */
function LogMessage(loggerName, level, message){
  this.loggerName = loggerName;
  this.message = message;
  this.level = level;
  this.time = Date.now();
}
LogMessage.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),

  get levelDesc() {
    if (this.level in Log4Moz.Level.Desc)
      return Log4Moz.Level.Desc[this.level];
    return "UNKNOWN";
  },

  toString: function LogMsg_toString(){
    return "LogMessage [" + this._date + " " + this.level + " " +
      this.message + "]";
  }
};

/*
 * Logger
 * Hierarchical version.  Logs to all appenders, assigned or inherited
 */

function Logger(name, repository) {
  this._name = name;
  this._repository = repository;
  this._appenders = [];
}
Logger.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),

  parent: null,

  _level: null,
  get level() {
    if (this._level != null)
      return this._level;
    if (this.parent)
      return this.parent.level;
    dump("log4moz warning: root logger configuration error: no level defined\n");
    return Log4Moz.Level.All;
  },
  set level(level) {
    this._level = level;
  },

  _appenders: null,
  get appenders() {
    if (!this.parent)
      return this._appenders;
    return this._appenders.concat(this.parent.appenders);
  },

  addAppender: function Logger_addAppender(appender) {
    for (let i = 0; i < this._appenders.length; i++) {
      if (this._appenders[i] == appender)
        return;
    }
    this._appenders.push(appender);
  },

  log: function Logger_log(message) {
    if (this.level > message.level)
      return;
    let appenders = this.appenders;
    for (let i = 0; i < appenders.length; i++){
      appenders[i].append(message);
    }
  },

  fatal: function Logger_fatal(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Fatal, string));
  },
  error: function Logger_error(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Error, string));
  },
  warn: function Logger_warn(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Warn, string));
  },
  info: function Logger_info(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Info, string));
  },
  config: function Logger_config(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Config, string));
  },
  debug: function Logger_debug(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Debug, string));
  },
  trace: function Logger_trace(string) {
    this.log(new LogMessage(this._name, Log4Moz.Level.Trace, string));
  }
};

/*
 * LoggerRepository
 * Implements a hierarchy of Loggers
 */

function LoggerRepository() {}
LoggerRepository.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),

  _loggers: {},

  _rootLogger: null,
  get rootLogger() {
    if (!this._rootLogger) {
      this._rootLogger = new Logger("root", this);
      this._rootLogger.level = Log4Moz.Level.All;
    }
    return this._rootLogger;
  },
  // FIXME: need to update all parent values if we do this
  //set rootLogger(logger) {
  //  this._rootLogger = logger;
  //},

  _updateParents: function LogRep__updateParents(name) {
    let pieces = name.split('.');
    let cur, parent;

    // find the closest parent
    for (let i = 0; i < pieces.length; i++) {
      if (cur)
        cur += '.' + pieces[i];
      else
        cur = pieces[i];
      if (cur in this._loggers)
        parent = cur;
    }

    // if they are the same it has no parent
    if (parent == name)
      this._loggers[name].parent = this.rootLogger;
    else
      this._loggers[name].parent = this._loggers[parent];

    // trigger updates for any possible descendants of this logger
    for (let logger in this._loggers) {
      if (logger != name && name.indexOf(logger) == 0)
        this._updateParents(logger);
    }
  },

  getLogger: function LogRep_getLogger(name) {
    if (!(name in this._loggers)) {
      this._loggers[name] = new Logger(name, this);
      this._updateParents(name);
    }
    return this._loggers[name];
  }
};

/*
 * Formatters
 * These massage a LogMessage into whatever output is desired
 * Only the BasicFormatter is currently implemented
 */

// Abstract formatter
function Formatter() {}
Formatter.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  format: function Formatter_format(message) {}
};

// FIXME: should allow for formatting the whole string, not just the date
function BasicFormatter(dateFormat) {
  if (dateFormat)
    this.dateFormat = dateFormat;
}
BasicFormatter.prototype = {
  _dateFormat: null,

  get dateFormat() {
    if (!this._dateFormat)
      this._dateFormat = "%Y-%m-%d %H:%M:%S";
    return this._dateFormat;
  },

  set dateFormat(format) {
    this._dateFormat = format;
  },

  format: function BF_format(message) {
    let date = new Date(message.time);
    return date.toLocaleFormat(this.dateFormat) + "\t" +
      message.loggerName + "\t" + message.levelDesc + "\t" +
      message.message + "\n";
  }
};
BasicFormatter.prototype.__proto__ = new Formatter();

/*
 * Appenders
 * These can be attached to Loggers to log to different places
 * Simply subclass and override doAppend to implement a new one
 */

function Appender(formatter) {
  this._name = "Appender";
  this._formatter = formatter? formatter : new BasicFormatter();
}
Appender.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),

  _level: Log4Moz.Level.All,
  get level() { return this._level; },
  set level(level) { this._level = level; },

  append: function App_append(message) {
    if(this._level <= message.level)
      this.doAppend(this._formatter.format(message));
  },
  toString: function App_toString() {
    return this._name + " [level=" + this._level +
      ", formatter=" + this._formatter + "]";
  },
  doAppend: function App_doAppend(message) {}
};

/*
 * DumpAppender
 * Logs to standard out
 */

function DumpAppender(formatter) {
  this._name = "DumpAppender";
  this._formatter = formatter;
}
DumpAppender.prototype = {
  doAppend: function DApp_doAppend(message) {
    dump(message);
  }
};
DumpAppender.prototype.__proto__ = new Appender();

/*
 * ConsoleAppender
 * Logs to the javascript console
 */

function ConsoleAppender(formatter) {
  this._name = "ConsoleAppender";
  this._formatter = formatter;
}
ConsoleAppender.prototype = {
  doAppend: function CApp_doAppend(message) {
    if (message.level > Log4Moz.Level.Warn) {
      Cu.reportError(message);
      return;
    }
    Cc["@mozilla.org/consoleservice;1"].
      getService(Ci.nsIConsoleService).logStringMessage(message);
  }
};
ConsoleAppender.prototype.__proto__ = new Appender();

/*
 * FileAppender
 * Logs to a file
 */

function FileAppender(file, formatter) {
  this._name = "FileAppender";
  this._file = file; // nsIFile
  this._formatter = formatter;
}
FileAppender.prototype = {
  __fos: null,
  get _fos() {
    if (!this.__fos)
      this.openStream();
    return this.__fos;
  },

  openStream: function FApp_openStream() {
    this.__fos = Cc["@mozilla.org/network/file-output-stream;1"].
      createInstance(Ci.nsIFileOutputStream);
    let flags = MODE_WRONLY | MODE_CREATE | MODE_APPEND;
    this.__fos.init(this._file, flags, PERMS_FILE, 0);
  },

  closeStream: function FApp_closeStream() {
    if (!this.__fos)
      return;
    try {
      this.__fos.close();
      this.__fos = null;
    } catch(e) {
      dump("Failed to close file output stream\n" + e);
    }
  },

  doAppend: function FApp_doAppend(message) {
    if (message === null || message.length <= 0)
      return;
    try {
      this._fos().write(message, message.length);
    } catch(e) {
      dump("Error writing file:\n" + e);
    }
  }
};
FileAppender.prototype.__proto__ = new Appender();

/*
 * RotatingFileAppender
 * Similar to FileAppender, but rotates logs when they become too large
 */

function RotatingFileAppender(file, formatter, maxSize, maxBackups) {
  this._name = "RotatingFileAppender";
  this._file = file; // nsIFile
  this._formatter = formatter;
  this._maxSize = maxSize;
  this._maxBackups = maxBackups;
}
RotatingFileAppender.prototype = {
  doAppend: function RFApp_doAppend(message) {
    if (message === null || message.length <= 0)
      return;
    try {
      this.rotateLogs();
      this._fos.write(message, message.length);
    } catch(e) {
      dump("Error writing file:\n" + e);
    }
  },
  rotateLogs: function RFApp_rotateLogs() {
    if(this._file.exists() &&
       this._file.fileSize < this._maxSize)
      return;

    this.closeStream();

    for (let i = this.maxBackups - 1; i > 0; i--){
      let backup = this._file.parent.clone();
      backup.append(this._file.leafName + "." + i);
      if (backup.exists())
        backup.moveTo(this._file.parent, this._file.leafName + "." + (i + 1));
    }

    let cur = this._file.clone();
    if (cur.exists())
      cur.moveTo(cur.parent, cur.leafName + ".1");

    // Note: this._file still points to the same file
  }
};
RotatingFileAppender.prototype.__proto__ = new FileAppender();

/*
 * LoggingService
 */

function Log4MozService() {
  this._repository = new LoggerRepository();
}
Log4MozService.prototype = {
  //classDescription: "Log4moz Logging Service",
  //contractID: "@mozilla.org/log4moz/service;1",
  //classID: Components.ID("{a60e50d7-90b8-4a12-ad0c-79e6a1896978}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),

  get rootLogger() {
    return this._repository.rootLogger;
  },

  getLogger: function LogSvc_getLogger(name) {
    return this._repository.getLogger(name);
  },

  newAppender: function LogSvc_newAppender(kind, formatter) {
    switch (kind) {
    case "dump":
      return new DumpAppender(formatter);
    case "console":
      return new ConsoleAppender(formatter);
    default:
      dump("log4moz: unknown appender kind: " + kind);
      return;
    }
  },

  newFileAppender: function LogSvc_newAppender(kind, file, formatter) {
    switch (kind) {
    case "file":
      return new FileAppender(file, formatter);
    case "rotating":
      // FIXME: hardcoded constants
      return new RotatingFileAppender(file, formatter, ONE_MEGABYTE * 5, 0);
    default:
      dump("log4moz: unknown appender kind: " + kind);
      return;
    }
  },

  newFormatter: function LogSvc_newFormatter(kind) {
    switch (kind) {
    case "basic":
      return new BasicFormatter();
    default:
      dump("log4moz: unknown formatter kind: " + kind);
      return;
    }
  }
};

Log4Moz.Service = new Log4MozService();

Log4Moz.enumerateInterfaces = function(aObject) {
  let interfaces = [];

  for (i in Ci) {
    try {
      aObject.QueryInterface(Ci[i]);
      interfaces.push(i);
    }
    catch(ex) {}
  }

  return interfaces;
};

Log4Moz.enumerateProperties = function(aObject, aExcludeComplexTypes) {
  let properties = [];

  for (p in aObject) {
    try {
      if (aExcludeComplexTypes &&
          (typeof aObject[p] == "object" || typeof aObject[p] == "function"))
        continue;
      properties.push(p + " = " + aObject[p]);
    }
    catch(ex) {
      properties.push(p + " = " + ex);
    }
  }

  return properties;
};