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 }