One of the first things I want to do after spending any time in an application is enable logging. We all know that trace() is the moral equivalent of System.out.println(), sharing its ease of use, ubiquity and distastefulness. Logging (whether you use the Flex logging API or some other tool) is far superior, for reasons best left to another post.
Usually the TraceTarget is good enough for my purposes as a developer, but I recently found myself tasked to build a utility application that would be testing the behavior of other components and loaded SWF files, and I want all of my lovely log messages to be displayed in a panel within the application itself. It turns out that this is very straightforward, just as long as you already know how to do it. Here's how (or at least here's how I did it).
1. Create a Simple Log Target
First, I created a simple log target that would provide an easy way for a visual component to display the log messages. I settled on creating a target that exposed a bindable ArrayCollection. To keep things from spinning wildly out of control, I added a configurable limit on the number of log message the ArrayCollection would retain, but other than that I didn't design this with performance in mind; it's a utility class for a utility application, and I'd want to do a more thorough job of analyzing how it would fare under high stress before it was deployed in a production application.
package com.chrisluebcke.utils
{
import mx.collections.ArrayCollection;
import mx.core.mx_internal;
import mx.logging.targets.LineFormattedTarget;
use namespace mx_internal;
/**
* A log target that provdes an ArrayCollection (with a configurable maximum size) that can
* (for example) be used by UI components to display log messages on-screen.
*/
public class ArrayCollectionLogTarget extends LineFormattedTarget
{
[Bindable]
[ArrayElementType("String")]
/**
* The collection containing the log messages
*/
public var log : ArrayCollection;
/**
* The maximum number of log messages.
*/
public var maxMessages : Number = 10000;
/**
* Constructor.
*/
public function ArrayCollectionLogTarget()
{
super();
log = new ArrayCollection();
}
override mx_internal function internalLog(message : String) : void {
log.disableAutoUpdate();
log.addItem(message);
if (log.length > maxMessages) {
log.removeItemAt(0);
}
log.enableAutoUpdate();
}
}
}
The bit about mx_internal is what takes you hours to figure out the first time you try to write a useful subclass of LineFormattedTarget. You're welcome. Other than that, this should be pretty straightforward. (It's very helpful to look at the code for TraceTarget, which is really even simpler than this, as a guide).
2. Instantiate and Bind The Target
To use the target, you need to declare it in MXML. I'm not sure if doing so somewhere besides your top-level Application (or subclass thereof) would work, but it always seems to go there. (I'm sure that you could create a more flexible system by creating your log targets via ActionScript. Another day).
<utils:ArrayCollectionLogTarget id="acLogTarget"
includeDate="false"
includeTime="true"
includeCategory="true"
includeLevel="true"
level="{LogEventLevel.DEBUG}"/>
(This is, naturally, exactly like setting up a TraceTarget.)
With that done, you can now use the target's log property (a bindable ArrayCollection) however you'd like. Say, in a List:
<mx:Panel id="logPanel" title="Log" width="100%" height="50%" >
<mx:List id="logList"
horizontalScrollPolicy="on"
width="100%" height="100%"
dataProvider="{acLogTarget.log}"/>
</mx:Panel>
Now any logger statements that exceed your log level (and match any filters) will show up in UI, as well as any other targets you've configured. I've found this quite useful, and I hope you do too.