1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.env.event.distributor.EventDistributorControl;
    18  import org.as2lib.env.event.distributor.SimpleEventDistributorControl;
    19  import org.as2lib.env.log.LogHandler;
    20  import org.as2lib.env.log.ConfigurableLogger;
    21  import org.as2lib.env.log.ConfigurableHierarchicalLogger;
    22  import org.as2lib.env.log.HierarchicalLogger;
    23  import org.as2lib.env.log.LogMessage;
    24  import org.as2lib.env.log.LogLevel;
    25  import org.as2lib.env.log.logger.AbstractLogger;
    26  
    27  /**
    28   * {@code SimpleHierarchicalLogger} is a simple implementation of the
    29   * {@code ConfigurableLogger} and {@code ConfigurableHierarchicalLogger}
    30   * interfaces.
    31   * 
    32   * <p>This logger is thus capable of functioning correctly in a hierarchy. It is
    33   * normally used with the {@code LoggerHierarchy} repository.
    34   *
    35   * <p>The basic methods to write log messages are {@link #log}, {@link #debug},
    36   * {@link #info}, {@link #warning} and {@link #fatal}.
    37   * 
    38   * <p>The first thing to note is that you can log messages at different levels.
    39   * These levels are {@code DEBUG}, {@code INFO}, {@code WARNING}, {@code ERROR} and
    40   * {@code FATAL}. Depending on what level has been set only messages at a given
    41   * level are logged. The levels are organized in a hierarchical manner. This means
    42   * if you set the log level to {@code ALL} every messages is logged. If you set it
    43   * to {@code ERROR} only messages at {@code ERROR} and {@code FATAL} level are
    44   * logged and so on. It is also possible to define your own set of levels. You can
    45   * therefor use the {@link #isEnabled} and {@link #log} methods. If you do not set
    46   * a log level the level of its parent is used to decide whether the message shall
    47   * be logged.
    48   *
    49   * <p>To do not waste unnecessary performance in constructing log messages that
    50   * are not logged you can use the {@link #isEnabled}, {@link #isDebugEnabled},
    51   * {@link #isInfoEnabled}, {@link #isWarningEnabled}, {@link #isErrorEnabled}
    52   * and {@link #isFatalEnabled} methods.
    53   * 
    54   * <p>Note that the message does in neither case have to be a string. That means
    55   * you can pass-in messages and let the actual handler or logger decide how to
    56   * produce a string representation of the message. That is in most cases done by
    57   * using the {@code toString} method of the specific message. You can use this
    58   * method to do not lose performance in cases where the message is not logged.
    59   *
    60   * <p>The actual logging is made by log handlers. To configure and access the
    61   * handlers of this logger you can use the methods {@link #addHandler},
    62   * {@link #removeHandler}, {@link #removeAllHandlers} and {@link #getAllHandlers}.
    63   * There are a few pre-defined handlers for different output devices. Take a look
    64   * at the {@code org.as2lib.env.log.handler} package for these. This logger does
    65   * not only use the handlers of itself but also the ones of its parents.
    66   * 
    67   * <p>Note that advantage of this class's hierarchical support is not taken in the
    68   * following example. Take a look at the class documentation of the
    69   * {@code LoggerHierarchy} for an example that uses this support.
    70   *
    71   * <code>
    72   *   var logger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("myLogger");
    73   *   logger.setLevel(SimpleHierarchicalLogger.ALL);
    74   *   logger.addHandler(new TraceHandler());
    75   *   if (logger.isInfoEnabled()) {
    76   *       logger.info("This is an information message.");
    77   *   }
    78   * </code>
    79   *
    80   * @author Simon Wacker
    81   * @see org.as2lib.env.log.repository.LoggerHierarchy
    82   */
    83  class org.as2lib.env.log.logger.SimpleHierarchicalLogger extends AbstractLogger implements ConfigurableLogger, ConfigurableHierarchicalLogger {
    84  	
    85  	/** Makes the static variables of the super-class accessible through this class. */
    86  	private static var __proto__:Function = AbstractLogger;
    87  	
    88  	/** The actual level. */
    89  	private var level:LogLevel;
    90  	
    91  	/** Says whether the handlers array already contains the parents' handlers. */
    92  	private var addedParentHandlers:Boolean;
    93  	
    94  	/** Stores the parent. */
    95  	private var parent:HierarchicalLogger;
    96  	
    97  	/** The name of this logger. */
    98  	private var name:String;
    99  	
   100  	/** Distributor control that controls the distributor. */
   101  	private var distributorControl:EventDistributorControl;
   102  	
   103  	/** Typed distributor that distributes messages to all log handlers. */
   104  	private var distributor:LogHandler;
   105  	
   106  	/**
   107  	 * Constructs a new {@code SimpleHierarchicalLogger} instance.
   108  	 *
   109  	 * @param name the name of this new logger
   110  	 */
   111  	public function SimpleHierarchicalLogger(name:String) {
   112  		setName(name);
   113  		distributorControl = new SimpleEventDistributorControl(LogHandler, false);
   114  		distributor = distributorControl.getDistributor();
   115  		addedParentHandlers = false;
   116  	}
   117  	
   118  	/**
   119  	 * Returns the parent of this logger.
   120  	 *
   121  	 * <p>This logger uses the parent to get the log level, if no one has been set to
   122  	 * this logger manually and to get the handlers of its parents to log messages.
   123  	 *
   124  	 * @return the parent of this logger
   125  	 */
   126  	public function getParent(Void):HierarchicalLogger {
   127  		return parent;
   128  	}
   129  	
   130  	/**
   131  	 * Sets the parent of this logger.
   132  	 *
   133  	 * <p>The parent is used to obtain needed configuration like handlers and levels.
   134  	 *
   135  	 * @param parent the parent of this logger
   136  	 */
   137  	public function setParent(parent:HierarchicalLogger):Void {
   138  		this.parent = parent;
   139  	}
   140  	
   141  	/**
   142  	 * Returns the name of this logger.
   143  	 *
   144  	 * <p>The name is a fully qualified name and the different parts are separated by
   145  	 * periods. The name could for example be {@code "org.as2lib.core.BasicClass"}.
   146  	 *
   147  	 * @return the name of this logger
   148  	 */
   149  	public function getName(Void):String {
   150  		return name;
   151  	}
   152  	
   153  	/**
   154  	 * Sets the name of this logger.
   155  	 *
   156  	 * <p>The name must exist of the path as well as the actual identifier. That means
   157  	 * it must be fully qualified.
   158  	 * 
   159  	 * <p>The {@link LoggerHierarchy} prescribes that the different parts of the name
   160  	 * must be separated by periods.
   161  	 *
   162  	 * @param name the name of this logger
   163  	 */
   164  	public function setName(name:String):Void {
   165  		this.name = name;
   166  	}
   167  	
   168  	/**
   169  	 * Sets the log level.
   170  	 *
   171  	 * <p>The {@code level} determines which messages to log and which not.
   172  	 *
   173  	 * <p>The {@code level} is allowed to be set to {@code null} or {@code undefined}.
   174  	 * If you do so the {@link #getLevel} method returns the level of the parent.
   175  	 *
   176  	 * @param level the new level to control the logging of messages
   177  	 * @see #getLevel
   178  	 */
   179  	public function setLevel(level:LogLevel):Void {
   180  		this.level = level;
   181  	}
   182  	
   183  	/**
   184  	 * Returns the log level of this logger.
   185  	 *
   186  	 * <p>If the level has not been set, that means is {@code undefined}, the level of
   187  	 * the parent will be returned.
   188  	 *
   189  	 * <p>{@code null} or {@code undefined} will only be returned if this level is not
   190  	 * defined and the parent's {@code getLevel} method returns {@code null} or
   191  	 * {@code undefined}.
   192  	 *
   193  	 * @return the log level of this logger
   194  	 */
   195  	public function getLevel(Void):LogLevel {
   196  		if (level === undefined) return getParent().getLevel();
   197  		return level;
   198  	}
   199  	
   200  	/**
   201  	 * Adds the new {@code handler}.
   202  	 *
   203  	 * <p>Log handlers are used to actually log the messages. They determine what
   204  	 * information to log and to which output device.
   205  	 *
   206  	 * <p>This method simply does nothing if the passed-in handler is {@code null} or
   207  	 * {@code undefined}.
   208  	 *
   209  	 * @param handler the new log handler to log messages
   210  	 */
   211  	public function addHandler(handler:LogHandler):Void {
   212  		if (handler) {
   213  			distributorControl.addListener(handler);
   214  		}
   215  	}
   216  	
   217  	/**
   218  	 * Removes all occerrences of the passed-in {@code handler}.
   219  	 *
   220  	 * <p>If the passed-in {@code handler} is {@code null} or {@code undefined} the
   221  	 * method invocation is simply ignored.
   222  	 *
   223  	 * @param handler the log handler to remove
   224  	 */
   225  	public function removeHandler(handler:LogHandler):Void {
   226  		if (handler) {
   227  			distributorControl.removeListener(handler);
   228  		}
   229  	}
   230  	
   231  	/**
   232  	 * Removes all added log handlers.
   233  	 */
   234  	public function removeAllHandlers(Void):Void {
   235  		distributorControl.removeAllListeners();
   236  	}
   237  	
   238  	/**
   239  	 * Returns all handlers this logger broadcasts to when logging a message.
   240  	 *
   241  	 * <p>These handlers are the once directly added to this logger and the once of
   242  	 * its parents.
   243  	 *
   244  	 * <p>The handlers of the parents are obtained via the parents
   245  	 * {@code getAllHandlers} method which is supposed to also return the handlers of
   246  	 * its parent and so on.
   247  	 *
   248  	 * <p>This method never returns {@code null} but an empty array if there are no
   249  	 * handlers added to this logger nor to its parents.
   250  	 *
   251  	 * <p>Note that this method stores the parents handlers itself if it once obtained
   252  	 * them. That is when you first log a message. It then always works with the
   253  	 * stored handlers. That means that handlers added to its parents after the
   254  	 * handlers have once been stored are not recognized.
   255  	 *
   256  	 * @return all added log handlers and the ones of the parents
   257  	 */
   258  	public function getAllHandlers(Void):Array {
   259  		if (!addedParentHandlers) addParentHandlers();
   260  		return distributorControl.getAllListeners();
   261  	}
   262  	
   263  	/**
   264  	 * Adds the parent handlers to the distributor.
   265  	 */
   266  	private function addParentHandlers(Void):Void {
   267  		var parentHandlers:Array = getParent().getAllHandlers();
   268  		if (parentHandlers) {
   269  			distributorControl.addAllListeners(parentHandlers);
   270  		}
   271  		addedParentHandlers = true;
   272  	}
   273  	
   274  	/**
   275  	 * Checks whether this logger is enabled for the passed-in {@code level}.
   276  	 *
   277  	 * {@code false} will be returned if:
   278  	 * <ul>
   279  	 *   <li>This logger is not enabled for the passed-in {@code level}.</li>
   280  	 *   <li>The passed-in {@code level} is {@code null} or {@code undefined}.</li>
   281  	 * </ul>
   282  	 *
   283  	 * <p>Using this method as shown in the class documentation may improve performance
   284  	 * depending on how long the log message construction takes.
   285  	 *
   286  	 * @param level the level to make the check upon
   287  	 * @return {@code true} if this logger is enabled for the given level else
   288  	 * {@code false}
   289  	 * @see #log
   290  	 */
   291  	public function isEnabled(level:LogLevel):Boolean {
   292  		if (!level) return false;
   293  		return (getLevel().toNumber() >= level.toNumber());
   294  	}
   295  	
   296  	/**
   297  	 * Checks if this logger is enabled for debug level log messages.
   298  	 *
   299  	 * <p>Using this method as shown in the class documentation may improve performance
   300  	 * depending on how long the log message construction takes.
   301  	 *
   302  	 * @return {@code true} if debug messages are logged
   303  	 * @see org.as2lib.env.log.level.AbstractLogLevel#DEBUG
   304  	 * @see #debug
   305  	 */
   306  	public function isDebugEnabled(Void):Boolean {
   307  		return (getLevel().toNumber() >= debugLevelAsNumber);
   308  	}
   309  	
   310  	/**
   311  	 * Checks if this logger is enabled for info level log messages.
   312  	 *
   313  	 * <p>Using this method as shown in the class documentation may improve performance
   314  	 * depending on how long the log message construction takes.
   315  	 *
   316  	 * @return {@code true} if info messages are logged
   317  	 * @see org.as2lib.env.log.level.AbstractLogLevel#INFO
   318  	 * @see #info
   319  	 */
   320  	public function isInfoEnabled(Void):Boolean {
   321  		return (getLevel().toNumber() >= infoLevelAsNumber);
   322  	}
   323  	
   324  	/**
   325  	 * Checks if this logger is enabled for warning level log messages.
   326  	 *
   327  	 * <p>Using this method as shown in the class documentation may improve performance
   328  	 * depending on how long the log message construction takes.
   329  	 *
   330  	 * @return {@code true} if warning messages are logged
   331  	 * @see org.as2lib.env.log.level.AbstractLogLevel#WARNING
   332  	 * @see #warning
   333  	 */
   334  	public function isWarningEnabled(Void):Boolean {
   335  		return (getLevel().toNumber() >= warningLevelAsNumber);
   336  	}
   337  	
   338  	/**
   339  	 * Checks if this logger is enabled for error level log messages.
   340  	 *
   341  	 * <p>Using this method as shown in the class documentation may improve performance
   342  	 * depending on how long the log message construction takes.
   343  	 *
   344  	 * @return {@code true} if error messages are logged
   345  	 * @see org.as2lib.env.log.level.AbstractLogLevel#ERROR
   346  	 * @see #error
   347  	 */
   348  	public function isErrorEnabled(Void):Boolean {
   349  		return (getLevel().toNumber() >= errorLevelAsNumber);
   350  	}
   351  	
   352  	/**
   353  	 * Checks if this logger is enabled for fatal level log messages.
   354  	 *
   355  	 * <p>Using this method as shown in the class documentation may improve performance
   356  	 * depending on how long the log message construction takes.
   357  	 *
   358  	 * @return {@code true} if fatal messages are logged
   359  	 * @see org.as2lib.env.log.level.AbstractLogLevel#FATAL
   360  	 * @see #fatal
   361  	 */
   362  	public function isFatalEnabled(Void):Boolean {
   363  		return (getLevel().toNumber() >= fatalLevelAsNumber);
   364  	}
   365  	
   366  	/**
   367  	 * Logs the passed-in {@code message} at the given {@code level}.
   368  	 *
   369  	 * <p>The {@code message} is only logged when this logger is enabled for the
   370  	 * passed-in {@code level}.
   371  	 *
   372  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   373  	 * the ones of its parents or more specifically to the ones returned by the 
   374  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   375  	 * of its parents and so on.
   376  	 *
   377  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   378  	 * message is logged. They are stored in this logger to reference them faster.
   379  	 *
   380  	 * @param message the message object to log
   381  	 * @param level the specific level at which the {@code message} shall be logged
   382  	 * @see #isEnabled
   383  	 */
   384  	public function log(message, level:LogLevel):Void {
   385  		if (isEnabled(level)) {
   386  			if (!addedParentHandlers) addParentHandlers();
   387  			distributor.write(new LogMessage(message, level, name));
   388  		}
   389  	}
   390  	
   391  	/**
   392  	 * Logs the passed-in {@code message} at debug level.
   393  	 *
   394  	 * <p>The {@code message} is only logged when the level is set to {@code DEBUG} or
   395  	 * a level above.
   396  	 *
   397  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   398  	 * the ones of its parents or more specifically to the ones returned by the 
   399  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   400  	 * of its parents and so on.
   401  	 *
   402  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   403  	 * message is logged. They are stored in this logger to reference them faster.
   404  	 *
   405  	 * @param message the message object to log
   406  	 * @see #isDebugEnabled
   407  	 */
   408  	public function debug(message):Void {
   409  		log(message, debugLevel);
   410  	}
   411  	
   412  	/**
   413  	 * Logs the passed-in {@code message} at info level.
   414  	 *
   415  	 * <p>The {@code message} is only logged when the level is set to {@code INFO} or
   416  	 * a level above.
   417  	 *
   418  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   419  	 * the ones of its parents or more specifically to the ones returned by the 
   420  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   421  	 * of its parents and so on.
   422  	 *
   423  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   424  	 * message is logged. They are stored in this logger to reference them faster.
   425  	 *
   426  	 * @param message the message object to log
   427  	 * @see #isInfoEnabled
   428  	 */
   429  	public function info(message):Void {
   430  		log(message, infoLevel);
   431  	}
   432  	
   433  	/**
   434  	 * Logs the passed-in {@code message} at warning level.
   435  	 *
   436  	 * <p>The {@code message} is only logged when the level is set to {@code WARNING}
   437  	 * or a level above.
   438  	 *
   439  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   440  	 * the ones of its parents or more specifically to the ones returned by the 
   441  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   442  	 * of its parents and so on.
   443  	 *
   444  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   445  	 * message is logged. They are stored in this logger to reference them faster.
   446  	 *
   447  	 * @param message the message object to log
   448  	 * @see #isWarningEnabled
   449  	 */
   450  	public function warning(message):Void {
   451  		log(message, warningLevel);
   452  	}
   453  	
   454  	/**
   455  	 * Logs the passed-in {@code message} at error level.
   456  	 *
   457  	 * <p>The {@code message} is only logged when the level is set to {@code ERROR} or
   458  	 * a level above.
   459  	 *
   460  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   461  	 * the ones of its parents or more specifically to the ones returned by the 
   462  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   463  	 * of its parents and so on.
   464  	 *
   465  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   466  	 * message is logged. They are stored in this logger to reference them faster.
   467  	 *
   468  	 * @param message the message object to log
   469  	 * @see #isErrorEnabled
   470  	 */
   471  	public function error(message):Void {
   472  		log(message, errorLevel);
   473  	}
   474  	
   475  	/**
   476  	 * Logs the passed-in {@code message} at fatal level.
   477  	 *
   478  	 * <p>The {@code message} is only logged when the level is set to {@code FATAL} or
   479  	 * a level above.
   480  	 *
   481  	 * <p>The {@code message} is broadcasted to all log handlers of this logger and to
   482  	 * the ones of its parents or more specifically to the ones returned by the 
   483  	 * parent's {@code getAllHandlers} method, that normally also returns the handlers
   484  	 * of its parents and so on.
   485  	 *
   486  	 * <p>Note that the handlers of the parents are resloved only once, when the first
   487  	 * message is logged. They are stored in this logger to reference them faster.
   488  	 *
   489  	 * @param message the message object to log
   490  	 * @see #isFatalEnabled
   491  	 */
   492  	public function fatal(message):Void {
   493  		log(message, fatalLevel);
   494  	}
   495  	
   496  }