//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.logging { import mx.logging.errors.InvalidCategoryError; import mx.managers.ISystemManager; import mx.managers.SystemManager; import mx.resources.IResourceManager; import mx.resources.ResourceManager; [ResourceBundle("logging")] /** * Provides pseudo-hierarchical logging capabilities with multiple format and * output options. * The log system consists of two major components, the logger and a target. * You can use the logger to send information to a target. * The target is responsible for formatting and general output of the log data. *

* Loggers are singleton instances created for a particular category of * information. * Typically, the category is the package name of the component * that desires to log information. * The category provides users the ability to specify what log information they * are interested in. * Multiple categories can be selected and combined with regular expressions. * This allows for both broad and narrow logging information to be acquired. * For example, you might be interested in all logging information under * the "mx.messaging" and "mx.rpc" packages and want the output from these * packages to be formatted as XML. * To get the all of the logging information under the "mx.messaging" category * including sub-packages and components a wildcard expression is required, such as * "mx.messaging.~~". * See the code example below for more details. *

*

Targets provide the output mechanism of the data being logged. * This mechanism typically includes formatting, transmission, or storage, but * can be anything possible under the VM. * There are two targets provided: MiniDebugTarget and * TraceTarget. * Each of these writers take the current log information and "sends" it * somewhere for display and/or storage. * Targets also provide the specification of what log data to output. *

* * @example *
 *  ... 
 *  import mx.logging.targets.*;
 *  import mx.logging.*;
 *
 *  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);
 *  } 
 *  ...
 *  
* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class Log { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class variables // //-------------------------------------------------------------------------- /** * @private * Sentinal value for the target log level to indicate no logging. */ private static var NONE:int = int.MAX_VALUE; /** * @private * The most verbose supported log level among registered targets. */ private static var _targetLevel:int = NONE; // Initialize target level to a value out of range. /** * @private * An associative Array of existing loggers keyed by category */ private static var _loggers:Array; /** * @private * Array of targets that should be searched any time * a new logger is created. */ private static var _targets:Array = []; /** * @private * Storage for the resourceManager getter. * This gets initialized on first access, * not at static initialization time, in order to ensure * that the Singleton registry has already been initialized. */ private static var _resourceManager:IResourceManager; /** * @private * A reference to the object which manages * all of the application's localized resources. * This is a singleton instance which implements * the IResourceManager interface. */ private static function get resourceManager():IResourceManager { if (!_resourceManager) _resourceManager = ResourceManager.getInstance(); return _resourceManager; } //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * Indicates whether a fatal level log event will be processed by a * log target. * * @return true if a fatal level log event will be logged; otherwise false. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function isFatal():Boolean { return (_targetLevel <= LogEventLevel.FATAL) ? true : false; } /** * Indicates whether an error level log event will be processed by a * log target. * * @return true if an error level log event will be logged; otherwise false. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function isError():Boolean { return (_targetLevel <= LogEventLevel.ERROR) ? true : false; } /** * Indicates whether a warn level log event will be processed by a * log target. * * @return true if a warn level log event will be logged; otherwise false. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function isWarn():Boolean { return (_targetLevel <= LogEventLevel.WARN) ? true : false; } /** * Indicates whether an info level log event will be processed by a * log target. * * @return true if an info level log event will be logged; otherwise false. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function isInfo():Boolean { return (_targetLevel <= LogEventLevel.INFO) ? true : false; } /** * Indicates whether a debug level log event will be processed by a * log target. * * @return true if a debug level log event will be logged; otherwise false. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function isDebug():Boolean { return (_targetLevel <= LogEventLevel.DEBUG) ? true : false; } /** * Allows the specified target to begin receiving notification of log * events. * * @param The specific target that should capture log events. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function addTarget(target:ILoggingTarget):void { if (target) { var filters:Array = target.filters; var logger:ILogger; // need to find what filters this target matches and set the specified // target as a listener for that logger. for (var i:String in _loggers) { if (categoryMatchInFilterList(i, filters)) target.addLogger(ILogger(_loggers[i])); } // if we found a match all is good, otherwise we need to // put the target in a waiting queue in the event that a logger // is created that this target cares about. _targets.push(target); if (_targetLevel == NONE) _targetLevel = target.level else if (target.level < _targetLevel) _targetLevel = target.level; } else { var message:String = resourceManager.getString( "logging", "invalidTarget"); throw new ArgumentError(message); } } /** * Stops the specified target from receiving notification of log * events. * * @param The specific target that should capture log events. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function removeTarget(target:ILoggingTarget):void { if (target) { var filters:Array = target.filters; var logger:ILogger; // Disconnect this target from any matching loggers. for (var i:String in _loggers) { if (categoryMatchInFilterList(i, filters)) { target.removeLogger(ILogger(_loggers[i])); } } // Remove the target. for (var j:int = 0; j<_targets.length; j++) { if (target == _targets[j]) { _targets.splice(j, 1); j--; } } resetTargetLevel(); } else { var message:String = resourceManager.getString( "logging", "invalidTarget"); throw new ArgumentError(message); } } /** * Returns the logger associated with the specified category. * If the category given doesn't exist a new instance of a logger will be * returned and associated with that category. * Categories must be at least one character in length and may not contain * any blanks or any of the following characters: * []~$^&\/(){}<>+=`!#%?,:;'"@ * This method will throw an InvalidCategoryError if the * category specified is malformed. * * @param category The category of the logger that should be returned. * * @return An instance of a logger object for the specified name. * If the name doesn't exist, a new instance with the specified * name is returned. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function getLogger(category:String):ILogger { checkCategory(category); if (!_loggers) _loggers = []; // get the logger for the specified category or create one if it // doesn't exist var result:ILogger = _loggers[category]; if (result == null) { result = new LogLogger(category); _loggers[category] = result; } // check to see if there are any targets waiting for this logger. var target:ILoggingTarget; for (var i:int = 0; i < _targets.length; i++) { target = ILoggingTarget(_targets[i]); if (categoryMatchInFilterList(category, target.filters)) target.addLogger(result); } return result; } /** * This method removes all of the current loggers from the cache. * Subsquent calls to the getLogger() method return new instances * of loggers rather than any previous instances with the same category. * This method is intended for use in debugging only. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function flush():void { _loggers = []; _targets = []; _targetLevel = NONE; } /** * This method checks the specified string value for illegal characters. * * @param value The String to check for illegal characters. * The following characters are not valid: * []~$^&\/(){}<>+=`!#%?,:;'"@ * @return true if there are any illegal characters found, * false otherwise * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static function hasIllegalCharacters(value:String):Boolean { return value.search(/[\[\]\~\$\^\&\\(\)\{\}\+\?\/=`!@#%,:;'"<>\s]/) != -1; } // private members /** * This method checks that the specified category matches any of the filter * expressions provided in the filters Array. * * @param category The category to match against * @param filters A list of Strings to check category against. * @return true if the specified category matches any of the * filter expressions found in the filters list, false * otherwise. * @private */ private static function categoryMatchInFilterList(category:String, filters:Array):Boolean { var result:Boolean = false; var filter:String; var index:int = -1; for (var i:uint = 0; i < filters.length; i++) { filter = filters[i]; // first check to see if we need to do a partial match // do we have an asterisk? index = filter.indexOf("*"); if (index == 0) return true; index = index < 0 ? index = category.length : index -1; if (category.substring(0, index) == filter.substring(0, index)) return true; } return false; } /** * This method will ensure that a valid category string has been specified. * If the category is not valid an InvalidCategoryError will * be thrown. * Categories can not contain any blanks or any of the following characters: * []`*~,!#$%^&()]{}+=\|'";?><./@ or be less than 1 character in length. * @private */ private static function checkCategory(category:String):void { var message:String; if (category == null || category.length == 0) { message = resourceManager.getString( "logging", "invalidLen"); throw new InvalidCategoryError(message); } if (hasIllegalCharacters(category) || (category.indexOf("*") != -1)) { message = resourceManager.getString( "logging", "invalidChars"); throw new InvalidCategoryError(message); } } /** * @private * This method resets the Log's target level to the most verbose log level * for the currently registered targets. */ private static function resetTargetLevel():void { var minLevel:int = NONE; for (var i:int = 0; i < _targets.length; i++) { if (minLevel == NONE || _targets[i].level < minLevel) minLevel = _targets[i].level; } _targetLevel = minLevel; } } }