//////////////////////////////////////////////////////////////////////// // Logging of informational, warning, and error messages. Log is a // singleton which handles all the message reporting to the user, // ensuring that messages are displayed and sent to a log file on disk // as needed. You should not use cerr and cout! // // Author: Stan Seibert // // Initialization // // In the main() function, Log::Init() is called to open the log // file and set the verbosity levels for display to the screen and // for writing to disk. The levels are (in ascending verbosity): // - Log::WARN: Something has gone wrong. // - Log::INFO: Short information messages about what the // program is currently doing. // - Log::DETAIL: Longer messages describing the settings being // used. This should be sufficient to know what the program did and // what information it used. // - Log::DEBUG: Very long messages relevant only for // troubleshooting a software malfunction. // // By default, WARN and INFO are shown to the screen, and WARN, INFO, // and DETAIL are written to disk. Messages with a verbosity higher // than the current setting are discarded. // // Usage // // To send messages to the logging system, there are 4 global // ostream-like objects: warn, info, detail, debug. You // can send strings, numbers, etc to them using the << operator: // // info << "Adding FitCentroid to event loop.\n"; // warn << "No seed found for fit, using default.\n"; // detail << "Fit converged in " << iterations << " iterations.\n"; // debug << "Hit tube list: \n"; // // You do not need if-statements to check the current log level, as // this will be done for you. Note that while these objects behave // much like the ostream cout and cerr, they are not subclasses of // ostream. For example endl does not work on these objects. You // should use "\n" or newline instead. (For the curious, these // are STLplus textio objects.) // // Die: When Really Bad Stuff Happens // // If your code encounters a major problem, it is best to bail out // immediately and tell the user why. For this purpose, use the Die() // method: // // Log::Die("Could not open " + filename + " for input."); // // This will print that message to the "warn" log stream and then // terminate the program. It is generally best to use Die() if you // want to make very certain the user sees your error. Warnings that do not // terminate the program are usually not noticed in the flood of // output that is sent to the screen. // // Assert: Good for sanity checks // // Works like standard assert(), but lets you print something to the user // before dying if condition fails. // // REVISION HISTORY: // - 25/01/2016 N. Barros (UPenn): // - Switched use_buffer to be false by default and GetLogBuffer to // retrieve the contents from the log file instead of the buffer. // - This fixes the long standing "memory leak" in SNO+ RAT investigated by Billy Liggins. // - 02/02/2014 N. Barros (Upenn): // - Completely removed use_buffer since it had no purpose without the buffer itself. // - 20/07/2016 M. Mottram: add ROOT IO constructor for ROOT6 compatibility. //////////////////////////////////////////////////////////////////////// #ifndef __RAT_Log__ #define __RAT_Log__ #include #include "dprintf.hpp" #include "fileio.hpp" #include "stringio.hpp" #include "multiio.hpp" #include "string_utilities.hpp" #include #include #include #include class TRootIOCtor; namespace RAT { // CINT, as usual, has trouble with things like calling a POSIX function // that has existed since 1979. #ifdef __CINT__ #define TTY(x) false #else #define TTY(x) isatty(x) #endif // Define all of the ANSI/VT100 colors, even though white and black // should probably never be used. (Don't assume that the user's // terminal's background is either of these!). This is not, however, a // general interface for terminal disaply attributes, and doesn't allow // for underscored, reversed, blinking, dim, or hidden text. I doubt we // want these. const char * const BLACK __attribute__((unused)) = TTY(1)?"\033[30m" :""; const char * const BBLACK __attribute__((unused)) = TTY(1)?"\033[30;1m":""; // bold black const char * const RED __attribute__((unused)) = TTY(1)?"\033[31m" :""; const char * const BRED __attribute__((unused)) = TTY(1)?"\033[31;1m":""; // bold red const char * const GREEN __attribute__((unused))= TTY(1)?"\033[32m" :""; const char * const BGREEN __attribute__((unused)) = TTY(1)?"\033[32;1m":""; // bold green const char * const YELLOW __attribute__((unused)) = TTY(1)?"\033[33m" :""; const char * const BYELLOW __attribute__((unused)) = TTY(1)?"\033[33;1m":""; // bold yellow const char * const BLUE __attribute__((unused)) = TTY(1)?"\033[34m" :""; const char * const BBLUE __attribute__((unused)) = TTY(1)?"\033[34;1m":""; // bold blue const char * const MAGENTA __attribute__((unused))= TTY(1)?"\033[35m" :""; const char * const BMAGENTA __attribute__((unused))= TTY(1)?"\033[35;1m":""; // bold magenta const char * const CYAN __attribute__((unused)) = TTY(1)?"\033[36m" :""; const char * const BCYAN __attribute__((unused)) = TTY(1)?"\033[36;1m":""; // bold cyan const char * const WHITE __attribute__((unused)) = TTY(1)?"\033[37m" :""; const char * const BWHITE __attribute__((unused)) = TTY(1)?"\033[37;1m":""; // bold cyan const char * const CLR __attribute__((unused)) = TTY(1)?"\033[m" :""; // clear #undef TTY class Log { public: /* Verbosity levels */ enum Level { WARN=0, /* Warning messages */ INFO=1, /* Short informational messages */ DETAIL=2, /* Longer messages related to settings and normal operation */ DEBUG=3 /* Messages only of use to troubleshoot broken code */ }; /* Initialize the logging system. * * _filename Name of log file to open for writing. * display Verbosity level for display on screen * log Verbosity level for writing to log file. */ static bool Init(std::string _filename, Level display=INFO, Level log=DETAIL); /* Get the current verbosity level for display on screen. */ static int GetDisplayLevel() { return display_level; }; /* Get the current verbosity level for display, in Geant4 convention */ static int GetG4DisplayLevel() { switch(GetDisplayLevel()) { case WARN: return 3; case INFO: return 4; case DETAIL: return 5; case DEBUG: return 6; default: return 3; } } /* Set verbosity level for display on screen. */ static void SetDisplayLevel(Level level); /* Get the current verbosity level for writing to log file */ static int GetLogLevel() { return log_level; }; /* Set verbosity level for writing to log file. */ static void SetLogLevel(Level level); /* Write message to warn stream and immediately terminate, sending return_code back to OS. */ static void Die(std::string message, int return_code=1) __attribute__((noreturn)); /* Write message to warn stream and immediately terminate if condition is not true. return_code is returned to the OS to signal job failure */ static void Assert(bool condition, std::string message, int return_code=1); /* Return reference to string containing entire log from this session */ static const std::string GetLogBuffer(); /* Add macro commands to buffer. Don't forget newlines! */ static void AddMacro(const std::string &contents) { macro += contents; }; /* Get the macro commands that have been run so far */ static const std::string &GetMacro() { return macro; }; /* Enable/Disable tracing of RATDB accesses. * * AddDBAccess() does nothing if tracing is disabled (default) */ static void SetDBTraceState(bool state=true) { enable_dbtrace = state; }; /* Get current DB tracing state. */ static bool GetDBTraceState() { return enable_dbtrace; }; /* Add a RATDB integer access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, int value); /* Add a RATDB float access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, float value); /* Add a RATDB double access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, double value); /* Add a RATDB string access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::string &value); /* Add a RATDB integer array access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value); /* Add a RATDB float array access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value); /* Add a RATDB double array access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value); /* Add a RATDB string access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value); /* Add a RATDB string access to the DB trace. */ inline static void TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const json::Value &value); static TMap *GetDBTraceMap() { return dbtrace; }; static void ClearDBTraceMap() { dbtrace->Clear(); } /* For ROOT6 compatibility */ Log(TRootIOCtor*) {}; protected: /* This class cannot be instantiated (except ROOT IO constructor!). */ Log() {}; /* Add a string key/value pair to the dbtrace map */ inline static void AddDBEntry(const std::string &key, const std::string &value); /* Reset output streams to according to verbosity levels. * * Call this method after changing display_level or log_level * to update where warn, info, detail, debug send * messages. */ static void SetupIO(); /* Utility function to remove all output streams from an omtext object. */ static void ClearOMText(omtext *out); /* Pointers to global output streams in order of verbosity. */ static omtext *outstreams[4]; static std::string filename; /* Name of log file. */ static oftext logfile; /* Log file object */ static int display_level; /* Current display verbosity */ static int log_level; /* Current log file verbosity */ static std::string macro; /* Buffer of macro commands run so far */ static bool enable_dbtrace; /* Enable RATDB tracing? */ static TMap *dbtrace; /* Record of all accessed RATDB fields */ }; extern omtext warn; extern omtext info; extern omtext detail; extern omtext debug; // Inline functions void Log::AddDBEntry(const std::string &key, const std::string &value) { if (enable_dbtrace && !dbtrace->FindObject(key.c_str())) dbtrace->Add(new TObjString(key.c_str()), new TObjString(value.c_str())); } // The parameter is type double so this can be used as a seed for to_ratdb_double_string() inline std::string to_ratdb_float_string(double value) { if (value == 0.0) return std::string("0.0"); else return ::to_string(value); } inline std::string to_ratdb_double_string(double value) { std::string str = to_ratdb_float_string(value); size_t pos = str.find("d"); if (pos == std::string::npos) str += "d"; return str; } inline std::string escape_ratdb_string(const std::string &value) { if (value.find("\"") != std::string::npos) Log::Die("RATDB Trace Eror: value string with quotes inside!"); return "\"" + value + "\""; } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, int value) { AddDBEntry(table + "[" + index + "]."+field, ::to_string(value)); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, float value) { AddDBEntry(table + "[" + index + "]."+field, to_ratdb_float_string(value)); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, double value) { AddDBEntry(table + "[" + index + "]."+field, to_ratdb_double_string(value)); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::string &value) { AddDBEntry(table + "[" + index + "]."+field, escape_ratdb_string(value)); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value) { std::string key = table + "[" + index + "]."+field; if (!enable_dbtrace || dbtrace->FindObject(key.c_str())) return; std::string str_value("[ "); for (unsigned i=0; i < value.size(); i++) { str_value += ::to_string(value[i]) + ", "; } str_value += "]"; AddDBEntry(key, str_value); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value) { std::string key = table + "[" + index + "]."+field; if (!enable_dbtrace || dbtrace->FindObject(key.c_str())) return; std::string str_value("[ "); for (unsigned i=0; i < value.size(); i++) { str_value += to_ratdb_float_string(value[i]) + ", "; } str_value += "]"; AddDBEntry(key, str_value); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value) { std::string key = table + "[" + index + "]."+field; if (!enable_dbtrace || dbtrace->FindObject(key.c_str())) return; std::string str_value("[ "); for (unsigned i=0; i < value.size(); i++) { str_value += to_ratdb_double_string(value[i]) + ", "; } str_value += "]"; AddDBEntry(key, str_value); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const std::vector &value) { std::string key = table + "[" + index + "]."+field; if (!enable_dbtrace || dbtrace->FindObject(key.c_str())) return; std::string str_value("[ "); for (unsigned i=0; i < value.size(); i++) { str_value += escape_ratdb_string(value[i]) + ", "; } str_value += "]"; AddDBEntry(key, str_value); } void Log::TraceDBAccess(const std::string &table, const std::string &index, const std::string &field, const json::Value &value) { std::string key = table + "[" + index + "]."+field; if (!enable_dbtrace || dbtrace->FindObject(key.c_str())) return; std::stringstream str_value; json::Writer writer(str_value); writer.putValue(value); AddDBEntry(key, str_value.str()); } } // namespace RAT #endif