The ZOOM ErrorLogger Package

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 be integrated with the ZOOM exception mechanism, such that a user may ZMthrow an exception containing an error message to be logged;
  6. to allow for (to the greatest reasonable extent) customizations tailoring to various environmental and system consideration.

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:

The package is designed such that certain things can easily be customized without discarding the rest. For instance, the formatting of the error text may be modified beyond the ways the standard framework controls permit; or the customization may avoid the use of C++ features and system support assumptions that may not be desired in a Level 2 context.

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

Classes and Concepts

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 are 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 top 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 owns 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, through the ELadministrator that the framework instantiates, 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. This ELadministrator is not in scope in the user code in individual modules, so only the framework has this centralized control.

Key Classes

The first three of these classes are of relevance to the ordinary physicist user. The remaining classes are of relevance only to the frameworker.
Encapsulation of the fundamental behavior supporting issuing of messages. Each module would ordinarily instantiate one of these, and make it available to all its subroutines; we assume it would be called errlog.
A class allowing for the definition of a fixed set of severity levels. The package provides the levels.
An error message object. A program can prepare one of these independent of the act of logging it. The users forms an ErrorObj when the ZMthrow mechanism is used in conjunction with error logging.

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.
Encapsulation of the behavior of a "sink" for the logged information to go to.
Derived from ELdestinationI:
A destination implementing our agreed behavior and formatting for sending messages to an ostream (which could be cout, cerr, or an ofstream for a file).
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.
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.
A destination implementing storage and output of message frequency statistics and summary tables.
An object to act as a proxy for an ELdestination providing the full spectrum of public methods available to control a destination's behavior.
An interface for an object providing methods to obtain text describing run, event, and other framework-wide concepts.
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, (for example in a Level 2 context where generic strings would be unacceptable) the interfaces use ELstring instead. Our provided classes typedef this to std::string.