diff --git a/log.cpp b/log.cpp index ab17e01b..77b61470 100644 --- a/log.cpp +++ b/log.cpp @@ -54,6 +54,9 @@ ALL_LEVELS[] = { static util::level_t to_level (std::string name) { + if (std::empty (name)) + return util::EMERGENCY; + static const std::map NAME_LEVELS = { { "EMERGENCY", util::EMERGENCY }, { "ALERT", util::ALERT }, @@ -106,8 +109,14 @@ util::operator<< (std::ostream& os, util::level_t l) /////////////////////////////////////////////////////////////////////////////// -static util::level_t -log_level (void) +// Determine what the value for LOG_LEVEL should be at the beginning of +// execution given the system environment. +// +// Note that the LOG macros _cannot_ be used from within this function as it +// will likely result in infinite recursion. +static +util::level_t +initial_log_level (void) { const char *env = getenv ("LOG_LEVEL"); if (!env) @@ -116,12 +125,42 @@ log_level (void) try { return to_level (env); } catch (...) { - LOG_ERROR("Invalid environment LOG_LEVEL: '%s'", env); + std::clog << "Invalid environment LOG_LEVEL: '" << env << "'\n"; return util::DEFAULT_LOG_LEVEL; } } +//----------------------------------------------------------------------------- +// We shouldn't ever actually get to use the default value, but we set it to +// the most verbose option just in case we've made a mistake elsewhere. + +static bool s_log_level_done; +static util::level_t s_log_level_value; + +//----------------------------------------------------------------------------- +util::level_t +util::log_level (level_t _level) +{ + s_log_level_value = _level; + s_log_level_done = true; + return s_log_level_value; +} + + +//----------------------------------------------------------------------------- +util::level_t +util::log_level (void) +{ + if (!s_log_level_done) { + s_log_level_value = initial_log_level (); + s_log_level_done = true; + } + + return s_log_level_value; +} + + //----------------------------------------------------------------------------- static bool needs_break (util::level_t level) @@ -195,8 +234,7 @@ level_width (void) void util::log (util::level_t level, const std::string &msg) { - static const util::level_t LOG_LEVEL = log_level (); - if (level <= LOG_LEVEL) { + if (level <= log_level ()) { static const size_t time_len = strlen("YYYY-mm-dd HHMMhSS") + 1; std::string time_string (time_len - 1, '\0'); time_t unix_time = time (nullptr); @@ -209,7 +247,7 @@ util::log (util::level_t level, const std::string &msg) return; } - std::cerr << time_string << " [" + std::clog << time_string << " [" << level_colour (level) << std::setw (trunc_cast (level_width ())) << std::left diff --git a/log.hpp b/log.hpp index c9e3ee1c..aa30788c 100644 --- a/log.hpp +++ b/log.hpp @@ -32,7 +32,6 @@ namespace util { /////////////////////////////////////////////////////////////////////////// // rfc5424 log levels. It is assumed they are contiguous to simplify array // indexing in logging code. - // enum level_t { EMERGENCY, /** system is unusable */ ALERT, /** action must be taken immediately */ @@ -58,14 +57,39 @@ namespace util { operator<< (std::ostream&, level_t); + /////////////////////////////////////////////////////////////////////////// + // Query or set the global logging level filter. If the severity passed to + // a logging call is less than this then the output is discarded. + // + // The initial logging level can be controlled by setting the environment + // variable LOG_LEVEL to a string corresponding to the names of the values + // in level_t. + // + // Otherwise we fall back to the hard coded default from above. + // + // As a special case, a blank value for the environment variable LOG_LEVEL + // corresponds to the maximum log level. It indicates a preference for + // minimum output, and may be changed to disable all output in future + // updates. + level_t log_level (void); + level_t log_level (level_t); + /////////////////////////////////////////////////////////////////////////// void log (level_t, const std::string &msg); + template - void log (level_t, const char (&fmt)[N], const Args&...); + void + log (level_t, const char (&fmt)[N], const Args&...); //------------------------------------------------------------------------- + // Various convenience macros for logging specific strings with a well + // known severity. + // + // LOG_DEBUG is treated similarly to assert; if NDEBUG is defined then we + // compile out the statement so as to gain a little runtime efficiency + // speed. #define LOG_EMERGENCY(...) do { util::log(util::EMERGENCY, ##__VA_ARGS__); } while (0) #define LOG_ALERT(...) do { util::log(util::ALERT, ##__VA_ARGS__); } while (0) #define LOG_CRITICAL(...) do { util::log(util::CRITICAL, ##__VA_ARGS__); } while (0) @@ -74,7 +98,7 @@ namespace util { #define LOG_WARN(...) do { util::log(util::WARN, ##__VA_ARGS__); } while (0) #define LOG_NOTICE(...) do { util::log(util::NOTICE, ##__VA_ARGS__); } while (0) #define LOG_INFO(...) do { util::log(util::INFO, ##__VA_ARGS__); } while (0) -#if defined(ENABLE_DEBUGGING) +#if !defined(NDEBUG) #define LOG_DEBUG(...) do { util::log(util::DEBUG, ##__VA_ARGS__); } while (0) #else #define LOG_DEBUG(...) do { ; } while (0)