The State of Logging in ActionScript 3

19th October, 2008 – 12:14 pm

I’ve been looking at Logging Frameworks in ActionScript 3 over the past few days and I’m afraid that I’m slightly at a loss as for which approach is the best to take. There are a few Logging Frameworks out there, but Adobe has kindly supplied an intrinic Logging Framework, however, it’s not without it’s shortcomings.

The biggest problem that I’ve come up against is that there is no easy way to set the Category for the Log.  The Flex Logging Framework allows you to set the category of the log when you retrieve the ILogger instance.  This can in turn be used by the Log Filtering system to specify which categories you would like to log.  Ideally you set the category String to be the name of the Class you are currently Logging, however, due to the rather shaky reflection tools avaliable, there’s no easy way to automatically get the name of the current Class.  To compound the problem even further, getQualifiedClassName returns illegal characters (The semi-colon seperating the Package and the Class Name) and Flex Logging Framework will throw and Illegal Arguments error if you try and use it as the category name.  So, in order to have automatic categories generated, we have to do a bit of a curly shuffle with a StackTrace and a Helper Method

package uk.co.jonnyreeves.logging
{
        import mx.logging.ILogger;
        import mx.logging.ILoggingTarget;
        import mx.logging.Log
        import flash.utils.getQualifiedClassName;       

        /**
         * Flex Logger Helper, automatically returns the Category Name when used inside
         * a Class.
         *
         * @author jonny reeves
         */

        public class Logger
        {
                /**
                 * Returns an instance of Flex Logger with the correct category name for the calling Class
                 *
                 * @return ILogger instance with the correct category set for the calling Class.
                 */

                public static function getLogger() : ILogger
                {
                        var category : String = "";
                        category = getCallerFromStackTrace();
                        return Log.getLogger(category);
                }
               
               
                /**
                 * Triggers a StackTrace and extracts the calling method from it, neat.
                 */

                private static function getCallerFromStackTrace() : String
                {
                        var callerMethod : String = "";
                        var nullArray : Array;
                       
                        // Pop a StackTrace by creating a RunTime error.
                        try
                        {
                                nullArray.push("whoops");
                        }
                        catch (e : Error)
                        {
                                var stackTrace : String = new Error().getStackTrace();
                        }
                       
                        // Extract the ClassName from the StackTrack
                        if (stackTrace != null)
                        {
                                var parts : Array = new Array();
                                stackTrace.split("\n").join("");
                                parts = stackTrace.split("      at ");
                                if (parts[3] != null)
                                {
                                        // Handle Both Class Init and internal method cases
                                        if (parts[3].indexOf("$cinit") != -1)
                                        {
                                                callerMethod = parts[3].split("$cinit")[0];
                                        }
                                        else
                                        {
                                                callerMethod = parts[3].split("/")[0];
                                        }
                                }
                        }
                       
                        return getCategoryNameFor(callerMethod);
                }

               
                /**
                 * Helper method which takes an ActionScript 3 Formatted Class Name
                 * and replaces the semi-colons seperating the package and Class name
                 * with a Single Period for use as teh Flex Logging Category name
                 *
                 * @param qualifiedClassName
                 * @return String Legal Flex Logging Category String
                 */

                private static function getCategoryNameFor(qualifiedClassName : String) : String
                {
                        return qualifiedClassName.indexOf("::") > -1 ? qualifiedClassName.split("::").join(".") : qualifiedClassName;
                }
        }
}
 

This can now be used inside any Class you wish to Log by delcaring a private, static instance of ILogger, for example:

package uk.co.jonnyreeves.logging
{
        public class LoggingExample
        {
                /**
                 * Instance of the Flex Logging Framework via the Logger Helper Class which will
                 * automatically set the Category name to uk.co.jonnyreeves.logging.LoggingExample
                 */

                private static var Logger : ILogger = Logger.getLogger();

                /**
                 * You can now log away, don’t forget to initialise your Flex Logger Instance (Log) first
                 * by setting the Target Publisher (not shown).
                 */

                public function LoggingExample()
                {
                        Logger.info("Hello World!");
                }
        }
}
 

Although this is helpful it still does not address a couple of other fundimental issues:

  • The output from the Flex Logging Framework does not include the enclosing method or line number from which the error was triggered (although this information can extracted from a StackTrace and automatically appended to the Logged Message by the Publisher).
  • The Flex Logging Framework does not intelligently handle datatypes - unfortunatley we can only passing String’s to Flex Logger - however, we can get around this by making use of 3rd Party Consoles such as Thunderbolt (which also, helpfully includes a LoggingTarget).
  • Although the Flex Logging Framework allows you to supply a custom Filters Array (and this can even be updated at runtime), it does not appear to include a simple way to configure this via an external XML configuration file. However, I’m fairly confident that I can role my own (just a shame that I have to!)

I’m going to be working on the three points above over the next few days and see if I can come up with a solution which stays as faithful to the intrinsic Logging framework as possible but allows the flexibility of some of the other Loggers out there (Luminic Box, XRay, etc).

Post a Comment