Using the logging API

The logging API lets an application capture and write messages to a target’s configured output. Typically the output is equivalent to the global trace() method, but it can be anything that an active target supports.

The logging API consists of the following parts:

Logger
The logger provides an interface for sending a message to an active target. Loggers implement the ILogger interface and call methods on the Log class. The two classes of information used to filter a message are category and level. Each logger operates under a category. A category is a string used to filter all messages sent from that logger. For example, a logger can be acquired with the category “orange”. Any message sent using the “orange” logger only reaches those targets that are listening for the “orange” category. In contrast to the category that is applied to all messages sent with a logger, the level provides additional filtering on a per-message basis. For example, to indicate that an error occurred within the “orange” subsystem, you can use the error level when logging the message. The supported levels are defined by the LogEventLevel class. The Flex framework classes that use the logging API set the category to the fully qualified class name as a convention.

Log target
The log target defines where log messages are written. Flex predefines a single log target: TraceTarget, which is the most commonly used log target. This log target connects the logging API to the trace system so that log messages are sent to the same location as the output of the trace() method. For more information on the trace() method, see Using the global trace() method.

You can also write your own custom log target. For more information, see Implementing a custom logger with the logging API.

Destination
The destination is where the log message is written. Typically, this is a file, but it can also be a console or something else, such as an in-memory object. The default destination for TraceTarget is the flashlog.txt file. You configure this destination on the client.

The following example shows a sample relationship between a logger, a log target, and a destination:

You can also use the logging API to send messages from custom code you write. You can do this when you create a set of custom APIs or components or when you extend the Flex framework classes and you want users to be able to customize their logging. For more information, see Implementing a custom logger with the logging API.

The following packages within the Flex framework are the only ones that use the logging API:

  • mx.rpc.*

  • mx.messaging.*

  • mx.data.*

To configure client-side logging in MXML or ActionScript, create a TraceTarget object to log messages. The TraceTarget object logs messages to the same location as the output of the trace() statements. You can also use the TraceTarget to specify which classes to log messages for, and what level of messages to log.

The levels of logging messages are defined as constants of the LogEventLevel class. The following table lists the log level constants and their numeric equivalents, and describes each message level:

Logging level constant (int)

Description

ALL (0)

Designates that messages of all logging levels should be logged.

DEBUG (2)

Logs internal Flex activities. This is most useful when debugging an application.

Select the DEBUG logging level to include DEBUG, INFO, WARN, ERROR, and FATAL messages in your log files.

INFO (4)

Logs general information.

Select the INFO logging level to include INFO, WARN, ERROR, and FATAL messages in your log files.

WARN (6)

Logs a message when the application encounters a problem. These problems do not cause the application to stop running, but could lead to further errors.

Select the WARN logging level to include WARN, ERROR, and FATAL messages in your log files.

ERROR (8)

Logs a message when a critical service is not available or a situation has occurred that restricts the use of the application.

Select the ERROR logging level to include ERROR and FATAL messages in your log files.

FATAL (1000)

Logs a message when an event occurs that results in the failure of the application.

Select the FATAL logging level to include only FATAL messages in your log files.

The log level lets you restrict the amount of messages sent to any running targets. Whatever log level you specify, all “lower” levels of messages are written to the log. For example, if you set the log level to DEBUG, all log levels are included. If you set the log level to WARNING, only WARNING, ERROR, and FATAL messages are logged. If you set the log level to the lowest level of message, FATAL, only FATAL messages are logged.

Using the logging API with data services

The data services classes are designed to use the logging API to log client-side and server-side messages.

Enable the logging API with data services

  1. Create a TraceTarget logging target and set the value of one or more filter properties to include the classes whose messages you want to log. You can filter the log messages to a specific class or package. You can use wildcards (*) when defining a filter.

  2. Set the log level by using the level property of the log target. You can also add detail to the log file output, such as the date and time that the event occurred, by using properties of the log target.

  3. When you create a target within ActionScript, call the Logclass’s addTarget() method to add the new target to the logging system. Calling the addTarget() method is not required when you create a target in MXML. As long as the client is using the debugger version of Flash Player and meets the requirements described in Configuring the debugger version of Flash Player to record trace() output, the messages are logged.

The following example configures a TraceTarget logging target in ActionScript:

<?xml version="1.0"?>
<!-- logging/ActionScriptTraceTarget.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    creationComplete="initLogging();"
    height="600">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>
    
    <fx:Script>
        <![CDATA[
        import mx.collections.ArrayCollection;
        import mx.logging.targets.*;
        import mx.logging.*;

        [Bindable]
        public var myData:ArrayCollection;

        private function initLogging():void {
            /* Create a target. */
            var logTarget:TraceTarget = new TraceTarget();

            /* Log only messages for the classes in the mx.rpc.* and 
               mx.messaging packages. */
            logTarget.filters=["mx.rpc.*","mx.messaging.*"];

            /* Log all log levels. */
            logTarget.level = LogEventLevel.ALL;

            /* Add date, time, category, and log level to the output. */
            logTarget.includeDate = true;
            logTarget.includeTime = true;
            logTarget.includeCategory = true;
            logTarget.includeLevel = true;

            /* Begin logging. */
            Log.addTarget(logTarget);
        }
        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- HTTPService is in the mx.rpc.http.* package -->
        <mx:HTTPService id="srv" 
            url="../assets/trace_example_data.xml" 
            useProxy="false" 
            result="myData=ArrayCollection(srv.lastResult.data.result)"/>
    </fx:Declarations>

    <mx:LineChart id="chart" dataProvider="{myData}" showDataTips="true">
        <mx:horizontalAxis>
            <mx:CategoryAxis categoryField="month"/>
        </mx:horizontalAxis>
        <mx:series>
            <mx:LineSeries yField="apple" name="Apple"/>
            <mx:LineSeries yField="orange" name="Orange"/>
            <mx:LineSeries yField="banana" name="Banana"/>
        </mx:series>
    </mx:LineChart>

    <s:Button id="b1" click="srv.send();" label="Load Data"/>

</s:Application>

The executing SWF file for the previous example is shown below:

In the preceding example, the filters property is set to log messages for all classes in the mx.rpc and mx.messaging packages. In this case, it logs messages for the HTTPService class, which is in the mx.rpc.http.* package.

You can also configure a log target in MXML. When you do this, though, you must be sure to use an appropriate number (such as 2) rather than a constant (such as DEBUG) for the log level.

The following example sets the values of the filters for a TraceTarget logging target by using MXML syntax:

<?xml version="1.0"?>
<!-- charts/MXMLTraceTarget.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    creationComplete="initApp();"
    height="600">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Declarations>
        <!-- HTTPService is in the mx.rpc.http.* package -->
        <mx:HTTPService id="srv" 
            url="../assets/trace_example_data.xml" 
            useProxy="false" 
            result="myData=ArrayCollection(srv.lastResult.data.result)"/>

        <mx:TraceTarget id="logTarget" 
            includeDate="true" 
            includeTime="true" 
            includeCategory="true" 
            includeLevel="true">
            <mx:filters>
                <fx:Array>
                    <fx:String>mx.rpc.*</fx:String>
                    <fx:String>mx.messaging.*</fx:String>
                </fx:Array>
            </mx:filters>
            <!-- 0 is represents the LogEventLevel.ALL constant. -->
            <mx:level>0</mx:level>
        </mx:TraceTarget>
    </fx:Declarations>
    
    <fx:Script>
        <![CDATA[
        import mx.collections.ArrayCollection;
        import mx.logging.Log;

        [Bindable]
        public var myData:ArrayCollection;
        
        private function initApp():void {
            Log.addTarget(logTarget);
        }        
        ]]>
    </fx:Script>

    <mx:LineChart id="chart" dataProvider="{myData}" showDataTips="true">
        <mx:horizontalAxis>
            <mx:CategoryAxis categoryField="month"/>
        </mx:horizontalAxis>
        <mx:series>
            <mx:LineSeries yField="apple" name="Apple"/>
            <mx:LineSeries yField="orange" name="Orange"/>
            <mx:LineSeries yField="banana" name="Banana"/>
        </mx:series>
    </mx:LineChart>

    <s:Button id="b1" click="srv.send();" label="Load Data"/>

</s:Application>

The executing SWF file for the previous example is shown below:

Implementing a custom logger with the logging API

If you write custom components or an ActionScript API, you can use the logging API to access the trace system in the debugger version of Flash Player. You do this by defining your log target as a TraceTarget, and then calling methods on your logger when you log messages.

The following example extends a Button control. It writes log messages for the startup life cycle events, such as initialize and creationComplete, and the common UI events, such as click and mouseOver.

// logging/MyCustomLogger.as
package { // The empty package.
    import mx.controls.Button;
    import flash.events.*;
    import mx.logging.*;
    import mx.logging.targets.*;
        
    public class MyCustomLogger extends Button {

        private var myLogger:ILogger;

        public function MyCustomLogger() {
            super();
            initListeners();
            initLogger();
        }
        private function initListeners():void {
            // Add event listeners life cycle events.
            addEventListener("preinitialize", logLifeCycleEvent);
            addEventListener("initialize", logLifeCycleEvent);
            addEventListener("creationComplete", logLifeCycleEvent);
            addEventListener("updateComplete", logLifeCycleEvent);
            
            // Add event listeners for other common events.
            addEventListener("click", logUIEvent);      
            addEventListener("mouseUp", logUIEvent);        
            addEventListener("mouseDown", logUIEvent);      
            addEventListener("mouseOver", logUIEvent);      
            addEventListener("mouseOut", logUIEvent);       
        }
        private function initLogger():void {
            myLogger = Log.getLogger("MyCustomClass");
        }

        private function logLifeCycleEvent(e:Event):void {
            if (Log.isInfo()) {
                myLogger.info(" STARTUP: " + e.target + ":" + e.type);
            }
        }

        private function logUIEvent(e:MouseEvent):void {
            if (Log.isDebug()) {
                myLogger.debug(" EVENT:   " + e.target + ":" + e.type);
            }
        }
    }
}

Within the application that uses the MyCustomLogger class, define a TraceTarget, as the following example shows:

<?xml version="1.0"?>
<!-- logging/LoadCustomLogger.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    xmlns="*" >

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Declarations>
        <mx:TraceTarget level="0" 
            includeDate="true" 
            includeTime="true" 
            includeCategory="true" 
            includeLevel="true">
            <mx:filters>
                <fx:Array>
                    <fx:String>*</fx:String>
                </fx:Array>
            </mx:filters>
        </mx:TraceTarget>
    </fx:Declarations>

    <MyCustomLogger label="Click Me"/>

</s:Application>

The executing SWF file for the previous example is shown below:

After running this application, the flashlog.txt file looks similar to the following:

3/9/2009 18:58:05.042 [INFO] MyCustomLogger STARTUP: Main_3.mcc:MyCustomLogger:preinitialize 
3/9/2009 18:58:05.487 [INFO] MyCustomLogger STARTUP:  
Main_3.mcc:MyCustomLogger:initialize 
3/9/2009 18:58:05.557 [INFO] MyCustomLogger STARTUP: Main_3.mcc:MyCustomLogger:creationComplete 
3/9/2009 18:58:05.567 [INFO] MyCustomLogger STARTUP: Main_3.mcc:MyCustomLogger:updateComplete 
3/9/2009 18:58:05.577 [INFO] MyCustomLogger STARTUP: Main_3.mcc:MyCustomLogger:updateComplete 
3/9/2009 18:58:05.577 [INFO] MyCustomLogger STARTUP: Main_3.mcc:MyCustomLogger:updateComplete 
3/9/2009 18:58:06.849 [DEBUG] MyCustomLogger EVENT: Main_3.mcc:MyCustomLogger:mouseOver 
3/9/2009 18:58:07.109 [DEBUG] MyCustomLogger EVENT: Main_3.mcc:MyCustomLogger:mouseDown 
3/9/2009 18:58:07.340 [DEBUG] MyCustomLogger EVENT: Main_3.mcc:MyCustomLogger:mouseUp 
3/9/2009 18:58:07.360 [DEBUG] MyCustomLogger EVENT: Main_3.mcc:MyCustomLogger:click 
3/9/2009 18:58:07.610 [DEBUG] MyCustomLogger EVENT: Main_3.mcc:MyCustomLogger:mouseOut

To log a message, you call the appropriate method of the ILogger interface. The ILogger interface defines a method for each log level: debug(), info(), warn(), error(), and fatal(). The logger logs messages from these calls if their levels are at or under the log target’s logging level. If the target’s logging level is set to all, the logger records messages when any of these methods are called.

To improve performance, a static method corresponding to each level exists on the Log class, which indicates if any targets are listening for a specific level. Before you log a message, you can use one of these methods in an if statement to avoid running the code. The previous example uses the Log.isDebug() and Log.isInfo() static methods to ensure that the messages are of level INFO or DEBUG before logging them.

The previous example logs messages dispatched from any category because the TraceTarget’s filters property is set to the wildcard character (*). The framework code sets the category of the logger to the fully qualified class name of the class in which logging is being performed. This is by convention only; any String specified when calling Log.getLogger(x) is the category required in a filters property to receive the message.

When you set the filters property for logging within the Flex framework, you can restrict this to a certain package or packages, or to other classes. To restrict the logging to your custom class only, add the category specified when the logger was acquired (“MyCustomLogger”) to the filters Array, as the following example shows:

<mx:filters> 
    <mx:Array> 
        <mx:String>MyCustomLogger</mx:String> 
    </mx:Array> 
</mx:filters>

In ActionScript, you can set the filters property by using the following syntax:

traceTarget.filters = ["p1.*", "p2.*", "otherPackage*"];

The wildcard character can appear only at the end of a value in the Array.

The Log.getLogger() method sets the category of the logger. You pass this method a String that defines the category.

Note: The Flex packages that use the logging API set the category to the current class name by convention, but it can be any String that falls within the filters definitions.

The value of the category must fall within the definition of at least one of the filters for the log message to be logged. For example, if you set the filters property to something other than “*” and you use Log.getLogger("MyCustomLogger"), the filter Array must include an entry that matches MyCustomLogger, such as “MyCustomLogger” or “My*”.

You can include the logger’s category in your log message, if you set the logger’s includeCategory property to true.

You can also use the ILogger interface’s log() method to customize the log message, and you can specify the logging level in that method. The following example logs messages that use the log level that is passed into the method:

package { // The empty package.
    // logging/MyCustomLogger2.as
    import mx.controls.Button;
    import flash.events.*;
    import flash.events.MouseEvent;
    import mx.logging.*;
    import mx.logging.targets.*;
        
    public class MyCustomLogger2 extends Button {

        private var myLogger:ILogger;

        public function MyCustomLogger2() {
            super();
            initListeners();
            initLogger();
        }
        private function initListeners():void {
            // Add event listeners life cycle events.
            addEventListener("preinitialize", logLifeCycleEvent);
            addEventListener("initialize", logLifeCycleEvent);
            addEventListener("creationComplete", logLifeCycleEvent);
            addEventListener("updateComplete", logLifeCycleEvent);
            
            // Add event listeners for other common events.
            addEventListener("click", logUIEvent);      
            addEventListener("mouseUp", logUIEvent);        
            addEventListener("mouseDown", logUIEvent);      
            addEventListener("mouseOver", logUIEvent);      
            addEventListener("mouseOut", logUIEvent);       
        }
        private function initLogger():void {
            myLogger = Log.getLogger("MyCustomClass");
        }

        private function logLifeCycleEvent(e:Event):void {
            if (Log.isInfo()) {
                dynamicLogger(LogEventLevel.INFO, e, "STARTUP");
            }
        }

        private function logUIEvent(e:MouseEvent):void {
            if (Log.isDebug()) {
                dynamicLogger(LogEventLevel.DEBUG, e, "EVENT");
            }
        }
        
        private function dynamicLogger(
                level:int, 
                e:Event, prefix:String):void {
            var s:String = "__" + prefix + "__" + e.currentTarget + 
                ":" + e.type;
            myLogger.log(level, s);
        }

    }
}