One of the good parts about including things with scope is that you don’t have to worry about changing them into a more extension-friendly namespace format, which is nice.

So I didn’t have to adapt log4js to get it to play nicely in chrome at all. However, I did have to change it to make it a little more Andy friendly.

Basically, I wanted the log output to hit the console (using dump()) and to look like…

startTime logLevel [fileName:lineNum] message\n
eg.


23/03/07 22:10:11:342 DEBUG [overlay.js:14] A debug message
23/03/07 22:10:12:589 DEBUG [overlay.js:21] Another message

Log4js can’t log to a command prompt using dump(). That was an easy change, we just needed to add another appender, as below.


/**
 * Appender writes the logs to the Shell of Firefox when it is launched with
 * firefox -console (and has the necessary properties enabled)
 * PLEASE NOTE - Only works in Firefox
 * @constructor
 * @extends Appender
 * @param logger log4js instance this appender is attached to
 * @author Andrew Mutton
 */
function FirefoxConsoleAppender(logger) {
...
	this.layout = new MozStackLayout(true);
}

FirefoxConsoleAppender.superclass = Appender.prototype;
FirefoxConsoleAppender.prototype = {
	/**
	 * @see Appender#doAppend
	 */
	doAppend: function(loggingEvent) {
		dump(this.layout.format(loggingEvent));
	},
...

So now we can dump to the console. Notice that there’s a new Layout there too? You probably didn’t, since not many people use log4js I imagine.

The new layout is there to take advantage of the information we can get from Components.stack - notably, line numbers and source files.


/**
 * MozStackLayout is a layout that takes advantage of stack data from Mozilla.
 *
 * startTime logLevel [fileName:lineNum] messagen
 *
 *
 * @constructor
 * @extends Layout
 * @author Andrew Mutton
 */
function MozStackLayout(showDate) {
	this.LINE_SEP  = "n";
	this.showDate = showDate;
}
MozStackLayout.prototype = {
	format: function(loggingEvent) {
	    if (this.showDate && Log4js.simpledate) {
    	    var d = new Log4js.simpledate.SimpleDate(loggingEvent.startTime.getTime());
          	return d.toFormattedString("~d/~k/~y ~H:~m:~s:~S") + " " + loggingEvent.level.toString() + " [" +  loggingEvent.getStackFileName() + ":" + loggingEvent.getStackLineNum() + "] " + loggingEvent.message + this.LINE_SEP;
	    } else {
	        return loggingEvent.level.toString() + " [" +  loggingEvent.getStackFileName() + ":" + loggingEvent.getStackLineNum() + "] " + loggingEvent.message + this.LINE_SEP;
	    }
	},
...

this is the bit that uses simpledate: to format the date in a less ugly way than you usually get for free from Javascript. I honestly have no idea why there isn’t a nice date formatter built into the language. It is ridiculous.

There’s a few more changes here as well. I had to change the log() method so that it attempts to get stack data (if it’s available).


log: function(message, logLevel) {
    if (Components && Components.stack) {
        var stack = Components.stack.caller;
        if (stack.toString().indexOf("log4js.js") != -1) {
            stack = stack.caller;
        }
        var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel, message, this, stack.toString());
    } else {
        var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel, message, this);
    }
...

and also LoggingEvent, so that it can give the stack information


getStackFileName : function() {
    if (this.stackInfo != null) {
        var split = this.stackInfo.split(" :: ");
        var fileName = split[1];
        if (fileName.lastIndexOf("/") != -1) {
            var pathSplit = fileName.split("/");
            fileName = pathSplit[pathSplit.length - 1];
        }
        return fileName
    }
    return null;
},

getStackLineNum : function() {
    if (this.stackInfo != null) {
        var split = this.stackInfo.split(" :: ");
        var lineNum = split[3].split(" ")[1];
        return lineNum;
    }
    return null;
}

So there you have it. Not very interesting today, but this should be the last infrastructure post. I think we can begin doing things properly now.

I’m very interested in getting log4js to be configurable via a properties files (ala log.properties in log4j). This doesn’t seem too hard, so I may have to check it out. An alternative to log.properties would be to write the configuration in JS. Something like…


logger("booksmarts.log").atLevel("DEBUG")
    .withAppender("FirefoxConsoleAppender")
    .andLayout("MozStackLayout");

this would be too simple though. Maybe something more elaborate. Who can really say eh?