[qt] Implemented log-abort-level flag for Qt Desktop app.

This commit is contained in:
Yuri Gorshenin 2017-06-06 14:36:33 +03:00
parent 3ea5960a80
commit 9b98ba2983
5 changed files with 251 additions and 140 deletions

View file

@ -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 <algorithm>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <mutex>
#include <sstream>
#include <vector>
namespace my
{
class LogHelper
{
int m_threadsCount;
std::map<threads::ThreadID, int> 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<int>(16 - m_lens[level])) << sec << " ";
}
};
std::mutex g_logMutex;
void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<LogLevel>(std::distance(names.begin(), it));
return true;
}
std::vector<std::string> const & GetLogLevelNames()
{
static std::vector<std::string> const kNames = {
{"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}};
return kNames;
}
class LogHelper
{
int m_threadsCount;
std::map<threads::ThreadID, int> 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<int>(16 - m_lens[level])) << sec << " ";
}
};
std::mutex g_logMutex;
void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg)
{
std::lock_guard<std::mutex> 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<std::mutex> 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

View file

@ -5,54 +5,62 @@
#include "base/src_point.hpp"
#include <atomic>
#include <string>
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<std::string> const & GetLogLevelNames();
using TLogLevel = std::atomic<LogLevel>;
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<LogLevel>;
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)

View file

@ -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

View file

@ -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 <QtCore/QDir>
@ -24,17 +26,44 @@
#include <QtWidgets/QApplication>
#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.

View file

@ -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