This is the second part about the latest update of ThunderBolt AS3. ThunderBolt AS3 is an open source logger extension for Flex 2 or Flash ActionScript 3 applications using Firebug within Firefox.

In part 1 I described a way for using ThunderBolt AS3 with the Flex 2 Logging Framework using an own log target called ThunderBoltTarget. This is a handy extension, but it has a disadvantage: The Flex 2 Logging Framework doesn’t support outputs of nested objects because the original Flex 2 Log instance uses an instance of the LogLogger which dispatches only a message of the logged object typed as String.

For this issue is better to use the ThunderBolt Logger instance directly for logging objects and its nested objects including all properties ;-) . With the latest release I have added a common way for using log levels as well. Check out the the following instructions.

Example

Get Adobe Flash player

 

Screen Shot: Logging to Firebug

Instructions

  1. Grab the latest ThunderBolt AS3 package (includes two classes only) from its repository via SVN on Google Code or download it directly. Make sure that you have installed Firebug as well.
  2. For logging to Firebug call the ThunderBolt Logger class methods such as info, warn, error, debug. You don’t need to create an instance of Logger because all of its public methods and public properties are static.

    Code examples using ThunderBolt

    1. //
    2. // import ThunderBolt Logger
    3. import org.osflash.thunderbolt.Logger;
    4.  
    5. //
    6. // log a string as an info message
    7. var myString: String = "Lorem ipsum";
    8. Logger.info ("A simple string", myString);
    9.  
    10. //
    11. // Log two objects (or more ;-) ) as an error message
    12. var myNumber: int = 5;
    13. var myString2: String = "Lorem ipsum";
    14. Logger.error ("Two log objects: A number typed as int and a string", myNumber, myString2);
    15.  
    16. //
    17. // Log an array with a nested object as a warn message
    18. var myArray: Array = ["firstValue",{x: 100, y: 200}, "secondValue"];
    19. Logger.warn ("An array with a nested object: ", myArray);
    20.  
    21. //
    22. // Log an object with a nested array as a debug message
    23. var myObject: Object = {exampleArray: ["firstValue", "secondValue"], y: 10, exampleString: "Hello", nestedObject: {x: 100, y: 200}};
    24. Logger.debug ("An object with a nested object and nested array", o);
    25.  
    26. //
    27. // Optionally you can hide the time stamp
    28. Logger.includeTime = false;
  3. That’s all – happy logging ;-)

Source

You will find the latest source of the ThunderBolt AS 3 package on Google Code too. It’s open source and based on the Mozilla Public License 1.1.

ThunderBolts core class named “Logger.as” ( Download code )

  1. /**
  2. * Logging Flex and AS3 projects with Firebug using ThunderBolt AS3
  3. *
  4. * @version  0.9.1
  5. * @author   Jens Krause [www.websector.de]
  6. * @date  06/29/07
  7. * @see    http://www.websector.de/blog/?s=thunderbolt
  8. * @see    http://code.google.com/p/flash-thunderbolt/
  9. * @source   http://flash-thunderbolt.googlecode.com/svn/trunk/as3/
  10. *
  11. * ***********************
  12. * HAPPY LOGGING ;-)
  13. * ***********************
  14. *
  15. */
  16.  
  17. package org.osflash.thunderbolt
  18. {
  19.     import flash.external.ExternalInterface;
  20.     import flash.utils.describeType;
  21.     import flash.utils.getQualifiedClassName;
  22.     import flash.utils.getDefinitionByName;
  23.     import mx.logging.LogEventLevel;
  24.     import mx.logging.LogEvent;
  25.  
  26.     /**
  27.     * Thunderbolts AS3 Logger class
  28.     */
  29.     public class Logger
  30.     {
  31.         //
  32.         // Firebug supports 4 log levels only
  33.         protected static const INFO: String = "info";
  34.         protected static const WARN: String = "warn";
  35.         protected static const ERROR: String = "error";
  36.         protected static const LOG: String = "log";
  37.  
  38.         protected static const FIELD_SEPERATOR: String = " :: ";       
  39.         protected static const MAX_DEPTH: int = 255;   
  40.         private static var _stopLog: Boolean = false;
  41.  
  42.         private static var depth: int
  43.         private static var logLevel: String;                       
  44.  
  45.         public static var includeTime: Boolean = true
  46.  
  47.         /**
  48.          * Logs info messages including objects for calling Firebug
  49.          *
  50.          * @param   msg            log Message
  51.          * @param   logObjects        log objects
  52.          *
  53.          */  
  54.         public static function info (msg: String = null, … logObjects): void
  55.         {
  56.             Logger.trace(LogEventLevel.INFO, msg, logObjects);     
  57.         }
  58.        
  59.         /**
  60.          * Logs warn messages including objects for calling Firebug
  61.          *
  62.          * @param   msg            log Message
  63.          * @param   logObjects        log objects
  64.          *
  65.          */  
  66.         public static function warn (msg: String = null, … logObjects): void
  67.         {
  68.             Logger.trace(LogEventLevel.WARN, msg, logObjects);     
  69.         }
  70.  
  71.         /**
  72.          * Logs error messages including objects for calling Firebug
  73.          *
  74.          * @param   msg            log Message
  75.          * @param   logObjects        log objects
  76.          *
  77.          */  
  78.         public static function error (msg: String = null, … logObjects): void
  79.         {
  80.             Logger.trace(LogEventLevel.ERROR, msg, logObjects);   
  81.         }
  82.        
  83.         /**
  84.          * Logs debug messages messages including objects for calling Firebug
  85.          *
  86.          * @param   msg            log Message
  87.          * @param   logObjects        log objects
  88.          *
  89.          */  
  90.         public static function debug (msg: String = null, … logObjects): void
  91.         {
  92.             Logger.trace(LogEventLevel.DEBUG, msg, logObjects);   
  93.         }      
  94.                 
  95.         /**
  96.          * Calls Firebugs command line API to write log information
  97.          *
  98.          * @param   msg            log Message
  99.          * @param   logObjects        log objects
  100.          */   
  101.         public static function trace (level: Number = 0, msg: String = null, … logObjects): void
  102.         {
  103.             depth = 0;
  104.             // get log level
  105.             logLevel = Logger.getLogLevel(level);
  106.             // add log level to log messagef
  107.             var logMsg: String = "[" + logLevel.toUpperCase() + "] ";
  108.             // add time to log message
  109.             if (includeTime) logMsg += getCurrentTime();
  110.             // add message text to log message
  111.             logMsg += (msg != null && msg.length) ? msg : "";
  112.             // call Firebug            
  113.             ExternalInterface.call("console." + logLevel, logMsg);
  114.             // log objects     
  115.             for (var i:uint = 0; i < logObjects.length; i++)
  116.             {
  117.                 Logger.logObject(logObjects[i]);
  118.             }     
  119.         }
  120.        
  121.         /**
  122.          * Translates Flex log levels to Firebugs log levels
  123.          *
  124.          * @param   msg
  125.          * @return  level description
  126.          *
  127.          */  
  128.         private static function getLogLevel (logLevel: Number): String
  129.         {
  130.             var level: String;
  131.            
  132.             switch (logLevel)
  133.             {
  134.                 case LogEventLevel.INFO:
  135.                     level = Logger.INFO;
  136.                 break;
  137.                 case LogEventLevel.WARN:
  138.                     level = Logger.WARN;
  139.                 break;       
  140.                 case LogEventLevel.ERROR:
  141.                     level = Logger.ERROR;
  142.                 break;
  143.                 // Firebug doesn’t support a fatal level
  144.                 // so we use here Firebugs ERROR level when you’re using ThunderBoltTarget
  145.                 case LogEventLevel.FATAL:
  146.                     level = Logger.ERROR;
  147.                 break;
  148.                 default:
  149.                     // for LogEventLevel.DEBUG && LogEventLevel.ALL
  150.                     // so we use here Firebugs LOG level when you’re using ThunderBoltTarget
  151.                     level = Logger.LOG;
  152.             }
  153.  
  154.             return level;
  155.         }
  156.                
  157.         /**
  158.          * Logs nested instances and properties
  159.          *
  160.          * @param   logObj    log object
  161.          * @param   id        short description of log object
  162.          */ 
  163.         private static function logObject (logObj: *, id: String = null): void
  164.         {   
  165.            
  166.            
  167.             if (depth < Logger.MAX_DEPTH)
  168.             {
  169.                 ++ depth;
  170.                
  171.                 var propID: String = id || "";
  172.                 var description:XML = describeType(logObj);    
  173.                 var type: String = description.@name;
  174.                
  175.                 if (primitiveType(type))
  176.                 {               
  177.                     var msg: String = (propID.length)   ?     "[" + type + "] " + propID + " = " + logObj
  178.                                                         :   "[" + type + "] " + logObj;
  179.                                                            
  180.                     ExternalInterface.call("console." + Logger.LOG, msg);
  181.                 }
  182.                 else if (type == "Object")
  183.                 {
  184.                     ExternalInterface.call("console.group", "[Object] " + propID);                   
  185.                     for (var element: String in logObj)
  186.                     {
  187.                       logObject(logObj[element], element);                 
  188.                     }
  189.                     ExternalInterface.call("console.groupEnd");
  190.                 }
  191.                 else if (type == "Array")
  192.                 {
  193.                     /* don’t create a group on depth 1 when we are using the … (rest) parameter calling by Logger.trace() ;-) */
  194.                     if (depth > 1) ExternalInterface.call("console.group", "[Array] " + propID);                                   
  195.                     for (var i: int = 0; i < logObj.length; i++)
  196.                     {
  197.                       logObject(logObj[i]);            
  198.                     }
  199.                     ExternalInterface.call("console.groupEnd");                    
  200.                 }
  201.                 else
  202.                 {
  203.                     // log private props as well – thx Rob Herman [http://www.toolsbydesign.com] ;-)
  204.                     var list: XMLList = description..accessor;         
  205.                    
  206.                     if (list.length())
  207.                     {
  208.                         for each(var item: XML in list)
  209.                         {
  210.                             var propItem: String = item.@name;
  211.                             var typeItem: String = item.@type;             
  212.                             var access: String = item.@access;
  213.                            
  214.                             // log objects && properties accessing "readwrite" and "readonly" only
  215.                             if (access && access != "writeonly")
  216.                             {
  217.                                 //TODO: filter classes
  218.                                 // var classReference: Class = getDefinitionByName(typeItem) as Class;
  219.                                 var valueItem: * = logObj[propItem];
  220.                                 logObject(valueItem, propItem);
  221.                             }
  222.                         }               
  223.                     }
  224.                     else
  225.                     {
  226.                         logObject(logObj, type);                   
  227.                     }
  228.                 }
  229.  
  230.             }
  231.             else
  232.             {
  233.                 // call one stop message only ;-)
  234.                 if (!_stopLog)
  235.                 {
  236.                     ExternalInterface.call("console." + Logger.WARN, "STOP LOGGING: More than " + depth + " nested objects or properties.");
  237.                     _stopLog = true;
  238.                 }         
  239.             }                           
  240.         }
  241.            
  242.         /**
  243.          * Checking for primitive types
  244.          *
  245.          * @param   type        type of object
  246.          * @return  isPrimitiveType  isPrimitiveType
  247.          *
  248.          */       
  249.         private static function primitiveType (type: String): Boolean
  250.         {
  251.             var isPrimitiveType: Boolean;
  252.            
  253.             switch (type)
  254.             {
  255.                 case "Boolean":
  256.                 case "void":
  257.                 case "int":
  258.                 case "uint":
  259.                 case "Number":
  260.                 case "String":
  261.                 case "undefined":
  262.                 case "null":
  263.                     isPrimitiveType = true;
  264.                 break;     
  265.                 default:
  266.                     isPrimitiveType = false;
  267.             }
  268.  
  269.             return isPrimitiveType;
  270.         }
  271.  
  272.         /**
  273.          * Creates a valid time value
  274.          * @param   number        Hour, minute or second
  275.          * @return  string     A valid hour, minute or second
  276.          */
  277.         
  278.         private static function getCurrentTime ():String
  279.         {
  280.             var currentDate: Date = new Date();
  281.            
  282.             var currentTime: String =   "time "
  283.                                         + timeToValidString(currentDate.getHours())
  284.                                         + ":"
  285.                                         + timeToValidString(currentDate.getHours())
  286.                                         + ":"
  287.                                         + timeToValidString(currentDate.getMinutes())
  288.                                         + ":"
  289.                                         + timeToValidString(currentDate.getSeconds())
  290.                                         + "."
  291.                                         + timeToValidString(currentDate.getMilliseconds()) + FIELD_SEPERATOR;
  292.             return currentTime;
  293.         }
  294.                
  295.         /**
  296.          * Creates a valid time value
  297.          * @param   number        Hour, minute or second
  298.          * @return  string     A valid hour, minute or second
  299.          */
  300.         
  301.         private static function timeToValidString(timeValue: Number):String
  302.         {
  303.             return timeValue > 9 ? timeValue.toString() : "0" + timeValue.toString();
  304.         }
  305.        
  306.        
  307.     }
  308. }

Related Articles

6 Responses to “[Update - Part 2] Logging Flex 2 and AS3 applications with Firebug and ThunderBolt”

  1. Rob Herman Says:

    I have noticed that if you are using a class object and set its variables to private that the Logger will not be able to access them.

    You can however access them through the getters by changing one line of code.

    Inside the logger class, change
    var list: XMLList = description..variable;
    to
    var list: XMLList = description..accessor;

  2. sectore Says:

    Rob, thanks a lot. I’ve updated the ThunderBolt AS3 package based on your comment – check it out ;-)

  3. dzq Says:

    “Press F12 to open Firebug”
    Could you tell me how ?
    It’s best if you can show me the source of this part.
    Thanks a lot.

  4. sectore Says:

    @dzg: For more information about ThunderBolt (incl. source) check out its project on Google Code:
    http://code.google.com/p/flash-thunderbolt/

  5. Valeeum Says:

    Hi,

    I am really excited to start using this debugger. I wanted to know if it’s possible to output in the firebug window the source class and line number of where the debug statements are originated from. Please forgive me if this has already been addressed elsewhere.

    Thanks,
    V

  6. Swiz Localization (with l10nInjection) and Logging | GridLinked to RUX Says:

    [...] now with extra features for Logging and l10nInjection. In fact, this application now uses the ThunderBolt AS3 classes to log output to the Firebug console. That is very [...]

Leave a Reply

Follow sectore on Twitter