The Open Anzo Project

Semantic Application Middleware

Overview

Anzo.JS provides some logging utilities that are used internally and can also be used by application developers. The logging tools are based on log4javascript which is itself based on log4j.

There are already various log in JavaScript without needing to use a special framework. For example, typical alert calls, calling Firebug's console logging methods, etc. Anzo.JS's log facilities provide some conveniences above those methods such as

  • The ability to dynamically change the log level for loggers. So you can filter the amount of logging by level and by particular component.
  • The ability to dynamically instrument a namespace hierarchy for method entry/exit tracing.
  • A user interface for changing log levels.

The Anzo.JS library internally uses the logging described in this document and so even if application developers aren't using this logging themselves, it can be still useful for debugging problems developing with Anzo.JS.

Basic Logging

To add logging support to a class, declare it like this:

dojo.require("anzo.log");

(function(){
var log = anzo.log.getLogger("mypkg.MyClass");

dojo.declare("mypkg.MyClass", null, {
  sampleMethod : function () {
    log.debug("a log message at DEBUG level.");
    log.error("a log message at ERROR level.");
  }
});

})();

The call to anzo.log.getLogger creates a logger with the given name. The convention is that the name should match the class name. Notice the logger is inside a closure rather than being declared globally.

Loggers have various methods to log messages at different levels. Specifically, fatal, error, warn, info, debug, and trace. Loggers have a current log level. The logger will only output messages that are at least as high a level as its current level. You can change the logger's current level with the getLevel method and change it with the setLevel. The anzo.log.log4javascript namespace defines the following constants for the various log levels.

  • anzo.log.log4javascript.Level.OFF
  • anzo.log.log4javascript.Level.FATAL
  • anzo.log.log4javascript.Level.ERROR
  • anzo.log.log4javascript.Level.WARN
  • anzo.log.log4javascript.Level.INFO
  • anzo.log.log4javascript.Level.DEBUG
  • anzo.log.log4javascript.Level.TRACE
  • anzo.log.log4javascript.Level.ALL

So if I wanted to only see log messages of INFO severity or higher (WARN, ERROR, and FATAL) for the sample mypkg.MyClass class, then I could do something like this:

var log = anzo.log.getLogger("mypkg.MyClass");
log.setLevel(anzo.log.log4javascript.Level.INFO);

But it is rarely necessary to change log levels programatically like that. Instead, it is more convenient to load the configuration from a text file and apply it as described below, or using the the LogControlPanel GUI.

Calling anzo.log.getLogger multiple times for the same logger will reuse the same logger object.

Log Output

When you create a logger using the anzo.log.getLogger, it is setup by default to the log level specified by anzo.log.defaultLogLevel which is normally WARN. The logger outputs its data by default to the browser's "console". In Firefox, the console is the Firebug Console. In Safari it outputs messages using Safari's console.log method. In Opera, it uses Opera's opera.postError mechanism. In Internet Explorer, you can include Firebug Lite to see the log messages.

Loading Log Configuration From Files

You can set the log level of all loggers by using a configuration file. The anzo.log.applyConfig takes a string with a format very similar to the Log4j properties file configuration format. For example:

# A typical anzo.log configuration file.

anzo.client.AnzoClient=DEBUG # Set the AnzoClient logger to DEBUG level

# Loggers for the tests 
anzo.tests.client.AnzoClientTest=DEBUG
anzo.tests.client.ClientGraphTest=DEBUG
anzo.tests.client.GraphTableTest=DEBUG
anzo.tests.client.JMSModelServiceTest=DEBUG
anzo.tests.client.JMSNotificationRegistrationServiceTest=DEBUG

You can use the anzo.log.applyConfig method to set the log level of various loggers all at once. This is especially useful for manipulating log levels from a file, since the string could be loaded from a file.

Note that the anzo.log.applyConfig only supports setting log levels. It doesn't support changing appenders, or log layouts, etc. The loggers are obtained by using the anzo.log.getLogger method.

This method is used by the Anzo.JS unit test system to load a log configuration as described in the section below.

Log Control Panel

Log Control Panel Widget Screenshot

Anzo.JS includes a user interface widget which can be used to control log settings at runtime. It can edit log levels for various loggers and can instrument methods to entry/exit tracing.

The best way to open the log console is using a little bookmarklet. Add a bookmark to your browser that contains the URL:

javascript:dojo.require("anzo.log.LogControlPanel");anzo.log.LogControlPanel.launchLogControlPanel()

The bookmarklet will open a popup window with the LogControlPanel inside, letting you edit your log levels dynamically.

Custom embedding of LogControlPanel

The widget is called anzo.log.LogControlPanel and is a typical Dojo widget. To include the widget in you application, create it like any other dojo widget. For example, using programmatic instantiation you might render the widget like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  ...
  <script type="text/javascript" src="../../dojo/dojo.js"></script>
  <script type="text/javascript">
    dojo.require("anzo.log.LogControlPanel");
    function addLogWidget() {
      var logPanel = new anzo.log.LogControlPanel({}, dojo.byId("panelDiv"));		
      logPanel.startup();
    }
    dojo.addOnLoad(addLogWidget);
  </script>
</head>
<body>
<div id="panelDiv"></div>
</body>
</html>

Method Entry/Exit Trace Instrumentation

Sometimes it can be useful to simply see a log message for every method invocation and exit. Having a simple "method start" and "method end" message for every method can help with debugging. Anzo.JS let's you add this sort of tracing dynamically using the anzo.log.addMethodTracing method.

You add method tracing to a JavaScript namespace object and all of its methods recursively. For example, I could easily instrument the dojo library by doing something like this:

anzo.log.addMethodTracing("dojo");

Now every time a method in the dojo namespace or its subnamespaces is called, log messages will appear at entry and exit for those methods. The messages are logged in the TRACE level which doesn't appear by default (since loggers default to WARN). So to see the messages, you need to set the log level to TRACE. You can change the log level using the Log Control Panel user interface or programatically as in this example:

var dojoLogger = anzo.log.getLogger("dojo");
dojoLogger.setLevel(anzo.log.log4javascript.Level.TRACE);

// Now call a dojo method to see the trace log messages
dojo.isArray({ });

You see log messages show up that look something like:

14:56:07 TRACE dojo - ENTER dojo.isArray([object Object])
14:56:07 TRACE dojo - EXIT dojo.isArray returned false

You can instrument only portions of the namespace as well. For example, if I wanted to instrument only Anzo.JS's anzo.rdf namespace, then I could do:

anzo.log.addMethodTracing("anzo.rdf");

The loggers are created such that the follow the namespace. So if, after having added tracing to the anzo.rdf namespace, I wanted to see entry/exit trace messages for only the anzo.rdf.Statement class, I could simply set its logger's level to TRACE.

var logger = anzo.log.getLogger("anzo.rdf.Statement");
logger.setLevel(anzo.log.log4javascript.Level.TRACE);

Keep in mind that often, many of an object's methods are actually part of the object's prototype. The constructor and the prototype have different loggers so you want may want to enable both if you want all methods called on such an instance to be traced. For example:

var logger = anzo.log.getLogger("anzo.rdf.Statement");
logger.setLevel(anzo.log.log4javascript.Level.TRACE);
logger = anzo.log.getLogger("anzo.rdf.Statement.prototype");
logger.setLevel(anzo.log.log4javascript.Level.TRACE);

Be careful when instrumenting namespace heirarchies that may contain cycles. It could cause an infinite loop when trying to instrument such namespaces. By default addMethodTracing skips instrumenting certain properties that are known to have cycles. For example, dojo.doc, dojo.global, constructor, and a few others. You can specify which properties to skip via on optional argument to addMethodTracing. See the method documentation for more information.

Logging in Anzo.JS Unit Tests

Logging can be especially useful when debugging unit tests. The Anzo.JS unit test framework is setup to load log configuration from a specific file upon startup. The best way to debug unit tests is to add anzo.log loggers and log statements to the tests. You can then easily raise of lower the log level of the test loggers and of the Anzo.JS code itself via the unit test log config file.

The Anzo.JS unit test framework will load the log configuration found at:

openanzo-js/src/anzo/tests/anzolog.properties

It will load the file at startup before running any of the tests.

How the unit tests load the config file

The Anzo.JS unit test framework loads the logging configuration file via its prepareAnzo.js file. That file is run by the D.O.H. unit test framework because we pass it as the testUrl parameter to runner.html. For example, see openanzo-js/src/anzo/tests/runLiveTests.html.

Inside prepareAnzo.js, the code loads the file into a string and passes the string to anzo.log.applyConfig.

var logConfig = anzo.tests.utilities.loadTextFile("../../anzo/tests/anzolog.properties");
anzo.log.applyConfig(logConfig);

Attachments

  • loggerControlUI.jpg (52.0 kB) -Log Control Panel Widget Screenshot, added by jordi on 02/28/08 14:56:46.
Copyright © 2007 - 2008 OpenAnzo.org