WASI AIR Logging Console & Logging Util Tools


Introduction

About a year ago I revisited an old friend — a simple SWF with a TextArea that outputs log messages from another SWF via the LocalConnection. I wrote it for Xcelsius since it’s a black box when you’re running custom Flex Connectors and UI Components and I needed a way to not only see the errors in the Flash Debug Player, but get more info…thus, the reemergence of my old-school Flash Logger that I originally wrote in Flash 5. You can read more about it here.

Recently, however, I realized that this simple tool was actually much more valuable than I had remembered, as I’ve had several clients that have had some difficulties logging in or running the app as expected for the first time…you know the schpeel…it’s all hooked up and ready to go and you’ve been logging into the app for months from your dev machine and it works great…but then suddenly the client tries to run it and nada…and since they don’t have a Flash/Flex Builder IDE all ready to rock with debugging, you might be stuck wondering why they can’t get in…ah-ha, just fire up the good-ole WASI Logging Console and suddenly your mystery is solved.

See It In Action!

  • Logging Test Application — Small test app that uses the logging framework and spits out log statements every second. I have a link to the console below for quick testing.
  • Logging Console Test Application — Allows you to see the Console in action when you run it and the Logging Test Application at the same time.

Assets

Quick Explanation…How Does it Work?

The WASI LocalConnection (LC) Logger  consists of 2 parts:

  1. A SWF with a LC that receives messages — ie, logs statements — and displays them in a simple Flex List.
  2. A Logging Framework that provides basic logging methods you’d expect (debug, info, warn, error, and fatal) with a LC sender that pushes these messages to the receiving SWF.

Logger Basics

The WASI Logger outputs log statements to both the Flex/Flash builder IDE and the WASI AIR Logging Console with the same-old, friendly Flex logging sytax you;d expect. Here’s some sample code using it in an application:

logger.debug("entering myMethod"); // log statement in myMethod

And here’s what the output looks like to both the IDE’s console and the AIR Console:

4/12/2010 07:03:57 PM [DEBUG] com.webappsolution.controller.FilterControllerTest myMethod // output to the Flex IDE console

Compile-Time & Run-Time Logging Configuration

The Logging Framework also provides the ability to set up logging configuration for log levels and log filters at both compile-time and run-time. To configure logging for Compile time, simply add the following import and variable to your main application’s script block:

import com.webappsolution.logging.LoggingUtils;
private static var loggerSetup:* = LoggingUtils.setupLogging(LogEventLevel.ALL, ["*"]);

In the previous example, the logger is set up with log level of All and sets the filters to include all classes (even the Flex SDKs basic messaging classes for HTTPService so you might want to consider using a package level that’s appropriate to your application.)

To configure logging for Run-time, simply add the following query string to your application’s debug configuration or URL in the browser window:

http://wasi.com/app.swf?logLevel=error&logFilters=com.webappsolution.controller.*,com.webappsolution.delegate.*

Note the 2 parameters in bold; the following depicts their use and possible values:

  • logLevel — The desired log level for logging with the following values (must be in lowercase):
    • debug
    • info
    • warn
    • error
    • fatal
  • logFilters — The desired package and/or class names you’d like to see in the logger. Examples include fully qualified class names or packages with asterisks at the end.
    • Class; eg com.webappsolution.controller.MyController — Will only show log statements for the class MyController.
    • Packages; eg com.webappsolution.controller.* – Will show the log statements for all the classes in the package “com.webappsolution.controller”

You can apply multiple filters in the url by comma separating them: com.webappsolution.controller.*,com.webappsolution.delegate.*,com. webappsolution.MyClass

Using Run-time configs will override whatever was specified in the Compile-time configs.

NOTE: You must run the application from a web server in order for runtime log configs to work since Flash/Flex Builder throws an error when you try to create debug configurations with a query string at the end (again unless running from a web server).

NOTE: applying log levels and filters only effect the log statements in the IDE — all log statements are sent to the WASI AIR Logging Console as it has it’s own set of tools to do the same filtering.

WASI AIR Logging Console Features - Top Tool Bar

Since the Console receives all the logging messages, we’ll review its set of tools that help developers achieve the same functionality as aforementioned.

Log Level ComboBox — Contains a list of all log levels and allows developers to filter the list of log messages in the console by selecting these levels.

Log Filter AutoComplete Field — Allows developers to search for classes and packages to filter the list by; the list of possible filters is automatically generated for the developer at runtime once the console starts receiving messages and sorts them by alphabetical order. Smply start tpying in the full qualified name of a class or package and you’ll be presented with all matches.

To the right of the Log Filter AutoComplete Field is a helper ComboBox that allows the developer to sort the list of filters by class name, packages, or both — this is incredibly useful if you are logging to of classes. You can delete and/or remove filters by backspace the selected item in the AutoComplete field or clicking on the “Clear Filters” button.

NOTE: You can apply both log level and log filters to the list at the same time in any order.

Right-click — Developers can right-click on an item in the list and filter directly by selecting “Filter by Item” in the context menu. Coming soon — copy the individual log message with right-click.

WASI AIR Logging Console Features - Bottom Tool Bar

Clear Log Button — Simply click this to clear the entire log list.

Copy to Clipboard Button — Clicking this copies the entire list of log messages to your clipboard for easy cut and paste.

Auto Scroll CheckBox — Selecting the checkbox forces the list of log messages to scroll automatically to the bottom o the list. This can be useful if you’re watching real-time messages with logs.

Pretty Colors CheckBox — Selecting the checkbox color codes the log levels in the log list.

What it Does NOT

  • Can’t currently accept log messages over 40k — this can easily happen if you’re trying to log a large amount of data like, XML. I’ll push this out shortly by splitting up the chunks I’m sending across the wire to be < 40k.
  • Can’t run more than one of these suckers at a time (since the LC relies on a unique ID). Could allow the developer to enter in a unique ID for each version of the console and app, but I don’t think this is necessary at the moment.
  • Can’t enter multiple filters. This wouldn’t be too hard and is scheduled to come out soon.
  • No AIR badge…I’m being lazy right now and wanted to get the bare bones out for some quick feedback.
  • Not currently on Google Code but will be by EOW as an open source project.

Getting Started

This thing is pretty simple really and requires very little setup or modification to your code:

  1. Download the WASILogConsoleLibrary.swc and drop it into the libs folder of your project.
  2. In your project add the following, imports:
    1. import com.webappsolution.logging.LoggingUtils;
    2. import mx.logging.LogEventLevel;
  3. In your main application file, add the logging setup: private static var loggerSetup:* = LoggingUtils.setupLogging(LogEventLevel.ALL, ["*"]);
  4. In classes you want to log, create the following:
    1. import com.webappsolution.logging.LocalConnectionLog;
    2. import mx.logging.ILogger;
    3. private static const logger:ILogger = LocalConnectionLog.getLogger(FilterDelegateTest);
    4. Call some log methods like:
      1. logger.debug(”we are logging”);
  5. Download the WASI Logger Air Console (WASILogConsoleAIR.air).
  6. Install the AIR application, run it, and then run your project that you just added logging to.

Setting Up in Main App :: Example Code

Again, the key thing to note here is the line where we set up the logger with LoggingUtils.setupLogging(LogEventLevel.ALL, ["*"]);

<mx:Application
	xmlns:mx="http://www.adobe.com/2006/mxml"
	creationComplete="this.init()">

	<mx:Script>
		<![CDATA[
			import com.webappsolution.controller.FilterControllerTest;
			import com.webappsolution.delegate.FilterDelegateTest;
			import com.webappsolution.logging.LoggingUtils;

			import mx.logging.LogEventLevel;

			private static var loggerSetup:* = LoggingUtils.setupLogging(LogEventLevel.ALL, ["*"]);

			private function init():void
			{
				var filterDelegateTest:FilterDelegateTest = new FilterDelegateTest();
				var filterControllerTest:FilterControllerTest = new FilterControllerTest();
			}

			protected function button1_clickHandler(event:MouseEvent):void
			{
				var urlRequest:URLRequest = new URLRequest("http://www.webappsolution.com/air/wasi-logging-console/WASILogConsole.html");
				navigateToURL(urlRequest, "_blank");
			}

		]]>
	</mx:Script>

</mx:Application>

Using the Logger :: Example Code

Here you should note the instantiation of the logger as a static constant, private static const logger:ILogger = LocalConnectionLog.getLogger(FilterControllerTest);, as well as the use thereof with normal logging syntax, logger.debug("Constructor");.

Just pass in the name of the class you’re implementing logging in in the method LocalConnectionLog.getLogger(); and you’re good to go!

package com.webappsolution.controller
{
	import com.webappsolution.logging.LocalConnectionLog;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import mx.logging.ILogger;

	public class FilterControllerTest
	{
		private static const logger:ILogger = LocalConnectionLog.getLogger(FilterControllerTest);

		public function FilterControllerTest()
		{
			// all log levels
			logger.debug("Constructor");
			logger.info("Constructor");
			logger.warn("Constructor");
			logger.error("Constructor");
			logger.fatal("Constructor");

			var timer:Timer = new Timer(5000);
			timer.addEventListener(TimerEvent.TIMER, onTimer);
			timer.start();
		}

		private function onTimer(e:TimerEvent):void
		{
			logger.debug("Constructor");
			logger.info("Constructor");
			logger.warn("Constructor");
			logger.error("Constructor");
			logger.fatal("Constructor");
		}
	}
}

NOTE: You can also fire up this Logging Test Application that allows you to play with various filters and log levels. It has a timer that spits out logs messages every second. Simply open this link up and then open up the WASI AIR Logging Console and you should see some messages start to populate the list.

Special Thanks

  • Abdul Qabiz — I used your com.abdulqabiz.utils.QueryString class to nab the parameters out of the query string.
  • Hillel Coren — I used your AutoComplete component to create the package and class filter AutoComplete Search Field.
  • Jesse Warden — We’ve been rapping back and forth about the fact that we both had one so we decided to combine some of the functionality between the two into this final tool.
  • Final Thoughts

    We’ll continue to update this enhancement requests and fix any bugs that y’all find, so let’em fly!

    Post to Twitter Tweet This Post

, , , , ,

  1. #1 by Nathan5.x - April 15th, 2010 at 02:52

    Pretty good stuff.

    Thanks for sharing this. :)

  2. #2 by Xavi Beumala - April 16th, 2010 at 03:46

    Hi,

    I think that using LocalConnectionLog is a bit intrusive in the code and highly couples the code to proposed logging solution. I normally prefer using something more like:

    private var logger : ILogger = Log.getLogger(”path.to.my.Clazz”)

    which is not only coupled to Flex SDK and configure any specific Logger (LocalConnectionLogger) as a target from a configuration point of view. This way the logging code is not tighted to any implementation, swapping to a different impl would then be just a matter of changing configuration.

    Using this approach you can even have different possible targets (i.e: TraceTarget for development, ServerLogTarget that sends logs across the wire, etc.)

  3. #3 by brianr - April 19th, 2010 at 09:20

    True, I could’ve just created the LocalConnectionTarget and I’ve been considering that since the beginning, but this is the design I decided to go with for a first iteration. The fact is that you can still add any targets you want by simply doing:

    var fireBugTarget:FireBugTarget = new FireBugTarget()
    Log.addTarget(fireBugTarget);

    so I didn’t feel like I was hindering anyone one with this approach.

    But I may still go back to making the LC Logger a Target so you’re not locked into importing our base classes. I’ll play around with it later this wk.

    The logger will always be coupled to the Flex SDK, as I didn’t make it for Flash/AS3 apps.

(will not be published)