//////////////////////////////////////////////////////////////////////////////// // // 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.
*
* ...
* 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;
}
}
}