Moris' Note Book

本博客所有内容皆收集于网上,非本人原创,非心情日记,非研究心得,只是自己浏览的东西的收集
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Flash Logging - A Better Trace(RT)

Posted on 2007-09-19 08:52  moris  阅读(300)  评论(0)    收藏  举报

原文:http://www.communitymx.com/content/article.cfm?page=1&cid=CBBDC

Flash Logging - A Better Trace

By: Joey Lott

Page 1 of 5

Set for printing

Next

Nearly every ActionScript developer uses trace(). It's useful and it's generally faster and more efficient than trying to use the debugger. However, trace() can only do so much. It only runs in the test player, and it's difficult to manage effectively (turning it on and off, applying formatting, etc.). In this article, you'll learn how to use a logging infrastructure to create much more effective output.

Getting Started

The logging system we'll discuss in this article is a subset of ASCBLibrary called the Logging library. You can install the entire ASCBLibrary or just the Logging library if you prefer. You can download either from http://www.person13.com/ascblibrary. Download the appropriate zip file and extract the files to a location on your local disk. Make sure to add the directory to your global Flash classpath if necessary. For example, if you extract the files to C:\ASCBLibrary, you should add C:\ASCBLibrary to your global Flash classpath. You can add the directory to your classpath by choosing Edit>Preferences in Flash, selecting the ActionScript tab, and pressing the ActionScript 2.0 Settings button. This opens the ActionScript Settings dialog box in which you can add a new entry pointing to the directory into which you extracted the files.

Understanding the Logging Library Benefits

The Logging library provides an infrastructure that enables you to create much more effective logging than you would likely achieve with trace() by itself. The Logging library provides the following advantages:

  • Logging to handlers that function within and outside of the test player — using the Output panel, other SWFs, shared objects, socket listeners, and more
  • Pre-formatted log output that can automatically include time/date stamps, origin of log statement, and more
  • Priority levels so that you can enable or disable logging at different levels of priority and to different handlers independently

Getting Started Logging

In order to get started you need to familiarize yourself with the different aspects of the logging library. There are five basic parts:

  • The log manager
  • The configuration file
  • The logger instance
  • The handlers
  • The formatters

In the following sections we'll discuss each of the aspects in more detail.

The Log Manager

The log manager is a Singleton instance of the LogManager class. The log manager, as the name implies, keeps track of the logger instances, reading the configuration data, and other management tasks. For the most part you don't need to do much with the log manager. However, you'll likely find it useful to use the log manager to get notified when it has initialized. Because the logging system reads configuration data from an XML file, it needs a few milliseconds before it will be ready to start logging. Therefore you will generally want to tell your application to start only once the log manager says it's initialized by dispatching an initialized event.

You can get a reference to the log manager by using the static getLogManager() method of the LogManager class.

import ascb.util.logging.LogManager;

var lmInstance:LogManager = LogManager.getLogManager();

You can then add a listener to the log manager using the addEventListener() method. The log manager dispatches just one event: initialized.

lmInstance.addEventListener("initialized", this);

function initialized():Void {
  // Start the application.
}

If you don't wait for the log manager to initialize, it won't adversely affect anything else in the application. However, until the log manager initializes, no log output will occur.

Configuring the Log Manager

The log manager reads configuration information from an XML file. You must have a file named logconfiguration.xml in the same directory as the .swf for logging to work. The configuration file tells the log manager what handlers to use, and what formatters and levels to use with those handlers. At the time of this writing there are two built-in handlers: a trace handler and a local connection handler. The basic configuration file that enables both handlers looks like the following:

<logconfiguration>
  <handlers>
    <trace>true</trace>
    <localconnection>true</localconnection>
  </handlers>
</logconfiguration>

You can set the value of a handler to false to disable it. That has the same effect as omitting the entry from the configuration file. The order of the handlers doesn't matter, but they must be nested within the handlers tag as in the preceding example.

 

Adding Loggers

In order to add log output you need to use loggers. Loggers are instances of the Logger class. You can create a logger using the getLogger() factory method of the Logger class. The method requires one parameter: a string indicating the name of the logger. Generally, if the logger is used within an ActionScript class you should use the fully-qualified class name as the logger name. For example, if the class is com.domain.ClassExample, you could create a logger as follows:

// The following assumes you've imported
// ascb.util.logging.Logger.
var lInstance:Logger = Logger.getLogger("com.domain.ClassExample");

The name you give to a logger is used in two ways:

  • Each logger must have a unique name. The log manager keeps track of existing loggers. If you call getLogger() with the name of an existing logger it will return a reference to that logger rather than create a new one.
  • Each log record that is output contains the name of the logger that created it. That information can be written to the log output to identify the origins of statements.

Once you've created a logger instance, you can call the log() method to send a log record to the handlers. You must tell the log() method two things: a level and a message. The level is an integer value that specifies the priority of the log record. You can use the constants from the ascb.util.logging.Level class. In ascending level of priority they are OFF, FINEST, FINER, FINE, CONFIG, DEBUG, INFO, WARNING, SEVERE, and ALL. In the next section you'll learn how you can apply different levels to different handlers. A handler will only handle log records that are within its level scope. For example, if the trace handler is set to the CONFIG level, it will handle any log record with a level from FINEST to CONFIG. If a handler's level is set to OFF that's the same as setting the handler's configuration value to false.

The message parameter should be a string, and it gets sent along to the handler for output. Typically the message is analogous to whatever you would pass to a trace() statement.

lInstance.log(Level.DEBUG, "Logging enabled");

The Logger class also has some methods that are simpler than using log() if you want to send a log record with a level of DEBUG, INFO, WARNING, or SEVERE. You can simply use the debug(), info(), warning(), or severe() methods. Each method requires only the message parameter, and not a level. The level is assumed based on the method you call.

lInstance.debug("Debug message");

Understanding Handlers

Handlers are classes that can register with the log manager in order to receive log records from logger instances. There are two built-in handlers: the trace handler and the local connection handler. You can enable those handlers in the configuration file using the trace and localconnection tags as shown in the earlier example. The trace handler writes log records to the Output panel when you run an .swf in the test player. The local connection handler sends log record output across a local connection called _logger, calling a method named onMessage().

By default each handler handles log records at every level. However, if you prefer, you can specify a top-most level that a handler should deal with. You can do so using the level attribute of the corresponding tag in the configuration file. Specify the name of one of the Level class constants. For example, if you want the trace handler to deal with log records only up to the CONFIG level, you can use the following tag in the configuration file:

<trace level="CONFIG">true</true>

You can also create custom handlers as discussed shortly.

Formatting Log Records

Handlers use the default formatter unless you specify otherwise in the configuration file. In many cases the default formatter works. But if you want to create a custom formatter, you can do so rather simply by creating a formatter class. A formatter class needs just one method: format(). The format() method should accept one parameter: a log record object. A log record object has three properties:

  • name - the name of the logger that created the record
  • level - the level of the record
  • message - the message

You can use these properties in order to create and return a string. If you want to get the name of the Level constant corresponding to the level property value, you can use the Level.LABELS array. The following is the format() method from the default formatter class, and it illustrates what you need to know in order to create a custom formatter:

public function format(oLogRecord:Object):String {
  return "Log Message\n\tlevel - " + Level.LABELS[oLogRecord.level] + "\n\ttime - " + new Date() + "\n\tname - " + oLogRecord.name + "\n\tmessage - " + oLogRecord.message;
}

If you create a custom formatter, there are two things you need to do to use it:

  • Specify the fully-qualified class name as the value of the formatter attribute of the corresponding handler tag in the configuration file.

    <trace formatter="com.domain.CustomFormatter">true</trace>

  • Add a reference to a made-up static property of the formatter class to your application so that the class is compiled into the .swf. For example, the following will make sure com.domain.CustomFormatter is compiled into the application.

    com.domain.CustomFormatter["a"];

Creating Custom Handlers

You can create custom handlers by creating new classes that extend ascb.util.logging.Handler. The constructor should accept two parameters: a formatter (typed as Object) and a level. It can then call the super constructor, passing along the same two parameters. The class should also implement a log() method. The log() method should accept a log record object. It should determine whether or not the level of the log record should get handled. Subclasses of ascb.util.logging.Handler inherit two private properties: _nLevel (the level of the handler) and _fmtInstance (the formatter). You only need to create custom handlers if you want to send log output to other places such as files, socket servers, etc. A specific example is beyond the scope of this article simply because of the other technologies necessary (such as a socket server). However, if you look at TraceHandler and LocalConnectionHandler, you'll not likely have any difficulty understanding how they work as examples.

Once you've created a custom handler class you need to then add a line to the configuration file. Within the handler tag you can add another element with the name matching the fully-qualified class name of the custom handler. For example, if you named the custom class com.domain.CustomHandler, the configuration file might look like the following:

<logconfiguration>
  <handlers>
    <trace>true</trace>
    <localconnection>true</localconnection>
    <com.domain.CustomHandler>true</com.domain.CustomHandler>
  </handlers>
</logconfiguration>

You also need to make sure the class is compiled into the .swf. In order to do that you can use the same technique you can employ with the formatters — reference a made-up static property in your ActionScript code:

com.domain.CustomHandler["a"];

Creating a LocalConnection Logger Example

In order to understand how the Logging library can be useful, we'll create a simple example that uses the local connection handler.

  1. Create a new Flash document to be the log window that will receive the log messages.
  2. Save the file as LogWindow.fla.
  3. Add an instance of the TextArea component to the stage. Give it an instance name of ctaLog. Set its dimensions to 550 by 400, and place it at 0,0.
  4. Add the following code to the first keyframe on the main timeline:

    var lcLog:LocalConnection = new LocalConnection();

    // The local connection should listen over _logger.
    lcLog.connect("_logger");

    // When the onMessage() method is called, add the
    // message to the text area.
    lcLog.onMessage = function(sMessage:String):Void {
      ctaLog.text += sMessage + newline;
      ctaLog.vPosition = ctaLog.maxVPosition;
    };

  5. Export the .swf.
  6. Create a new configuration file named logconfiguration.xml.
  7. Add the following code to the logconfiguration.xml file:

    <logconfiguration>
      <handlers>
        <trace>true</trace>
        <localconnection>true</localconnection>
      </handlers>
    </logconfiguration>

  8. Create a new Flash document to actually send the log messages, and save it to the same directory as the configuration file.
  9. Add the following code to the first keyframe of the main timeline:

    import ascb.util.logging.LogManager;
    import ascb.util.logging.Logger;

    var lmInstance:LogManager = LogManager.getLogManager();
    lmInstance.addEventListener("initialized", this);
    var lInstance:Logger = Logger.getLogger("Main");

    function initialized():Void {
      setInterval(lInstance, "debug", 1000, "log message");
    }

  10. Open LogWindow.swf in the standalone player, and test the other movie. You should see log messages displaying in the Output panel as well as in LogWindow.swf, once per second.

Conclusion

In this article you have learned the basics of how to use the ASCBLibrary Logging library to facilitate more effective logging in your Flash applications.