Check out "Do you speak JavaScript?" - my latest video course on advanced JavaScript.
Language APIs, Popular Concepts, Design Patterns, Advanced Techniques In the Browser

AS3: Tracing in Flash/Flex

Inspired by the article of Dru Kepple I decided to share my experience in Debugging processes in flash/flex enviroment.

The article of Dru Kepple explains very well the types of errors related to the AS3 development. I'm not going to digg in this area, but really recommend to check this out.You can download all the files used in this article here.

1. Simple tracing

I'm absolutelly sure that you know the trace method. It gives us ability to show some data in the Output panel. It is extremely helpful when you have to see the value of some variable or just to follow the workflow of you application.

trace("This text will be shown in the Output panel");

It's good to know that, together with the Output panel the string used in trace method is also sent to a file located on your computer. Depending on your operating system the file is stored in different locations:

Windows 95/98/ME/2000/XP:C:\\Documents and Settings\\username\\Application Data\\Macromedia\\Flash Player\\LogsWindows Vista:C:\\Users\\username\\AppData\\Roaming\\Macromedia\\Flash Player\\LogsMacintosh OS X:/Users/username/Library/Preferences/Macromedia/Flash Player/Logs/Linux:/home/username/.macromedia/Flash_Player/Logs/

When you run your flash app in a browser you can check this file and you will probably see your traces. More information about the logging process of the flash player could be found on this page.

2. Tracing by using a wrapper class

The trace function is good, but I prefer to use a wrapper for it. By doing that I can disable/enable the logging whenever I want. The class also stores the whole logging text and it could be saved or sent somewhere.

package {
  public class Debug {
    public static
    var enable: Boolean = true;
    private static
    var _text: String = "";
    public static
    function echo(str: Object): void {
      if (enable) {
        trace("-> " + str);
        _text = "-> " + str + "\\n" + _text;
      }
    }
  }
}

If you are using Adobe Flash as a development enviroment you have probably seen an option in the Publish Settings panel called Omit trace actions. If you select it, your traces will not be sent to the Output panel or stored in the log file. I'm using FlashDevelop together with Flex SDK and there is an option to build the project in Release mode. This also prevents the showing of the traces. Because of such kind of cases or because I need to see the traces directly in the browser I added additional functionalities to the Debug class. I created a debug holder - MovieClip with TextField inside. As you may guess the logging text is shown in the TextField. The holder is hidden by default. It's visible only when the user clicks several special keys. I decided to use the following combination - Ctrl + Shift + F11. What you have to do is to call the addDebugHolder method once in your application passing a DisplayObject. Here is the full source code of the Debug class:

package {
    import flash.display.MovieClip;
    import flash.events.KeyboardEvent;
    import flash.net.*;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.*;
    public class Debug {
      public static
      var enable: Boolean = true;
      private static
      var _textFieldAsOutput: TextField;
      private static
      var _text: String = "";
      private static
      var _debugHolder: MovieClip;
      private static
      var _root: * ;
      public static
      function echo(str: Object): void {
        if (enable) {
          _text = "-> " + str + "\\n" + _text;
          trace("-> " + str);
          if (_debugHolder) {
            updateDebugHolder();
          }
        }
      }
      public static
      function addDebugHolder(rootMovie: MovieClip, txtWidth: Number = 400, bgAlpha: Number = 0.9, autoShow: Boolean = false, fontColor: uint = 0x999999): void {
        _root = rootMovie;
        _debugHolder = new MovieClip();
        _debugHolder.visible = autoShow;
        var bg: MovieClip = new MovieClip();
        bg.name = "background";
        bg.graphics.beginFill(0xFFFFFF, bgAlpha);
        bg.graphics.drawRect(0, 0, txtWidth, 1);
        var tf: TextFormat = new TextFormat();
        _textFieldAsOutput = new TextField();
        tf.font = "Verdana";
        tf.size = 9;
        tf.color = fontColor;
        _textFieldAsOutput.autoSize = TextFieldAutoSize.LEFT;
        _textFieldAsOutput.defaultTextFormat = tf;
        _textFieldAsOutput.width = txtWidth;
        _debugHolder.addChild(bg);
        _debugHolder.addChild(_textFieldAsOutput);
        rootMovie.addChild(_debugHolder);
        rootMovie.stage.addEventListener(KeyboardEvent.KEY_DOWN, onRootKeyDown);
      }
      private static
      function onRootKeyDown(e: KeyboardEvent): void {
        if (e.ctrlKey && e.shiftKey && e.keyCode == 122) {
          if (_debugHolder) {
            if (_debugHolder.visible) {
              _debugHolder.visible = false;
            } else {
              _debugHolder.visible = true;
            }
          }
        }
      }
      private static
      function updateDebugHolder(): void { // setting text			_textFieldAsOutput.text = _text;						// updating background's height			_debugHolder.getChildByName("background").height = _textFieldAsOutput.height;						// updating holder's display list position (it should be always on top)			if(_root.getChildAt(_root.numChildren-1) != _debugHolder) {				if(_root.contains(_debugHolder)) {					_root.removeChild(_debugHolder);				}				_root.addChildAt(_debugHolder, _root.numChildren-1);			}					}	}}

Simple example that illustrates the usage of the class:

var swf = new FlashObject( "http://krasimirtsonev.com/files/debugging/UsingDebugClass.swf", "animationSwf", "550", "300", "9", "#000000" ); window.onload = function() { swf.write("animation"); }; Every click on the big gray area adds a new rectangle to the stage filled with the color selected in the ColorPicker. Make some clicks and hit Ctrl + Shift + F11.

3. SWF to SWF communication

Another interesting way of debugging is to make a connection between your SWF(let's call it client) and some other SWF(let's call it host). The idea is to send and monitor your traces in the host application. For example firstly, open this url (it contains the host SWF) and then the client here. Click several times in the client's SWF and see the result in the other side. What I used for this communication is LocalConnection class. Here is the code of the two SWFs:

// the host's swf
    package {
      import flash.display.MovieClip;
      import flash.net.LocalConnection;
      public class UsingSWF2SWFHost extends MovieClip {
        private
        var _conn: LocalConnection;
        private
        var _debugStr: String = "";
        public
        function UsingSWF2SWFHost() {
          debug("constructor");
          _conn = new LocalConnection();
          _conn.allowDomain("*");
          _conn.client = this;
          try {
            _conn.connect("SWF2SWFDebugging");
          } catch (err: Error) {
            debug("Can't connect!");
          }
        }
        private
        function debug(str: String): void {
          field.text = _debugStr = str + "\\n" + _debugStr;
        }
        public
        function callDebug(str: String): void {
          debug(str);
        }
      }
    }
    
    // the client's swf
    
    package {
      import flash.display.MovieClip;
      import flash.events.Event;
      import flash.events.MouseEvent;
      import flash.net.LocalConnection;
      import flash.events.StatusEvent;
      public class UsingSWF2SWFClient extends MovieClip {
        private
        var _conn: LocalConnection;
        public
        function UsingSWF2SWFClient(): void {
          _conn = new LocalConnection();
          _conn.addEventListener(StatusEvent.STATUS, onStatus);
          debug("constructor");
          stage.addEventListener(MouseEvent.CLICK, onStageClick);
        }
        private
        function onStageClick(e: Event): void {
          debug("onStageClick x=" + mouseX + " y=" + mouseY);
        }
        private
        function debug(str: String): void {
          trace(str);
          try {
            _conn.send("SWF2SWFDebugging", "callDebug", str);
          } catch (err: Error) {
            trace("Can't send! Message: " + err.message);
          }
        }
        private
        function onStatus(event: StatusEvent): void {
          switch (event.level) {
            case "status":
              // trace("send() succeeded");			
              break;
            case "error":
              trace("send() failed");
              break;
          }
        }
      }
    }

Have in mind that the maximum data that you can send between the SWFs is 40K. Something else that I have to mention here is that you can send an object. It is passed by value not by reference, but you can still make some useful things like transferring JSON object, accessing public variable or even call a public method from the client's SWF file.

4. Debugging with Monster Debugger

Monster Debugger is probably the most useful debugger tool that I found. It is an open source debugger for Adobe Flash, Flex and AIR. I believe that the authors of this application use similar SWF to SWF communication with LocalConnection. I strongly recommend to check their tour page and give a chance to the debugger. It will definitelly save you a lot of time.You can download all the files used in this article here.

If you enjoy this post, share it on Twitter, Facebook or LinkedIn.