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

Generated on Tue Jan 25 21:18:21 2005 for Mango by doxygen 1.3.6