Flex in Application Logging

Posted by jkahn on December 22, 2007 · 4 mins read

For an LCDS application that we are building, I wanted to be able to display log messages to the screen, such as "Sending request to server" and "Received server response: OK." Although this has little to do with actual functionality, I wanted to document my efforts to create this component.

To utilize the Flex logging framework, we need to provide a Logger and a Log Target. The Logger, of course, does the logging itself based on two factors: (1) its category and (2) the logging level. This is similar to the operation of the popular Java logging framework Log4J. The Log Target defines where the messages are written. Flex comes with the TraceTarget predefined - this is the target for all trace() statements. For this example, I will also provide an MXML component that can be used to display log messages on screen.

First, the Logger class:

    public class Logger
    {
        //~ Instance Attributes -----------------------------------------------
        private var logger : ILogger;

        //~ Constructor -------------------------------------------------------
        /**
         * SingletonEnforcer parameter ensures that only the getInstance
         * method can create an instance of this class.
         */
        public function Logger(category : String, enforcer : SingletonEnforcer) : void
        {
            this.logger = Log.getLogger(category);
        }

        //~ Methods -----------------------------------------------------------
        public static function getInstance(category : String) : Logger
        {
            return new Logger(category, new SingletonEnforcer());
        }

        public static function addTarget(target : ILoggingTarget) : void
        {
            Log.addTarget(target);
        }

        public static function removeTarget(target : ILoggingTarget) : void
        {
            Log.removeTarget(target);
        }

        public function debug(message : String) : void
        {
            this.logger.debug(message);
        }

        // other logging methods by level (e.g. warn, error)

    }

With our Logger class in place, we can now define a LogTarget class that defines a TextArea to which the log messages will be written:

    // be sure to use the mx_internal namespace (and import it)
    import mx.core.mx_internal;
    use namespace mx_internal;

    public class LogPanelTarget extends LineFormattedTarget
    {
        private var console : TextArea;

        public function LogPanelTarget(console : TextArea)
        {
            super();
            this.console = console;
        }

        override mx_internal function internalLog(message : String) : void
        {
            this.console.text += message + "\n";
            }
        }
    }

Finally, our LogPanel MXML component brings everything together by (1) instantiating a new Logger and (2) providing a TextArea for log messages:

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
        width="300" height="300" initialize="init()">
    <mx:Script>
        <![CDATA[
            import mx.logging.Log;
            import mx.logging.LogEventLevel;
            import mx.collections.ArrayCollection;

            public  var open   : Boolean = true;
            private var target : LogPanelTarget;
            [Bindable] private var targets : ArrayCollection;

            private function init() : void
            {
                this.target = new LogPanelTarget(output);
                // here we define which categories are written to our target
                this.target.filters = ["iamjosh.samples.*"];
                // and here which log levels are written
                this.target.level   = LogEventLevel.ALL;
                this.target.includeCategory = true;
                this.target.includeTime = true;
                Logger.addTarget(this.target);
            }

            private function setVerticalPosition() : void
            {
                this.output.verticalScrollPosition =
                        this.output.maxVerticalScrollPosition + 1;
            }
        ]]>
    </mx:Script>

    <mx:TextArea  id="output" x="0" y="0" width="100%" height="100%"
            editable="false" updateComplete="this.setVerticalPosition()"
            verticalScrollPolicy="on"/>
</mx:Canvas>

We can now include the LogPanel component anywhere we wish and log messages from anywhere in our code within the category "iamjosh.samples.*" will be written to it. For example:

    public class Foo
    {
        // notice that we provide a category here, this determines if the log
        // message will be written to a give LogTarget
        private var logger : Logger = Logger.getInstance("iamjosh.samples.Foo");

        public function logSomething() : void
        {
            this.logger.debug("I log because I can");
        }
    }

That's it. Although I would prefer to define where my log messages go external to the code, Flex's logging framework is fairly straightforward and easy to work with.

References