Properly format multiline logs

As a side effect, colouring on Windows should now work
regardless of version
This commit is contained in:
moneromooo-monero 2019-06-22 14:42:48 +00:00
parent 2c171a9b02
commit 32f725d32f
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
8 changed files with 217 additions and 66 deletions

View file

@ -18,6 +18,7 @@
#include "easylogging++.h"
#include <unistd.h>
#include <boost/algorithm/string.hpp>
#if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
INITIALIZE_EASYLOGGINGPP
@ -133,6 +134,50 @@ static void abort(int status, const std::string& reason) {
#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
}
static el::Color colorFromLevel(el::Level level)
{
switch (level)
{
case Level::Error: case Level::Fatal: return el::Color::Red;
case Level::Warning: return el::Color::Yellow;
case Level::Debug: return el::Color::Green;
case Level::Info: return el::Color::Cyan;
case Level::Trace: return el::Color::Magenta;
default: return el::Color::Default;
}
}
static void setConsoleColor(el::Color color, bool bright)
{
#if ELPP_OS_WINDOWS
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
switch (color)
{
case el::Color::Red: SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Green: SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Yellow: SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Blue: SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Magenta: SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Cyan: SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); break;
case el::Color::Default: default: SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); break;
}
#else
if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
{
switch (color)
{
case el::Color::Red: ELPP_COUT << (bright ? "\033[1;31m" : "\033[0;31m"); break;
case el::Color::Green: ELPP_COUT << (bright ? "\033[1;32m" : "\033[0;32m"); break;
case el::Color::Yellow: ELPP_COUT << (bright ? "\033[1;33m" : "\033[0;33m"); break;
case el::Color::Blue: ELPP_COUT << (bright ? "\033[1;34m" : "\033[0;34m"); break;
case el::Color::Magenta: ELPP_COUT << (bright ? "\033[1;35m" : "\033[0;35m"); break;
case el::Color::Cyan: ELPP_COUT << (bright ? "\033[1;36m" : "\033[0;36m"); break;
case el::Color::Default: default: ELPP_COUT << "\033[0m"; break;
}
}
#endif
}
} // namespace utils
} // namespace base
@ -609,19 +654,34 @@ void Configurations::unsafeSetGlobally(ConfigurationType configurationType, cons
// LogBuilder
void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) {
void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level, Color color) {
if (!m_termSupportsColor) return;
const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
if (level == Level::Error || level == Level::Fatal)
*logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
else if (level == Level::Warning)
*logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
else if (level == Level::Debug)
*logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
else if (level == Level::Info)
*logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
else if (level == Level::Trace)
*logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
if (color == Color::Red)
*logLine = ELPP_LITERAL("\x1b[1;31m") + *logLine + resetColor;
else if (color == Color::Yellow)
*logLine = ELPP_LITERAL("\x1b[1;33m") + *logLine + resetColor;
else if (color == Color::Green)
*logLine = ELPP_LITERAL("\x1b[1;32m") + *logLine + resetColor;
else if (color == Color::Cyan)
*logLine = ELPP_LITERAL("\x1b[1;36m") + *logLine + resetColor;
else if (color == Color::Magenta)
*logLine = ELPP_LITERAL("\x1b[1;35m") + *logLine + resetColor;
else if (color == Color::Blue)
*logLine = ELPP_LITERAL("\x1b[1;34m") + *logLine + resetColor;
else if (color == Color::Default)
{
if (level == Level::Error || level == Level::Fatal)
*logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
else if (level == Level::Warning)
*logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
else if (level == Level::Debug)
*logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
else if (level == Level::Info)
*logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
else if (level == Level::Trace)
*logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
}
}
// Logger
@ -2372,13 +2432,32 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
m_data = data;
base::TypedConfigurations* tc = m_data->logMessage()->logger()->typedConfigurations();
const base::LogFormat* logFormat = &tc->logFormat(m_data->logMessage()->level());
dispatch(base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), &tc->subsecondPrecision(m_data->logMessage()->level()))
+ "\t" + convertToChar(m_data->logMessage()->level()) + " " + m_data->logMessage()->message() + "\n",
m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog));
const auto &logmsg = m_data->logMessage();
const auto msg = logmsg->message();
if (strchr(msg.c_str(), '\n'))
{
std::vector<std::string> v;
boost::split(v, msg, boost::is_any_of("\n"));
for (const std::string &s: v)
{
LogMessage msgline(logmsg->level(), logmsg->color(), logmsg->file(), logmsg->line(), logmsg->func(), logmsg->verboseLevel(), logmsg->logger(), &s);
dispatch(base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), &tc->subsecondPrecision(m_data->logMessage()->level())) + "\t" + convertToChar(m_data->logMessage()->level()) + " ",
s + "\n",
m_data->logMessage()->logger()->logBuilder()->build(&msgline,
m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog));
}
}
else
{
dispatch(base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), &tc->subsecondPrecision(m_data->logMessage()->level()))
+ "\t" + convertToChar(m_data->logMessage()->level()) + " ", m_data->logMessage()->message() + "\n",
m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog));
}
}
void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLine, base::type::string_t&& logLine) {
void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine) {
if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) {
if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(
@ -2404,9 +2483,14 @@ void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLine, base::
}
if (m_data->dispatchAction() != base::DispatchAction::FileOnlyLog) {
if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&rawLine, m_data->logMessage()->level());
ELPP_COUT << ELPP_COUT_LINE(rawLine);
const el::Level level = m_data->logMessage()->level();
const el::Color color = m_data->logMessage()->color();
el::base::utils::setConsoleColor(el::base::utils::colorFromLevel(level), false);
ELPP_COUT << rawLinePrefix;
el::base::utils::setConsoleColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default);
ELPP_COUT << rawLinePayload;
el::base::utils::setConsoleColor(el::Color::Default, false);
ELPP_COUT << std::flush;
}
}
}
@ -2447,7 +2531,7 @@ void AsyncLogDispatchCallback::handle(const LogDispatchData* data) {
if ((data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog)
&& data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level(), data->logMessage()->color());
ELPP_COUT << ELPP_COUT_LINE(logLine);
}
// Save resources and only queue if we want to write to file otherwise just ignore handler
@ -2739,7 +2823,7 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee
ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
}
}
Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
Writer(Level::Debug, Color::Default, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
<< "Logger [" << loggerId << "] is not registered yet!";
m_proceed = false;
} else {
@ -2813,7 +2897,7 @@ void Writer::processDispatch() {
void Writer::triggerDispatch(void) {
if (m_proceed) {
if (m_msg == nullptr) {
LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel,
LogMessage msg(m_level, m_color, m_file, m_line, m_func, m_verboseLevel,
m_logger);
base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch();
} else {
@ -2826,7 +2910,7 @@ void Writer::triggerDispatch(void) {
}
if (m_proceed && m_level == Level::Fatal
&& !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
base::Writer(Level::Warning, Color::Default, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
<< "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
std::stringstream reasonStream;
reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"

View file

@ -604,6 +604,15 @@ enum class Level : base::type::EnumType {
/// @brief Represents unknown level
Unknown = 1010
};
enum class Color : base::type::EnumType {
Default,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
};
} // namespace el
namespace std {
template<> struct hash<el::Level> {
@ -2225,7 +2234,7 @@ class LogBuilder : base::NoCopy {
ELPP_INTERNAL_INFO(3, "Destroying log builder...")
}
virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0;
void convertToColoredOutput(base::type::string_t* logLine, Level level);
void convertToColoredOutput(base::type::string_t* logLine, Level level, Color color);
private:
bool m_termSupportsColor;
friend class el::base::DefaultLogDispatchCallback;
@ -2503,14 +2512,17 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
} // namespace base
class LogMessage {
public:
LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func,
base::type::VerboseLevel verboseLevel, Logger* logger) :
m_level(level), m_file(file), m_line(line), m_func(func),
m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) {
LogMessage(Level level, Color color, const std::string& file, base::type::LineNumber line, const std::string& func,
base::type::VerboseLevel verboseLevel, Logger* logger, const base::type::string_t *msg = nullptr) :
m_level(level), m_color(color), m_file(file), m_line(line), m_func(func),
m_verboseLevel(verboseLevel), m_logger(logger), m_message(msg ? *msg : logger->stream().str()) {
}
inline Level level(void) const {
return m_level;
}
inline Color color(void) const {
return m_color;
}
inline const std::string& file(void) const {
return m_file;
}
@ -2531,6 +2543,7 @@ class LogMessage {
}
private:
Level m_level;
Color m_color;
std::string m_file;
base::type::LineNumber m_line;
std::string m_func;
@ -2781,7 +2794,7 @@ class DefaultLogDispatchCallback : public LogDispatchCallback {
void handle(const LogDispatchData* data);
private:
const LogDispatchData* m_data;
void dispatch(base::type::string_t&& rawLine, base::type::string_t&& logLine);
void dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine);
};
#if ELPP_ASYNC_LOGGING
class AsyncLogDispatchCallback : public LogDispatchCallback {
@ -3242,10 +3255,10 @@ class NullWriter : base::NoCopy {
/// @brief Main entry point of each logging
class Writer : base::NoCopy {
public:
Writer(Level level, const char* file, base::type::LineNumber line,
Writer(Level level, Color color, const char* file, base::type::LineNumber line,
const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
base::type::VerboseLevel verboseLevel = 0) :
m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
m_msg(nullptr), m_level(level), m_color(color), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {
}
@ -3299,6 +3312,7 @@ class Writer : base::NoCopy {
protected:
LogMessage* m_msg;
Level m_level;
Color m_color;
const char* m_file;
const base::type::LineNumber m_line;
const char* m_func;
@ -3317,10 +3331,10 @@ class Writer : base::NoCopy {
};
class PErrorWriter : public base::Writer {
public:
PErrorWriter(Level level, const char* file, base::type::LineNumber line,
PErrorWriter(Level level, Color color, const char* file, base::type::LineNumber line,
const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
base::type::VerboseLevel verboseLevel = 0) :
base::Writer(level, file, line, func, dispatchAction, verboseLevel) {
base::Writer(level, color, file, line, func, dispatchAction, verboseLevel) {
}
virtual ~PErrorWriter(void);
@ -3353,14 +3367,14 @@ template <typename T>
void Logger::log_(Level level, int vlevel, const T& log) {
if (level == Level::Verbose) {
if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) {
base::Writer(Level::Verbose, "FILE", 0, "FUNCTION",
base::Writer(Level::Verbose, Color::Default, "FILE", 0, "FUNCTION",
base::DispatchAction::NormalLog, vlevel).construct(this, false) << log;
} else {
stream().str(ELPP_LITERAL(""));
releaseLock();
}
} else {
base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log;
base::Writer(level, Color::Default, "FILE", 0, "FUNCTION").construct(this, false) << log;
}
}
template <typename T, typename... Args>
@ -3460,18 +3474,18 @@ LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace)
#endif // ELPP_COMPILER_MSVC
#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \
writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \
writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \
ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \
writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \
ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \
writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \
ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \
writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
class PerformanceTrackingData {
public: