#include "util/log.hpp" #include "util/isatty.hpp" #include #include #include #include #include #include namespace osrm::util { namespace { static const char COL_RESET[]{"\x1b[0m"}; static const char RED[]{"\x1b[31m"}; static const char YELLOW[]{"\x1b[33m"}; #ifdef ENABLE_DEBUG_LOGGING static const char MAGENTA[]{"\x1b[35m"}; #endif // static const char GREEN[] { "\x1b[32m"}; // static const char BLUE[] { "\x1b[34m"}; // static const char CYAN[] { "\x1b[36m"}; } // namespace void LogPolicy::Unmute() { m_is_mute = false; } void LogPolicy::Mute() { m_is_mute = true; } bool LogPolicy::IsMute() const { return m_is_mute; } LogLevel LogPolicy::GetLevel() const { return m_level; } void LogPolicy::SetLevel(LogLevel level) { m_level = level; } void LogPolicy::SetLevel(std::string const &level) { // Keep in sync with LogLevel definition if (boost::iequals(level, "NONE")) m_level = logNONE; else if (boost::iequals(level, "ERROR")) m_level = logERROR; else if (boost::iequals(level, "WARNING")) m_level = logWARNING; else if (boost::iequals(level, "INFO")) m_level = logINFO; else if (boost::iequals(level, "DEBUG")) m_level = logDEBUG; else m_level = logINFO; } LogPolicy &LogPolicy::GetInstance() { static LogPolicy runningInstance; return runningInstance; } std::string LogPolicy::GetLevels() { // Keep in sync with LogLevel definition return "NONE, ERROR, WARNING, INFO, DEBUG"; } Log::Log(LogLevel level_, std::ostream &ostream) : level(level_), stream(ostream) { Init(); } Log::Log(LogLevel level_) : level(level_), buffer{}, stream{buffer} { Init(); } void Log::Init() { std::lock_guard lock(get_mutex()); if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel()) { const bool is_terminal = IsStdoutATTY(); auto format = [is_terminal](const char *level, const char *color) { const auto timestamp = std::chrono::system_clock::now(); return fmt::format("{}[{:%FT%H:%M:}{:%S}] [{}] ", is_terminal ? color : "", timestamp, timestamp.time_since_epoch(), level); }; switch (level) { case logNONE: break; case logWARNING: stream << format("warn", YELLOW); break; case logERROR: stream << format("error", RED); break; case logDEBUG: #ifdef ENABLE_DEBUG_LOGGING stream << format("debug", MAGENTA); #endif break; default: // logINFO: stream << format("info", ""); break; } } } std::mutex &Log::get_mutex() { static std::mutex mtx; return mtx; } /** * Close down this logging instance. * This destructor is responsible for flushing any buffered data, * and printing a newline character (each logger object is responsible for only one line) * Because sub-classes can replace the `stream` object - we need to verify whether * we're writing to std::cerr/cout, or whether we should write to the stream */ Log::~Log() { std::lock_guard lock(get_mutex()); if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel()) { const bool usestd = (&stream == &buffer); const bool is_terminal = IsStdoutATTY(); if (usestd) { switch (level) { case logNONE: break; case logWARNING: case logERROR: std::cerr << buffer.str(); std::cerr << (is_terminal ? COL_RESET : ""); std::cerr << std::endl; break; case logDEBUG: #ifndef ENABLE_DEBUG_LOGGING break; #endif case logINFO: default: std::cout << buffer.str(); std::cout << (is_terminal ? COL_RESET : ""); std::cout << std::endl; break; } } else { stream << (is_terminal ? COL_RESET : ""); stream << std::endl; } } } UnbufferedLog::UnbufferedLog(LogLevel level_) : Log(level_, (level_ == logWARNING || level_ == logERROR) ? std::cerr : std::cout) { stream.flags(std::ios_base::unitbuf); } } // namespace osrm::util