Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

Hierarchy.d

Go to the documentation of this file.
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 

Generated on Sun Nov 7 19:06:51 2004 for Mango by doxygen 1.3.6