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.core.BasicClass; 18 import org.as2lib.env.except.IllegalArgumentException; 19 import org.as2lib.env.except.IllegalStateException; 20 import org.as2lib.env.log.LoggerRepository; 21 import org.as2lib.env.log.ConfigurableHierarchicalLogger; 22 import org.as2lib.env.log.Logger; 23 import org.as2lib.env.log.logger.SimpleHierarchicalLogger; 24 import org.as2lib.env.log.logger.RootLogger; 25 import org.as2lib.env.log.level.AbstractLogLevel; 26 import org.as2lib.env.log.repository.ConfigurableHierarchicalLoggerFactory; 27 28 /** 29 * {@code LoggerHierarchy} organizes loggers in a hierarchical structure. 30 * 31 * <p>It works only with loggers that are capable of acting properly in a hierarchy. 32 * These loggers must implement the {@link ConfigurableHierarchicalLogger} 33 * interface. 34 * 35 * <p>The names of the loggers must be fully qualified and the differnt parts of 36 * the preceding structure/path must be separated by periods. 37 * 38 * <p>This repository takes care that the parents of all loggers are correct and 39 * updates them if necessary. The hierarchical loggers themselves are responsible 40 * of obtaining the level and handlers from its parents if necessary and desired. 41 * 42 * <p>Example: 43 * <code> 44 * var repository:LoggerHierarchy = new LoggerHierarchy(); 45 * LogManager.setLoggerRepository(repository); 46 * var traceLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.as2lib"); 47 * traceLogger.addHandler(new TraceHandler()); 48 * repository.addLogger(traceLogger); 49 * // in some other class or something 50 * var myLogger:Logger = LogManager.getLogger("org.as2lib.mypackage.MyClass"); 51 * myLogger.warning("Someone did something he should not do."); 52 * </code> 53 * 54 * <p>The message is traced because the namespace of {@code myLogger} is the same 55 * as the one of {@code traceLogger}. You can of course add multiple handlers to 56 * one logger and also multiple loggers to different namespaces. 57 * 58 * @author Simon Wacker 59 */ 60 class org.as2lib.env.log.repository.LoggerHierarchy extends BasicClass implements LoggerRepository { 61 62 /** Stores the root of this hierarchy. */ 63 private var root:ConfigurableHierarchicalLogger; 64 65 /** Stores added loggers. */ 66 private var loggers:Array; 67 68 /** This factory is used when no custom factory is specified. */ 69 private var defaultLoggerFactory:ConfigurableHierarchicalLoggerFactory; 70 71 /** 72 * Constructs a new {@code LoggerHierarchy} instance. 73 * 74 * <p>Registers the root logger with name {@code "root"} if the {@code root}'s 75 * {@code getName} method returns {@code null} or {@code undefined}. Otherwise it 76 * will be registered with the name returned by the {@code root}'s {@code getName} 77 * method. 78 * 79 * <p>If the passed-in {@code root} is {@code null} or {@code undefined} an 80 * instance of type {@link RootLogger} with name {@code "root"} and log level 81 * {@code ALL} will be used instead. 82 * 83 * @param root the root of the hierarchy 84 */ 85 public function LoggerHierarchy(root:ConfigurableHierarchicalLogger) { 86 if (!root) root = new RootLogger(AbstractLogLevel.ALL); 87 this.root = root; 88 loggers = new Array(); 89 if (root.getName() == null) { 90 loggers["root"] = root; 91 } else { 92 loggers[root.getName()] = root; 93 } 94 } 95 96 /** 97 * Returns either the factory set via {@link #setDefaultLoggerFactory} or the 98 * default one. 99 * 100 * <p>The default factory returns instances of type 101 * {@link SimpleHierarchicalLogger}. 102 * 103 * @return the factory used as default 104 */ 105 public function getDefaultLoggerFactory(Void):ConfigurableHierarchicalLoggerFactory { 106 if (!defaultLoggerFactory) defaultLoggerFactory = getNormalLoggerFactory(); 107 return defaultLoggerFactory; 108 } 109 110 /** 111 * Returns the normal factory that returns instances of class 112 * {@link SimpleHierarchicalLogger}. 113 * 114 * @return the normal factory 115 */ 116 private function getNormalLoggerFactory(Void):ConfigurableHierarchicalLoggerFactory { 117 var result:ConfigurableHierarchicalLoggerFactory = getBlankConfigurableHierarchicalLoggerFactory(); 118 // Not function(Void) because mtasc compiler complains with the following message: 119 // type error Local variable redefinition : Void 120 result.getLogger = function():ConfigurableHierarchicalLogger { 121 return new SimpleHierarchicalLogger(); 122 }; 123 return result; 124 } 125 126 /** 127 * Sets the factory used to obtain loggers that have not been set manually before. 128 * 129 * @param defaultLoggerFactory the factory to use as default 130 */ 131 public function setDefaultLoggerFactory(defaultLoggerFactory:ConfigurableHierarchicalLoggerFactory):Void { 132 this.defaultLoggerFactory = defaultLoggerFactory; 133 } 134 135 /** 136 * Returns the root logger of this hierarchy. 137 * 138 * @return the root logger of this hierarchy 139 */ 140 public function getRootLogger(Void):Logger { 141 return root; 142 } 143 144 /** 145 * Adds a new logger to this hierarchical repository. 146 * 147 * <p>The logger is automatically integrated into the hierarchy. 148 * 149 * @param logger the logger to add to this hierarchy 150 * @throws IllegalArgumentException if the passed-in {@code logger} is {@code null} 151 * or {@code undefined} or if the passed-in {@code logger}'s {@code getName} method 152 * returns {@code null} or {@code undefined} or if a logger with the {@code logger}'s 153 * name is already in use 154 */ 155 public function addLogger(logger:ConfigurableHierarchicalLogger):Void { 156 if (!logger) throw new IllegalArgumentException("Logger to add is not allowed to be null or undefined.", this, arguments); 157 var name:String = logger.getName(); 158 if (name == null || name == "") throw new IllegalArgumentException("Name is not allowed to be null, undefined or a blank string.", this, arguments); 159 if (loggers[name] && loggers[name] instanceof ConfigurableHierarchicalLogger) { 160 throw new IllegalArgumentException("Name [" + name + "] is already in use.", this, arguments); 161 } 162 getLoggerByFactory(name, getSingletonFactory(logger)); 163 } 164 165 /** 166 * Returns the factory used to obtain the passed-in {@code logger}. 167 * 168 * @param logger the logger to be returned by the returned factory 169 * @return a factory that returns the passed-in {@code logger} 170 */ 171 private function getSingletonFactory(logger:ConfigurableHierarchicalLogger):ConfigurableHierarchicalLoggerFactory { 172 var result:ConfigurableHierarchicalLoggerFactory = getBlankConfigurableHierarchicalLoggerFactory(); 173 result.getLogger = function(Void):ConfigurableHierarchicalLogger { 174 return logger; 175 }; 176 return result; 177 } 178 179 /** 180 * Returns the logger appropriate to the given {@code name}. 181 * 182 * <p>The {@code name} can exist of a path as well as the actual specifier, for 183 * example {@code org.as2lib.core.BasicClass}. In case no logger instance has been 184 * put for the passed-in {@code name} a new will be created by the set factory, 185 * that by default obtains all its configuration from the parent logger. 186 * 187 * <p>{@code null} will be returned if passed-in {@code name} is {@code null} or 188 * {@code undefined}. 189 * 190 * @param name the name of the logger to obtain 191 * @return the logger corresponding to the {@code name} 192 */ 193 public function getLogger(name:String):Logger { 194 if (name == null) return null; 195 return getLoggerByFactory(name, null); 196 } 197 198 /** 199 * Returns the logger corresponding to the passed-in {@code name}. 200 * 201 * <p>If a logger with the passed-in name is not explicitely registered the logger 202 * returned by the factory is registered with the passed-in {@code name}, 203 * integrated in the hierarchy and returned. 204 * 205 * <p>The {@code name} can exist of a path as well as the actual specifier, for 206 * example {@code org.as2lib.core.BasicClass}. In case no logger instance has been 207 * put for the passed-in {@code name} a new will be created by the set factory, 208 * that by default obtains all its configuration from the parent logger. 209 * 210 * <p>{@code null} will be returned if the passed-in {@code name} is {@code null} 211 * or {@code undefined}. 212 * 213 * <p>If the passed-in {@code factory} is {@code null} or {@code undefined} the 214 * default one will be used. 215 * 216 * @param name the name of the logger to return 217 * @return the logger appropriate to the passed-in {@code name} 218 */ 219 public function getLoggerByFactory(name:String, factory:ConfigurableHierarchicalLoggerFactory):Logger { 220 if (name == null) return null; 221 if (!factory) factory = getDefaultLoggerFactory(); 222 var result = loggers[name]; 223 if (!result) { 224 result = factory.getLogger(); 225 result.setName(name); 226 loggers[name] = result; 227 updateParents(result); 228 } else if (result instanceof Array) { 229 var children:Array = result; 230 result = factory.getLogger(); 231 result.setName(name); 232 loggers[name] = result; 233 updateChildren(children, result); 234 updateParents(result); 235 } 236 return result; 237 } 238 239 /** 240 * Updates the affected parents. 241 * 242 * @param l the newly added logger 243 */ 244 private function updateParents(l:ConfigurableHierarchicalLogger):Void { 245 var n:String = l.getName(); 246 var f:Boolean = false; 247 var s:Number = n.length; 248 for (var i:Number = n.lastIndexOf(".", s-1); i >= 0; i = n.lastIndexOf(".", i-1)) { 249 var t:String = n.substring(0, i); 250 var o = loggers[t]; 251 if (!o) { 252 loggers[t] = [l]; 253 } else if (o instanceof Logger) { 254 f = true; 255 l.setParent(o); 256 break; 257 } else if (o instanceof Array) { 258 o.push(l); 259 } else { 260 throw new IllegalStateException("Obtained object [" + o + "] is of an unexpected type.", this, arguments); 261 } 262 } 263 if (!f) l.setParent(root); 264 } 265 266 /** 267 * Updates the affected children of the node. 268 * 269 * @param c the children to update 270 * @param l the logger that now replaces the node 271 */ 272 private function updateChildren(c:Array, l:ConfigurableHierarchicalLogger):Void { 273 var s:Number = c.length; 274 for (var i:Number = 0; i < s; i++) { 275 var z:ConfigurableHierarchicalLogger = c[i]; 276 if (z.getParent().getName().indexOf(l.getName()) != 0) { 277 l.setParent(z.getParent()); 278 z.setParent(l); 279 } 280 } 281 } 282 283 /** 284 * Returns a blank configurable hierarchical logger factory. That is a logger 285 * factory with no implemented methods. 286 * 287 * @return a blank configurable hierarchical logger factory 288 */ 289 private function getBlankConfigurableHierarchicalLoggerFactory(Void):ConfigurableHierarchicalLoggerFactory { 290 var result = new Object(); 291 result.__proto__ = ConfigurableHierarchicalLoggerFactory["prototype"]; 292 result.__constructor__ = ConfigurableHierarchicalLoggerFactory; 293 return result; 294 } 295 296 }