#ifndef TCOMETLog_hxx_seen #define TCOMETLog_hxx_seen #include #include #include #include namespace COMET { class ICOMETLog; } //////////////////////////////////////////////////////////// // $Id: ICOMETLog.hxx,v 1.11 2010/02/22 17:18:53 mcgrew Exp $ /// Provide control over log and error output. The ICOMETLog class provides a /// set of static methods which control the amount of output from the COMET /// logging macros. These macros come in two varieties (logging macros, and /// error reporting macros). The level of output from the logging and error /// macros is controlled separately. /// /// The logging macros print the output to the log file with a simple header /// which allows them to be located using a search tool (like grep). All of /// these macros accept a \ref streamish and can produce multiple lines of /// output. The logging macros are: /// /// - COMETLog() -- Print output at the LogLevel output level. This is for /// messages that should be written to the output file during /// production. /// - COMETInfo() -- Print output at the InfoLevel output level. /// - COMETVerbose() -- Print output at the VerboseLevel output level. /// /// The indentation levels of log messages can be controlled using the /// COMET::ICOMETLog::IncreaseIndentation() and /// COMET::ICOMETLog::DecreaseIndentation() static methods. These should be used /// in pairs. /// /// The error macros print output to the error file with a header indicating /// the error level (ERROR, SEVERE, WARN, DEBUG, or TRACE), the file and the /// line of code where the message originated. Error macros should be used to /// report any problem found during execution. They are particularly useful /// for debugging. The syntax for the error macros is the same as for the /// logging macros. The error macros are: /// /// - COMETError() -- Print output at the ErrorLevel output level. This /// should be reserved for messages printed right before you expect the /// program to crash. /// - COMETSevere() -- Print output at the SevereLevel output level. This /// should be reserved for error conditions where event data is going to be /// lost, or the event will contain incorrect information. /// - COMETWarn() -- Print output at the WarnLevel output level. This should /// be used when a correctable, but unexpected, problem is found with an /// event. /// - COMETDebug() -- Print output at the DebugLevel output level. This /// should be used for general debugging output. /// - COMETTrace() -- Print output at the TraceLevel output level. This /// should be used for very verbose debugging output. /// /// In addition to the default logging and error macros, there are macros /// that take a "trace" name. The level of output from each type of trace can /// be controlled separately. The named variety of macro takes two arguments /// (a \ref trace and a \ref streamish). The named macros are /// COMETNamedLog(), COMETNamedInfo(), COMETNamedVerbose(), COMETNamedError(), /// COMETNamedSevere(), COMETNamedWarn(), COMETNamedDebug(), and /// COMETNamedTrace(). /// /// \section defaultLogging Using the Default Logging Macros /// /// The logging (and error) output macros are used like functions, except /// they take arguments that can be output to a stream variable (a so called /// \ref streamish). /// /// \code /// int i = 42; /// double pi = 3.1415; /// /// COMETLog("The answer is " << i); /// COMETInfo("Multi-line output is" << std::endl << " very useful."); /// COMETVerbose("Have a piece of " << pi); /// \endcode /// /// If the logging level is set to ICOMETLog::LogLevel produces: /// /// \code /// % The answer is 42. /// \endcode /// /// But if the logging level is set to TCOMET::VerboseLevel, this writes: /// /// \code /// % The answer is 42. /// %% Multi-line output is /// very useful. /// %%% Have a piece of 3.1415 /// \endcode /// /// The logging level can be set using the "cometlog.config" file (see \ref /// cometlogConfig) to define a log level /// /// \code /// log.default.level = VerboseLevel # also LogLevel or InfoLevel /// \endcode /// /// \section namedLogging Using the Named Logging Macros /// /// The named logging macros are used in the same way as the unnamed macros, /// except that you must provide a string as the first argument. This string /// is then used to define a "trace" which can be turned on explicitly without /// causing output from other logging statements. /// /// \code /// int i = 42; /// double pi = 3.1415; /// /// COMETNamedLog("traceName", /// "The answer is " << i /// << ". So we might as well have " << pi); /// COMETNamedInfo("traceName", /// "Multi-line output is" << std::endl /// << " very useful."); /// COMETNamedInfo("anotherTrace","Won't be printed"); /// \endcode /// /// If the "traceName" logging level is set to ICOMETLog::InfoLevel or above, /// this produces: /// /// \code /// # The answer is 42. So we might as well have 3.1415 /// # Multi-line output is /// very useful. /// \endcode /// /// The logging level for traceName can be set using the configuration file /// statement. /// /// \code /// log.traceName.level = InfoLevel /// \endcode /// /// \section cometlogConfig The ICOMETLog Configuration File /// /// The configuration of the ICOMETLog object can be handled using static /// methods, or using a configuration file that is read by a call to the /// ICOMETLog::Configure() method. When the ICOMETLog::Configure() method is /// called, it will first try to read "cometlog.config" in the local /// directory. If ICOMETLog::Configure() is called with a file name, the /// named configuration file will be read after the cometlog.config file and /// can override any settings. /// /// The ICOMETLog configuration file uses a simple line oriented syntax. /// /// - Comments are begun with "#" /// - Configuration statements are case-sensitive /// - Each line has one configuration statement /// - The configuration statement syntax is /// - a field name (defined below) /// - an equal sign /// - a field value /// /// The legal field names are /// /// - log.file -- The name of a log file. This can be either a string /// enclosed in double quotes, STDCOUT, or STDCERR. If the file exists, then /// output will be appended to the end. If the file doesn't exist, it will /// be created. /// /// - error.file -- The name of the error file. This has the same syntax as /// log.file. If an error file isn't provided, then the error output /// will go to the same destination as the logging output. /// /// - log.default.level -- The default logging level. This accepts the name /// of a logging level (QuietLevel, LogLevel, InfoLevel, or VerboseLevel). /// /// - error.default.level -- The default error level. This accepts the /// name of a error level (SilentLevel, ErrorLevel, SevereLevel, /// WarnLevel, DebugLevel, TraceLevel). /// /// - log.[trace].level -- Set the logging level for the named trace. The /// level names are the same as log.default.level. /// /// - error.[trace].level -- Set the error level for the named trace. The /// level names are the same as error.default.level. /// /// An example of the cometlog.config file shows how it might be used. This /// file causes the log messages to be printed in "output.log", and the error /// messages to be printed in "output.err". The default log level is set to /// ICOMETLog::InfoLevel so that messages are written from all COMETLog(), /// COMETNamedLog(), COMETInfo(), and COMETNamedInfo() macros. The default /// error level is set to ICOMETLog::SevereLevel so messages are written /// from all COMETError(), COMETNamedError(), COMETSevere(), and /// COMETNamedSevere() macros. The error level for the "myTrace" trace is /// set to ICOMETLog::DebugLevel, so messages are written for any /// COMETNamedWarn() and COMETNamedDebug() macro which has a trace argument of /// "myTrace" (e.g. COMETNamedWarn("myTrace","some output") will write "some /// output" into the log file). /// /// \code /// # An atypical example cometlog.config /// log.file = "output.log" # Set the name of the output file. /// error.file = "output.err" # Set the name of the error file. /// /// log.default.level = InfoLevel /// error.default.level = SevereLevel /// /// error.myTrace.level = DebugLevel # set myTrace to use TraceLevel. /// /// # End of cometlog.config /// \endcode /// /// Here is an example of a typical cometlog.config file that would be used /// during debuging. This assumes that your routine is using the \ref trace /// MyRoutine (a bad name!), and prints all of the error messages to the /// terminal. /// /// \code /// # A Typical cometlog.config file. Copy this into your directory as you /// # are debugging a program. /// /// # Uncomment the next line to save the log and debugging output to /// # output.log. /// # log.file = "output.log" /// /// # Print all of the possible log messages. /// log.default.level = VerboseLevel /// /// # Only print error messages of WarnLevel or above. /// error.default.level = WarnLevel /// /// # Print all of the debugging messages from my code. /// error.MyRoutine.level = TraceLevel /// /// # End of cometlog.config /// \endcode /// /// \section logLevel Log Levels /// /// The available log output levels are: /// /// - ICOMETLog::QuietLevel -- No log output. /// /// - ICOMETLog::LogLevel -- This is for messages that should be printed in /// the log file. Messages from COMETLog(), and COMETNamedLog() will be /// printed. /// /// - ICOMETLog::InfoLevel -- This is for messages that will be useful in log /// files, but may be skipped during large production jobs. Messages from /// COMETInfo(), and COMETNamedInfo() will be printed. /// /// - ICOMETLog::VerboseLevel -- This is for messages that curious users can /// use to track the progress of local jobs, but which do not belong in a /// production log file. Messages from COMETVerbose(), and /// COMETNamedVerbose() will be printed. /// /// \section debugLevel Error Levels /// /// The available error levels are: /// /// - ICOMETLog::SilentLevel -- No error output. Exception: Message from /// COMETError() are not suppressed even when the error output level is /// set to SilentLevel. These messages can be suppressed by defining /// COMET_ERROR_OUTPUT to false in the source code /// /// - ICOMETLog::ErrorLevel -- This should be reserved for messages that are /// printed just before you expect a program to crash, and which should /// never be supressed. Messages from COMETError() and COMETNamedError() /// are printed at this level. /// /// - ICOMETLog::SevereLevel -- This is for messages that are printed when an /// event has triggered a problem where the code is going to produce an /// incorrect result, but probably won't crash. Messages from COMETSevere() /// and COMETNamedSevere() are printed. /// /// - ICOMETLog::WarnLevel -- This is for messages printed when a problem has /// been found, but it is unlikely to cause significant problems in the /// analysis. Messages from COMETWarn() and COMETNamedWarn() are printed. /// /// - ICOMETLog::DebugLevel -- This is for general debugging messages. /// Messages from COMETDebug() and COMETNamedDebug() are printed. /// /// - ICOMETLog::TraceLevel -- This is for really verbose debugging messages. /// Messages from COMETTrace() and COMETNamedTrace() are printed. /// /// ICOMETLog is singleton class that controls the level of output from the /// COMET error and logging statements. All of the user visible /// functions are static. /// /// \section trace Trace Name /// /// A "trace name" is a string that can be used to define a group of log (or /// error) messages that will be printed at the same logging (or error) /// level. These are used with the "Named" variant of the log macros: /// /// \code /// COMETNamedLog("traceArgument","some output"); /// \endcode /// /// \section streamish Streamish Argument /// /// A "streamish argument" is an argument to a logging macro that can be /// compiled into a stream output statement. For example, a call /// /// \code /// COMETLog("The answer is " << 42); /// \endcode /// /// has one streamish argument, and will print "A streamish argument" into the /// log file. The ICOMETLog macros will accept any set of << operator /// arguments that could be printed to std::cout. NOTE: the streamish /// argument doesn't start with a << operator. /// /// \section logRationale Rationale /// /// Writing messages to track the progress of a job, or to help debug software /// has a few specific requirements. In particular, it needs to meet (at /// least) the following desiderata. /// /// -# The amount of output should be controllable at run-time. /// -# The generated code should be very efficient and impose as small a CPU /// requirement as possible. /// -# When a message is "switched off" at run-time, the text for the /// message should not be formatted and none of the routines required to /// generate the text should be called. /// -# It must be possible for the log writing code to be removed at /// compilation time so that it will have no affect on critical sections of /// code. /// -# It must be possible to direct log and error output to separate /// streams. /// -# The type safety and formating of the normal C++ stream operators should /// be leveraged. /// /// These desiderata basically the usual log/error output scheme where the /// basic log code looks like this: /// /// \code /// #ifdef INCLUDE_DEBUGGING_CODE /// if (0 fErrorTraces; static std::map fLogTraces; static int fIndentation; ICOMETLog(); }; /// INTERNAL: A macro to handle the output of an error message. This is used /// by the user visible macros. #ifndef _COMET_OUTPUT_ERROR # define _COMET_OUTPUT_ERROR(trace,outStream) \ do { \ std::ios::fmtflags save = COMET::ICOMETLog::GetDebugStream().flags(); \ COMET::ICOMETLog::GetDebugStream() << trace << __FILE__ \ << ":" << __LINE__ << ": " \ << outStream \ << std::setprecision(6) \ << std::setw(0) \ << std::setfill(' ') \ << std::endl; \ COMET::ICOMETLog::GetDebugStream().flags(save); \ } while (0) #endif /// Set this to false if the error output code should not be included in /// the executable. This can be redefined in user code and depends on the /// optimizers to not emit code a constant contitionals (that's the usual /// behavior for a compiler). #ifndef COMET_ERROR_OUTPUT # define COMET_ERROR_OUTPUT true #endif #ifndef COMETError /// Print an error message that cannot be suppressed by changing the error /// output level. The use of COMETError() can generated a lot of chatter, so /// it should be reserved to cases where you expect the program to crash (for /// example, the next line is a call to abort()), and prefer the COMETSevere /// macro. The COMETError macro takes one \ref streamish providing the error /// message. # define COMETError(outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ _COMET_OUTPUT_ERROR("ERROR: ",outStream); \ } \ } while (0) #else #warning COMETError has been redefined and unexpected behaviour may result. #endif #ifndef COMETNamedError /// Print an named error message that appears at the default error output /// level. The use of COMETNamedError() should generally be reserved to cases /// where you expect the program to crash, or for which data is going to be /// lost. This macro takes two arguments: The first argument must be a \ref /// trace (a string). The second argument is a \ref streamish providing the /// error message. # define COMETNamedError(trace,outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::ErrorLevel <= COMET::ICOMETLog::GetDebugLevel(trace)) \ _COMET_OUTPUT_ERROR("ERROR[" << trace << "]: ", outStream); \ } \ } while (0) #else #warning COMETNamedError has been redefined and unexpected behaviour may result. #endif #ifndef COMETSevere /// Print an error message that appears at the COMET::ICOMETLog::SevereLevel of /// error output. This macro should be reserved for error conditions /// where event data is going to be lost, or where the event might contain /// incorrect information. This macro takes one \ref streamish providing the /// error message. # define COMETSevere(outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::SevereLevel <= COMET::ICOMETLog::GetDebugLevel()) \ _COMET_OUTPUT_ERROR("SEVERE: ",outStream); \ } \ } while (0) #else #warning COMETSevere has been redefined and unexpected behaviour may result. #endif #ifndef COMETNamedSevere /// Print an error message that appears at the COMET::ICOMETLog::SevereLevel of /// error output. This macro should be reserved for error conditions /// where event data is going to be lost, or where the event might contain /// incorrect information. This macro takes two arguments: The first argument /// must be a \ref trace (a string). The second argument is a \ref streamish /// providing the error message. # define COMETNamedSevere(trace,outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::SevereLevel <= COMET::ICOMETLog::GetDebugLevel(trace)) \ _COMET_OUTPUT_ERROR("SEVERE[" << trace << "]: ", outStream); \ } \ } while (0) #else #warning COMETNamedSevere has been redefined and unexpected behaviour may result. #endif #ifndef COMETWarn /// Print an error message that appears at the COMET::ICOMETLog::WarnLevel of /// error output. This macro should be used when a correctable, but /// unexpected, problem is found with an event. This macro takes one \ref /// streamish providing the error message. # define COMETWarn(outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::WarnLevel <= COMET::ICOMETLog::GetDebugLevel()) \ _COMET_OUTPUT_ERROR("WARNING: ",outStream); \ } \ } while (0) #else #warning COMETWarn has been redefined and unexpected behaviour may result. #endif #ifndef COMETNamedWarn /// Print an error message that appears at the COMET::ICOMETLog::WarnLevel of /// error output. This macro should be used when a correctable, but /// unexpected, problem is found with an event. This macro takes two /// arguments: The first argument must be a \ref trace (a string). The second /// argument is a \ref streamish providing the error message. # define COMETNamedWarn(trace,outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::WarnLevel <= COMET::ICOMETLog::GetDebugLevel(trace)) \ _COMET_OUTPUT_ERROR("WARNING[" << trace << "]: ", outStream); \ } \ } while (0) #else #warning COMETNamedWarn has been redefined and unexpected behaviour may result. #endif #ifndef COMETDebug /// Print an debugging message that appears at the COMET::ICOMETLog::DebugLevel of /// error output. This macro should be used to print output needed during /// the debugging. The COMETTrace() macro should be used during debugging to /// provide traces of the code execution. This macro takes one \ref streamish /// providing the error message. #define COMETDebug(outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::DebugLevel <= COMET::ICOMETLog::GetDebugLevel()) \ _COMET_OUTPUT_ERROR("DEBUG: ",outStream); \ } \ } while (0) #else #warning COMETDebug has been redefined and unexpected behaviour may result. #endif #ifndef COMETNamedDebug /// Print an debugging message that appears at the COMET::ICOMETLog::DebugLevel of /// error output. This macro should be used to print output needed during /// the debugging. The COMETTrace() macro should be used during debugging to /// provide traces of the code execution. This macro takes two arguments: The /// first argument must be a \ref trace (a string). The second argument is a /// \ref streamish providing the error message. #define COMETNamedDebug(trace,outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::DebugLevel <= COMET::ICOMETLog::GetDebugLevel(trace)) \ _COMET_OUTPUT_ERROR("DEBUG[" << trace << "]: ", outStream); \ } \ } while (0) #else #warning COMETNamedDebug has been redefined and unexpected behaviour may result. #endif #ifndef COMETTrace /// Print an debugging message that appears at the COMET::ICOMETLog::TraceLevel of /// error output. This macro should be used to print short messages that /// trace the execution of code being debugged. This macro takes one \ref /// streamish providing the error message. #define COMETTrace(outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::TraceLevel <= COMET::ICOMETLog::GetDebugLevel()) \ _COMET_OUTPUT_ERROR("TRACE:",outStream); \ } \ } while (0) #else #warning COMETTrace has been redefined and unexpected behaviour may result. #endif #ifndef COMETNamedTrace /// Print an debugging message that appears at the COMET::ICOMETLog::TraceLevel of /// error output. This macro should be used to print short messages that /// trace the execution of code being debugged. This macro takes two /// arguments: The first argument must be a \ref trace (a string). The second /// argument is a \ref streamish providing the error message. #define COMETNamedTrace(trace,outStream) \ do { \ if (COMET_ERROR_OUTPUT) { \ if (COMET::ICOMETLog::TraceLevel <= COMET::ICOMETLog::GetDebugLevel(trace)) \ _COMET_OUTPUT_ERROR("TRACE[" << trace << "]: ", outStream); \ } \ } while (0) #else #warning COMETNamedTrace has been redefined and unexpected behaviour may result. #endif /// INTERNAL: A macro to handle the output of a log message. This is used /// by the user visible macros. #ifndef _COMET_OUTPUT_LOG #define _COMET_OUTPUT_LOG(trace,outStream) \ do { \ std::ios::fmtflags save = COMET::ICOMETLog::GetLogStream().flags(); \ COMET::ICOMETLog::GetLogStream() << trace \ << COMET::ICOMETLog::MakeIndent() \ << outStream \ << std::setprecision(6) \ << std::setw(0) \ << std::setfill(' ') \ << std::endl; \ COMET::ICOMETLog::GetLogStream().flags(save); \ } while (0) #endif /// Set this to false if the logging output code should not be included in /// the executable. This can be redefined in user code and depends on the /// optimizers to not emit code a constant contitionals (that's the usual /// behavior for a compiler). #ifndef COMET_LOG_OUTPUT # define COMET_LOG_OUTPUT true #endif #ifndef COMETLog /// Print a message to the log that appears at the COMET::ICOMETLog::LogLevel of /// output. This should be reserved for log messages that should appear /// during production. This macro takes one \ref /// streamish providing the log message. #define COMETLog(outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::LogLevel <= COMET::ICOMETLog::GetLogLevel()) \ _COMET_OUTPUT_LOG("% ",outStream); \ } \ } while (0) #else #warning COMETLog has been redefined and unexpected logging \ behaviour may result. #endif #ifndef COMETNamedLog /// Print a message to the log that appears at the COMET::ICOMETLog::LogLevel of /// output. This macro takes two arguments: The first argument must be a \ref /// trace (a string). The second argument is a \ref streamish providing the /// log message. #define COMETNamedLog(trace,outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::LogLevel <= COMET::ICOMETLog::GetLogLevel(trace)) \ _COMET_OUTPUT_LOG("% [" << trace << "] ",outStream); \ } \ } while (0) #else #warning COMETNamedLog has been redefined and unexpected logging \ behaviour may result. #endif #ifndef COMETInfo /// Print a message to the log that appears at the COMET::ICOMETLog::InfoLevel of /// output. This macro takes one \ref streamish providing the log message. #define COMETInfo(outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::InfoLevel <= COMET::ICOMETLog::GetLogLevel()) \ _COMET_OUTPUT_LOG("%% ",outStream); \ } \ } while (0) #else #warning COMETInfo has been redefined and unexpected logging \ behaviour may result. #endif #ifndef COMETNamedInfo /// Print a message to the log that appears at the COMET::ICOMETLog::InfoLevel of /// output. This macro takes two arguments: The first argument must a \ref /// trace (a string). The second argument is a \ref streamish providing the /// log message. #define COMETNamedInfo(trace,outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::InfoLevel <= COMET::ICOMETLog::GetLogLevel(trace)) \ _COMET_OUTPUT_LOG("%% [" << trace << "] ",outStream); \ } \ } while (0) #else #warning COMETNamedInfo has been redefined and unexpected logging \ behaviour may result. #endif #ifndef COMETVerbose /// Print a message to the log that appears at the COMET::ICOMETLog::VerboseLevel of /// output. This macro takes one \ref streamish providing the log message. #define COMETVerbose(outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::VerboseLevel <= COMET::ICOMETLog::GetLogLevel()) \ _COMET_OUTPUT_LOG("%%% ",outStream); \ } \ } while (0) #else #warning COMETVerbose has been redefined and unexpected logging \ behaviour may result. #endif #ifndef COMETNamedVerbose /// Print a message to the log that appears at the COMET::ICOMETLog::VerboseLevel of /// output. This macro takes two arguments: The first argument must be a \ref /// trace (a string). The second argument is a \ref streamish providing the /// log message. #define COMETNamedVerbose(trace,outStream) \ do { \ if (COMET_LOG_OUTPUT) { \ if (COMET::ICOMETLog::VerboseLevel <= COMET::ICOMETLog::GetLogLevel(trace)) \ _COMET_OUTPUT_LOG("%%% [" << trace << "] ",outStream); \ } \ } while (0) #else #warning COMETNamedVerbose has been redefined and unexpected logging \ behaviour may result. #endif #endif