[Update - Part 2] Logging Flex 2 and AS3 applications with Firebug and ThunderBolt

Posted on June 20, 2007

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

To see this content latest Flash Player Plugin is required.

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.

 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.

  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 }

Any feedback?

comments powered by Disqus