-----------------------------
			-----------------------------
			The ZOOM ErrorLogger Package
			-----------------------------
			-----------------------------

                Feature Guide and Design notes version 8        6/18/99

The following syntaxes and semantics are based on:

* Original design discussions including M. FIschler, J. Linnemann, L. Sexton, 
  and J. Kowalkowski, in Spring 1998.

* Realizations occurring during implementation of features, Fall 1998.

* Enhancements responding to user input and correction of flaws found
  during feature coverage testing, SUmmer 1999.

* Enhancements responding to user experience and further requests, 
  Summer 2000.

As of 9/00 the package was fully implemented.  

The intent is that neither the physicist NOR THE FRAMEWORKER should
need to change any code as the features get implemented, and this has been 
met in the enhancement cycle.  In the original intention, the goal was that
none of the headers these users would include were expected to change further,
so user code should not be forced to recompile as more ErrorLogger features 
were enabled.  In some cases an enhancement meant coding a new user-level 
feature was omitted -- there of course the header involved has to change.

Various portions of these design notes are suitable for extraction as
users guides:  The users guide for physicists would consist of the "How
the Physicist Logs Errors" sections.  The guide for frameworkers would
be everything up to the "Customization Hooks" chapter.

Change Log:

4/7/99	MF	Section 10: Additional constructor for ErrorLog("packageName").

4/7/99	MF	Section 11: Warning about not supplying an ELcout as a stream
			    for a destination.

6/18/99 MF	Section 8:  ELseverityLevel constructed from string.

6/18/99 MF	Section 18: Clarification that if range is entered with start
			    higher than end, nothing happens.

6/18/99 MF	Section 5:  \n in items, and the nobr manipulator.

6/18/99 MF	Section 13: Clarification that if no threshold is set, it is
			    by default ELzeroSeverity.

6/18/99 MF	Section 24: newlineAfterItems and newlineBeforeTime.

7/2/99  jv      Section 9:  automatic attachment of a destination if there is
                            none attached by the first log.

7/2/99  jv      Section 13: Default threshold for ELdestControl is 
	                    ELzeroSeverity not ELwarning.

7/2/99  jv      Section 15: Function that suppresses termination summary

7/2/99  jv	Section 24: separateTime, separateEpilogue and includeSerial
			    functions added

7/2/99  jv	Note 10:    Removed the info about ELcout default constructor

9/22/00 mf		    Removed all references to ELcout, other than 
			    leaving note 10 for historical purposes.

9/22/00 mf	fundament.  Replaced early mention of ELsaveBuffer with the 
			    discussion of the ELcollected and ELerrorList
			    classes which obviate it.

9/22/00	mf	section 9:  Replaced ELsaveBuffer with a discussion of 
			    ELcollected and ELerrorList.

9/22/00	mf	section 13: Added section 13.5:  Filtering by Module.

9/22/00	mf	section 21: ELsaveBuffer Local Semantics becomes 
			    ELErrorList Local Semantics, which are much simpler.

9/22/00	mf	section 26: ELsaveBuffer Semantics and layout is removed.

9/22/00	mf	The following features were added after user experience, and
		are documented on the web pages.  We have not modified these
		design notes to reflect these new capabilities:

		(1) Filtering by module
		(2) Collective error logging via ELcollected
		(3) The ELerrorList destination (obviating ELsaveBuffer)

11/1/00 mf	Mods reflecting the above capabilities.

1/15/01 mf	Another new capability: per-destination setLineLength().

2/13/01 mf	Another new capability: squelching "ErrorLog Established" 

3/15/01	mf	Three new capabilities:  
		(1) statisticsMap
		(2) discardThreshold
		(3) hex output

4/5/01	mf	Enhancement of filerModule capabilities: ignoreModule,
		respondToModule.

10/19/01 mf	Section 5:  Eliminate the nobr manipulator.

2/11/03  mf	Section 9: getELdestControl

2/11/03	mf	Section 13.8 and 2.5: Establishing Debug Verbosity Levels 

2/11/03 mf	Section 5: Message format contains time zone.

6/23/03 mf	Section 7: moduleName() and subroutineName()

6/24/03 mf	Section 23.8: changing ostream for an ELoutput


				Contents
				--------

Fundamentals
  Purpose and Scope
  Design Considerations
  Classes and Concepts

ErrorLogger Semantics and Syntax
  How the Physicist Logs Errors
    1) How the Physicist has access to the logger
    2) How a physicist issues a log message
    2.5) Messages for Debugging
    3) How a physicist can issue a log message in multiple statements:
    4) How an instance of ErrorObj may be formed
    5) What will the error message output look like
    6) What summary information will be available
    7) How the physicist can indicate what is being done
    8) A list of the available severity levels
  Frameworker's Basics of Setting up and Using Error Loggers
    9) How the frameworker sets up for logging
    10) How the Module (or Package) Sets Up errlog
    11) Global ErrorLog Instantiation
    12) ELcontextSupplier: How the run and event numbers are indicated:
    13) Basics of Thresholds and Limits
    13.5) Filtering by Module or multiple modules
    13.7) DiscardThreshold    
    13.8) DebugVerbosityLevel
    14) Controlling abort behavior
    15) Obtaining error statistics summaries
    16) Clearing statistics and/or limits
  Further Options for the Frameworker
    17) Checking on errors
    18) Error counts
    19) Traces and control over whether traces are logged
    20) Time span in conjunction with limits
    21) ELerrorList Local Semantics
    22) Accessing Saved Error Messages
    23) How ELstatistics information is kept
    23.5) Access to the statistics map
    24) Formatting control available in ELoutput
    24.5) Hex output formatting via ErrorLog
  Customization Hooks and Issues Relating to Collective Logging
    25) What an ELdestination Must Do
    26) (removed)
    27) Avoiding lists, strings, streams, and templates
    28) Table Limitations
    29) Direct logging to one destination
    30) Custom Severity Levels?

Example of use of the package

Technical Design
  Design Patterns and C++ Techniques Used
  Rejected Alternatives
  Minutiae and Notes
  Known Concerns

Stubs and intended implementation order

		     ******************
		     *	Fundamentals  *
		     ******************

			  Purpose and Scope
			  -----------------

The purpose of this package is:

  1) to let user code issue ("log") error and warning messages;

  2) to provide a uniform syntax and set of concepts for logging,
     across the different experiments and different groups in an
     experiment;

  3) to provide a uniform and sensible logger behavior, in terms of
     information output and formatting;

  4) to provide means of controlling multiple log destinations, output
     limitations, and other behavior;

  5) to allow for (to the greatest reasonable extent) customizations
     tailoring to various environmental and system consideration.

Equally important is the time scale for achieving this:

  6) Items 1, 2 and a basic semblance of behavior 3 should be in place
     within a few weeks, so that physicists can begin inserting error
     logging into their code, without concern that those lines will
     have to change later.

  7) At that point of release, all header files defining the interface,
     which would be included either by physicist code, module base
     classes, or the framework, should be frozen.  This avoids the
     burden of extra compilations of user code, and guarantees
     stability of the interface.

  8) The full behavior 3 and 4 should be in place within a month of
     that point.  This has no particular justification in terms of
     coding needs, but provides a criterion by which to limit
     complexity of the feature set.

  9) Tailoring for special environments, and other non-essential
     support work, may proceed after the package has been delivered.
     This support would include integrating traceback capabilities into
     the log information, and creation of ZMexception subclasses that
     log via this ErrorLogger mechanism.

						-- Purpose and Scope --

In terms of concepts explained below, a "physicist" inserts logging
statements into the code.  These work because a "frameworker" has set
up the logger, specified one or more log destinations, and established
various behavior controls.  A standard form of behavior is performed
when errors occur or summaries are requested.

Ease and cost of use is critical:

  9) Overall, if only the standard features and and basic controls are
     used, the package ought to be very easy for the physicist to use,
     clear and simple for the frameworker to set up, and not a heavy
     burden in terms of code to compile or executable code pulled in.

A "customizer" may, by introducing derived classes, change that
behavior.  For instance, the formatting of the error text may be
modified beyond the ways the framework controls permit; or the
customization may avoid C++ features and system support assumptions
that may not be desired in a Level 2 context.

Having said what the package intends to do, we set some boundaries by
listing issues this package does not intend to address:

  A) Beyond providing clear ways to derive classes for customization,
     the issue of implementing these customizations is not addressed.

  B) This package addresses issues of "collective logging," that is, 
     coordination by some central entity of error log destinations 
     stemming from multiple processes, in the simplest possible manner: 
       1. The collection mechanism leaves the details of interprocess 
	  communication completely up to the experiment. 
       2. A way for code to access a history of error messages is provided 
	  in the form of a (flitered) std::list of error message objects. 
	  If this list is desired, the user code is responsible for 
          periodically examining and clearing it. 

			Design Considerations
			---------------------

In the text of this section, whenever a class which is part of this
package is mentioned for the first time, we will indicate this by
hyphens.  All classes are briefly described in the next section
("Classes and Concepts") and detailed later in this document.

Let us define a "physicist" as anyone coding "on top of" some framework
level, whose code may want to issue log messages.  And define a
"frameworker" as the person providing the means for the physicists to
do things (like error logging) WITHOUT extraneous knowledge of the
mechanism; the frameworker, in a sense, controls the flow of the job.

The structure of the framework we have in mind is that there is a
collection of classes, which for the purpose of this document we shall
refer to as "modules" (D0 calls these packages).  Each module is a
class derived from a standard Module class; and the overall framework
has some way of registering all the modules and seeing that some key
method of each one is invoked when appropriate.  Anything in the
framework above the level of modules, or in the Module base class
itself, we will say is in the realm of the frameworker; anything in
methods of the derived specific modules is said to be physicist code.

The fundamental idea is that the framework instantiates an
--ELadministrator-- object, and through its methods attaches various
forms of sinks derived from --ELdestination--.  Then the base Module
class will have an instance of --ErrorLog-- as a protected (or public)
variable; for illustrations in this document we assume that is assigned
the name "errlog."  Since this is a variable at module scope, all
methods of the specific module class can use errlog, but methods of
different modules will be using different ErrorLog instances; that is,
an ErrorLog can own information about the module.

The variable "errlog" makes the logger is made available to the user,
who can issue errors in a syntax like

   errlog (ELerror, "Too much energy") << "E = " << totalEnergy << endmsg;

The frameworker can control aspects of the behavior of the logger and
the individual destinations, such as limits on how may times a given
message is to be output.

Those fundamentals, combined with the details of what aspects of
behavior can be controlled, would dictate the (simplest) design of the
package, were it not for the requirement that customization be
accommodated.  Three possible customizations we have in mind are:

  1) It should be possible to link in error logging without requiring
     true streams and STL streams, and without "post-initialization"
     use of memory allocation on the heap.  (But this should not stop
     the normal form of the package from using these useful concepts!)

  2) It should be possible to derive from the ELdestination class in
     such a way that things like centralized log collection can be
     implemented.

  3) To the farthest extent practical (BUT NO FURTHER), the design
     should allow for customization of such behaviors as message
     formatting, save buffering, and so forth.

It turns out that the same design philosophy helps with both of these.
In areas where flexibility is wanted, we define classes that
encapsulate the use and behavior of each concept, and derive from these
classes that implement our intended behaviors.  For example,
ELdestination defines what we mean by a destination object, while
--ELoutput-- and --ELerrorList-- are two specific classes derived from
that base.  The encapsulation classes will carefully specify:

  * Which methods must be implemented with behavior agreeing with our
    expectations;

  * Which methods must be present but may be "ignored" when invoked, or
    may safely be implemented with very different behavior than what we
    had in mind;

  * Guarantees concerning the ways the package will use the classes.

Also, in doing our implementations, we will try to isolate behaviors
that we anticipate a customizer may want to modify, such as the use of
std::strings, from the bulk of the code, which the customizer can
likely make use of.

			Classes and Concepts
			--------------------

Each of these is discussed in the Syntax and Semantics discussion but
here I wish to enumerate them to orient the reader:

ELadministrator         Object providing methods for overall control of
			the error logger.  This class uses the
			"Singleton" pattern.  The framework should
			instance() one ELadministrator, and through it,
			attach various ELdestination sinks for log
			message to flow to.
  ELadministratorX

ErrorLog                Encapsulation of the fundamental behavior
			supporting issuing of messages.
  ErrorLogX             Fundamental object supporting issuing of
			messages to the error logger.  The ErrorLog
			class provided will use templating to allow the
			physicist to use operator<< with arbitrary
			classes; a different derived class might avoid
			templates and be more restrictive

ELdestination           Encapsulation of the behavior of a "sink" for
			the logged information to go to.
  ELdestinationX

  Derived from ELdestination:

  ELoutput              A specific class implementing our agreed
			behavior and formatting for sending messages to
			an ostream (which could be cout, cerr, or an
			ofstream for a file).

  ELcollected		A destination derived from ELouput, which invokes a 
			user-supplied transport mechanism to send the formatted
         		message which ELoutput would have streamed, to a central 
			message logger. 

  ELerrorList         	A destination derived from ELouput, which inserts each 
			ErrorObj onto a std::list. User code can use methods of
	         	std::list to access the objects and/or clear the list, 
			and can use methods of ErrorObj to get information about
			each message. 

  ELstatistics          A specific class, implementing storage and
			output of message frequency statistics and
			summary tables.

ELdestControl           An object to act as a proxy for an
			ELdestination which is attached to the logger,
			faithfully providing the full spectrum of
			public methods available from the destination
			object.
  ELdestControlX

ELseverityLevel         A class allowing for the definition of a fixed
			set of severity levels.  The package provides
			the levels.  Each severity level is a an
			instance of this class, and contains methods
			delivering its unique level number, symbol, and
			name.

						-- Classes and Concepts --

ErrorObj                An error message object.  While the ErrorObj
			class provided uses templating to allow
			operator<< with arbitrary classes, a custom
			substitute might provide operator<< only from
			an ELstring.
  ErrorObjX             Provides implementation and non-interface
			material so that ErrorObj may be frozen at an
			early stage.

ELcontextSupplier       An interface for an object providing three
			methods (which can be equivalent) to obtain
			info about run, event, and other framework-wide
			concepts.

ELstring                Because quite a few methods throughout this
			package work with strings of characters, it is
			very useful to have a data type with the
			semantics of std::string.  To allow for
			substitution of a different class, the
			interfaces and use ELstring instead; our
			provided classes will typedef this to
			std::string.

ELlimitTable            Class implementing the logic and table storage
			needed to allow a destination to decide whether
			or not to ignore an error based on having seen
			too many of that type of error.

ELextendedID            The combination of id, severity, process,
			module, subroutine.  This defines one type of
			message, for the purposes of summary
			statistics.  It does NOT include additional
			text, timestamp, or context.

	      ****************************************
	      *	The ErrorLogger Syntax and Semantics *
	      ****************************************

		    How the Physicist Logs Errors
		    -----------------------------

1) How the Physicist has access to the logger
---------------------------------------------

   Both experiments have the concept of an organizational step in the
   course of processing, under control of some responsible person or
   group.  One experiment calls this a "package," another a "module";
   but in both cases it is implemented in code as a set of structural
   rules that the framework requires its various major components to
   follow.  In particular, there is an object encapsulating each entire
   package (or module); and this is derived from some
   framework-provided base class which we will call Module.

   Each module has an object at scope of the Module class, which for
   illustration sake, we will name "errlog."  The question of what the
   writer of the base Module class must do to establish this log object
   is answered below in "How the Module Sets Up errlog."

   So the physicist working in a methods of that module sees "errlog"
   as an instance of --ErrorLog--.  A physicist coding for a different
   module will see some other instance of ErrorLog (with the same name
   "errlog" if things are set up as we recommend).  But these are thin
   shells attached to a single instance of an --ELadministrator--
   object that represents the actual logger.

   A logger can be associated with one or more destinations.  Each
   destination represents a "sink" for error information; it is a class
   with methods to accept and deal with error message information, and
   to allow the frameworker to control various behavior aspects.  In
   any event, the frameworker has established the logger and set up the
   destinations (possibly driven by information from a job control
   file).  The physicist logging errors need not be concerned with the
   destinations which have been set up.

   To get the headers allowing the use of ErrorLog, the compilation
   unit has to include one header:

	#include "ErrorLogger/ErrorLog.h".

   However, since the physicist code is going to be part of a class
   that is derived from a Module base class, and the Module base sets
   up errlog, that will already have included the necessary
   ErrorLog.h.  So in normal use the physicist does not have to include
   that line; nothing extra need be included to get access to errlog.

   Also, since this product is namespace protected, the user must
   specify that namespace zmel is in general use:

	using namespace zmel;

   ZOOM provides a mechanism to use if you wish the use of namespaces
   to be disabled/enabled depending on a define.  Instead of "using
   namespace zmel":

	ZM_USING_NAMESPACE( zmel )  /* using namespace zmel; */

					    -- How the Physicist Logs Errors --

2) How a physicist issues a log message:
----------------------------------------

   errlog (ELerror, "Too much energy") << "E = " << totalEnergy << endmsg;
     a       b            c                 d            e           f

   Here is the breakdown of this syntax:

   a)   The ErrorLog object has a () method which returns something you
	can treat like an ostream, that is, you can do << to it.  It
	takes two mandatory arguments -- a severity and a message ID.
	The presence of these arguments has the effect of saying that
	this is the start of an error message.

   b)   ELerror is one of the ErrorLogger severity levels.  They all
	start with EL (they are listed below).  They are all instances
	(provided by the ErrorLogger package) of the class
	--ELseverityLevel--.  Invoking this operator() of the ErrorLog
	object has the effect of starting a new error message, and
	establishing the severity of that error.

   c)   The second argument is a string (or char*), and determines the
	message identifier (ID).  Although the entire content of the
	string will go into the output text, the message ID is
	considered for statistics and limits purposes to be the leading
	20 characters of this string, padded with trailing blanks.

   d)   There follow an arbitrary number of further outputs to the
	message.  These will together form an informational string,
	which most destinations will print after the ID, with suitable
	line-break insertions.  These further outputs are optional.

   e)   Notice that the further arguments need not be strings (although
	you could, if you wish, format them all up into one string by
	using a stringstream).  In the example, the double totalEnergy
	is put to the stream.  Any data type or class that could be
	streamed to cout can be sent to the log message in this way.

   f)   The endmsg at the end of the message is treated specially, and
	indicates that the message is over.  Only one endmsg should be
	placed in a message; the user is free to insert explicit "\n"
	characters and/or endl should control of multiple lines be
	desired.

	See Note 1 about endmsg.

   An alternative to doing log (sev, id) is to use the provided ERRLOG
   macro:

	ERRLOG ( sev, id )				equivalent to
	errlog ( sev, id ) << __FILE__ <<":" << __LINE__ << " "

   This assumes that the ErrorLog available is named errlog; a second
   macro is

	ERRLOGTO ( logname, sev, id )			equivalent to
	logname ( sev, id ) << __FILE__ <<":" << __LINE__ << " "



					    -- How the Physicist Logs Errors --
2.5) Messages for Debugging:
----------------------------

An alternative way to issue a message is to provide to errlog(), instead of a 
severity and id, a single integer representing a debugging level.  

	errlog (3) << "Some key information" << endmsg;

If the framework for the job has set the debug verbosity level to this value 
or higher, then the message will emerge.  But if the verbosity level is lower 
than the integer provided -- and by default if the framework does nothing the
level is considered to be zero -- then the message will be ignored with little
runtime overhead cost.


					    -- How the Physicist Logs Errors --

3) How a physicist can issue a log message in multiple stamements:
------------------------------------------------------------------

   errlog (ELerror, " "Too much energy") << "E = " << totalEnergy;
   if ( condition ) {
     errlog << "more stuff";
   }
   errlog << "yet more stuff" << endmsg;
      g				   h


   g)   The ErrorLog object also has a direct << method.  This streams
	more into its message string, without declaring a new error or
	establishing a severity level.  Thus you may continue to build
	up the message, by having repeated lines which do not use
	endmsg until the last one.

   h)   Although key error information (id, severity, timestamp,
	context, and so forth) is captured immediately when the log ()
	method is done, the --ErrorObj-- object remains "open" to
	additional data until either the endmsg is encountered, or some
	other invocation of errlog () occurs.

    If the physicist forgets to start a new message with log (severity,
    id) and instead then does errlog << stuff, then assuming the
    previous message was closed off with an endmsg (or this is the
    first one for this log), a new message will be opened.  The
    severity assigned to such a message will be ELunspecified.

    An error message is dispatched to each of the potential logging
    destinations when it is closed.  If upon termination there is still
    an active message being issued, the destinations and
    ELadministrator will terminate it, thus sending it to each
    destination, and preventing information from being lost.  See note
    12:  "Termination issues," for an outline of the technical steps
    needed to accomplish this.

					    -- How the Physicist Logs Errors --

4) How an instance of ErrorObj may be formed independently
----------------------------------------------------------

   An --ErrorObj-- is an object representing an error message.

   Although normally neither the physicist nor the frameworker works
   with an ErrorObj directly, one can form such an object and later
   send it to a log.  This might be done, for example, if you want to
   build up some history of what was done, but only log it if some
   ghastly condition later occurs.

	ErrorObj myMsg ( ELwarning, "Suspicious Pt" );
		   j        k             l

	errlog (myMsg);
                  m

	myMsg = ErrorObj ( ELsevere, "Out of space" );
	myMsg << "was doing step" << 20 ;
                      n               p

	errlog (myMsg);

   j)   You can instantiate some pre-defined ErrorObj and, when and if
	an error happens, use it.

   k,l) The constructor for ErrorObj takes the two mandatory fields:
	severity and id.

   m)   We support sending one of these formed messages to the log.

   n)   ErrorObj has operator<< so you can pump further info into it,
	just as in the case of pumping more into the log.

   p)   Notice in both this and the previous example we did not put
	endmsg.  endmsg is inappropriate in ErrorObj's, and ought not
	to compile.  When an ErrorObj is supplied to a log (as in
	errlog(myMsg)) this is known to be complete and the implied
	endmsg is supplied automatically.

					    -- How the Physicist Logs Errors --

5) What will the error message output look like:
------------------------------------------------

   Although each possible destination may do different things with an
   error message, the ErrorLogger package supplies a standard
   --ELoutput-- destination which will output (to a stream or file) as
   follows:

%ERLOG-w  Too much energy: E = 834.750032 d0L2proc5 CTCDRVmodule CTCTRKsubr
	  10-Jul-1999 14:49:03 CST run=234 event=543

   This is the default formatting; if some portion of the message would
   overrun the end of an 80-column line, it will instead be started on
   the next line, indenting to align with the first character of the
   id.

   A space is inserted to separate each item (each object put to the log via 
   the << operator) from the next.  The user can force line breaks by 
   putting a \n as the first or last character in an item.  (If \n appears
   in the middle of an item, indentation will not be done and the column 
   formatter may introduce an unneeded new line when it thinks 80 columns 
   would be exceeded.)

The following nobr feature has been eliminated fromthe requirements because 
nobody was feeling its lack and implementation would be a fair amount of work:
----
   The user can prevent the formatter from inserting a line break between 
   two items by using the nobr manipulator: 

	errlog << item1 << nobr << item2 << item3;

   In the above example, item1 and item2 will appear on the same line, even
   if that would extend past column 80.  Each nobr applies only once:  item3 
   could be placed on the next line.
----

   The frameworker can control some aspects of the format produced by
   ELoutput (discussed below). A custom ELdestination can make more
   complex format changes.


6) What summary information will be available
---------------------------------------------

There will be a method (normally invoked by the framework at the end of
a job or run) to deliver a summary, either to one or more of the log
destinations or to another ostream or file.  The summary information
will be a table by message type (message type means the combination of
ID, process, module, subroutine, and severity) of count.  If there were
any occurrences of a message that were not logged anywhere due to
limits, that is indicated by an asterisk.

A second part supplies up to 3 example contexts for each message type:
The first two occurrences, and the last one.

In a third part there will be information about total counts at each
severity level.  Counts are given since the last clear, and also a
total for the whole job.

The responsibility of triggering the output of summary information
belongs to the frameworker rather than the individual physicists.  An
illustration of the format of the summary information is given in the
section on "Obtaining error statistics summaries."

					    -- How the Physicist Logs Errors --

7) How the physicist can indicate what is being done:
-----------------------------------------------------

   The physicist may OPTIONALLY declare the name of the subroutine
   currently executing.  When an error is logged, this name will go
   into the message, and also into the overall statistics.  The user
   will generally only declare fairly large steps, to avoid some
   overhead.

	errlog.setSubroutine( "myName" );

   An alternative is available:  If after the ID string, an item
   is a string of the form "@SUB=myname", then myname is treated as the
   subroutine name, regardless of what setSubroutine() has set up.

	errlog ( ELsevere, "Bank Confusion" ) << "@SUB=prepare_IO" << endmsg;

   In most frameworks, the framework will automatically set up the module
   (package) and in some frameworks, the framework may automatically
   set the subroutine at key points.  The physicist can find out the name 
   of the module and/or subroutine:
   
        std::string modname = errlog.moduleName();
        std::string subname = errlog.subroutineName();
	
8) A list of the available severity levels:
-------------------------------------------

   Each severity level has a corresponding ELseverityLevel object, for
   example, ELwarning.  Each such object has two relevant behaviors:

    a) Methods getSymbol() and getName() to get its symbol and full name
       respectively; the destinations will use these methods to prepare
       text.

    b) There is a comparison to ask whether one ErrorSeverity is more
       severe than another.  So the concept of logging everything above a
       certain severity is meaningful.

   The ErrorSeverity objects -- instantiated globally by the package -- are:

  Severity object	Symbol	Full name	Intention
  ---------------	------	---------	---------
  ELzeroSeverity	--	--		used for thresholds only
  ELincidental		..	..		flash this on a screen
  ELsuccess		-!	Success		report reaching a milestone
  ELinfo		-i      Info		information
  ELwarning		-w	Warning		warning
  ELwarning2		-W	Warning!	more serious warning
  ELerror		-e	Error		error detected
  ELerror2		-E	Error!		more serious error
  ELnextEvent		-n	Next		advise to skip to next event
  ELunspecified		??	??		severity was not specified
  ELsevere		-s	Severe		future results are suspect
  ELsevere2		-S	Severe!		more severe
  ELabort		-A	Abort!		suggest aborting
  ELfatal		-F	Fatal!		strongly suggest aborting!
  ELhighestSeverity	!!	!!		used for thresholds only

   The frameworker can control whether declaring severe, abort, or
   fatal errors actually will abort the job.  The intentions listed for
   all the error types are only advisory; it is up to the framework to
   do what the experiment intends.  If no abort threshold is set, then
   even ELfatal errors will not automatically abort the job.

   ELzeroSeverity and ELhighestSeverity are not supposed to be used in
   forming error messages but ae available to the frameworker when
   setting up various thresholds.

   An ELseverityLevel may be constructed from a string (or char*).  This 
   allows a user or framework to accept run-time input specifying what 
   severity to assign to a given possible error.  

	ELstring input;	cin >> input;
	ELseverityLevel mysev (input);
	// ...
	errlog (mysev, "this error");

   The options accepted for a given level are its symbol, its full name, 
   the name of its severity object -- these are as listed in the above table --
   and all-caps names:
	"ZERO"  	"INCIDENTAL"   	"SUCCESS"   	"INFO"      
	"WARNING"   	"WARNING2"  	"ERROR"     	"ERROR2"    
	"NEXT"      	"UNSPECIFIED" 	"SEVERE"    	"SEVERE2"   
	"ABORT"     	"FATAL"     	"HIGHEST"   

   The translation used is case-sensitive.  If no match is found, then
   ELunspecified will be used.

	Frameworker's Basics of Setting up and Using Error Loggers
	----------------------------------------------------------

9) How the frameworker sets up for logging
-------------------------------------------

   The ErrorLog objects that modules set up and that users see require
   that an ELadministrator exist.  This encapsulates all the framework
   control over how logging is done; in particular, the ELadministrator
   holds the information as to what destinations should be used, and
   the way to get context information when needed.

   The framework, outside the context of any ordinary module, should
   get an instance by invoking the ELadministrator::instance() method.
   It will then use methods of this to establish how to get context
   information, and to attach various ELdestination sinks.

   So first the framework instantiates the ELadministrator:

	#include "ErrorLogger/ELadministrator.h"
	ZM_USING_NAMESPACE( zmel ) /* using zmel; */

	ELadministrator * logger = ELadministrator::instance();

   Notice that the Singleton pattern returns a pointer to the single
   instance.

   Next the framework provides the ELcontextSupplier.  This is done by
   creating a class derived from ELcontextSupplier and overriding the
   pure virtual methods such as context().  An instance of this class
   is passed to logger.setContextSupplier().

	MyContextObj contx;  // See below for how the derived context
			     // supplier looks.
	logger->setContextSupplier (contx);

   Providing a context supplier is optional; it provides a way for the
   logger to get at the run and event numbers when an error is logged.
   If no supplier is provided, no run/event information will be
   associated with messages.  Further details are given below in
   "ELcontextSupplier: How the run and event numbers are indicated."

   The frameworker may also declare the name of the overall process (or
   job, or node in a farm situation).  When an error is logged, this
   name will go into the message.  This could be used to distinguish
   among many cooperating nodes which share the same output for
   destinations.

	logger->setProcess( "PCfarm81" );

						   -- Frameworker's Basics --

   Having instantiated logger and told it how to get context
   information, the framework must attach one or more destinations --
   sinks for the messages and statistics to be sent to.  An error
   logger with no sinks is probably useless.

   The logged information can go to each destination attached to the
   logger.  But not every message will be acted on by every
   destination; each is subject to thresholds and limits, as discussed
   below in "Basics of Thresholds and Limits." Each destination must be
   derived from ELdestination.  Four classes derived from
   ELdestination are provided by the ErrorLogger package:

     --ELoutput-- is constructed taking an ostream, and is the typical
     way to associate a log with either cout, cerr, or an ofstream for
     a file.  It has two forms of constructors:

	ELoutput xx(ostream &);	// An ostream (e.g. cerr).
	ELoutput xx("xfile");	// A file name.
				// This is mostly a convenience, since
				// one could have supplied an ofstream.
				// See Note 9: "ELoutput (fileName)"
				// for details.

     --ELcollected-- may be constructed taking an object of class derived
     from ELsender, which implements the framework's chosed transport 
     mechanism.  A frameworker in a multi-procss job could put one 
     or more ELcollecteds on the list to route messages to one or more
     collection points. 

     --ELerrorList-- may be constructed taking a list of ErrorObj's.
     A frameworker could put an ELerrorList to be able to have code 
     periodically examine any ErrorObjs that were logged.

     --ELstatistics-- is a table of message frequencies and sample
     contexts, which contains methods for generating run summaries.  It
     may also have an associated ostream, to which unreported
     statistics would be sent when a job terminates.

   The usual framework will use ELstatistics and one or more ELoutput sinks.


   To attach a destination, the frameworker must instantiate it, and
   attach the destination to the ELadministrator.  Attaching the
   destination returns an --ELdestControl-- object, which is a handle
   for controlling the behavior of that destination.  If no destination 
   is attached to the ELadministrator then one is attached automatically
   to "cout" when the first error is logged.

	ELoutput      logfileD("myfile.log");
	ELdestControl logfile = logger->attach( logfileD );

   Later, the ELdestControl may be used to control the behavior of this
   destination, as in

	logfile.setLimit("*", 20);

   Sometimes it is desirable to modify the behavior of a destination, from
   code other than the method which attached that destination to the logger.
   In that case, it is likely that the ELdestControl handle will have gone
   out of scope.  To be able to recover this handle, there is an optional 
   second aregument to attach, meant to take the name of the destination.
   
	ELdestControl logfile = logger->attach( logfileD, "logfile" );

   Subsequently, code can recover a handle to logfileD:
  );

  	// in later code, where that logfile has been lost
  	ELdestControl logfile
 	bool handleFound;
  	handleFound = errlog.getELdestControl ("myLogFile", logfile);
  	if (!handleFound) { ... oops, wrong name ... }
  
   A bit about object ownership and such is given in note 5:
   "ELdestControl details."  The important thing to know is that all
   actions that apply to the ELdestination class can be invoked off the
   ELdestControl; it is a faithful proxy.  Such methods include setting
   limits, thresholds, and time spans, triggering summary output, and
   even directly sending an ErrorObj.

						   -- Frameworker's Basics --

   Sometimes, a framework may wish to permanently or temporarily "turn
   a destination off."  This can be done by

	logfile.setThreshold(ELhighestSeverity)

   A typical example of the entire setup code in a framework, with a
   log to a file, one to the screen, and a save buffer, might be (see
   note 8 about includes):

	ZM_USING_NAMESPACE( zmel ) /* using zmel; */

	ELadministrator * logger = ELadministrator::instance();

	class MyContextObj : public ELcontextSupplier {
	public:
        ZM_COVARIANT_TYPE(ELcontextSupplier *, myContextObj *) clone() const {
	  return  new myContextObj( *this );
        }
	  ELstring context () {
	    ostringstream ost;
	    ost << "run= " << getRun() << " event= " << getEvent();
	    return ELstring(ost.str());
	  }
	  ELstring summaryContext () {
	    ostringstream ost;
	    ost <<  getRun() << "/" << getEvent();
	    return ELstring(ost.str());
	  }
	  ELstring fullContext () { return context(); }
	}
	MyContextObj contx;
	logger->setContextSupplier (contx);

	ELdestControl logcerr  = logger->attach ( ELoutput(cerr)      );
	ELdestControl logfile  = logger->attach ( ELoutput("myFileName.log") );
	ELdestControl logstats = logger->attach ( ELstatistics()  );
	std::list<ErrorObj> theList;
	ELdestControl errbuf   = logger->attach ( ELerrorList(theList) );

        logfile.setThreshold(ELwarning).setLimit("*", 10);
	logcerr.setThreshold(ELinfo).setLimit("*",20);
	logstats.setThreshold(ELwarning);
	errbuf.setThreshold(ELerror);
	// See "Basics of Thresholds and Limits"

   Note that although many modules may instantiate many ErrorLog
   objects, only one instance of ELadministrator actually will be
   formed.  This is achieved by making ELadministrator a singleton
   pattern; see Note 4 about "ErrorLog and the Singleton
   ELadministrator" for details.

   In this example, the last two destinations attached were the
   ELstatistics and then the save buffer.  This is the recommended
   order.  See "How ELstatistics information is kept" for an
   explanation of why ELstatistics should come after the ordinary
   destinations.

						   -- Frameworker's Basics --

10) How the Module (or Package) Sets Up errlog
----------------------------------------------

   (Although in these design notes we use the CDF term "Module" for the
   base class that a package of physicists' codes is associated with,
   in D0 the same concept is called a Package.)

    The class for each module should allow its methods to log errors by
   containing a variable object of the type ErrorLog.  This should be
   done by giving the base class Module, which all modules classes
   derive from, an instance of ErrorLog.  The name of this object
   should be known to the various physicists coding parts of the
   module.  We assume that it will be "errlog."  This should be placed
   in the protected section, not private, so that methods of the
   derived actual module classes can make use of errlog.

   The ErrorLog instance will contain a small amount of module-specific
   information, and depend heavily on the ELadministrator set up by the
   framework.  For instance, the ELadministrator will hold the list of
   destinations for messages sent to the log.  Since there is
   inherently only one instance of ELadministrator, the many instances
   of ErrorLog will send to destinations in a coherent and unified
   way.

   After instantiating errlog, the Module class should provide a module
   name, to use when methods of this class issue an error message to
   the log.  (Module classes in the framework generally have a way to
   know their name.) Destinations provided in the ErrorLogger package
   utilize the first 16 characters of this name wherever a brief module
   identification is wanted.

   Thus in the Module base class, the lines setting up the ErrorLog
   could look something like:

	class Module {
	public:
	  Module(module_selection) {
	    ...
	    errlog.setModule( "moduleName" );
	  };
	protected:
	  ...
	  ErrorLog errlog;
	}

   (An additional routine, setPackage(name), is identical in operation
   to setModule(name).)

   If this setup is used, the classes derived from Module, which
   contain the physicists' code, need do nothing further to be able to
   issue messages to errlog.

   An alternative constructor is available which would let a package declare
   at global scope an ErrorLog with a given package name:

	myerrlog = ErrorLog("myPackageName");

   There will be only one ELadministrator for the whole process.

   The mechanism for seeing that the base class Module knows the name
   associated with the derived class that is causing it to be
   instantiated -- the mysterious module_selection in the above example
   -- is discussed in Note 7:  Module Name.

11) Global ErrorLog Instantiation
---------------------------------

   Here, we discuss how to have an error log for code which is not
   underneath a module.  We show how to do this whether (a) you would
   want to assign some universal "utility" module name to such a log,
   or (b) you want the module name to change depending on the actual
   Module that originated the call to that external "utility code."

   Also, we will show how to hook the cout and cerr used in the utility
   (or anywhere in your code) to the log, so that lines streamed to
   those will come out through the error logger.  This should be
   considered a measure to avoid having to scan through large chunks of
   code utilizing cout or cerr, rather than a general alternative to
   the error log mechanism.

(a)
   Illustrations in this document (other than this section) assume a
   structuring into Module classes, with each Module instantiating an
   ErrorLog.  However, if necessary, one can instantiate a log at
   global scope.  For programs not completely organized into Modules,
   this permits any piece of code to issue log messages.

   The penalty for working that way is that the mechanism for pulling
   in a different module identification string for each class where a
   log message might originate is bypassed:  Whatever module is
   indicated for the global ErrorLog will be there for any use of that
   log.

   Of course, the techniques can be combined.  If a framework involves
   several modules, plus universally available "utilities" that are
   shared among modules and are not encapsulated by a class of their
   own, the latter might use a global ErrorLog to allow them to issue
   error messages.

   (For programs involving multiple compilation units, each one can
   instantiate an ErrorLog, or sources which are not in the same
   compilation unit as the one that instantiates the ErrorLog could
   declare that as an extern.  Because of the underlying Singleton
   pattern for the ELadministrator, the effect is the same.)

	#include "ErrorLogger/ErrorLog.h"
	ErrorLog errlog;

	main()  {
	// The main framework
	...
	errlog.setModule ("Framework Level");
	errlog (ELsevere, "Event Sequence Bad") << "Events went from"
			<< prior_event << " to " << event" << endmsg;
	...
	}

						   -- Frameworker's Basics --
(b)
   The problem with using a global errlog as shown above can be seen
   from the following scenario:

   Module Tracker (with name "TRACKER") calls the utility chiSqFit
   which is at global scope (not a member function of Tracker).
   chiSqFit detects a problem, and having been instrumented for
   logging, does

	errlog (ELwarning, "bad fit") << ndegrees << endmsg;

   where now errlog is a global scope logger which we assume has been
   set up with errlog.setModule("Framework Level").

   This message will appear in the log as having come from module
   "Framework Level" rather than module "TRACKER."  The frameworker may
   well judge that the identity of the originating module is much more
   important to know, particularly since the utility does have the
   opportunity to identify the subroutine in which the problem
   occurred.

   The solution to this dilemma lies in explicitly doing a
   setModule(name) to the global errlog when entering a new module.
   (You could conceivably automate this, by doing ::errlog.setModule in
   the Module code before the key function is invoked; but for
   simplicity we will illustrate doing this explicitly.)  Thus:

	main()  {
	// The main framework
	...
	errlog.setModule ("Framework Level");
	errlog (ELsevere, "Event Sequence Bad") << "Events went from"
			<< prior_event << " to " << event" << endmsg;
	...
    errlog.setModule("FindTracks"); // In case of use of the global errlog
	FindTracks.doit(event);  // The FindTracks module.
    errlog.setModule("DoPhysics");  // In case of use of the global errlog
	DoPhysics.doit(event);   // The DoPhysics module.
	...
	}

   Notice that those setModule calls will not be necessary if all the
   error logging happens in places where the errlog in scope is that of
   the module; in that case, the automatic mechanism gets the proper
   module name with no extra concern for the frameworker.

						   -- Frameworker's Basics --

12) ELcontextSupplier: How the run and event numbers are indicated:
-------------------------------------------------------------------

   The run and event are printed in the text of every error message.
   However, the framework does not call some setEvent() method every
   time a new event is started.  In order to avoid overhead for every
   event (when errors might be logged in only a tiny fraction of those
   events), this information is not pre-supplied by the framework.

   Instead, when an error is being logged, the logger asks for the run
   and event names at that point.  It asks by invoking the context()
   function of the --ELcontextSupplier-- object set up for the logger.

   The frameworker should create a class derived from ELcontextSupplier
   and pass an instance of that class to the constructor of the
   ELadministrator, as shown above ("How the frameworker sets up for
   logging").  

   The context supplier is specified via the setContextSupplier()
   method of ELadministrator.  If this method is not invoked, no
   context information will be associated with error messages, no
   user function massage error objects will be invoked, and no trace
   information will be output.

   An ELcontextSupplier defines the following simple interface:

	class ELcontextSupplier {
	public:
	  virtual ELcontextSupplier* clone() const = 0;
	  virtual ELstring context () const = 0;
	  virtual ELstring summaryContext () const = 0;
	  virtual ELstring fullContext () const = 0;
	  virtual void editErrorObj(ErrorObj & msg) const;
	  virtual ELstring traceRoutine() const;
	}

   As seen from this, the class derived from ELcontextSupplier **must**
   define the clone() method (a trivial recipe for this is given below) and
   the three virtual methods returning ELstring, though some may of those
   be identical to others.

   Although destinations are free to call for and use whichever context
   string they wish, the intent of the three forms is as follows:

   context()            is the form used by the typical
			output-to-a-log-file destination, for example
			ELoutput.  Here the string ought not to be too
			long, to avoid clutter in the log; but no
			limitation or truncation is imposed per se.  A
			typical string would be "run= 1234 event=
			12345".

   summaryContext()     is the form used by ELstatistics to get a
			string suitable for insertion into a table.
			This is length-critical, and will be truncated
			at 16 characters in the statistics summary.  A
			typical string would be 1234/12345.

   fullContext()        is a form intended for use where extra info may
			be useful and length is not a big
			consideration.  ELoutput can enable
			fullContext() instead of context() on a
			per-destination basis.  fullContext() would
			typically just return context(), but another
			example might be "run= 1234 event= 12345 reco
			version 3.2"

   If a custom form of ELstring is used which may have a fixed length
   limit, the context supplier should protect against over-writing past
   the end of the available space.

   Another mandatory method is the clone() method.  This, however, is just 
   a trivial copy into new memory.  The following "mantra" for writing a
   context supplier can be followed:

class myContextSupplier : public ELcontextSupplier  {                  
public:
  myContextSupplier * clone() const;
  ELstring       context() const; 
  ELstring       fullContext() const; 
  ELstring       summaryContext() const; 
};  // myContextSupplier

myContextSupplier::clone() const  {return  new myContextSupplier( *this )}
ELstring myContextSupplier::context() const  {
  ostringstream ev;
  /* whatever code it takes to form context string in ev */}
  return ev.str();
}                                                                    
ELstring myContextSupplier::fullContext()    const  { return context(); }
ELstring myContextSupplier::summaryContext() const  { return context(); }
                   
   This mantra assumes that fullContext and summaryContext are the same as
   the context string; it should be obvious how to put different code there.

   Those familair with Visual C++ on NT will note that the abouve mantra 
   will not work, since clone() is declared to return an ELcontextSupplier*
   in the base class ELcontextSupplier*.  Standard C++, but not Microsoft,
   supports covariant return types; VC++ requires exactly matching return
   types for virtual functions.  For this purpose, ZMenvironment.h provides 
   a macro which resolves to ELcontextSupplier* on NT:

ZM_COVARIANT_TYPE(ELcontextSupplier *, myContextSupplier *) clone() const;

   We recommend this be used to promote portability to NT while keeping type
   safety wherevere possible.  However, none of the functionality of the 
   ErrorLogger package is hindered by the assumption (used on NT) that clone()
   returns an ELcontextSUpplier*.

   The remaining routines are optional and need not be supplied in the
   user-specific class derived from ELcontextSupplier:

   The editErrorObj() routine will be called when the message is
   started.  It provides a hook to modify the module or subroutine
   information, the id, or any part of the message other than the
   actual text items (which would not have been established yet).  This
   method is optional; the editErrorObj() method of the base
   ELcontextSupplier class is an inline method doing nothing.

   (The editErrorObj() routine also provides a backdoor by which the framework
   can cause a specific routine to be called every time an error message is
   started.  It will be called exactly once for each message instantiated,
   and the message id, module and subroutine, and severity will be available
   at that point.)

   The traceRoutine() routine will be called for each destination that
   is going to log a trace (see section (19) Traces and control over 
   whether traces are logged).  It must return an ELstring, containing the
   text to be appended as the trace.  This method is optional; if it is not
   present, the traceRoutine() method of the base ELcontextSupplier class 
   provides a trace as best the package knows how.  Currently, we don't know
   how to provide a trace, so the string returned will be empty.  This may
   change for some or all systems in the future.

   (Obviously, traceRoutine() is another possible hook for a framework to tie
   handling routines, but since it is called only for destinations with
   appropriate trace thresholds, and may be called more than once for a given
   message, it may be unwise to use it this way.)

						   -- Frameworker's Basics --

13) Basics of Thresholds and Limits
----------------------------------

   Some nomenclature:  Thresholds and limits both apply to particular
   destinations.  When we say "limit", we shall always mean some count
   of messages of a particular description, beyond which some action will
   cease to happen.  A limit can be specified by message id, or for multiple
   id's in two ways:  A general wild card, or all messages of some severity
   level.

   When we say "threshold", we shall always mean some ELseverityLevel, at or
   above which some action would happen.

   One can set a limit or a threshold for each individual ELdestination.
   To do this, you use methods of the associated ELdestControl, which we
   will call "dest" for these examples.   The effects are as follows:

	Method				Effect
	------				------

   dest->setThreshold (severity)
						Suppress logging or acting on
						messages below this severity,
						for this destination.
   dest->setLimit     (id, n)
   dest->setLimit     (severity, n)
   dest->setLimit     ("*", n)
						For this destination, don't
						log past n instances of any
						given exception id matching the
						specified type.

   In case one has established two or more applicable limits, the limit used
   is the most specific applicable case: Specified ID before specific severity
   level, and both before wild card "*".  (See Note 6: "Limit Semantics" for
   further details.)

   As implied by the above chart, each ELdestination owns a threshold level
   (if no threshold is explicitly set, the threshold is ELzeroSeverity).
   Each ELdestination also owns a general limits table (indexed by severity 
   level) and a limits table (indexed by message id).  Each limits table 
   entry contains as data the limit and the count.

   Notice that the ELerrorList and ELcollected are just particular destinations. 
   That implies that filtering by severity and throttling by frequency for 
   those are set up via setThreshold and setLimit, just as for other 
   destinations.

   Also notice that ELstatistics is also a particular destination.  That means
   that filtering by severity is set up via setThreshold.  In the case of an
   ELstatistics destination, however, setLimit has no effect:  Once an error
   message id gets into that table, there is absolutely no cost to incrementing
   its count, so a limit would be useless.

						   -- Frameworker's Basics --

   The typical framework code related to these routines might look like:

   stats.setThreshold ( ELerror );     // Don't keep stats on messages
				       //   with severity below ELerror.
   screen.setThreshold (ELincidental); // Send messages of severity ELincidental
				       //   and up to the screen.
   logfile.setThreshold (ELwarning);   // Don't send to logfile unless severity
				       //   is at least ELwarning.

   logfile.setLimit ( "*", 10 );       // Generally don't log past the 10th
				       //   occurrence of a given message id.
   logfile.setLimit("Bank short",50);  // But log up to 50 occurrences of
				       //   "Bank short" for the logfile.
   savebuf.setLimit( "ELwarning", 6 ); // Don't save more than 6 occurrences of
				       //   any given warning in save buffer.

   Although a limit conceptually may or may not have been set, and if no limit
   whatsoever applies, no messages will be throttled out, the threshold
   severity level always has a value.  By default, in each individual
   destination, this threshold starts as ELzeroSeverity.

   For convenience, the ELadministrator has setThresholds() and setLimits()
   methods.  The meaning of those is to invoke the corresponding method for
   EVERY destination currently attached to the logger.  (That might change
   some limits previously established.)

   logger->setThresholds (severity)
   logger->setLimits     (id, n)
   logger->setLimits     (severity, n)
   logger->setLimits     ("*", n)

   A limit can be made "infinite" by setting it to -1.

   Finally, two features slightly soften the concept of throttling via a limit.
   The first is that a count is past its limit, action does not cease
   completely:  If the applicable limit for a given error would be L, then all
   if there is an excess E = count-L, instances with E/L = 2**N for any
   non-negative integer N, will be logged.  Thus if the limit is 5, you will
   see numbers 1, 2, 3, 4, 5, 10, 15, 25, 45, 85, and so forth.

   The second concept applies when a large bunch of errors of some type
   is followed by a long period with no such errors.  At that point the count
   toward the limit may be reset, so that a new cluster of these errors can
   again be logged.  This is explained below in "Time spans on Limits."

13.5 Filtering by Module or multiple modules
-------------------------------------------

   Individual destinations can filter the messages they will react to by module.
   The typical framework code related to this routines might look like:

   logfile.filterModule("Tracking");   // Ignore messages except those from the
				       // Tracking module.

   The model is that there is a `respondToModule' list and an `ignoreModule'
   list.  At any given time, either the `respondToModule' list or the 
   `ignoreModule' list may be active, but not both.

   Then there are two ways to call each of two methods to influence these:

   dest.ignoreModule("*"); 
        Clear `respondToModule' list, and filter out all messages, except 
	those coming from any module that is later added to the 
	`respondToModule' list. 
   dest.respondToModule("name"); 
   	Add this module to the `respondToModule' list, and remove it (if 
	present) from the `ignoreModule' list.
   dest.ignoreModule("name"); 
   	Add this module to the `ignoreModule' list, and remove it (if present) 
	from the `respondToModule' list.
   dest.respondToModule("*") 
	Clear `ignoreModule' list, and respond to all messages, except those 
	coming from any module that is later added to the `ignoreModule' list. 

   Two other available methods are:

   dest.filterModule("a") has the same effect as 
         dest.ignoreModule("*"); 
         dest.respondToModule("a"); 
   dest.excludeModule("a") has the same effect as 
         dest.respondToModule("*"); 
         dest.ignoreModule("a"); 

13.7 DiscardThreshold
---------------------

   The action taken when a message is sent to an ErrorLog includes formatting,
   timestamping, and "shopping" the message to each destination.  Even if no
   destination reacts to the message, this can take a fair aomount of time.

   The frameworker (or for that matter any user with access to errlog) can
   short-ciruit this by doing

   errlog.setDiscardThreshold (sev)

   The meaning is that any messages sent through the usual mechanism of
   errlog ( severity, id ) << items << endmsg, which have severity less than
   sev, will be discarded with the absolute minimum possible work.  The 
   message will not be shopped to destinations, and each operator<< (until 
   another errlog(sev, id) is started) will simply return, doing no 
   work.  The net effect is a speedup of null-message processing by a factor 
   of 30.

   The discardThreshold works like other thresholds in that if a message
   severity is greater or equal to the threshold, it can be reacted to.
   The discardThreshold takes precedence over individual destination's
   thresholds.

   The default discardThreshold is ELzeroSeverity.



						   -- Frameworker's Basics --
14) Controlling abort behavior
------------------------------

   The higher severity levels are used by physicists to indicate that they
   consider this error so severe as to warrant terminating the job.  However,
   ultimate control of this rests in the hands of the framework.  The control
   is associated with the logger (the ELadministrator) rather than any specific
   destination.

	logger->setAbortThreshold (ELfatal);

   By default, the abort threshold is ELabort.

   If the physicist issues a log message with severity as at or above the
   abort threshold, UPON COMPLETION OF THE LOG MESSAGE and dispatch of that to
   the various destinations, the logger will terminate the job by invoking
   exit (severityLevel).  Assuming the framework had established an atexit()
   handler, this would be called as the job exits.

   A plausible philosophy is to set the abort threshold at ELhighestSeverity, so
   that NO user message aborts the job.  In that case, we advise periodically
   checking for the presence of severe errors (see "Checking on errors").

						   -- Frameworker's Basics --
15) Obtaining error statistics summaries
----------------------------------------

   Assuming the frameworker has attached an instance of the provided
   ELstatistics destination to the logger, statistics summaries may be
   obtained.  ELstatistics has no visible reaction to being sent an error
   message, but places information into a table, which can be formatted and
   output.  For the purposes of the illustrations below, we will assume that
   the logger has attached an ELstatistics destination, with an ELdestControl
   called "stats."

   The summary of error statistics can be obtained as a string.  It can also
   be sent to a destination device (as a series of summary lines); ELoutput
   places these lines into its associated stream to output the summary.  Other
   destinations may treat these lines differently; in particular, ELcollected
   and ELstatistics ignore them.

   To obtain the summary:

	stats->summary(dest); // Sends the summary to this ELdestination.

	stats->summary(os);   // Sends the whole summary string as a char*,
			      // to some arbitrary ostream os.

	stats->summary(s&);   // Sends the whole summary string to ELstring s.

   Except when the framework supplies a string& s, the summary will be sent
   using char* arguments, a single line (with a fixed maximum length) at a
   time.

   Each of the above forms also has an optional last argument, containing a
   char* to insert into the first line of the summary string.
	stats->summary(dest, "summary title");
	stats->summary(os,   "summary title");
	stats->summary(s&,   "summary title");

   ELoutput puts up 40 characters of this title in the first line of the
   summary output.  (Custom destinations should protect themselves against
   misbehaving if sent an arbitrarily long title.)

   There is an additional means of having the summary sent to an ostream:  If
   the constructor of ELstatistics is supplied an ostream argument, then when
   the job completes the destructor for that ELstatistics will check to see
   if any information has changed since the last summary request.  If so, one
   final summary will automatically be sent to that ostream.  (This is safe
   assuming the user does not explicitly delete the ostream before the
   statistics destination goes away.)  If Elstatistics was constructed without
   specifying this terination ostream, then it will default to use cerr as
   this "termination ostream."  

   To suppress the termination summary altogether:

	stats.noTerminationSummary();

   Not every message will make it to become a summary entry:  The frameworker
   can set a threshold for the ELstatistics destination, filtering out errors
   below the severity of below (say) ELerror.  Also, ELstatistics can be
   constructed with a maximum number of error IDs it will keep track of.

						   -- Frameworker's Basics --

   The format of the summary information is that of three parts:
   The first part lists the errors which have occurred, with their frequencies.
   The second part re-lists just the identification, and supplies the contexts
   (that is, the run/event or up to 17 characters of the context string) of the
   first two and last examples of occurrences of each error.
   The third part lists the count of occurrences of each severity level.

   To illustrate:

%ERLOG  ===============  SUMMARY  ===  summary title  ==========================

Process job123a
 type  message id         sev   module         subroutine        count    total
 ---- -------------------- - ----------------- ----------------  -----    -----
    1 Bank Error           E central tracker   refine_candidate     97       97
    2 Bank Error           E central tracker   prepare_sigma         5        5
    3 Bank Error           E muon tracker      prepare_sigma         5        5
    4 Energy overflow      S CALhadron         sanityChecks          1        1
    5 Too many tracks      w track matching    start_seeds          11*      11
    6 Too many stray trax  w CTKtrack          countTracks          32       32
    7 Too many stray trax  E CTKtrack          checkTracks           4        4

* Some occurrences of this message were suppressed in all logs, due to limits.

Process job123b
 type  message id         sev  module/package     subroutine     count    total
 ---- -------------------- - ----------------- ----------------  -----    -----
    8 Bank Error           E central tracker   refine_candidate     91       91
  ...
12345 abcdefghijklmnpqrst -w abcdefghijklmnop  abcdefghijklmnop 123456* 1234567

 type   message id   Examples:  run/evt         run/evt          run/evt
 ---- -------------------       -------         -------          -------
    1 Bank Error             14231/54101      14231/54103      14239/59350
    2 Bank Error             14231/54215      14232/55506      14238/58190
    3 Bank Error             14231/54200      14232/55606      14239/59195
    4 Energy overflow        14235/60305
    5 Too many tracks        14231/54101      14231/54506      14238/59190
    6 Too many stray trax    14231/54164      14231/54171      14239/59195
    7 Too many stray trax    14231/54100      14231/54191      14239/60012
    8 Bank Error             15282/64102      15282/64106      15288/69358
12345 abcdefghijklmnpqrst  ABCDEFGHIJKLMNOP  ABCDEFGHIJKLMNOP  ABCDEFGHIJKLMNOP

 Severity	Number of occurences	  Total
 --------	--------------------	---------
    ..			275984		  512902
  SUCCESS	         21000             42000
  INFO			  2100		    4200
  WARNING		   325		     604
  ERROR			     4		       6
  ERROR!		     1		       1
  SEVERE		     1		       1

   The last lines of the first parts in this illustration demonstrate the
   maximum number of characters each field will display.

   A customizer is free to alter this format by providing a custom destination
   similar to ELstatistics.

						   -- Frameworker's Basics --

16) Clearing statistics and/or limits
-------------------------------------

   The method to clear the counts in the statistics being kept in
   the destination whose ELdestControl is "stats" is

	stats->clearSummary();

   This clears both the individual statistics (kept by message ID) and the
   counts for the various severity levels.  It would normally be used after
   having invoked stats->summary(..) in some form to stream the information
   somewhere.

   clearSummary() does not zero the aggregate counts for individual error IDs
   or for severity levels.  It also does not affect any limits set, or wipe
   the knowledge of the message IDs from the statistics tables.  (Thus there
   would remain a bunch of errors with counts of zero; when statistics are
   output these do not appear.)

   There is a more sweeping method:

	stats->wipe();

   This clears everything -- counts and aggregate counts for individual ID's
   and for severity levels.  The statistics is wiped clean, which may be
   relevant if memory management issues have imposed a limit on the number of
   message ID entries in the statistics table.

   The same routine also wipes out any information in the limits table.  This
   includes values which have been supplied by setLimit() (or setTimespan()),
   and counts of the individual instances of each message ID.  For the
   ELstatistics destination this is moot (since limits do not apply) but for
   ELoutput or ELerrorList this may be useful when a maximum number of
   message ID entries in the limits table has been imposed.

	dest->wipe();
	logger->wipe();	// Applies wipe() to all attached destinations

   This clears everything -- counts and aggregate counts for severity levels
   and for individual ID's, as well as any limits established.  (This includes
   the limits for "*" all messages.)  The table is wiped clean, which may be
   relevant if memory management issues imposes a limit on the number of
   message ID entries in the statistics table.

   Finally, there is a way to zero the counts going toward the limits of all
   IDs in a destination.  This does not affect ELstatistics (which does not
   use limits) and will not compactify the limits table (see Note 6: "Limit
   Semantics").  It also does not affect aggregate counts.

	dest->zero();

	       ****************************************
	       *  Further Options for the Frameworker *
	       ****************************************

17) Checking on errors
----------------------

   Since the severity levels above ELnext indicate advice that the flow of
   processing ought to be modified or ceased, it is advisable (in all but
   the most time-critical applications) that the framework check for the
   presence of such errors, between modules or between events.  To make this
   inexpensive, a routine checkSeverity() is provided.

	ErrorSeverity highest = logger->checkSeverity();
	if ( highest >= ELnext ) { do whatever };

   This routine provides the severity level of the highest error declared
   since the last checkSeverity(), or ELzeroSeverity if none have been
   declared since the last check.

18) Error counts
----------------

   You can obtain a cumulative count of errors of a given severity -- including
   those which occurred but were not sent to a destination or saved.

	logger->severityCount(ELerror);
	logger->severityCount(ELsevere, ELfatal);

   The two-argument form adds the counts of the severities from the first
   to the second level; for example, severe1, severe2, abort and fatal.  

   These counts are can be reset to zero by

	logger->resetSeverityCount(ELerror);
	logger->resetSeverityCount(ELerror, ELfatal);
	logger->resetSeverityCount();

   The latter resets all the severity counts.  Note that these methods
   of the ELadministrator "logger" have nothing to do with the counts kept
   in ELstatistics for summary purposes.

  In the two-argument forms of these routines, if the first is a higher 
  level than the second argument, the count will be zero and nothing will 
  be reset.


						   -- Further Options --

19) Traces and control over whether traces are logged
-----------------------------------------------------

   The package has the ability to send trace information to be output by
   destinations.  A trace is a string which might have the sort of stack 
   and argument trace one can get from the system plus debug information,
   or might have other information as specified by the traceRoutine() member
   of the ELcontextSupplier.

   Obviously, if a message is not logged at some destination (because it
   misses the severity threshold or has occurred more times than its limit)
   no trace is sent to that destination either.  But even if the message is
   logged, you may not want the additional information (and clutter) of a
   trace unless the message is sufficiently severe:

   dest->setTraceThreshold(severity);
			      -- For errors logged to this destination,
				 include the trace (if available) if severity
				 is at least this level.

   The default provided is setTraceThreshold(ELhighestSeverity), that is,
   unless a threshold is explicitly set, the trace routine is not called
   and no traces are output.

   Presently, there is no mechanism for obtaining the good traceback 
   information in a platform-independant way.  Until useful traceback 
   routines are made available for each platform, the default is for 
   the traceback to be null.

   However, as described in section 12 on the ELcontextSupplier, the user may
   supply a custom trace routine, to be invoked for each destination having 
   a suitably low trace threshold.  If there is useful experiment-dependant 
   trace-like information available, this may be a reasonable mechanism for 
   outputting (logging) that when errors occur.


						   -- Further Options --

20) Time span in conjunction with limits
---------------------------------------

   Sometimes, you may have a large bunch of errors logged in a short period of
   time, due to a specific problem within, say, a peculiar event.  The limit
   mechanism prevents an output destination from being swamped with more than
   N of these identical messages.

   If the messages continue to arrive on a regular basis, then this throttling
   should continue to apply.  But sometimes, there may be a burst of errors,
   hitting the limit, followed by a long hiatus, followed by another instance
   or burst of that type of error.  In that case, you may well wish to see
   the errors immediately after the dry spell.

   The way this is provided is by the concept of a time span.  A time span is
   a number of seconds associated with a given error type.  When a message
   arrives to ELoutput or ELerrorList (which use the same ELlimitTable class
   to implement their throttling behavior), the count (toward throttling based
   on the limit) might be zeroed.  The count is zeroed if the number of seconds
   between the previous occurrence of this type of error and the present
   occurrence exceeds the time span for this type of error.  Of course, if the
   count is zeroed, this and the next N errors will not be suppressed.

   (On systems where getting time information is impractical, time span will be
   moot.)

   The semantics of setting time spans, and choosing which time span is
   applicable, is exactly the same as for setting limits.  However, a
   time span is expressed as a float number of seconds t.

   dest->setTimespan     (id, t)
   dest->setTimespan     (severity, t)
   dest->setTimespan     ("*", t)
   logger->setTimespans  (id, t)
   logger->setTimespans  (severity, t)
   logger->setTimespans  ("*", t)

   A time span (or a limit) can be "unset" by supplying a honking large value
   that will never be reached.

						   -- Further Options --

21) ELerrorList Local Semantics
--------------------------------

   The ELerrorList is intended to support something that was done in Run I: ]
   At least one experiment had the framework examine errors AFTER an event was 
   completed.  One could imagine storing the error information in a data bank 
   tied to the event, for example.  

   The ELerrorList records an ErrorObj by pushing in onto a std::list
   using push_back(theErrorObj).  The list it works with is that list supplied
   to its constructor.

   ELerrorList will add one item to the ErrorObj:  It invokes fullContext()
   to get the full run/event context and adds that as a last item before pushing
   the ErrorObj onto the list.

   The list remains the property of (and the responsibility of) the framework.
   Thus the ELerrorList Local Semantics is merely the semantics of std::list.
   Of relevance:

	* The user can iterate through the list to get at each ErrorObj, and 
	  can examine each one.

	* The user can invoke list.clear() between events to keep the list
	  size minimal.

	* IMPORTANT -- The list must not be allowed to destruct while errors
	  might still be logged.  For example, the following is wrong and
	  will likely core dump:

	ErrorLog errlog;
	int main() {
	  setup();
	  doThingsThatMightLogErrors();
        }
        void setup() {
	  // ..
	  std::list<ErrorObj> theList;
	  logger->attach(ELerrorList(theList));
	}
	void doThingsThatMightLogErrors() {
	  // things had better **not** log errors, because the ELerrorList
	  // will still want to record them, but theList is not valid here.
	}
The operations which come from the user or frameworker, to impact ELerrorList
mostly come from the fact that it is an ELdestination subclass:

setThreshold()			Thresholds, limits and time spans are set
setLimit()			as for any other ELdestination class.
setTimespan()


						   -- Further Options --
22) Accessing Saved Error Messages
----------------------------------

   For the program to get at information contained in saved error objects,
   you need to know how to get at the items in the ELerrorList list, and
   having gotten an ErrorObj, how to get at its individual fields of data.

   The ELerrorList puts ErrorObjs onto its supplied list using the usual list 
   semantics.  To get at an item, you declare a const_iterator and use it.

	std::lit<ErrorObj>::const_iterator err;
	for ( err = theList->begin(); err != theList->end(); ++err ) {
	  if ( err->severity() == ELabort ) { think_about_aborting(); }
        }

   The methods for forming and adding information to an ErrorObj were already
   described in "How an instance of ErrorObj may be formed."  ErrorObj
   also has methods to get the various pieces of information, both those
   captured when the object was created, and those supplied directly by a user.

	ELseverityLevel severity();
	ELstring   id();
	ELstring   text();
	ELstring   process();
	ELstring   module();
	ELstring   subroutine();
	ELstring   context();
	ELstring   verboseContext();
	time_t     timeStamp();
	ELstring   fullMessage();
	int	   sequenceNumber();

   The last item bears explaining:  ErrorObjs sent and not ignored by
   ELsaveBuffer are assigned sequential numbers, so that a non-local entity
   can detect any skipped messages.  Only ErrorObj's obtained from the save
   buffer have a meaningful sequence number; if a user just constructs an
   ErrorObj it will have sequence number 0.

   Notice that only information associated with the error message is available
   in this way.  This would not include information describing how a specific
   destination (including an ELstatistics destination) would treat the error.
   In particular, there is no way to get from the count of how many times this
   type of error has occurred, directly from an ErrorObj.

						   -- Further Options --

23) How ELstatistics information is kept
----------------------------------------

   The error statistics kept by the ELstatistics destination are logically
   a map.  The combination of 20-byte message id, severity, process, module
   and subroutine, which we for convenience combine to form --ELextendedID--,
   acts as the key for this map.  The data is a (zero-able) count and an
   aggregate count for each type of message, plus the (brief) contexts
   (run/event) of the two first and one latest instance of occurences of such
   a message with non-null contexts.

   A last piece of data for each entry is a flag telling whether any message
   of this type has been throttled out of all the destinations because of
   limits.  In "How the framework sets up for logging," it was mentioned
   that ELstatistics should be attached after all the ordinary destinations.

	ELdestControl  logfile  = logger->attach ( ELoutput(cerr)      );
	ELdestControl  logcerr  = logger->attach ( ELoutput("myFileName.log"));
	ELdestControl  logstats = logger->attach ( ELstatistics(cout)  );
	ELdestControl  errbuf   = logger->attach ( ELsaveBuffer(20000) );

   This is the recommended order, for the following reason:  Each destination
   indicates whether it has ignored a given message because of a limit or
   threshold.  An ELstatistics, when passed an error object, notes whether any
   destination has YET actually logged the error message; if not, it will
   mark that error type as having an instance that was ignored by every
   destination due to limits.  So if you want that asterisk in the statistics
   summary to be meaningful, attach ELstatistics after all the destinations
   which might output the message.

   In the above example, we attached errbuf after ELstatistics because we
   want the * to appear in the summary if an error was neither logged to
   logfile nor logcerr.

   For instance, one could imagine a attaching a scrolling screen that will get
   millions of messages AFTER logstats, saying that if the instance only was
   output there, we want to note in the summary that some instance appears in
   no relevant destination logs.

   In addition to the information kept in the individual error table,
   a count and an aggregate count of errors by severity level is kept,
   so that that information may be supplied in the summary.

   You can access the ELstatistics information by the stats.summary(..) methods.

   The ELstatistics class provides the default constructor (using cerr as its
   ostream), and also constructors taking two pieces of information.  The 
   first is a limit on the number of distinct entries for which statistics 
   will be kept.  The second is an ostream to be used to output a final 
   statistics summary upon job termination (or whenever the ELstatistics
   object is destructed) if statistics have changed singe the last requested
   summary - see Note 12: Termination issues.

	ELstatistics( int spaceLimit=-1);
	ELstatistics( std::ostream osp );
	ELstatistics( int spaceLimit=-1, std::ostream & osp );

   The example above,

	ELdestControl  logstats = logger->attach ( ELstatistics(cout) );

   sets up statistics with no limit on the number of entries, and when logstats 
   is destructed, if it has changed since the last summary, it would send its
   summary as a string to cout.  

23.5 Access to the statistics map
---------------------------------

   The frameworker can obtain a std::map<ELextendedID, StatsCount> containing
   the same information available when an ELstatistics summary is requested.
   The difference is that for puposes within a program, it may be easier
   to extract information from the ELextendedID keys and StatsCount structures
   than to parse summary output lines.

	ELdestControl logstats = logger->attach ( ELstatistics()  );
	...
	std::map<ELextendedID, StatsCount> m = logstats.statisticsMap();

   To use this, one should know about the ELextendedID struct from 
   ELextendedID.h, and the StatsCount struct from ELmap.h.

   (There is no guarantee that ELstatistics keeeps this information in the form 
   of a map.  However, if it is kept in another form, then statisticsMap() will
   do the transformation and supply a map.)


23.8 Changing the ostream used by an ELoutput
---------------------------------------------

The frameworker can switch ostreams used by an ELoutput destination.
This is done, of course, via the associated destControl.
Three related signatures are supplied:
   
dest->changeFile(os)
dest->changeFIle("name")
dest->flush()


24) Formatting control available in ELoutput
--------------------------------------------

The ELoutput destination provided allows the frameworker to control some
aspects of the format outputted for each error message.  The methods
in the second column of this chart reflect the default behavior.

dest->suppressTime()			dest->includeTime()
dest->suppressModule()			dest->includeModule()
dest->suppressSubroutine()		dest->includeSubroutine()
dest->suppressText()			dest->includeText()
dest->suppressContext()			dest->includeContext()
dest->includeSerial()			dest->suppressSerial()
dest->useFullContext()			dest->useContext()

Note that these methods should be called as methods of the destination control
obtained when the ELoutput was attached to the logger.

Users can use \n at the start or end of items sent to the log, to control line
formatting.  ELoutput also supports a couple of routines to force newlines 
into the "epilogue" of context and time infomation appended after the last 
item:

dest->separateEpilogue()		dest->attachEpilogue()
dest->separateTime()			dest->attachTime()

The former places a newline after the last user-supplied item so that the 
run/event context, module name, and and time stamp come out on a fresh line.
The latter forces the timestamp to come out at the start of its own line 
(after the usual indent).  These may help with easy scanning of the log output.
Their inverse functions in the second column can be called to reverse the
process.

Another flexibility is setting line length:  The ELoutput destination 
by default formats messages lines by starting a new line whenever an 
item would go past column 80.  One can change that line length:

dest->setLineLength(len)		dest->getLIneLength()

Note that this is done separately for each destination, thus a log might
have 132-column formating while a screen output sticks to 80.  Note also
that for the ELstatistics destination, the formatting -- which assigns
fixed column widths for various fields -- is unaffected by this line length.

Another flexibility is squelching the "ErrorLog Established" message that
comes out at the top of the log.  This message can be very useful in cases
where a log was appended to an existing file.  Nonetheless, it is sometimes
desirable that a program with no problematic occurances emit absolutely
nothing to the error logging stream.  This is done when constructing the
ELoutput destination.  A second argument is accepted; this is a boolean which
defaults to true, but if false will squelch the ErrorLog Established 
message.

ELdestControl logcerr  = logger->attach ( ELoutput(cerr, false) );
ELdestControl logfile  = logger->attach ( ELoutput("myFileName.log", false) );

The default constructor for ELoutput does not provide this flexibility.

For further flexibility, a custom ELdestination must be created.


24.5 Hex output formatting via ErrorLog
---------------------------------------

When data of integer type is output, it is occasionally preferable to see
the value in hex.  The package does not support streaming the hex manipulator
into an ErrorLog, because technical considerations involving the details of
signature of hex make doing this in a portable manner too difficult.

Instead, the framworker or a user can set an ErrorLog to print all integer
items larger that some trigger value in a format like 258 [0x00000102]:

errlog.setHexTrigger (int n);

The rules are as follows:

  The logic determining whether to use the hex form is on a per-ErrorLog basis.

  Hex output is done whenever an int, long, or short (or the unsigned form 
  of one of those) is streamed to the errlog, and the absolute value of that 
  integer is greater than or equal to the hexTrigger established for that 
  ErrorLog.

  The default value of hexTrigger is negative.  A negative value turns off
  hex formatting.


	*****************************************************************
	* Customization Hooks and Issues Relating to Collective Logging *
	*****************************************************************

25) What an ELdestination Must Do
----------------------------------

In order for a custom ELdestination to work properly in the context of
how the physicists and frameworker can use it and how the logger will
interact with it, the destination class (and its associated ELdestControl)
must support the following methods:

The destination class must have:

bool log ( const ErrorObj & )	This is allowed (and encouraged, based on limits
				and thresholds) to decide to ignore or act upon
				this message.  If the message is acted upon,
				log() should return true so that statistics
				can know somebody logged it.

bool output(ELstring, ELseverityLevel)  
				If the severity level is at or above threshold,
				write this item out (into the log).

void summary( ELdestination & destination, const ELstring & title="" );
void summary( std::ostream  & destination, const ELstring & title="" );
void summary( ELstring      & destination, const ELstring & title="" );
				These are present for the purpose of
				producing formatted summary information
				and forwarding it to a destination.
				Any given destination is free to ignore these.

void summarization( const ELstring & title, const ELstring & summaryInfo );
				This function is responsible for emitting the
				provided summary information, and may emit
				additional headers, timestamps, footers, etc.,
				as desired.

ELdestination * clone();	Pure virtual in ELdestination; must be
				supplied.  This must new a copy of the
				destination object and return its address.
				See note 5 "ELdestControl Details" for the
				ownership of destinations issue.


						-- Customization Hooks --

The ELdestControl object has the following methods, which the destination
class must support:

setThreshold (ELseverityLevel sv)       The destination is free to ignore these,
setLimit     (ELstring s, int n)        as does ELstatistics which does not
setLimit     (ELseverityLevel sv,int n) apply limits or time spans.  If these
					are NOT ignored, then the string "*"
setTimespan  (ELstring s, int n)	must be treated as a wild card, and
setTimespan  (ELseverityLevel sv,int n) the semantics of precedence described
					in this document must apply.

setTableLimit (int n)			Impose a limit on the number of
					entries in any given table.  See note 3.

clearSummary();			Ignore clearSummary except for statistics
wipe();				destination; see "clearing Statistics and/or
zero();				Limits" behavior definitions.

setPreamble (ELstring preamble)		Establishes a substitute for the default
					%ERLOG start of message output.
					Pre-ambles of up to 6 chars will work
					properly with the standard formatter.
setNewline  (ELstring nl)			Establishes a substitute for the default
					\n as a newline character in the log.
					Provided in case NT needs this.


The control object for a destination intended as a statistics gatherer must
also support:

summary(dest, ELstring title=""); These methods should be ignored except in
summary(os,   ELstring title=""); the case of the controller of a statistics
summary(s&,   ELstring title=""); destination, which will output the summary
                                  as directed.

These are present (as immediate returns) in the base class ELdestControl.

The ELdestControl class has three protected pointers,
  ELdestination  * d;
  ELdestinationX * dx;
  ELdestControlX  * x;
It is possible to derive off this class, placing values do customized subclasses
of ELdestination and/or ELdestinationX into these pointers in the constructor
of the derived ELdestControl subclass.  This would minimize the amount of new
code one needs to write, and also may eliminate in some cases the need to
recompile framework compilation units when the header of the new customized
class changes.

						-- Customization Hooks --
26) ELsaveBuffer Semantics and Layout -- REMOVED
-------------------------------------

						-- Customization Hooks --

27) Avoiding lists, maps, strings, streams, and templates
----------------------------------------------------------

When first designing this with D0 L2 in mind, the following concerns came up:

Due to the lack of post-startup memory allocation, and desired avoidance of C++
library classes, there several types of worries:

A Structures that may in principle grow indefinitely.
B Use of C++ strings.
C Use of stringstreams to build strings.
D Use of streams other than stringstreams.
E Use of maps.
F Use of templates penetrating to user interface

Ideally, we would like to feel free to use all of these in a basic
implementation, yet have clean ways to avoid using any of them if necessary.

Here are the places where there can be concern:

A1 - The errorStats table could have unlimited entries
A2 - The errorLimits table could have unlimited entries
A3 - The save buffer ELbuffer might grow arbitrarily
A4 - The control structure for ELbuffer might grow arbitrarily
A5 - Capture buffers used to flush the ELbuffer need to be limited
A6 - The list of destinations could grow indefinitely

B1 - An item sent to log by << can be arbitrarily long
B2 - The formatted message string can be arbitrarily long
B3 - The date/time might be a C++ string
B4 - Part of the Context object might be a string
B5 - Objects contained in a class (e.g. ELerrorStats) might be strings

C1 - Use of stringstream to implement operator<<

D1 - An ELdestination might inherently be associated with a stream

E1 - Use of maps in the limitsTable
E2 - Use of maps in ELstatistics

F1 - The ErrorLog and ErrorObj classes use templates for operator<<.

We have followed several design principles to allow a customizer to resolve
any or all of these concerns.  These include keeping functionality that we
do insist on implementing via full C++ in subclasses below interface
definitions; the customizer can replace any of those subclasses which
violate the conditions of the limited environment.

The following techniques can be followed to avoid (or avoid using in a
post-init dynamic memory manner) the std:: concepts of list, string, and stream:

-- To avoid using lists and maps:

We use various ELlist_XXX as typedef-ed to std::list, for instance,
	typedef std::list<destination> ELlist_destI
so one option is to replace those with your own container classes, but we do
assume list semantics.  Similarly, we use various sorts of ELmap_XXX as
typedef-ed to std::map<XXX>, and there we assume map semantics.  To try to
avoid or put limits on those semantics:

ELadministrator
  To avoid lists altogether you could supply a custom ELadministrator.
  But if you can tolerate lists if they do not grow after init time, using the
  provided one should be fine.

ELstatsTable
  The errorLogger will have an additional set of constructors taking
  an instance of ELstatsTableI & which will allow substituting a derived
  class that does not do any undesired operations.

ELlimitsTable
  The ELdestination derived classes can utilize any limits table or mechanism
  desired.

ELsavebuffer
  A custom ELdestination may easily be put in place of ELsaveBuffer on the list
  of sinks (or however the ErrorLoggerI accepts sinks).

-- To avoid using strings:

The ErrorLogger package uses ELstring which is typedef-ed to std::string, so
one can easily substitute any class with similar semantics.  In particular,
a string class with some hacked allocator to avoid dynamic memory allocation
after some startup epoch would be a plausible substitute.  The following
classes would be places to check for whether your string semantics are adequate:

ErrorLog
  This one is easy:  All string-like arguments are done as C-strings via char*,
  and all internal strings with the exception of the error summary are stored
  as ELstrings with specified maximum lengths.

ErrorSummary
  When a summary is sent to a destination, it is sent one fixed-length char*
  representing a single line at a time.  So unless the framework explicitly
  does log.summary(string&), no strings or arbitrarily long char arrays
  which might have to be malloc-ed are used.

ErrorObj
  Our provided class does use a string to hold the formatted error message
  string, however, the customizer can substitute a different class or use
  a different form of ELstring.

ELlimitsTable
ELstatsTable
  Our provided classes for these do not use strings.

ELdestination
  Our provided class for ELdestination uses strings in an essential way.  One
  could derive off ELdestination to form sinks that do not use strings, or rely
  on a different form of ELstring.

ELsavebuffer
  Our ELsaveBuffer does not use strings, since it strives for a clean and
  remote-accessible fixed format.

						-- Customization Hooks --
-- To avoid using streams:

ErrorLogger
  ErrorLogger does not actually use streams, though the syntax is patterned
  to some extent after the string << syntax.

ELdestination
  Our provided ELdestination uses streams extensively.  A customizer would have
  to create a custom derived class to avoid this.

-- To avoid using templates:

Templates are a trickier matter.  We have avoided using templates in all but two
essential circumstances:

a) The std::list and std::map make use of templates, so to avoid templates
   altogether one would have to custom derive custom ELlist_dest and
   similar classes defined in ELlist.h and ELmap.h.

b) ErrorLog accepts items of information supplied to it by operator<<.  It
   formats these into ELstrings using an ostringstream to format the message,
   and sends them to the list of ELdestination and the error message object.
   Both the accepting and the formatting via ostringstream use templated
   operator<<, and this HAS TO BE, because otherwise the user would not be
   free to << any type of string, number or object to the logger!
   To avoid the use of templates, one would have to substitute a custom
   file in ErrorLog.icc, supporting the creation of ELstrings from some
   restricted set of object types.  By out rules, an ErrorLog must have <<
   from at least char*, so at least that trivial one must be present.

   Similarly, one would have to substitute a custom file for ErrorObj.icc,
   supporting operator<< from presumedly the same restricted set of types.

28) Table Limitations
---------------------

In implementations that allocate a fixed amount of space for tables, such as
a fixed number of ids in limit tables, the customizer needs to define the
behavior when these limits are exceeded.  We suggest the following:

  - When a statistics table (which is a pure map) cannot add another key of
    error, log that fact (the first time) as an ELwarning2 and ignore the
    overflow.

  - When the id-only part of a limits table gets full, additional setLimit
    commands log an ELwarning2 and become no-ops.

  - When the count part of a limits table, which is keyed off extended id,
    becomes full, log the first time as an ELerror2.  Additional types of
    errors are simply not limited.

						-- Customization Hooks --

29) Direct logging to one destination
--------------------------------------

Although in general all logging is done through ErrorLog objects, which work
through the ELadministrator, the frameworker may log a formed ErrorObj
directly to an ELdestControl.

	bool reacted = logfile.log ( ErrorObj & myMsg );

As implied, the destination's thresholds and limits still apply; the log()
method returns false if the destination did not react to this ErrorObj.

If this backdoor mechanism is used, it has bypassed the ErrorLog functions.
In particular, ErrorLog normally provides module and subroutine strings.
To restore this ability, the ErrorObj class has two further methods:

	myMsg.setModule     ( "whatever" );
	myMsg.setSubroutine ( "whatever" );

Note that (unless the destination happens to be ELstatistics) error messages
logged in this manner will not be reflected in the statistics.  Generally,
it is a better idea to send things to all destinations using

	errorlog (myMsg);

and let each destination filter out what it does not want.

Another point for custom destinations:  In ELdestination.h we have defined
the method log() which is used in two senses.  Generally, it is the method by
which errlog sends the full message to a dest (e.g., ELstatistics) that wants
the full message at once.  However, it can also be invoked directly as a way
to log the message to a destination via a back door.

The problem is that the former usage implies NOT processing individual items,
because they assumedly have been processed. The latter implies processing items
if that is how they get output.

To allow for this, the destination must be prepared for a backdoor log, if it
has an appropriate action to take.  In some cases (ELoutput, for instance) it
can know absolutely that log() was coming through the backdoor because
skipMessageObject was set so the ELadministrator would never call its log().
Then it can know to process the individual items.  I can imagine a dest that
reacts both to items and to the overall message.  In that case, one should
check msg.isFromELadministrator to see if the backdoor mechanism was used.

30) Custom Severity Levels?
---------------------------

The intent is that all assigned severity levels fall into one of the 14
levels provided.  (ELsev_highestSeverity and ELsev_zeroSeverity are not
counted here because they are intended only for establishing thresholds.)

In our experience, even allowing 14 levels of granularity is generally met
with derision.  Accordingly, we do not permit users to invent additional
levels.

		        *********************************
		        * Example of use of the Package *
		        *********************************

In this example, we assume a relatively simple framework in which a sequence
of physicists' modules, inheriting off a base class Module, are invoke for
each event.  The relevant files are:

frame1.h and .cc	The framework, including main()
Module.h and .cc	The module base class
FindTracks.h and .cc	The first  actual module to call
DoPhysics.h and .cc	The second actual module to call

We also assume that the way the framework calls the activity of a module is
to invoke its operator()(event &).  This is a syntactically elegant way, but
is far from the only possibility, and indeed both CDF and D0 use more
sophisticated schemes in their frameworks.

We will annotate this code in places relevant to the ErrorLogger package.  We
omit various things such as code guards from this example; they appear in the
actual test files.

// DoPhysics1.h -- Physicist header in module DoPhysics

#include "frame1.h"
#include "Module1.h"
#include "ZMutility/iostream"
using std::cout;
using std::endl;
class Event;

class DoPhysics : public Module {
			// Since DoPhysics is a Module, all member functions
			// of DoPhysics, in particular operator()(event),
			// have full access to all instance variables of
			// the underlying Module instance, in particular,
			// errlog.

public:
  DoPhysics (const string & name);
  void operator() (Event & event);
};

--------------

// DoPhysics1.cc  --  Physicist code in module DoPhysics

#include "ZMutility/ZMenvironment.h"
#include "frame1.h"
#include "DoPhysics1.h"
#include "ZMutility/iostream"
using std::cout;
using std::endl;

DoPhysics::DoPhysics (const string & name) : Module(name) {}
				// When the Module base class
				// is initialized with name, this
				// will do errlog.setModule(name)

void DoPhysics::operator() (Event & event) {

  event.data *= 3;
  errlog ( ELerror, "wrong data") << event.data << endmsg;
				// A sample error message issued.
  int i;
  for (i = 0; i<6; i += 2) {
    errlog ( ELinfo, "i =");
    errlog << i;
    event.data += i;
    errlog << " data = " << event.data;
    errlog << endmsg;
				// A message issued in several pieces
  }
  errlog ( ELsuccess, "Did step 2") << endmsg;
}

--------------

// FindTracks1.h and .cc are much the same as DoPhysics1.h and .cc

--------------

// Module.h -- the Module base class supplied by the framework

#include "ZMutility/ZMenvironment.h"
#include "ZMutility/iostream"
using std::cout;
using std::endl;
#include "ErrorLogger/ErrorLog.h"
#include "frame1.h"
class Event;

class Module {
public:
  Module (const string & name);
  virtual void operator()(Event& e) = 0; // do the processing for this module!
protected:
				// Note that this is the only line
  ErrorLog errlog;		// inserted because of the ErrorLogger
				// mechanism!  It creates an instance data
				// member errlog, which will contain the module
				// name and which is available to all the member
				// functions of the Module subclasses.
}; // Module

--------------

// Module.cc -- the Module base class supplied by the framework

#include "Module1.h"

Module::Module (const string & name) {
  errlog.setModule(name);
		// This is how the errlog variable knows
		// the name of the module being executed.
}

---------------

// frame1.h -- the framework's header

#include "ZMutility/ZMenvironment.h"
#include "ZMutility/iostream"
#include "ErrorLogger/ELcontextSupplier.h"

#include "ZMutility/sstream"
using std::ostringstream;

class Event {
  // This is a rather lame mock-up of what an Event looks like.  The framework
  // will pass an Event from one module to the next.
public:
  int data;
  Event(int d) : data(d) {}
}; // Event

class RunEventContext : public ELcontextSupplier {
public:
  string context();
  string fullContext();
  string summaryContext();
}; // RunEventContext

---------------

// frame1.cc -- the framework

#include "ZMutility/ZMenvironment.h"
#include "ErrorLogger/ELadministrator.h"
#include "ErrorLogger/ELdestControl.h"
#include "ErrorLogger/ELoutput.h"
#include "ZMutility/iostream"

#include "ZMutility/sstream"
using std::ostringstream;

using std::cout;
using std::endl;

#include "Module1.h"
#include "FindTracks1.h"
#include "DoPhysics1.h"

string RunEventContext::context() {
    ostringstream ev;
    ev << "event = " << FindTracks::event_counter();
    return ev.str();
		// This is how an error message, when it is issued,
		// finds out the context.  Thus you don't have do do
		// some setContext for each new event.
}
string RunEventContext::fullContext()    { return context(); }
string RunEventContext::summaryContext() { return context(); }

// The framework:
//	* Defines a class Event (above)
//	* Provides an ELcontextSUpplierI (above)
//	* Sets up a pure virtual base class Module (in module1.cc)
//	* Knows which classes derived from Module exist
//	* Contains main() which instantiates and invokes each of the
//	  various modules
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void main() {

  ELadministrator * logger = ELadministrator::instance();
				// ELadministrator is a singleton:
				// Any and all loggers use the same actual
				// instance.
  RunEventContext recontext;
  logger->setContextSupplier (recontext);
  logger->setAbortThreshold (ELabort);

  ELoutput      logfileD ( "myLogFile.frame1" );
  ELdestControl logfile  = logger->attach(logfileD);
  logfile.setThreshold (ELsuccess);

  ELoutput      outputD ( cout );
  ELdestControl output   = logger->attach(outputD);
  output.setThreshold (ELwarning);

  ErrorLog errlog;
  errlog.setModule ("framework");
				// Here we have created a global-scope
				// errlog for any utilities which are NOT
				// methods of a module with its own errlog.

  // The following is a list of the modules in this program:
	  FindTracks findTracks("TRACK FINDER");
	  DoPhysics  doPhysics("PHYSICS CODE");
  int n;
  for ( n=1; n<10; n++) {
    Event event(n);
    errlog (ELinfo, "Event Number ") << n << endmsg;
				// This demonstrates use of the global errlog.
    findTracks(event);
    doPhysics(event);
    cout << "data returns as " << event.data << "\n\n";
  }

  errlog ( ELabort, "Time to abort " ) << "n = " << n << endmsg;

}

		        ********************
			* Technical Design *
		        ********************

		Design Patterns and C++ Techniques Used
		---------------------------------------

ELadministrator "Destructable Singleton"
----------------------------------------

Logging is to be done under a unified, centralized control.  However, each
individual ErrorLog instance wants a pointer to an administrator.  For all
the reasons given in Gamma et al, it is better to use a singleton pattern than
to create a global instance of the administrator.

However, as noted by Vlissides, this does not allow for destructing the
single true instance.  Vlissides in C++ Reports "To Kill a Singleton" shows
a pattern which does allow for this.  The pattern used for ELadministrator is
that same one, modified with a suggestion due to Kowalkowski that makes it
more robust against compiler quirkiness.

Blind Adapters at the User and Framework Interfaces
----------------------------------------------------

One of the goals of the earliest implementation was that the headers seen
by both the physicist and the frameworker should not change after the initial
limited prototype release.  The concern is that recompiling all the framework
units is a big deal; so even if the interface remains stable, changing anything
in the headers is bad.  (Re-linking as more functions are enabled, on the
other hand, is OK.)  Since we had (by consensus or by fiat) firm definitions
of the functionality, this ought to be possible by defining interface
headers in the right way.

The problem that arose with the naive approach was as follows:  The header
file for the class contains public methods, and protected data and perhaps
supporting methods.  While the methods available to the user, can be considered
absolutely stable, it would be foolish to suppose that before implementing 90%
of the features, we can anticipate every piece of protected data required.

The first reaction to this is to say that the actual class is derived off the
pure interface class; the user includes just the interface header.  The
drawback to this is that now the constructor is in a header that the user is
not supposed to be concerned with or even including!  For instance, if
ErrorLog is the interface and the user has to declare an ErrorLogX, that is
bad.

The classic solution is to use the Adapter pattern C++ implementation suggested
in Gamma+3.  There ErrorLog would be a public ErrorLogI and a private ErrorLogX.
Aside from solvable issues of multiple inheritance, this is a no-go for us:
When ErrorLogX changes the user compilation units have to re-compile.

The solution chosen is what I call a "Blind Adapter" pattern.  The header seen
by the user provides an interface class (ErrorLog in our example) with ONLY:
* The agreed public methods
* A protected pointer to a struct of some type
  the user should not be concerned with (ErrorLogX)
Note that this pointer is declared by a forward declaration:

class ErrorLog {
public:
  methods();
protected:
  struct ErrorLogX;
  ErrorLogX * x;
}

Now ErrorLog does not need to include ErrorLogX.h, so when something changes
in the data structure needed, user and framework code is COMPLETELY ISOLATED
from those changes in terms of re-compilation.

The cost is that there is an extra level of indirection in some dispatched
methods.  This cost is quite small, and the same as the cost of virtual methods,
but the compiler is more likely to find optimizations in the latter case.

I call this pattern a blind adapter, but it is also the "Cheshire Cat" special
case of the "Bridge" pattern in Gamma+3.  As such, it has further possibilities:
For example, run-time choice of implementation is in principle possible.

This pattern is used for all the classes physicist or framework code ought
to talk to directly:
	ELadministrator 	ELadministratorX
	ELdestControl		ELdestControlX
	ErrorLog		ErrorLogX, ELadministrator, ELadministratorX {1}
	ELoutput		ELoutputX
	ELstatistics		ELstatisticsX
	ELsaveBuffer		ELsaveX
	ELdestination		ELdestinationX {2}

{1} The ErrorLog is itself a decorated proxy for ELadministrator.  Rather
    than go thru yet another level of indirection, we put the ELadministratorX
    pointer into the ErrorLog protected area as well.  Similarly, the

{2} Actually, the user would not typically mention ELdestination, but instead
    a specific subclass like ELoutput.

There is one shortfall of this pattern, where implementation actually must
be in ErrorLog.h:  The ability to accept << from any type implies a template
mechanism, and this has to be defined in a file that the user program including
ErrorLog.h will depend upon.  What that means is that the template has to
be implemented very carefully, to get it right the first time.  Fortunately,
it is quite a simple template.

			Rejected Alternatives
			---------------------

The particular syntaxes and features selected were picked in concert with
the CDF and D0 people, and represent a consensus.  We may have omitted things
that are worth doing.  However, we do not wish to re-trace ground covered by
decisions already made once.  Here, I list some of the proposed syntaxes, so
that when somebody says "why not change it to do this" we can say we considered
that and rejected it.

*** How a physicist issues a log message

  We considered
	log << ELerror << "id" << ...
  This was narrowly rejected because the accepted form makes it tough to
  accidentally use a data-dependent string for the id, e.g.
	log << ELerror << energy << " is too much energy" <<endl;
  Also, the accepted form makes it obvious that the severity level is needed
  for each new error.

  We considered
	log << ELerror("id") << ...
  This was rejected as a worse-that-both compromise between the above and the
  accepted syntax.

  We considered not permitting
	log (ELerror, "my id") << information << ... << note-no-endmsg
	log << more_information << ... << endmsg;
  the gain being that the users cannot accidentally string two messages
  together.  We do permit it because (a) the ability to structure a message
  in compound steps is useful, and (b) stringing two messages would require
  one mistake in each of the invocations:  Omitting endmsg and forgetting the
  () arguments for the new message.

  We considered
	log (ELerror, "my id") << information << ... << note-no-endmsg
	log (ELcontinue) << more_information << ... << endmsg;
  but aside from being "too cute" it does not solve any fundamental problem;
  two users can still conspire to form a jumbled message.

  We originally used endl where now we use endmsg.  The original reasoning was
  that since endl is already in use, it did not pre-empt a possible variable
  name.  The final reasoning is that people are used to the semantics of endl
  as being equivalent to \n, so we should provide a distinct "end of message"
  object.

*** How the physicist can indicate what is being done:

  We originally had $$ and then $SUB= as the subroutine indication string.
  We instead use @SUB= because the $ clashed with some in-use parser looking
  for $ as a delimiter.

						-- Rejected Alternatives --

*** Use of multiple loggers

  The intent when design started -- although this was not requested
  specifically -- was to permit a job to instantiate multiple loggers,
  as opposed to multiple sinks for one logger (which is supported).  Among
  the problems and complexity this introduced were the need for a more
  complex "multiple registered singletons" pattern to allow hooking to the
  logger, and the entire can of worms about two loggers talking to the same
  destination.  The original desire for multiple loggers was based on perhaps
  wanting two stats tables.  Now that the ELstatistics class is an ordinary
  ELdestination, that need goes away.

*** Built-in statistics object

  Originally, the statistics object was part of the logger.  This necessitated
  parallel (and confusing) control features for statistics and other sinks.
  The selected method also makes customization a bit easier, and has no
  disadvantages.

*** Overall limits per ErrorLogger

  We reject the idea of saying log.setLimit(..) meaning that every destination
  associated with this log should be throttled by that limit.  Aside from the
  fact that the save buffer and statistics destinations are unlikely to be
  able to share the same limits as other destinations, the problem is that
  explaining the order of precedence was getting to be impossible.

*** List of destinations in constructor

  Originally, the idea was to support only a fixed number (2) of ordinary
  destinations.  C++ cogniscenti at the 5/27 ZOOM meeting made a convincing
  argument that instead the constructor of ErrorLogger ought to take a list
  of destinations.  Eventually, we went over to the attach and hook semantics
  instead, for two reasons:  1) It is possible with these semantics to let the
  user retain control hooks to a destination, yet to avoid the chance of
  erroneously deleting the destination, causing the ErrorLogger to have an
  access violation or corrupt the heap. 2) If a user needs customization in
  the  direction of std container elimination, this choice decreases the
  set of things that would need to be customized.

*** Having "rights" to prevent accidental inappropriate operations

  Before going over to the ELadministrator pattern, the idea of a frameworker
  creating an object that the Physicists then use had a potentially worrisome
  aspect:  There was no way to prevent an ordinary user from doing things that
  were inappropriate, like altering limits and thresholds.  It was decided
  at the 5/21 meeting, however, that since this would require malicious, and
  not accidental, action, we would not take preventive measures like having
  the constructor for the logger supply a "rights" object which must be
  supplied to any "framework only" operation.  At any rate, now that the
  frameworker can keep complete control of ELadministrator and need no supply
  that object to other users, this worry is moot:  Our design does protect
  against inappropriate operations, without the awkward "rights" mechanism.

						-- Rejected Alternatives --

*** ELcontextSupplier object instead of pointer to function or known global

  Coming out of the 5/21 meeting it was decided that to avoid the need for a
  known-name global function providing context, we would instead supply the
  context function to the ErrorLogger constructor.  In a 6/23 meeting, jbk
  pointed out that the context object was much more in the spirit of C++, was
  somewhat less confusing syntactically, and had no disadvantages.

*** Methods to read out information

  It was felt that bulking the interface with methods like getLimit for the
  frameworker to read back things which the frameworker had set was unwise.
  In cases where the frameworker does not inherently know the answer, get
  methods are supplied:  To decipher an ErrorObj, and to extract statistics
  from a ELstatistics destination.

*** Multimap for the statistics table

  Before separating the statistics table as a destination, this table was
  doubling as the place where overall limits were kept, on a per-id basis.
  This would have been an ill-conceived design; at any rate, one of the
  consequences was that the key for the table was only the message id, and
  there could be multiple entries sharing the same key.  This is what a
  multimap is.  Now that the key to the table is the "extendedID", there is
  only one entry per key, and that makes it a map.

*** Getting at information in ELstatistics

   It would be possible to supply a way that a frameworker, with some
   difficulty, could use information in the ELstatistics table beyond just
   getting the summary.  However, either we would have to restrict this to
   access by known ELextendID, or we (and any future customizers) would have
   to work hard to supply some form of iterator.  It was decided at the 5/21
   meeting that the standard ELstatistics class should not be fattened up in
   this way.

*** The name log instead of errlog

   When doing sample implementations, I noticed collisions between the
   variable name log, and the math.h function of the same name.  Though
   you can survive this by judicious use of namespace, it will save some
   headaches to dodge the problem altogether.  Therefore, I now assume the
   Module class calls its ErrorLogger instance "errlog" and not "log."

*** To Get Started Immediately

   The following was put in place and rejected by both framework writers
   because they strongly prefer to change the Module class as seldom as
   possible:

   In order for to provide an implementation within weeks, for people to get
   started immediately without fear that physicist code will need to change,
   we can provide the following functionality subset, in these simple ways:

1) The ELseverityLevel class, and static instances of each defined severity
   level.

2) A static instance endmsg equivalent to endl.

3) An ELoutput class with a constructor taking an ostream &, and whose
   operator()(ELseverityLevel, char*) pumps %ERLOG, plus the full
   name of the ELseverityLevel, plus the char*, to the ostream.

4) If operator() returns an ELoutput, the ELoutput also should have a template
   for operator<< which just <<s the argument to the ostream.  However, since
   this version of ELoutput will not be permanent, it is quicker to have
   operator() return an ostream&.

5) ELoutput should also have a templated operator<< so the syntax
   log << "text" will work to continue a message.

6) A substitute for ErrorLog.h should be provided that just includes ELoutput.h.

   Given (1-6), the physicist can use the syntax described in the main parts
   (1-3) of "How a Physicist Logs Errors," without later modifying code when
   the real mechanism is present.  The frameworker would prepare this by
   constructing

	ELoutput log(desiredOstream);

   Note that the physicist code will not have to change when these are replaced
   by the full-feature classes.  The frameworker code, on the other hand, will
   have to change.

   If the above steps are done, then this version of ELoutput() will have to be
   discarded when the ELdestination and ELadministrator classes are there and
   the real ELoutput is to be written.  An equivalent approach, from the user
   viewpoint, would be to define ELdestination and a simplified ELadministrator
   class, and have the frameworker attach an ELoutput to the logger, as would
   be done in the final package.  Once this is done, multiple logs can be
   attached to the logger, and the framework code to establish logging will
   not need to be discarded later on.

*** Limits Table Mechanism

Previously,k we thought to combine the collection of limits by id, with he
counting of occurences by extended ID.  This led to the following scheme, which
was simplified away when we went to separate maps:

Because the limits are part of the statistics table, and the table is kept
by ID/module/sev while the limits are set by just ID, some care must be taken.
The algorithm is as follows:

1) An error comes in with some ID

	* Look up that ID in the table.
	   - If it is not there, look up the * limit and use that as the lim
	   - If it is there, look up limit in the first occurrence and use that
	     as the lim -- we will see that all occurences will match.
	     +  Then see if the Module/severity have null values.  This would
	        mean that the ID is in the table because of a setLimit call.
		In that case, CHANGE the module and severity of that entry
		to those of the present exception (this may be a delete and
		add operation).

	* Look up the specific ID/module/severity in the table.
	   - If it is not there, add an entry with that ID/module/severity
	     and with lim as the limit.  The error statistics are saved; the
	     message will be logged to Log and/or Screen depending on
	     thresholds, and the count is now 1.
	   - If it is there, increment the count and see if the count is more
	     than the limit for each destination; if so the message won't be
	     logged to that destination.

2) setLimit ( "an ID", n, m ) is called

	* Look in the table for items matching that ID.
	   - If any are found, change there limits to n and m, and continue
	     looking for other ID matches so that ALL limits for the same ID
	     are kept in synch.
	   - If no match for this ID is found, place an entry in the table with
	     this ID, these limits, counts of zero, and null for the module
	     and severity keys.

			Minutiae and Notes
			-----------------

Note 1 about endmsg:
--------------------

  We would say that the endmsg is required, but we cannot enforce that
  at the compiler level.  The consequences of omitting the endmsg are
  generally not serious:

  For most sorts of destinations, when an item (the ID or a later <<
  variable) is streamed to the the logger it will be streamed to
  that destination.  In that case, a user omitting endmsg will have
  little effect, unless the next error message issued forgets to
  use operator() to indicate a new error, and its severity and ID.
  In that double-mistake case the two messages would come out together as one.

  (Also, the trailing info -- the module, date, time and so forth, will
  not be output until the endmsg or the next message is encountered.  But
  these will be captured as soon as the severity for this message is
  encountered, so omitting endmsg will not lead to an incorrect apparent
  event number, for example.)

  For some destinations, for example, a "red-box" radio box in an
  operations window, the endmsg may be used to indicate that the full message
  is there and the window should be created.  For such cases, omitting
  the endmsg would delay the output of the message until some other
  message is started.

Note 2: Explanation of error message.
-------------------------------------

  %ERLOG-w  Too much energy
	    E = 834.750032
	    processname CTCDRVmodule CTCTRKsubr
	    10-Jul-1999 14:49:03 run=234 event=543
  %ERLOG:	All messages form the error logger will start with this;
		one can grep a file for this easily.

  -w:		Means the severity was ELwarning.  Might have been -e, -s, etc.

  Too much energy
		The first string passed in will be printed on the %ERLOG line.
		Although this string may be more that 20 characters, and will
		be printed out in its entirety, the message ID is restricted
		to the first 20 characters.

  E = 834.750032
		The further information, if any, is printed on a second line.

  processname	The framework may (optionally) establish a process name, to
		be output with every error message.  This, and all following
		information, will be printed on a new line after the "further"
		information.

  CTCDRVmodule	The framework may establish a module name, intended to
		represent the major part of the program that is executing,

  CTCTRKsubr	The physicist may declare that a subroutine has been entered.
		This is optional, and would obviously only be used for major
		steps where the performance cost is negligible.

  10-Jul-1999
  14:49:03	The date and time, along with run and event, will be on
		a separate line from the process, module, and subroutine
		unless the total combined line length would be 80 characters
		or less.

  run=234	These are mandatory, but in order to avoid overhead for every
  event=543	event (when errors might be logged in only a tiny fraction of
		events), the information is not pre-supplied by the framework.
		Instead, when an error is being logged, the logger asks for the
		run and event at that point.

Note 3: The statistics table mechanisms
---------------------------------------

The original contents of this note have been placed as a rejected alternative.
Originally, it was thought that a single multimap should be used to track both
set limits (by id) and limits/counts (by extended id).  Although this would be
possible, by separating things into two maps, the logic is substantially
simplified.

The actual limit table has two maps as just mentioned, plus an array of
severity defaults, plus a wild card default.

ELdestination has a method setTableLimit (int n) which imposes a strict limit
on the total number of entries in either the table of set specific limits or
the counting table.  (The latter is much more likely to be accidentally
exceeded.)  If an additional error comes in after the table is full, each
occurrence of that error type will be treated as if it were the first, and thus
not filtered by the limit mechanism.  (The threshold mechanism is still
applicable).


Note 4: ErrorLog and the Singleton ELadministrator
--------------------------------------------------

The full program links many modules, and each module will be instantiating
an ErrorLog object.  The ErrorLog object is the way a physicist, writing a
method of this module class, issues a log message.  The only "administrative"
responsibility in ErrorLog is that its constructor accepts a module name in
both normal and verbose versions.

The logging, of course has to be controlled by other administrative functions,
including attaching destinations.  But these are not associated with each
individual ErrorLog -- the logging system wants to present a unified set of
destinations and each destination wants to present a unified output responding
to messages issued by any module.  The methods and data relevant to these
universal functions are encapsulated by the class ELadministrator. The ErrorLog
class contains an instance of ELadministrator.  But to make sure all the
administrative information is unified, the ELadministrator class follows the
classic "singleton" pattern.  That is, the framework should get the
administrator by doing

	ELadministrator* logger;

and then can do things like

	logger->setContext(co);
	ELdestControl logfile = logger->attach(logfileD);

Note 5: ELdestControl details
-----------------------------

To avoid any possibility of a frameworker attaching a destination object to the
logger, then deleting the destination object and trapping the logger into
faulting when the next message tries to invoke methods of the deleted object,
the logger has to make a copy of the destination object.  But then, the
framework can't directly talk to the instance of the object in use by the
logger!  So logger->attach() returns an ELdestControl object, a hook to control
the destination.  The destination instantiated by the user can be deleted or
allowed to go out of scope.  The control object ought to stick around, though
the only consequence of letting that go out of scope would be that the framework
no longer has a way to affect the behavior of the destination.

We ensure that the control object faithfully accepts all methods applicable to
the ELdestination.  This includes setting limits, and directly sending a
log message.  If a custom destination contains methods outside the normal set
defined by ELdestination, then the customizer will also need to provide a
custom control object for that type of destination, inheriting off ELdestControl
but adding dispatchers for the new custom methods.  Then when attach returns
the ELdestControl, the framework can use that to construct an instance of
the derived control class.

The ELdestControl contains, of course, an ELdestination* pointing to the
particular destination object that was created by the clone() method when
logger->attach was called.  All of its methods dispatch through this pointer.
Since they are all virtual methods in class ELdestination, they will go
through the derived class virtual table, hence getting the correct versions.

Note 6: Limit Semantics
-----------------------

If an instance of a message with a particular ID is sent to a destination
before setLimit(ID) has been invoked for that ID, but while there is an
applicable wild card limit (for either "*" or the message's severity level),
then that ID is assigned an entry in the limits table, and the limit used
IS THE APPLICABLE WILD CARD LIMIT.  Changing a wild card limit will not affect
this entry once it is in the table.

For example,

	dest->setLimit ("*", 8);
	log ( ELerror, "Bank Limit");	// will use 8 as limit for Bank Error
	dest->setLimit ("*", 2);
	log ( ELerror, "Bank Limit");	// eight instances of Bank Error will
	log ( ELerror, "Bank Limit");	// be output -- not just 2.
log ( ELerror, "Bank Limit");
	...
log ( ELerror, "Bank Limit");

A consequence of this is that when dest->zero() is called, the entry for
Bank Error would remain in the limits table (still with a value of 8) as
if dest->setLimit("Bank Error", 8) had been called.

Note 7:  Module Name
--------------------

In the example in "How the Module Sets Up errlog" we assumed that the Module
class has a constructor taking as an argument some information that would
allow it to know the name by which this (derived) instance is to be known.
We called this information module_selection; it could be as simple as
a string, or involve other information that the error logger would not care
about.  At any rate, the info would be saved and the Module::getModuleName()
method would use this info to produce the name.  Thus we are assuming that
each particular class is providing that information in the initializer
list of its constructor, as in:

	class CentralTracker : public Module {
	public:
	  CentralTracker() : Module("CENTRAL TRACKER") {
	    // any other construction needed
	  }
	  virtual doTheWork(Event& e){ ... }
	  // whatever other methods and data CentralTracker needs
	};

	class Module {
	public:
	  Module (string name) {
	    errlog.setModule(name);		// This line was added
	  }
	  virtual doTheWork(Event& e) = 0;
	protected:
	  Errorlog errlog;			// This line was added
	};

Note that very few lines were added to make this Module class support
the ErrorLogger mechanism.

Initially, I thought a bit more complexity was needed.  Here is text from
an earlier version of this note:

	class CentralTracker : public Module {
	public:
	  CentralTracker() : Module("CENTRAL TRACKER") {
	    // any other construction needed
	  }
	  virtual doTheWork(Event& e){ ... }
	  // whatever other methods and data CentralTracker needs
	};

	class Module {
	public:
	  Module (char* name) : name_(name) {
	    errlog.setModule(getModuleName());
	  }
	  ELstring getModuleName() {return name_;}
	  virtual doTheWork(Event& e) = 0;
	protected:
	  ELstring name_;
	  Errorlog errlog;
	};

  There are more clever-looking ways to accomplish this, but I have found none
  that actually work.  For example, you can't directly supply the module name
  to the instance of ErrorLog instantiate as a class variable in the base class
  Module -- it has no way of knowing that name at that point!  And you could not
  make getModuleName a pure virtual in Module, relying on each derived module
  class to supply its own version which simply returns the name you want -- at
  the point this function is called, you are initializing the Module member of
  the derived class, so the virtual mechanism will not apply.

Note 8:  Includes
-----------------

People using the ErrorLogger classes should include the header for each
class they are using directly.  Thus the Physicist and the Module base class
need only

	#include "ErrorLogger/ErrorLog.h"

while the framework probably needs a collection like

	#include "ErrorLogger/ELadministrator.h"
	#include "ErrorLogger/ELcontextSupplier.h"
	#include "ErrorLogger/ELdestControl.h"
	#include "ErrorLogger/ELoutput.h"
	#include "ErrorLogger/ELstatistics.h"
	#include "ErrorLogger/ELsaveBuffer.h"
	#include "ErrorLogger/ErrorLog.h"

And everyone has to say

	using namespace zmel;
or
	ZM_USING_NAMESPACE( zmel ) /* using namespace zmel; */

Note 9:  ELoutput (fileName)
----------------------------

Constructing ELoutput from a string holding the file name is a convenience,
but it presents a couple of subtle issues.

  - What mode should the file be opened in (trunc to overwrite any existing
    file, app to append to the end if a file exists, or fail if the file
    exists)?

  - If the file is opened for append, how do we avoid logs from two jobs
    from looking like one longer error log?

  - What to do if the attempt to open the file fails.

  - Who owns (and deletes when appropriate) the ofstream created to represent
    this file?

We take the following positions:

  + The file is opened for append.  (If the file does not exist, it is
    effectively opened for simple writing.)

  + To separate two logs, we always place lines of ====== as divider strings
    before a log starts (even if it is the first thing in the file).

  + If the attempt to open the file fails, this destination instead logs to
    cerr -- we output a "meta error message" to indicate this switch.

  + If the ELoutput is constructed from a file name, it makes a new ofstream
    to represent that file.  It owns that for purposes of deleting *d at the
    end.

  + When a destination is attached to the ELadministrator, a new copy is made
    of the ELoutput object (to prevent any possibility of a user causing
    another user's error message to do a segfault by wiping out the
    destination object).  The clone() method ***also transfers ownership
    of the ofstream*** if it was constructed from a file name.  So the copy
    owned by the ELadministrator is the one that will delete the ofstream when
    it goes away.

When ELoutput is constructed from an ostream (which can of course be an
ofstream), it is assumed the framework has opened that stream with the
desired mode (app or trunc) and that if appending, has sent any desired
divider strings.  And it is assumed that any deleting or destructing of the
stream is handled by the frameworker or the system.

Note 10:  "ELcout: Replacing cout and/or cerr."
-----------------------------------------------

	OVERRIDING NOTE:

	Expert users pointed out several further subtleties associated with
	ELcout, and were of the opinion that using tis mechanism was always
	the wrong thing to do.  Therefore, we have removed ELcout from the
	package.  The following discussion is here for historical reasons
	only.

Although the ErrorLogger package is meant to augment rather than replace
cout and cerr, the package supports hooking cout and/or cerr to the logger
instead.  In section (11) we outlined how to do to this by declaring cout
and/or cerr to be variables of type ELcout.  Obviously, ELcout has to react
appropriately to the << syntax, routing the string it gets to ELdestinations.

That sweeps a lot of questions under the rug.  For example, there is no natural
analogue, in the context of ostream semantics, of supplying the severity level
and id.

Here, we answer those questions:

a) What are the constructors for ELcout?

  The constructor accepts one argument:  An ELseverityLevel.  This severity
  level is used (at least by standard logging destinations) only to compare to
  their threshold for deciding whether to react to or to ignore an output item
  sent to them.

b) How does the code use this mechanism (after constructing the ELcout)?

  This one is set by the very purpose of the mechanism:  You leave all your
  code sending things to cout (or cerr) unchanged.  Thus the ELcout type
  accepts all the stuff that can be streamed to an ostream.

c) What about special operations to the cout stream?

  The intent is to help the user whose existing code does a lot of ordinary
  cout output.

  Manipulators, which have meaning when sent via << to ostringstreams,
  will be accepted, but have no affect on output appearance.  Direct
  methods on cout (and in fact all methods other than via <<) are NOT
  SUPPORTED for ELcout.  For example, if cout is an ELcout, then
  cout.flush(), cout.good(), cout.setf(ios_base::showbase) will not
  compile.  Almost nobody uses these routinely.

d) How do we get the right cout in scope?

  If the compilation unit contains, in any compiled file, at global scope,
  the declaration of
	ELcout cout (level);
  instead of
	using std::cout;
  this will make the ELcout form available to all code in that compilation unit.

  Some compilers and/or includes may violate the standard and either have cout
  at anonymous global scope, or place
	using std::cout;
  into a header where it should not be.  Either of these would clash with the
  ELcout cout declaration.  One possible fix is to put that declaration into
  a namespace (say "mine") and explicitly say using mine::cout.

  A last-ditch brute-force approach if a name class becomes bothersome is to do
	ELcout myCout (level);
  and globally substitute myCout for cout.

e) How do we route an ELoutput destination to the REAL cout?

  In the compilation unit that includes the main framework, the frameworker
  will often be instantiating an ELoutput destination with the ostream cout
  in its constructor.  If cout is declared as an ELcout, then there is the
  potential for a terrible mess:  The logger sends a message to ELoutput,
  which outputs text to cout, which turns into output sent to ELoutput, and so
  forth.

  The solution is to explicitly say std::cout in the constructor of ELoutput:

	ELoutput outputD ( std::cout );

  To avoid utter confusion in the case of a mistake, ELcout declares a
  private (hence inaccessible) constructor taking an ELcout.

f) How do the standard destinations react to these?

  Of the standard destinations, ELstatistics and ELsaveBuffer will ignore
  items sent via the output() method.  Only ELoutput will react to them.
  ELoutput will send the unvarnished text of all items it gets in output()
  to its associated ostream.

g) Will things sent to ELcout appear in multiple places?

  If multiple destinations are attached to the log, a single cout<< can
  appear in each one.  However, two things may prevent a given output item
  from appearing at a given destination:

    - If the severity level appearing in the constructor of the ELcout is
      lower that the threshold for that destination, the item will not appear.

    - The base class ELdestinationI has a public bool data member
      skipOutputItems.  If this is set false, no output items will appear
      at that destination.

h) What about endl?

  Each item sent to an ELcout gets put into an ostringstream via <<.  The
  endl behaves in a sensible way:  It inserts \n or whatever the appropriate
  end-of-line sequence is for the particular machine.  The same is true for
  flush and ends.

  Since ELoutput merely sends on output() items as it gets them, the cout
  behavior is "what you do is what you get".  That is, if many outputs are done
  without doing any endl's or \n, then they will be strung together into a long
  line.

i) What about building messages in multiple steps?

  Again, ELoutput vomits one output() item at a time.  It makes no difference
  whether these are chained on one line or placed all over the code.

  However, one ought not mix cout into multi-step formation of a true message
  to errlog.  For instance:
    ELcout cout;
    errlog ( ELwarning, "track length" ) << "too long: ";
    cout << track.length() << "explanation";
    // better would be:   errlog << track.length() << "explanation";
    errlog << endmsg;
  would embed the cout stuff into the error message, but might screw up the
  decisions about formatting, line breaks, and so forth.

j) What does this imply about the nature of a generic ELdestination?

  The burden on ELdestination is that now it has a bool skipOutputItems,
  and must have an output(const ELstring &) method -- which will be a nop
  in the base class and can be overridden in ELoutput.

k) Does this type of output produce real ErrorObjs?

  NO.  Even though a severity is implied, that is only for the purpose of
  ELoutput deciding whether to suppress the items because of its threshold.
  We thought about creating a true message if the first would sent ends in a
  colon (and treating that word as the id); this opens up too large a can
  of worms.

l) How does the initialization of the ELcout instance work?

  Since each severity level is an instance of ELseverityLevel, and since
  ELcout cout (ELerror) would be done at global scope in most cases, one
  has to worry that the severity object (ELerror in this case) may not have
  been constructed before the ELcout is instantiated.

  To take care of this worry:  The constructor actually takes and stores a
  reference rather than an object:  ELcout (const ELseverityLevel &).  The
  various levels are declared as externs in ELseverityLevel.h.  So ELcout cout
  (ELerror) produces a reference to what at compilation time is an unresolved
  external; this is resolved at link time.  At the instant cout is constructed,
  this reference points to memory set aside for ELerror -- it may not as yet hold
  meaningful information.  But by the time any code is entered, ELerror will
  have been constructed, and thus the reference will be valid.

m) What is going to happen before ELcout is fully implemented?

  In order to avoid forcing user code that does ELcout cout (level) to
  recompile as ELcout evolves, we will keep ELcout as a proxy, with
  ELcoutX dealing with implementation needs.  However, even at the start
  this proxy had better send its stuff to ELoutput (and ELoutput had better
  output it); otherwise, the stub behavior would be to "swallow up" everything
  streamed to an ELcout -- this would be unacceptable.

Note 11:  Reasons for last round of header changes
--------------------------------------------------

The concept of a destination reacting immediately to items sent to the log,
rather than to the full message after completion, had two tempting benefits.
The first is that if the job somehow goes disastrously wrong, you get possibly
valuable information.  The second, which became moot, is that this made it
easier to hack together a temporary mock-up of the mechanism.  This second
turned out to be a complete red herring.

The first benefit is real, but overall, the complexity price paid for this
capability exceeds the benefit.

-----------------------
The choice of making ELseverityLevel an object, rather than an enum with
associated tables of name and symbol, was dictated by the idea of immediately
getting the syntax going by having the >> method for a severity output
its name.  Given that we later abandoned that early step, we could have
made it an enum.  But by then we had committed to allow user-defined extra
levels.

In retrospect, we should have done the simpler enum+tables approach, where
we would not do so much string copying and where comparisons would be more
straightforward.  This would cost us nay chance at user-defined levels, but
that would not have been a big price.


-----------------------
The choice of making ELseverityLevel an object, rather than an enum with
associated tables of name and symbol, was dictated by the idea of immediately
getting the syntax going by having the >> method for a severity output
its name.  Given that we later abandoned that early step, we could have
made it an enum.  But by then we had committed to allow user-defined extra
levels.

In retrospect, we could have done the simpler enum+tables approach, where
we would not do so much string copying and where comparisons would be more
straightforward.  This would cost us any chance at user-defined levels, but
that would not have been a big price.  The solution adopted is to create
a very lightweight ELseverityLevel class, with a table of names and symbols
and << defined in those terms.

-----------------------

In the ErrorLog class I used log() to refer to logging one item that arrived
via <<.  This name is confusing, since in destinations, log means taking the
whole message.  However, ErrorLog was frozen before I noticed this confusion.

-----------------------

The use of the blind adaptor pattern should be made more uniform:  If a user
sees class Abc, its implementation auxiliary class should be AbcX, and the
pointer to it should always be called x.

Note 12:  Termination Issues

On termination, if there is still an "active" message (a message started
by errlog() and not yet completed by endmsg), it has to be sent to the
destinations.  There is a problem with this:  If the sending is done as
part of the ELadministratorX destructor, then there is no guarantee that
the destinations are still around.  And if it is done as part of the
destination destructor, then naively, there is no guarantee that the
ELadministrator -- which has the active message object, as well as the flag
indicating that is the case -- has not already disappeared.  That is, the user
may have instantiated the destination before or after the first instance() of
the ELadministrator singleton was invoked.

Upon further reflection, we know that the destinations ON THE LIST IN THE
ELadministrator were instantiated after the administrator, because they are
copies of the user-instantiated destination objects, made when attach() is done.
In fact, the list is a list of ELdestination*, so these won't ever go
away till we explicitly delete them in a loop!

Other termination activities include output of a final statistics if things
have changed.  Again, we need to know the destination objects are still around.

So the strategy is:  In the destructor for ELadministrator, if there is an
active message, use the same technique that endmsg would use to send that to
every destination.  In the destructor for an ELstatistics destination, emit a
summary to the designated termination ostream under the title "Termination
Summary", but only if changes have occurred since the last summary was
generated.  Finally, walk the list of attached destinations, and delete each.

		Stubs and intended implementation order
		---------------------------------------

This list is not meant to be inclusive; it covers those items which occurred to
coders because code nearby to the stubbed code was being implemented.

Note that these are implementation matters only.  For example, setModule can
already be used, but till its stub is corrected, it will have no effect.

1: Immediate cleanup:
--------------------

1.1  Close (and dispatch) active ErrorObj, if any, in ErrorLog.cc.
1.2  Make sure ErrorObj m will stick around!

2: First thing
--------------

2.1  setThresholds
2.2  setAbortThreshold

3: Early on
-----------

3.1  setProcess
3.2  setContextSupplier
3.3  setModule
3.4  setPackage not yet implemented \n";
3.5  Put the date/time in the log header

4: First wave of implementation
-------------------------------

4.1  setSubroutine
4.2  checkSeverity
4.3  setLimits
4.4  add to limits table
4.5  ELstatistics is currently skipping the message object
4.6  ELstatistics destination
4.7  ELstatistics::summary

5: Second wave
--------------

5.1  log(ErrorObj)
5.2 ELadministrator::wipe()
5.3 setLimit(id)
5.4 setLimit(severity)

6: Early Basic functionality set
---------------------------------

6.1  severityCount
6.2  resetSeverityCount

7: Basic functionality set
---------------------------

7.1  backdoor log to destination
7.2  clearSummary()
7.3  wipe()
7.4  zero()
7.5  conditionalize formats
7.6  Time stamp
7.7  option of using fullContext()
7.8  wipe() in ELoutput
7.9  zero() in ELoutput
7.10 clearSummary() in ELstatistics
7.11 wipe() in ELstatistics
7.12 zero() in ELstatistics
7.13 detach
7.14 logic in ErrorObj to tell whether message was acted on by any dest

8: Refinements
--------------

8.1  setTimespans
8.2  wipe() to limits table
8.3  setTImespan(severity)

9: Later Refinements
--------------------

9.1  setTableLimit
9.2  ELstatistics destination with limit