00001 /******************************************************************************* 00002 00003 @file Hierarchy.d 00004 00005 Copyright (C) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 00027 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00028 00029 00030 @version Initial version, October 2004 00031 @author Kris 00032 00033 00034 *******************************************************************************/ 00035 00036 module mango.log.Hierarchy; 00037 00038 private import mango.log.Appender; 00039 00040 /******************************************************************************* 00041 00042 Pull in additional functions from the C library 00043 00044 *******************************************************************************/ 00045 00046 extern (C) 00047 { 00048 int memcmp (void *, void *, int); 00049 } 00050 00051 /******************************************************************************* 00052 00053 This is the real Logger implementation, hidden behind the public 00054 abstract frontage. 00055 00056 *******************************************************************************/ 00057 00058 private class LoggerInstance : Logger 00059 { 00060 private LoggerInstance next, 00061 parent; 00062 00063 private char[] name; 00064 private Level level; 00065 private Appender appender; 00066 private Hierarchy hierarchy; 00067 private bool additive, 00068 breakpoint; 00069 00070 /*********************************************************************** 00071 00072 Construct a LoggerInstance with the specified name for the 00073 given hierarchy. By default, logger instances are additive 00074 and are prohibited from emitting events. 00075 00076 ***********************************************************************/ 00077 00078 protected this (Hierarchy hierarchy, char[] name) 00079 { 00080 this.hierarchy = hierarchy; 00081 this.level = Level.None; 00082 this.additive = true; 00083 this.name = name; 00084 } 00085 00086 /*********************************************************************** 00087 00088 Is this logger enabed for the specified Level? 00089 00090 ***********************************************************************/ 00091 00092 final bool isEnabled (Level level) 00093 { 00094 return level >= this.level; 00095 } 00096 00097 /*********************************************************************** 00098 00099 Is this a breakpoint Logger? 00100 00101 ***********************************************************************/ 00102 00103 final bool isBreakpoint () 00104 { 00105 return breakpoint; 00106 } 00107 00108 /*********************************************************************** 00109 00110 Is this logger additive? That is, should we walk ancestors 00111 looking for more appenders? 00112 00113 ***********************************************************************/ 00114 00115 final bool isAdditive () 00116 { 00117 return additive; 00118 } 00119 00120 /*********************************************************************** 00121 00122 Append a trace message 00123 00124 ***********************************************************************/ 00125 00126 final void trace (char[] msg) 00127 { 00128 append (Level.Trace, msg); 00129 } 00130 00131 /*********************************************************************** 00132 00133 Append an info message 00134 00135 ***********************************************************************/ 00136 00137 final void info (char[] msg) 00138 { 00139 append (Level.Info, msg); 00140 } 00141 00142 /*********************************************************************** 00143 00144 Append a warning message 00145 00146 ***********************************************************************/ 00147 00148 final void warn (char[] msg) 00149 { 00150 append (Level.Warn, msg); 00151 } 00152 00153 /*********************************************************************** 00154 00155 Append an error message 00156 00157 ***********************************************************************/ 00158 00159 final void error (char[] msg) 00160 { 00161 append (Level.Error, msg); 00162 } 00163 00164 /*********************************************************************** 00165 00166 Append a fatal message 00167 00168 ***********************************************************************/ 00169 00170 final void fatal (char[] msg) 00171 { 00172 append (Level.Fatal, msg); 00173 } 00174 00175 /*********************************************************************** 00176 00177 Return the name of this Logger (sans the appended dot). 00178 00179 ***********************************************************************/ 00180 00181 final char[] getName () 00182 { 00183 int i = name.length; 00184 if (i > 0) 00185 --i; 00186 return name[0 .. i]; 00187 } 00188 00189 /*********************************************************************** 00190 00191 Return the Level this logger is set to 00192 00193 ***********************************************************************/ 00194 00195 final Level getLevel () 00196 { 00197 return level; 00198 } 00199 00200 /*********************************************************************** 00201 00202 Set the current level for this logger (and only this logger). 00203 00204 ***********************************************************************/ 00205 00206 final void setLevel (Level level) 00207 { 00208 setLevel (level, false); 00209 } 00210 00211 /*********************************************************************** 00212 00213 Set the current level for this logger, and (optionally) all 00214 of its descendents. 00215 00216 ***********************************************************************/ 00217 00218 final void setLevel (Level level, bool force) 00219 { 00220 this.level = level; 00221 hierarchy.updateLoggers (this, force); 00222 } 00223 00224 /*********************************************************************** 00225 00226 Set the breakpoint status of this logger. 00227 00228 ***********************************************************************/ 00229 00230 final void setBreakpoint (bool enabled) 00231 { 00232 breakpoint = enabled; 00233 hierarchy.updateLoggers (this, false); 00234 } 00235 00236 /*********************************************************************** 00237 00238 Set the additive status of this logger. See isAdditive(). 00239 00240 ***********************************************************************/ 00241 00242 final void setAdditive (bool enabled) 00243 { 00244 additive = enabled; 00245 } 00246 00247 /*********************************************************************** 00248 00249 Add (another) appender to this logger. Appenders are each 00250 invoked for log events as they are produced. At most, one 00251 instance of each appender will be invoked. 00252 00253 ***********************************************************************/ 00254 00255 final void addAppender (Appender next) 00256 { 00257 if (appender) 00258 next.setNext (appender); 00259 appender = next; 00260 } 00261 00262 /*********************************************************************** 00263 00264 Remove all appenders from this Logger 00265 00266 ***********************************************************************/ 00267 00268 final void clearAppenders () 00269 { 00270 appender = null; 00271 } 00272 00273 00274 /*********************************************************************** 00275 00276 Get number of milliseconds since this application started 00277 00278 ***********************************************************************/ 00279 00280 final ulong getUptime () 00281 { 00282 return Event.getUptime(); 00283 } 00284 00285 /*********************************************************************** 00286 00287 Append a message to this logger via its appender list. 00288 00289 ***********************************************************************/ 00290 00291 private final void append (Level level, char[] s) 00292 { 00293 if (isEnabled (level)) 00294 { 00295 uint masks; 00296 LoggerInstance l = this; 00297 Event event = Event.allocate (); 00298 00299 try { 00300 // set the event attributes 00301 event.set (hierarchy, level, s, name[0..name.length-1]); 00302 00303 // combine appenders from all ancestors 00304 do { 00305 Appender appender = l.appender; 00306 00307 // this level have an appender? 00308 while (appender) 00309 { 00310 uint mask = appender.getMask(); 00311 00312 // have we used this appender already? 00313 if ((masks & mask) == 0) 00314 { 00315 // no - append message and update mask 00316 appender.append (event); 00317 masks |= mask; 00318 } 00319 // process all appenders for this node 00320 appender = appender.getNext (); 00321 } 00322 // process all ancestors 00323 } while (l.additive && ((l = l.parent) !== null)); 00324 00325 // make sure we release the event ... 00326 } finally 00327 Event.deallocate (event); 00328 } 00329 } 00330 00331 /*********************************************************************** 00332 00333 See if the provided Logger is a good match as a parent of 00334 this one. Note that each Logger name has a '.' appended to 00335 the end, such that name segments will not partially match. 00336 00337 ***********************************************************************/ 00338 00339 private final bool isCloserAncestor (LoggerInstance other) 00340 { 00341 int length = other.name.length; 00342 00343 // possible parent if length is shorter 00344 if (length < name.length) 00345 // does the prefix match? Note we append a "." to each 00346 if (length == 0 || 00347 memcmp (&other.name[0], &name[0], length) == 0) 00348 // is this a better (longer) match than prior parent? 00349 if ((parent is null) || (length >= parent.name.length)) 00350 return true; 00351 return false; 00352 } 00353 } 00354 00355 00356 /******************************************************************************* 00357 00358 The Logger hierarchy implementation. We keep a reference to each 00359 logger in a hash-table for convenient lookup purposes, plus keep 00360 each logger linked to the others in an ordered chain. Ordering 00361 places shortest names at the head and longest ones at the tail, 00362 making the job of identifying ancestors easier in an orderly 00363 fashion. For example, when propogating levels across descendents 00364 it would be a mistake to propogate to a child before all of its 00365 ancestors were taken care of. 00366 00367 *******************************************************************************/ 00368 00369 class Hierarchy 00370 { 00371 private char[] name, 00372 address; 00373 00374 private LoggerInstance root; 00375 private LoggerInstance[char[]] loggers; 00376 00377 /*********************************************************************** 00378 00379 Construct a hierarchy with the given name. 00380 00381 ***********************************************************************/ 00382 00383 this (char[] name) 00384 { 00385 this.name = name; 00386 this.address = "network"; 00387 00388 // insert a root node; the root has an empty name 00389 root = new LoggerInstance (this, ""); 00390 } 00391 00392 /********************************************************************** 00393 00394 Return the name of this Hierarchy 00395 00396 **********************************************************************/ 00397 00398 char[] getName () 00399 { 00400 return name; 00401 } 00402 00403 /********************************************************************** 00404 00405 Return the address of this Hierarchy. This is typically 00406 attached when sending events to remote monitors. 00407 00408 **********************************************************************/ 00409 00410 char[] getAddress () 00411 { 00412 return address; 00413 } 00414 00415 /********************************************************************** 00416 00417 Set the name of this Hierarchy 00418 00419 **********************************************************************/ 00420 00421 void setName (char[] name) 00422 { 00423 this.name = name; 00424 } 00425 00426 /********************************************************************** 00427 00428 Set the address of this Hierarchy. The address is attached 00429 used when sending events to remote monitors. 00430 00431 **********************************************************************/ 00432 00433 void setAddress (char[] address) 00434 { 00435 this.address = address; 00436 } 00437 00438 /*********************************************************************** 00439 00440 Return the root node. 00441 00442 ***********************************************************************/ 00443 00444 LoggerInstance getRootLogger () 00445 { 00446 return root; 00447 } 00448 00449 /*********************************************************************** 00450 00451 Return the instance of a Logger with the provided name. If 00452 the instance does not exist, it is created at this time. 00453 00454 ***********************************************************************/ 00455 00456 synchronized LoggerInstance getLogger (char[] name) 00457 { 00458 name ~= "."; 00459 00460 LoggerInstance l = loggers [name]; 00461 00462 if (l is null) 00463 { 00464 // create a new logger 00465 l = new LoggerInstance (this, name); 00466 00467 // insert into linked list 00468 insertLogger (l); 00469 00470 // look for and adjust children 00471 updateLoggers (l, true); 00472 00473 // insert into map 00474 loggers [name] = l; 00475 } 00476 00477 return l; 00478 } 00479 00480 /********************************************************************** 00481 00482 Iterate over all Loggers in list 00483 00484 **********************************************************************/ 00485 00486 int opApply (int delegate(inout Logger) dg) 00487 { 00488 int result = 0; 00489 LoggerInstance curr = root; 00490 00491 while (curr) 00492 { 00493 // BUG: this uncovers a cast() issue in the 'inout' delegation 00494 Logger l = curr; 00495 if ((result = dg (l)) != 0) 00496 break; 00497 curr = curr.next; 00498 } 00499 return result; 00500 } 00501 00502 /*********************************************************************** 00503 00504 Loggers are maintained in a sorted linked-list. The order 00505 is maintained such that the shortest name is at the root, 00506 and the longest at the tail. 00507 00508 This is done so that updateLoggers() will always have a 00509 known environment to manipulate, making it much faster. 00510 00511 ***********************************************************************/ 00512 00513 private void insertLogger (LoggerInstance l) 00514 { 00515 LoggerInstance prev, 00516 curr = root; 00517 00518 while (curr) 00519 { 00520 // insert here if the new name is shorter 00521 if (l.name.length < curr.name.length) 00522 if (prev is null) 00523 throw new Exception ("invalid hierarchy"); 00524 else 00525 { 00526 l.next = prev.next; 00527 prev.next = l; 00528 return; 00529 } 00530 else 00531 // find best match for parent of new entry 00532 propogate (l, curr, true); 00533 00534 // remember where insertion point should be 00535 prev = curr; 00536 curr = curr.next; 00537 } 00538 00539 // add to tail 00540 prev.next = l; 00541 } 00542 00543 /*********************************************************************** 00544 00545 Propogate hierarchical changes across known loggers. 00546 This includes changes in the hierarchy itself, and to 00547 the various settings of child loggers with respect to 00548 their parent(s). 00549 00550 ***********************************************************************/ 00551 00552 private void updateLoggers (LoggerInstance changed, bool force) 00553 { 00554 LoggerInstance logger = root; 00555 00556 // scan all loggers 00557 while (logger) 00558 { 00559 propogate (logger, changed, force); 00560 00561 // try next entry 00562 logger = logger.next; 00563 } 00564 } 00565 00566 /*********************************************************************** 00567 00568 Propogate changes in the hierarchy downward to child Loggers. 00569 Note that while 'parent' and 'breakpoint' are always forced 00570 to update, the update of 'level' is selectable. 00571 00572 ***********************************************************************/ 00573 00574 private void propogate (LoggerInstance logger, LoggerInstance changed, bool force) 00575 { 00576 // is the changed instance a better match for our parent? 00577 if (logger.isCloserAncestor (changed)) 00578 { 00579 // update parent (might actually be current parent) 00580 logger.parent = changed; 00581 00582 // if we don't have an explicit level set, inherit it 00583 if (logger.level == Logger.Level.None || force) 00584 logger.level = changed.level; 00585 00586 // always force breakpoints to follow parent settings 00587 logger.breakpoint = changed.breakpoint; 00588 } 00589 } 00590 } 00591 00592 00593