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