Extending Flex’s logging framework to write custom log files

In these days I played with Flex’s logging framework and I extended it to write log messages to files, both plain text .log files and dynamic HTML, CSS formatted and Javascript powered files with the ability to filter message type (debug, error, fatal and so on).

The logging framework is a powerful feature that comes with Flex’s sdk and it’s composed by the classes under mx.logging package, which includes the two “subpackages” mx.logging.errors and mx.logging.target.

Java developers should be already confident whit such framework, because logging api are commonly used in Java programming,  Actionscript /Flash/Flex developers may find this tool a novelty (as I did).

The objective of logging framework is to provide a tool that offers a far better, flexible and centralized way to debug an application than simple use a lot of trace() callings. With Flex’s debugging framework we are able to print and filtering among different types of message based on their severity, such:

  • DEBUG
  • ERROR
  • FATAL
  • INFO
  • WARN

We can also print the timestamp of the message and the class it refers to, and finally we can simultaneously print messages to different targets.

How it works

The logging framework is basically represented by 3 units: a logger, a target and a log event. The first component is the main class of the framework and its job is to register the different “loggable classes”, register one or more targets and dispatch log events. A target is a class which receives a LogEvent event and handles it by printing its information (message, date, level…) somewhere. A LogEvent event is dispatched when one of the logger methods such: debug(), error(), fatal(), info(), log() or warn() is called.

The steps required to use the logging system are the following:

1. Registering the classes for logging:

package com.daveoncode {

import mx.logging.*;

public class Foo {

private var _logger:ILogger;

public function Foo() {

this._logger = Log.getLogger("com.daveoncode.Foo");
this._logger.debug("debugging message from Foo class");
this._logger.warn("warning message from Foo class");

}

}
}

The code above will register the class Foo under the package com.daveoncode and then it will broadcast a debug and a warning message

2. Initialize and configure a logging target such TraceTarget (typically in the main class):
var basicTarget:TraceTarget = new TraceTarget();

// Apply filters
basicTarget.filters = ["com.daveoncode.*"];

// Log all log levels.
basicTarget.level = LogEventLevel.ALL;

// Add date, time, category, and log level to the output
basicTarget.includeDate = true;
basicTarget.includeTime = true;
basicTarget.includeCategory = true;
basicTarget.includeLevel = true;

The code above will print log messages of all “subscribed” classes inside the package com.daveoncode, including all information available (date, time, category, level)

3. Add the target to the logger:

Log.addTarget(basicTarget);

The previous steps can be repeated as many times as we need, in order to registering several classes and several targets (perhaps with different filters and info settings).

How to extend the framework

To extend the framework we can create new target classes, which print LogEvent data to different location and display data as we like. In my experiment I created a “mini subframework” which can print logs to text files or html files. I created the following 7 classes:

  1. LogFileTarget
  2. IStreamDataWriter
  3. AbstractDataWriter
  4. PlainTextDataWriter
  5. HTMLDataWriter
  6. InvalidLogFileTargetError
  7. UnsupportedWriteModeError

The first, as the name suggest is a custom target class, which extends LineFormattedTarget (a target class available in Flex’s framework), it has a parametric constructor which allows the user to choose the name of the file, its directory, the type of file (txt or html) and the charset that will be used to write the file. IStreamDataWriter is an interface which simply declares one method called writeData(), the main purpose of this interface is to use it in polymorphism. IStreamDataWriter is implemented by AbstractDataWriter as an abstract method (abstract is not supported yet in as3, but is treated as abstract), this class also has a secondary method which is used by two subclasses PlainTextDataWriter and HTMLDataWriter. These classes have the duty to physically write data to the file by handling the event received by LogFileTarget which delegates the business logic to them and are used based on the writeMode configuration of  LogFileTarget, which for ease has two public constants called MODE_PLAIN_TEXT and MODE_HTML. InvalidLogFileTargetError and UnsupportedWriteModeError are obviously two custom error which are both (potentially) thrown by LogFileTarget.

This is a sample code using my own classes:
// this will print to an html file
var HTMLTarget:LogFileTarget = new LogFileTarget("applicationLog", File.desktopDirectory, LogFileTarget.MODE_HTML);

// this will print to a txt file
var txtTarget:LogFileTarget = new LogFileTarget("applicationLog", File.desktopDirectory, LogFileTarget.MODE_PLAIN_TEXT);

HTMLTarget.filters = ["*"];

// Log all log levels.
HTMLTarget.level = LogEventLevel.ALL;

// Add date, time, category, and log level to the output.
HTMLTarget.includeDate = true;
HTMLTarget.includeTime = true;
HTMLTarget.includeCategory = true;
HTMLTarget.includeLevel = true;

txtTarget.filters = ["*"];

// Log all log levels.
txtTarget.level = LogEventLevel.ALL;

// Add date, time, category, and log level to the output.
txtTarget.includeDate = true;
txtTarget.includeTime = true;
txtTarget.includeCategory = true;
txtTarget.includeLevel = true;

// Add targets
Log.addTarget(HTMLTarget);
Log.addTarget(txtTarget);

Of course this experiment will works only in AIR applications, because we need to use flash.fileystem classes.

These are the two different output that my classes will generate:
Text file
HTML file

The cool aspect of html version (LogFileTarget.MODE_HTML) is that you can filter by log type :)

If you want to use or take a look to my experiment, feel free to download my logging package here (let me know your opinion)