diff --git a/base/logging.cpp b/base/logging.cpp index 0f1e002a9b..1ceb63e2f7 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -1,5 +1,5 @@ -#include "base/assert.hpp" #include "base/logging.hpp" +#include "base/assert.hpp" #include "base/macros.hpp" #include "base/mutex.hpp" #include "base/thread.hpp" @@ -8,94 +8,139 @@ #include "std/target_os.hpp" //#include "std/windows.hpp" +#include #include #include +#include #include #include +#include namespace my { - class LogHelper - { - int m_threadsCount; - std::map m_threadID; - - int GetThreadID() - { - int & id = m_threadID[threads::GetCurrentThreadID()]; - if (id == 0) - id = ++m_threadsCount; - return id; - } - - my::Timer m_timer; - - char const * m_names[5]; - size_t m_lens[5]; - - public: - LogHelper() : m_threadsCount(0) - { - m_names[0] = "DEBUG"; m_lens[0] = 5; - m_names[1] = "INFO"; m_lens[1] = 4; - m_names[2] = "WARNING"; m_lens[2] = 7; - m_names[3] = "ERROR"; m_lens[3] = 5; - m_names[4] = "CRITICAL"; m_lens[4] = 8; - } - - void WriteProlog(std::ostream & s, LogLevel level) - { - s << "LOG"; - - s << " TID(" << GetThreadID() << ")"; - s << " " << m_names[level]; - - double const sec = m_timer.ElapsedSeconds(); - s << " " << std::setfill(' ') << std::setw(static_cast(16 - m_lens[level])) << sec << " "; - } - }; - - std::mutex g_logMutex; - - void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg) - { - std::lock_guard lock(g_logMutex); - - static LogHelper logger; - - std::ostringstream out; - logger.WriteProlog(out, level); - - out << DebugPrint(srcPoint) << msg << std::endl; - std::cerr << out.str(); - - CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level)); - } - - void LogMessageTests(LogLevel level, SrcPoint const &, std::string const & msg) - { - std::lock_guard lock(g_logMutex); - - std::ostringstream out; - out << msg << std::endl; - std::cerr << out.str(); - - CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level)); - } - - LogMessageFn LogMessage = &LogMessageDefault; - - LogMessageFn SetLogMessageFn(LogMessageFn fn) - { - std::swap(LogMessage, fn); - return fn; - } - -#ifdef DEBUG - TLogLevel g_LogLevel = {LDEBUG}; - TLogLevel g_LogAbortLevel = {LERROR}; -#else - TLogLevel g_LogLevel = {LINFO}; - TLogLevel g_LogAbortLevel = {LCRITICAL}; -#endif +std::string ToString(LogLevel level) +{ + auto const & names = GetLogLevelNames(); + CHECK_LESS(level, names.size(), ()); + return names[level]; } + +bool FromString(std::string const & s, LogLevel & level) +{ + auto const & names = GetLogLevelNames(); + auto it = std::find(names.begin(), names.end(), s); + if (it == names.end()) + return false; + level = static_cast(std::distance(names.begin(), it)); + return true; +} + +std::vector const & GetLogLevelNames() +{ + static std::vector const kNames = { + {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}}; + return kNames; +} + +class LogHelper +{ + int m_threadsCount; + std::map m_threadID; + + int GetThreadID() + { + int & id = m_threadID[threads::GetCurrentThreadID()]; + if (id == 0) + id = ++m_threadsCount; + return id; + } + + my::Timer m_timer; + + char const * m_names[5]; + size_t m_lens[5]; + +public: + LogHelper() : m_threadsCount(0) + { + m_names[0] = "DEBUG"; + m_lens[0] = 5; + m_names[1] = "INFO"; + m_lens[1] = 4; + m_names[2] = "WARNING"; + m_lens[2] = 7; + m_names[3] = "ERROR"; + m_lens[3] = 5; + m_names[4] = "CRITICAL"; + m_lens[4] = 8; + } + + void WriteProlog(std::ostream & s, LogLevel level) + { + s << "LOG"; + + s << " TID(" << GetThreadID() << ")"; + s << " " << m_names[level]; + + double const sec = m_timer.ElapsedSeconds(); + s << " " << std::setfill(' ') << std::setw(static_cast(16 - m_lens[level])) << sec << " "; + } +}; + +std::mutex g_logMutex; + +void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg) +{ + std::lock_guard lock(g_logMutex); + + static LogHelper logger; + + std::ostringstream out; + logger.WriteProlog(out, level); + + out << DebugPrint(srcPoint) << msg << std::endl; + std::cerr << out.str(); + + CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level)); +} + +void LogMessageTests(LogLevel level, SrcPoint const &, std::string const & msg) +{ + std::lock_guard lock(g_logMutex); + + std::ostringstream out; + out << msg << std::endl; + std::cerr << out.str(); + + CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level)); +} + +LogMessageFn LogMessage = &LogMessageDefault; + +LogMessageFn SetLogMessageFn(LogMessageFn fn) +{ + std::swap(LogMessage, fn); + return fn; +} + +LogLevel GetDefaultLogLevel() +{ +#if defined(DEBUG) + return LDEBUG; +#else + return LINFO; +#endif // defined(DEBUG) +} + +LogLevel GetDefaultLogAbortLevel() +{ +#if defined(DEBUG) + return LERROR; +#else + return LCRITICAL; +#endif // defined(DEBUG) +} + +TLogLevel g_LogLevel = {GetDefaultLogLevel()}; +TLogLevel g_LogAbortLevel = {GetDefaultLogAbortLevel()}; +} // namespace my diff --git a/base/logging.hpp b/base/logging.hpp index 678a1b1685..fc9fd1d9f0 100644 --- a/base/logging.hpp +++ b/base/logging.hpp @@ -5,54 +5,62 @@ #include "base/src_point.hpp" #include +#include namespace my { - enum LogLevel +enum LogLevel +{ + LDEBUG, + LINFO, + LWARNING, + LERROR, + LCRITICAL +}; + +std::string ToString(LogLevel level); +bool FromString(std::string const & s, LogLevel & level); +std::vector const & GetLogLevelNames(); + +using TLogLevel = std::atomic; +typedef void (*LogMessageFn)(LogLevel level, SrcPoint const &, std::string const &); + +LogLevel GetDefaultLogLevel(); +LogLevel GetDefaultLogAbortLevel(); + +extern LogMessageFn LogMessage; +extern TLogLevel g_LogLevel; +extern TLogLevel g_LogAbortLevel; + +/// @return Pointer to previous message function. +LogMessageFn SetLogMessageFn(LogMessageFn fn); + +void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg); +void LogMessageTests(LogLevel level, SrcPoint const & srcPoint, std::string const & msg); + +/// Scope Guard to temporarily suppress specific log level, for example, in unit tests: +/// ... +/// { +/// LogLevelSuppressor onlyLERRORAndLCriticalLogsAreEnabled; +/// TEST(SomeFunctionWhichHasDebugOrInfoOrWarningLogs(), ()); +/// } +struct ScopedLogLevelChanger +{ + LogLevel m_old = g_LogLevel; + ScopedLogLevelChanger(LogLevel temporaryLogLevel = LERROR) { g_LogLevel = temporaryLogLevel; } + ~ScopedLogLevelChanger() { g_LogLevel = m_old; } +}; + +struct ScopedLogAbortLevelChanger +{ + LogLevel m_old = g_LogAbortLevel; + ScopedLogAbortLevelChanger(LogLevel temporaryLogAbortLevel = LCRITICAL) { - LDEBUG, - LINFO, - LWARNING, - LERROR, - LCRITICAL - }; - - using TLogLevel = std::atomic; - typedef void (*LogMessageFn)(LogLevel level, SrcPoint const &, std::string const &); - - extern LogMessageFn LogMessage; - extern TLogLevel g_LogLevel; - extern TLogLevel g_LogAbortLevel; - - /// @return Pointer to previous message function. - LogMessageFn SetLogMessageFn(LogMessageFn fn); - - void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg); - void LogMessageTests(LogLevel level, SrcPoint const & srcPoint, std::string const & msg); - - /// Scope Guard to temporarily suppress specific log level, for example, in unit tests: - /// ... - /// { - /// LogLevelSuppressor onlyLERRORAndLCriticalLogsAreEnabled; - /// TEST(SomeFunctionWhichHasDebugOrInfoOrWarningLogs(), ()); - /// } - struct ScopedLogLevelChanger - { - LogLevel m_old = g_LogLevel; - ScopedLogLevelChanger(LogLevel temporaryLogLevel = LERROR) { g_LogLevel = temporaryLogLevel; } - ~ScopedLogLevelChanger() { g_LogLevel = m_old; } - }; - - struct ScopedLogAbortLevelChanger - { - LogLevel m_old = g_LogAbortLevel; - ScopedLogAbortLevelChanger(LogLevel temporaryLogAbortLevel = LCRITICAL) - { - g_LogAbortLevel = temporaryLogAbortLevel; - } - ~ScopedLogAbortLevelChanger() { g_LogAbortLevel = m_old; } - }; -} + g_LogAbortLevel = temporaryLogAbortLevel; + } + ~ScopedLogAbortLevelChanger() { g_LogAbortLevel = m_old; } +}; +} // namespace my using ::my::LDEBUG; using ::my::LINFO; @@ -62,9 +70,27 @@ using ::my::LCRITICAL; // Logging macro. // Example usage: LOG(LINFO, (Calc(), m_Var, "Some string constant")); -#define LOG(level, msg) do { if ((level) < ::my::g_LogLevel) {} \ - else { ::my::LogMessage(level, SRC(), ::my::impl::Message msg);} } while (false) +#define LOG(level, msg) \ + do \ + { \ + if ((level) < ::my::g_LogLevel) \ + { \ + } \ + else \ + { \ + ::my::LogMessage(level, SRC(), ::my::impl::Message msg); \ + } \ + } while (false) // Logging macro with short info (without entry point) -#define LOG_SHORT(level, msg) do { if ((level) < ::my::g_LogLevel) {} \ - else { ::my::LogMessage(level, my::SrcPoint(), ::my::impl::Message msg);} } while (false) +#define LOG_SHORT(level, msg) \ + do \ + { \ + if ((level) < ::my::g_LogLevel) \ + { \ + } \ + else \ + { \ + ::my::LogMessage(level, my::SrcPoint(), ::my::impl::Message msg); \ + } \ + } while (false) diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt index 0e33d333dc..6471c3970b 100644 --- a/qt/CMakeLists.txt +++ b/qt/CMakeLists.txt @@ -14,6 +14,7 @@ execute_process( include_directories( ${OMIM_ROOT}/3party/glm + ${OMIM_ROOT}/3party/gflags/src ${CMAKE_CURRENT_BINARY_DIR} ) @@ -74,6 +75,7 @@ omim_link_libraries( base freetype expat + gflags icu jansson protobuf diff --git a/qt/main.cpp b/qt/main.cpp index 3182646158..0cf3066611 100644 --- a/qt/main.cpp +++ b/qt/main.cpp @@ -13,8 +13,10 @@ #include "std/cstdio.hpp" #include "std/cstdlib.hpp" +#include "std/sstream.hpp" #include "3party/Alohalytics/src/alohalytics.h" +#include "3party/gflags/src/gflags/gflags.h" #include @@ -24,17 +26,44 @@ #include #endif +DEFINE_string(log_abort_level, my::ToString(my::GetDefaultLogAbortLevel()), + "Log messages severity that causes termination."); + namespace { - class FinalizeBase +bool ValidateLogAbortLevel(char const * flagname, string const & value) +{ + my::LogLevel level; + if (!my::FromString(value, level)) { - public: - ~FinalizeBase() + ostringstream os; + auto const & names = my::GetLogLevelNames(); + for (size_t i = 0; i < names.size(); ++i) { - // optional - clean allocated data in protobuf library - // useful when using memory and resource leak utilites - //google::protobuf::ShutdownProtobufLibrary(); + if (i != 0) + os << ", "; + os << names[i]; } + + printf("Invalid value for --%s: %s, must be one of: %s\n", flagname, value.c_str(), + os.str().c_str()); + return false; + } + return true; +} + +bool const g_logAbortLevelDummy = + google::RegisterFlagValidator(&FLAGS_log_abort_level, &ValidateLogAbortLevel); + +class FinalizeBase +{ +public: + ~FinalizeBase() + { + // optional - clean allocated data in protobuf library + // useful when using memory and resource leak utilites + // google::protobuf::ShutdownProtobufLibrary(); + } }; #if defined(OMIM_OS_WINDOWS) //&& defined(PROFILER_COMMON) @@ -59,10 +88,17 @@ namespace #else typedef FinalizeBase InitializeFinalize; #endif -} +} // namespace int main(int argc, char * argv[]) { + google::SetUsageMessage("Desktop application."); + google::ParseCommandLineFlags(&argc, &argv, true); + + my::LogLevel level; + CHECK(my::FromString(FLAGS_log_abort_level, level), ()); + my::g_LogAbortLevel = level; + Q_INIT_RESOURCE(resources_common); // Our double parsing code (base/string_utils.hpp) needs dots as a floating point delimiters, not commas. diff --git a/qt/qt.pro b/qt/qt.pro index a5188f9133..5a46cbcb19 100644 --- a/qt/qt.pro +++ b/qt/qt.pro @@ -3,13 +3,15 @@ ROOT_DIR = .. DEPENDENCIES = qt_common map drape_frontend openlr routing search storage tracking traffic routing_common \ indexer drape partners_api local_ads platform editor geometry \ - coding base freetype expat jansson protobuf osrm stats_client \ + coding base freetype expat gflags jansson protobuf osrm stats_client \ minizip succinct pugixml oauthcpp stb_image sdf_image icu DEPENDENCIES += opening_hours \ include($$ROOT_DIR/common.pri) +INCLUDEPATH *= $$ROOT_DIR/3party/gflags/src + TARGET = MAPS.ME TEMPLATE = app CONFIG += warn_on