monero/external/easylogging++/easylogging++.cc
2023-10-31 09:09:55 +01:00

3557 lines
134 KiB
C++

//
// Bismillah ar-Rahmaan ar-Raheem
//
// Easylogging++ v9.96.7
// Cross-platform logging library for C++ applications
//
// Copyright (c) 2012-2018 Zuhd Web Services
// Copyright (c) 2012-2018 @abumusamq
//
// This library is released under the MIT Licence.
// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE
//
// https://zuhd.org
// http://muflihun.com
//
#define EASYLOGGING_CC
#include "easylogging++.h"
#include <atomic>
#include <unistd.h>
#if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
INITIALIZE_EASYLOGGINGPP
#endif
namespace el {
// el::base
namespace base {
// el::base::consts
namespace consts {
// Level log values - These are values that are replaced in place of %level format specifier
// Extra spaces after format specifiers are only for readability purposes in log files
static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO");
static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG");
static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING");
static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR");
static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL");
static const base::type::char_t* kVerboseLevelLogValue =
ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE");
static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I");
static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D");
static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W");
static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E");
static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F");
static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V");
static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T");
// Format specifiers - These are used to define log format
static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app");
static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger");
static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread");
static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level");
static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort");
static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime");
static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file");
static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase");
static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line");
static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc");
static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func");
static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user");
static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host");
static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg");
static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel");
static const char* kDateTimeFormatSpecifierForFilename = "%datetime";
// Date/time
static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August",
"September", "October", "November", "December"
};
static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g";
static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m";
static const int kYearBase = 1900;
static const char* kAm = "AM";
static const char* kPm = "PM";
// Miscellaneous constants
static const char* kNullPointer = "nullptr";
#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
static const base::type::VerboseLevel kMaxVerboseLevel = 9;
static const char* kUnknownUser = "user";
static const char* kUnknownHost = "unknown-host";
//---------------- DEFAULT LOG FILE -----------------------
#if defined(ELPP_NO_DEFAULT_LOG_FILE)
# if ELPP_OS_UNIX
static const char* kDefaultLogFile = "/dev/null";
# elif ELPP_OS_WINDOWS
static const char* kDefaultLogFile = "nul";
# endif // ELPP_OS_UNIX
#elif defined(ELPP_DEFAULT_LOG_FILE)
static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE;
#else
static const char* kDefaultLogFile = "myeasylog.log";
#endif // defined(ELPP_NO_DEFAULT_LOG_FILE)
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
static const char* kDefaultLogFileParam = "--default-log-file";
#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
static const char* kLoggingFlagsParam = "--logging-flags";
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
static const char* kValidLoggerIdSymbols =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
static const char* kConfigurationComment = "##";
static const char* kConfigurationLevel = "*";
static const char* kConfigurationLoggerId = "--";
}
// el::base::utils
namespace utils {
/// @brief Aborts application due with user-defined status
static void abort(int status, const std::string& reason) {
// Both status and reason params are there for debugging with tools like gdb etc
ELPP_UNUSED(status);
ELPP_UNUSED(reason);
#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
// Ignore msvc critical error dialog - break instead (on debug mode)
_asm int 3
#else
#ifdef NDEBUG
::_exit(1);
#else
::abort();
#endif
#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)
{
static const char *no_color_var = getenv("NO_COLOR");
static const bool no_color = no_color_var && *no_color_var; // apparently, NO_COLOR=0 means no color too (as per no-color.org)
if (no_color)
return;
#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
// el
// LevelHelper
const char* LevelHelper::convertToString(Level level) {
// Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
if (level == Level::Global) return "GLOBAL";
if (level == Level::Debug) return "DEBUG";
if (level == Level::Info) return "INFO";
if (level == Level::Warning) return "WARNING";
if (level == Level::Error) return "ERROR";
if (level == Level::Fatal) return "FATAL";
if (level == Level::Verbose) return "VERBOSE";
if (level == Level::Trace) return "TRACE";
return "UNKNOWN";
}
struct StringToLevelItem {
const char* levelString;
Level level;
};
static struct StringToLevelItem stringToLevelMap[] = {
{ "global", Level::Global },
{ "debug", Level::Debug },
{ "info", Level::Info },
{ "warning", Level::Warning },
{ "error", Level::Error },
{ "fatal", Level::Fatal },
{ "verbose", Level::Verbose },
{ "trace", Level::Trace }
};
Level LevelHelper::convertFromString(const char* levelStr) {
for (auto& item : stringToLevelMap) {
if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) {
return item.level;
}
}
return Level::Unknown;
}
Level LevelHelper::convertFromStringPrefix(const char* levelStr) {
if ((strncmp(levelStr, "GLOBAL", 6) == 0) || (strncmp(levelStr, "global", 6) == 0))
return Level::Global;
if ((strncmp(levelStr, "DEBUG", 5) == 0) || (strncmp(levelStr, "debug", 5) == 0))
return Level::Debug;
if ((strncmp(levelStr, "INFO", 4) == 0) || (strncmp(levelStr, "info", 4) == 0))
return Level::Info;
if ((strncmp(levelStr, "WARNING", 7) == 0) || (strncmp(levelStr, "warning", 7) == 0))
return Level::Warning;
if ((strncmp(levelStr, "ERROR", 5) == 0) || (strncmp(levelStr, "error", 5) == 0))
return Level::Error;
if ((strncmp(levelStr, "FATAL", 5) == 0) || (strncmp(levelStr, "fatal", 5) == 0))
return Level::Fatal;
if ((strncmp(levelStr, "VERBOSE", 7) == 0) || (strncmp(levelStr, "verbose", 7) == 0))
return Level::Verbose;
if ((strncmp(levelStr, "TRACE", 5) == 0) || (strncmp(levelStr, "trace", 5) == 0))
return Level::Trace;
return Level::Unknown;
}
void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
do {
if (fn()) {
break;
}
*startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
} while (*startIndex <= lIndexMax);
}
// ConfigurationTypeHelper
const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) {
// Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
if (configurationType == ConfigurationType::Enabled) return "ENABLED";
if (configurationType == ConfigurationType::Filename) return "FILENAME";
if (configurationType == ConfigurationType::Format) return "FORMAT";
if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION";
if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
return "UNKNOWN";
}
struct ConfigurationStringToTypeItem {
const char* configString;
ConfigurationType configType;
};
static struct ConfigurationStringToTypeItem configStringToTypeMap[] = {
{ "enabled", ConfigurationType::Enabled },
{ "to_file", ConfigurationType::ToFile },
{ "to_standard_output", ConfigurationType::ToStandardOutput },
{ "format", ConfigurationType::Format },
{ "filename", ConfigurationType::Filename },
{ "subsecond_precision", ConfigurationType::SubsecondPrecision },
{ "milliseconds_width", ConfigurationType::MillisecondsWidth },
{ "performance_tracking", ConfigurationType::PerformanceTracking },
{ "max_log_file_size", ConfigurationType::MaxLogFileSize },
{ "log_flush_threshold", ConfigurationType::LogFlushThreshold },
};
ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) {
for (auto& item : configStringToTypeMap) {
if (base::utils::Str::cStringCaseEq(configStr, item.configString)) {
return item.configType;
}
}
return ConfigurationType::Unknown;
}
void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
do {
if (fn()) {
break;
}
*startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
} while (*startIndex <= cIndexMax);
}
// Configuration
Configuration::Configuration(const Configuration& c) :
m_level(c.m_level),
m_configurationType(c.m_configurationType),
m_value(c.m_value) {
}
Configuration& Configuration::operator=(const Configuration& c) {
if (&c != this) {
m_level = c.m_level;
m_configurationType = c.m_configurationType;
m_value = c.m_value;
}
return *this;
}
/// @brief Full constructor used to sets value of configuration
Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
m_level(level),
m_configurationType(configurationType),
m_value(value) {
}
void Configuration::log(el::base::type::ostream_t& os) const {
os << LevelHelper::convertToString(m_level)
<< ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
<< ELPP_LITERAL(" = ") << m_value.c_str();
}
/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) :
m_level(level),
m_configurationType(configurationType) {
}
bool Configuration::Predicate::operator()(const Configuration* conf) const {
return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
}
// Configurations
Configurations::Configurations(void) :
m_configurationFile(std::string()),
m_isFromFile(false) {
}
Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining,
Configurations* base) :
m_configurationFile(configurationFile),
m_isFromFile(false) {
parseFromFile(configurationFile, base);
if (useDefaultsForRemaining) {
setRemainingToDefault();
}
}
bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) {
// We initial assertion with true because if we have assertion diabled, we want to pass this
// check and if assertion is enabled we will have values re-assigned any way.
bool assertionPassed = true;
ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true,
"Configuration file [" << configurationFile << "] does not exist!");
if (!assertionPassed) {
return false;
}
bool success = Parser::parseFromFile(configurationFile, this, base);
m_isFromFile = success;
return success;
}
bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) {
bool success = Parser::parseFromText(configurationsString, this, base);
if (success) {
m_isFromFile = false;
}
return success;
}
void Configurations::setFromBase(Configurations* base) {
if (base == nullptr || base == this) {
return;
}
base::threading::ScopedLock scopedLock(base->lock());
for (Configuration*& conf : base->list()) {
set(conf);
}
}
bool Configurations::hasConfiguration(ConfigurationType configurationType) {
base::type::EnumType lIndex = LevelHelper::kMinValid;
bool result = false;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
result = true;
}
return result;
});
return result;
}
bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) {
base::threading::ScopedLock scopedLock(lock());
#if ELPP_COMPILER_INTEL
// We cant specify template types here, Intel C++ throws compilation error
// "error: type name is not allowed"
return RegistryWithPred::get(level, configurationType) != nullptr;
#else
return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr;
#endif // ELPP_COMPILER_INTEL
}
void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) {
base::threading::ScopedLock scopedLock(lock());
unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex
if (level == Level::Global) {
unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either
}
}
void Configurations::set(Configuration* conf) {
if (conf == nullptr) {
return;
}
set(conf->level(), conf->configurationType(), conf->value());
}
void Configurations::setToDefault(void) {
setGlobally(ConfigurationType::Enabled, std::string("true"), true);
setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
#if defined(ELPP_NO_LOG_TO_FILE)
setGlobally(ConfigurationType::ToFile, std::string("false"), true);
#else
setGlobally(ConfigurationType::ToFile, std::string("true"), true);
#endif // defined(ELPP_NO_LOG_TO_FILE)
setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true);
setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
set(Level::Debug, ConfigurationType::Format,
std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
// INFO and WARNING are set to default by Level::Global
set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
}
void Configurations::setRemainingToDefault(void) {
base::threading::ScopedLock scopedLock(lock());
#if defined(ELPP_NO_LOG_TO_FILE)
unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false"));
#else
unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
#endif // defined(ELPP_NO_LOG_TO_FILE)
unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format,
std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
// INFO and WARNING are set to default by Level::Global
unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format,
std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
}
bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender,
Configurations* base) {
sender->setFromBase(base);
std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
bool parsedSuccessfully = false;
std::string line = std::string();
Level currLevel = Level::Unknown;
std::string currConfigStr = std::string();
std::string currLevelStr = std::string();
while (fileStream_.good()) {
std::getline(fileStream_, line);
parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
}
return parsedSuccessfully;
}
bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender,
Configurations* base) {
sender->setFromBase(base);
bool parsedSuccessfully = false;
std::stringstream ss(configurationsString);
std::string line = std::string();
Level currLevel = Level::Unknown;
std::string currConfigStr = std::string();
std::string currLevelStr = std::string();
while (std::getline(ss, line)) {
parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
}
return parsedSuccessfully;
}
void Configurations::Parser::ignoreComments(std::string* line) {
std::size_t foundAt = 0;
std::size_t quotesStart = line->find("\"");
std::size_t quotesEnd = std::string::npos;
if (quotesStart != std::string::npos) {
quotesEnd = line->find("\"", quotesStart + 1);
while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
// Do not erase slash yet - we will erase it in parseLine(..) while loop
quotesEnd = line->find("\"", quotesEnd + 2);
}
}
if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
if (foundAt < quotesEnd) {
foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
}
*line = line->substr(0, foundAt);
}
}
bool Configurations::Parser::isLevel(const std::string& line) {
return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
}
bool Configurations::Parser::isComment(const std::string& line) {
return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
}
bool Configurations::Parser::isConfig(const std::string& line) {
std::size_t assignment = line.find('=');
return line != "" &&
((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) &&
(assignment != std::string::npos) &&
(line.size() > assignment);
}
bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr,
Level* currLevel,
Configurations* conf) {
ConfigurationType currConfig = ConfigurationType::Unknown;
std::string currValue = std::string();
*line = base::utils::Str::trim(*line);
if (isComment(*line)) return true;
ignoreComments(line);
*line = base::utils::Str::trim(*line);
if (line->empty()) {
// Comment ignored
return true;
}
if (isLevel(*line)) {
if (line->size() <= 2) {
return true;
}
*currLevelStr = line->substr(1, line->size() - 2);
*currLevelStr = base::utils::Str::toUpper(*currLevelStr);
*currLevelStr = base::utils::Str::trim(*currLevelStr);
*currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
return true;
}
if (isConfig(*line)) {
std::size_t assignment = line->find('=');
*currConfigStr = line->substr(0, assignment);
*currConfigStr = base::utils::Str::toUpper(*currConfigStr);
*currConfigStr = base::utils::Str::trim(*currConfigStr);
currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
currValue = line->substr(assignment + 1);
currValue = base::utils::Str::trim(currValue);
std::size_t quotesStart = currValue.find("\"", 0);
std::size_t quotesEnd = std::string::npos;
if (quotesStart != std::string::npos) {
quotesEnd = currValue.find("\"", quotesStart + 1);
while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
currValue = currValue.erase(quotesEnd - 1, 1);
quotesEnd = currValue.find("\"", quotesEnd + 2);
}
}
if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
// Quote provided - check and strip if valid
ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in ["
<< currConfigStr << "]");
ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
// Explicit check in case if assertion is disabled
currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
}
}
}
ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
return false; // unrecognizable level or config
}
conf->set(*currLevel, currConfig, currValue);
return true;
}
void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
if (conf == nullptr) {
unsafeSet(level, configurationType, value);
}
}
void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
if (conf == nullptr) {
registerNew(new Configuration(level, configurationType, value));
} else {
conf->setValue(value);
}
if (level == Level::Global) {
unsafeSetGlobally(configurationType, value, false);
}
}
void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value,
bool includeGlobalLevel) {
if (includeGlobalLevel) {
set(Level::Global, configurationType, value);
}
base::type::EnumType lIndex = LevelHelper::kMinValid;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
set(LevelHelper::castFromInt(lIndex), configurationType, value);
return false; // Do not break lambda function yet as we need to set all levels regardless
});
}
void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value,
bool includeGlobalLevel) {
if (includeGlobalLevel) {
unsafeSet(Level::Global, configurationType, value);
}
base::type::EnumType lIndex = LevelHelper::kMinValid;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
return false; // Do not break lambda function yet as we need to set all levels regardless
});
}
// LogBuilder
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 (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;
}
}
void LogBuilder::setColor(Color color, bool bright) {
#if !ELPP_OS_WINDOWS
if (m_termSupportsColor)
#endif
el::base::utils::setConsoleColor(color, bright);
}
// Logger
Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
m_id(id),
m_typedConfigurations(nullptr),
m_parentApplicationName(std::string()),
m_isConfigured(false),
m_logStreamsReference(logStreamsReference) {
initUnflushedCount();
}
Logger::Logger(const std::string& id, const Configurations& configurations,
base::LogStreamsReferenceMap* logStreamsReference) :
m_id(id),
m_typedConfigurations(nullptr),
m_parentApplicationName(std::string()),
m_isConfigured(false),
m_logStreamsReference(logStreamsReference) {
initUnflushedCount();
configure(configurations);
}
Logger::Logger(const Logger& logger) {
m_id = logger.m_id;
m_typedConfigurations = logger.m_typedConfigurations ? new base::TypedConfigurations(*logger.m_typedConfigurations) : nullptr;
m_parentApplicationName = logger.m_parentApplicationName;
m_isConfigured = logger.m_isConfigured;
m_configurations = logger.m_configurations;
m_unflushedCount = logger.m_unflushedCount;
m_logStreamsReference = logger.m_logStreamsReference;
}
Logger& Logger::operator=(const Logger& logger) {
if (&logger != this) {
base::utils::safeDelete(m_typedConfigurations);
m_id = logger.m_id;
m_typedConfigurations = logger.m_typedConfigurations ? new base::TypedConfigurations(*logger.m_typedConfigurations) : nullptr;
m_parentApplicationName = logger.m_parentApplicationName;
m_isConfigured = logger.m_isConfigured;
m_configurations = logger.m_configurations;
m_unflushedCount = logger.m_unflushedCount;
m_logStreamsReference = logger.m_logStreamsReference;
}
return *this;
}
void Logger::configure(const Configurations& configurations) {
m_isConfigured = false; // we set it to false in case if we fail
initUnflushedCount();
if (m_typedConfigurations != nullptr) {
Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations());
if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
flush();
}
}
base::threading::ScopedLock scopedLock(lock());
if (m_configurations != configurations) {
m_configurations.setFromBase(const_cast<Configurations*>(&configurations));
}
base::utils::safeDelete(m_typedConfigurations);
m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
resolveLoggerFormatSpec();
m_isConfigured = true;
}
void Logger::reconfigure(void) {
ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
configure(m_configurations);
}
bool Logger::isValidId(const std::string& id) {
for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
return false;
}
}
return true;
}
void Logger::flush(void) {
ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
base::threading::ScopedLock scopedLock(lock());
base::type::EnumType lIndex = LevelHelper::kMinValid;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
flush(LevelHelper::castFromInt(lIndex), nullptr);
return false;
});
}
void Logger::flush(Level level, base::type::fstream_t* fs) {
if (fs == nullptr && m_typedConfigurations->toFile(level)) {
fs = m_typedConfigurations->fileStream(level);
}
if (fs != nullptr) {
fs->flush();
std::unordered_map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level);
if (iter != m_unflushedCount.end()) {
iter->second = 0;
}
Helpers::validateFileRolling(this, level);
}
}
void Logger::initUnflushedCount(void) {
m_unflushedCount.clear();
base::type::EnumType lIndex = LevelHelper::kMinValid;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
return false;
});
}
void Logger::resolveLoggerFormatSpec(void) const {
base::type::EnumType lIndex = LevelHelper::kMinValid;
LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
base::LogFormat* logFormat =
const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
return false;
});
}
// el::base
namespace base {
// el::base::utils
namespace utils {
// File
base::type::fstream_t* File::newFileStream(const std::string& filename) {
base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(),
base::type::fstream_t::out
#if !defined(ELPP_FRESH_LOG_FILE)
| base::type::fstream_t::app
#endif
);
#if defined(ELPP_UNICODE)
std::locale elppUnicodeLocale("");
# if ELPP_OS_WINDOWS
std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>);
elppUnicodeLocale = elppUnicodeLocaleWindows;
# endif // ELPP_OS_WINDOWS
fs->imbue(elppUnicodeLocale);
#endif // defined(ELPP_UNICODE)
if (fs->is_open()) {
fs->flush();
} else {
base::utils::safeDelete(fs);
ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
}
return fs;
}
std::size_t File::getSizeOfFile(base::type::fstream_t* fs) {
if (fs == nullptr) {
return 0;
}
// Since the file stream is appended to or truncated, the current
// offset is the file size.
std::size_t size = static_cast<std::size_t>(fs->tellg());
return size;
}
bool File::pathExists(const char* path, bool considerFile) {
if (path == nullptr) {
return false;
}
#if ELPP_OS_UNIX
ELPP_UNUSED(considerFile);
struct stat st;
return (stat(path, &st) == 0);
#elif ELPP_OS_WINDOWS
DWORD fileType = GetFileAttributesA(path);
if (fileType == INVALID_FILE_ATTRIBUTES) {
return false;
}
return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
#endif // ELPP_OS_UNIX
}
bool File::createPath(const std::string& path) {
if (path.empty()) {
return false;
}
if (base::utils::File::pathExists(path.c_str())) {
return true;
}
int status = -1;
char* currPath = const_cast<char*>(path.c_str());
std::string builtPath = std::string();
#if ELPP_OS_UNIX
if (path[0] == '/') {
builtPath = "/";
}
currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
#elif ELPP_OS_WINDOWS
// Use secure functions API
char* nextTok_ = nullptr;
currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
ELPP_UNUSED(nextTok_);
#endif // ELPP_OS_UNIX
while (currPath != nullptr) {
builtPath.append(currPath);
builtPath.append(base::consts::kFilePathSeperator);
#if ELPP_OS_UNIX
status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
#elif ELPP_OS_WINDOWS
status = _mkdir(builtPath.c_str());
currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
#endif // ELPP_OS_UNIX
}
if (status == -1) {
ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
return false;
}
return true;
}
std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) {
if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) {
return fullPath;
}
std::size_t lastSlashAt = fullPath.find_last_of(separator);
if (lastSlashAt == 0) {
return std::string(separator);
}
return fullPath.substr(0, lastSlashAt + 1);
}
void File::buildStrippedFilename(const char* filename, char buff[], const std::string &commonPrefix, std::size_t limit) {
if (!commonPrefix.empty()) {
if (!strncmp(filename, commonPrefix.c_str(), commonPrefix.size()))
filename += commonPrefix.size();
}
std::size_t sizeOfFilename = strlen(filename);
if (sizeOfFilename >= limit) {
filename += (sizeOfFilename - limit);
if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
filename += 3; // 3 = '..'
STRCAT(buff, "..", limit);
}
}
STRCAT(buff, filename, limit);
}
void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) {
const char *filename = fullPath.c_str();
std::size_t lastSlashAt = fullPath.find_last_of(separator);
filename += lastSlashAt ? lastSlashAt+1 : 0;
std::size_t sizeOfFilename = strlen(filename);
if (sizeOfFilename >= limit) {
filename += (sizeOfFilename - limit);
if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
filename += 3; // 3 = '..'
STRCAT(buff, "..", limit);
}
}
STRCAT(buff, filename, limit);
}
// Str
bool Str::wildCardMatch(const char* str, const char* pattern) {
while (*pattern) {
switch (*pattern) {
case '?':
if (!*str)
return false;
++str;
++pattern;
break;
case '*':
if (wildCardMatch(str, pattern + 1))
return true;
if (*str && wildCardMatch(str + 1, pattern))
return true;
return false;
default:
if (*str++ != *pattern++)
return false;
break;
}
}
return !*str && !*pattern;
}
std::string& Str::ltrim(std::string& str) {
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) {
return !std::isspace(c);
} ));
return str;
}
std::string& Str::rtrim(std::string& str) {
str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) {
return !std::isspace(c);
}).base(), str.end());
return str;
}
std::string& Str::trim(std::string& str) {
return ltrim(rtrim(str));
}
bool Str::startsWith(const std::string& str, const std::string& start) {
return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
}
bool Str::endsWith(const std::string& str, const std::string& end) {
return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
}
std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) {
std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
return str;
}
std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat,
const std::string& replaceWith) {
if (replaceWhat == replaceWith)
return str;
std::size_t foundAt = std::string::npos;
while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
str.replace(foundAt, replaceWhat.length(), replaceWith);
}
return str;
}
void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
const base::type::string_t& replaceWith) {
std::size_t foundAt = base::type::string_t::npos;
while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
str.erase(foundAt - 1, 1);
++foundAt;
} else {
str.replace(foundAt, replaceWhat.length(), replaceWith);
return;
}
}
}
#if defined(ELPP_UNICODE)
void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
const std::string& replaceWith) {
replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
}
#endif // defined(ELPP_UNICODE)
std::string& Str::toUpper(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(),
[](char c) {
return static_cast<char>(::toupper(c));
});
return str;
}
bool Str::cStringEq(const char* s1, const char* s2) {
if (s1 == nullptr && s2 == nullptr) return true;
if (s1 == nullptr || s2 == nullptr) return false;
return strcmp(s1, s2) == 0;
}
bool Str::cStringCaseEq(const char* s1, const char* s2) {
if (s1 == nullptr && s2 == nullptr) return true;
if (s1 == nullptr || s2 == nullptr) return false;
// With thanks to cygwin for this code
int d = 0;
while (true) {
const int c1 = toupper(*s1++);
const int c2 = toupper(*s2++);
if (((d = c1 - c2) != 0) || (c2 == '\0')) {
break;
}
}
return d == 0;
}
bool Str::contains(const char* str, char c) {
for (; *str; ++str) {
if (*str == c)
return true;
}
return false;
}
char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) {
char localBuff[10] = "";
char* p = localBuff + sizeof(localBuff) - 2;
if (n > 0) {
for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
*--p = static_cast<char>(n % 10 + '0');
} else {
*--p = '0';
--len;
}
if (zeroPadded)
while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
return addToBuff(p, buf, bufLim);
}
char* Str::addToBuff(const char* str, char* buf, const char* bufLim) {
while ((buf < bufLim) && ((*buf = *str++) != '\0'))
++buf;
return buf;
}
char* Str::clearBuff(char buff[], std::size_t lim) {
STRCPY(buff, "", lim);
ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro
return buff;
}
/// @brief Converst wchar* to char*
/// NOTE: Need to free return value after use!
char* Str::wcharPtrToCharPtr(const wchar_t* line) {
std::size_t len_ = wcslen(line) + 1;
char* buff_ = static_cast<char*>(malloc(len_ + 1));
# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
std::wcstombs(buff_, line, len_);
# elif ELPP_OS_WINDOWS
std::size_t convCount_ = 0;
mbstate_t mbState_;
::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
return buff_;
}
// OS
#if ELPP_OS_WINDOWS
/// @brief Gets environment variables for Windows based OS.
/// We are not using <code>getenv(const char*)</code> because of CRT deprecation
/// @param varname Variable name to get environment variable value for
/// @return If variable exist the value of it otherwise nullptr
const char* OS::getWindowsEnvironmentVariable(const char* varname) {
const DWORD bufferLen = 50;
static char buffer[bufferLen];
if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
return buffer;
}
return nullptr;
}
#endif // ELPP_OS_WINDOWS
#if ELPP_OS_ANDROID
std::string OS::getProperty(const char* prop) {
char propVal[PROP_VALUE_MAX + 1];
int ret = __system_property_get(prop, propVal);
return ret == 0 ? std::string() : std::string(propVal);
}
std::string OS::getDeviceName(void) {
std::stringstream ss;
std::string manufacturer = getProperty("ro.product.manufacturer");
std::string model = getProperty("ro.product.model");
if (manufacturer.empty() || model.empty()) {
return std::string();
}
ss << manufacturer << "-" << model;
return ss.str();
}
#endif // ELPP_OS_ANDROID
const std::string OS::getBashOutput(const char* command) {
#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
if (command == nullptr) {
return std::string();
}
FILE* proc = nullptr;
if ((proc = popen(command, "r")) == nullptr) {
ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
return std::string();
}
char hBuff[4096];
if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
pclose(proc);
const std::size_t buffLen = strlen(hBuff);
if (buffLen > 0 && hBuff[buffLen - 1] == '\n') {
hBuff[buffLen- 1] = '\0';
}
return std::string(hBuff);
} else {
pclose(proc);
}
return std::string();
#else
ELPP_UNUSED(command);
return std::string();
#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
}
std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal,
const char* alternativeBashCommand) {
#if ELPP_OS_UNIX
const char* val = getenv(variableName);
#elif ELPP_OS_WINDOWS
const char* val = getWindowsEnvironmentVariable(variableName);
#endif // ELPP_OS_UNIX
if ((val == nullptr) || ((strcmp(val, "") == 0))) {
#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
// Try harder on unix-based systems
std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
if (valBash.empty()) {
return std::string(defaultVal);
} else {
return valBash;
}
#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
ELPP_UNUSED(alternativeBashCommand);
return std::string(defaultVal);
#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
}
return std::string(val);
}
std::string OS::currentUser(void) {
#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
#elif ELPP_OS_WINDOWS
return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
#elif ELPP_OS_ANDROID
ELPP_UNUSED(base::consts::kUnknownUser);
return std::string("android");
#else
return std::string();
#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
}
std::string OS::currentHost(void) {
#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
#elif ELPP_OS_WINDOWS
return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
#elif ELPP_OS_ANDROID
ELPP_UNUSED(base::consts::kUnknownHost);
return getDeviceName();
#else
return std::string();
#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
}
bool OS::termSupportsColor(void) {
std::string term = getEnvironmentVariable("TERM", "");
return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
|| term == "screen" || term == "linux" || term == "cygwin"
|| term == "screen-256color" || term == "screen.xterm-256color";
}
// DateTime
void DateTime::gettimeofday(struct timeval* tv) {
#if ELPP_OS_WINDOWS
if (tv != nullptr) {
# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
const unsigned __int64 delta_ = 11644473600000000Ui64;
# else
const unsigned __int64 delta_ = 11644473600000000ULL;
# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
const double secOffSet = 0.000001;
const unsigned long usecOffSet = 1000000;
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
unsigned __int64 present = 0;
present |= fileTime.dwHighDateTime;
present = present << 32;
present |= fileTime.dwLowDateTime;
present /= 10; // mic-sec
// Subtract the difference
present -= delta_;
tv->tv_sec = static_cast<long>(present * secOffSet);
tv->tv_usec = static_cast<long>(present % usecOffSet);
}
#else
::gettimeofday(tv, nullptr);
#endif // ELPP_OS_WINDOWS
}
std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) {
struct timeval currTime;
gettimeofday(&currTime);
return timevalToString(currTime, format, ssPrec);
}
std::string DateTime::timevalToString(struct timeval tval, const char* format,
const el::base::SubsecondPrecision* ssPrec) {
struct ::tm timeInfo;
buildTimeInfo(&tval, &timeInfo);
const int kBuffSize = 30;
char buff_[kBuffSize] = "";
parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset),
ssPrec);
return std::string(buff_);
}
base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
if (time <= base::consts::kTimeFormats[i].value) {
break;
}
if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) {
break;
}
time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value);
unit = base::consts::kTimeFormats[i + 1].unit;
}
base::type::stringstream_t ss;
ss << time << " " << unit;
return ss.str();
}
unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime,
base::TimestampUnit timestampUnit) {
if (timestampUnit == base::TimestampUnit::Microsecond) {
return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -
static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));
}
// milliseconds
auto conv = [](const struct timeval& tim) {
return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000));
};
return static_cast<unsigned long long>(conv(endTime) - conv(startTime));
}
struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
#if ELPP_OS_UNIX
time_t rawTime = currTime->tv_sec;
::elpptime_r(&rawTime, timeInfo);
return timeInfo;
#else
# if ELPP_COMPILER_MSVC
ELPP_UNUSED(currTime);
time_t t;
# if defined(_USE_32BIT_TIME_T)
_time32(&t);
# else
_time64(&t);
# endif
elpptime_s(timeInfo, &t);
return timeInfo;
# else
// For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
time_t rawTime = currTime->tv_sec;
struct tm* tmInf = elpptime(&rawTime);
*timeInfo = *tmInf;
return timeInfo;
# endif // ELPP_COMPILER_MSVC
#endif // ELPP_OS_UNIX
}
char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
std::size_t msec, const base::SubsecondPrecision* ssPrec) {
const char* bufLim = buf + bufSz;
for (; *format; ++format) {
if (*format == base::consts::kFormatSpecifierChar) {
switch (*++format) {
case base::consts::kFormatSpecifierChar: // Escape
break;
case '\0': // End
--format;
break;
case 'd': // Day
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
continue;
case 'a': // Day of week (short)
buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
continue;
case 'A': // Day of week (long)
buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
continue;
case 'M': // month
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
continue;
case 'b': // month (short)
buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
continue;
case 'B': // month (long)
buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
continue;
case 'y': // year (two digits)
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
continue;
case 'Y': // year (four digits)
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
continue;
case 'h': // hour (12-hour)
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
continue;
case 'H': // hour (24-hour)
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
continue;
case 'm': // minute
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
continue;
case 's': // second
buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
continue;
case 'z': // subsecond part
case 'g':
buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim);
continue;
case 'F': // AM/PM
buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
continue;
default:
continue;
}
}
if (buf == bufLim) break;
*buf++ = *format;
}
return buf;
}
// CommandLineArgs
void CommandLineArgs::setArgs(int argc, char** argv) {
m_params.clear();
m_paramsWithValue.clear();
if (argc == 0 || argv == nullptr) {
return;
}
m_argc = argc;
m_argv = argv;
for (int i = 1; i < m_argc; ++i) {
const char* v = (strstr(m_argv[i], "="));
if (v != nullptr && strlen(v) > 0) {
std::string key = std::string(m_argv[i]);
key = key.substr(0, key.find_first_of('='));
if (hasParamWithValue(key.c_str())) {
ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
<< getParamValue(key.c_str()) << "]");
} else {
m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
}
}
if (v == nullptr) {
if (hasParam(m_argv[i])) {
ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
} else {
m_params.push_back(std::string(m_argv[i]));
}
}
}
}
bool CommandLineArgs::hasParamWithValue(const char* paramKey) const {
return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
}
const char* CommandLineArgs::getParamValue(const char* paramKey) const {
std::unordered_map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
}
bool CommandLineArgs::hasParam(const char* paramKey) const {
return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
}
bool CommandLineArgs::empty(void) const {
return m_params.empty() && m_paramsWithValue.empty();
}
std::size_t CommandLineArgs::size(void) const {
return m_params.size() + m_paramsWithValue.size();
}
base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
for (int i = 1; i < c.m_argc; ++i) {
os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
if (i < c.m_argc - 1) {
os << ELPP_LITERAL(" ");
}
}
return os;
}
} // namespace utils
// el::base::threading
namespace threading {
#if ELPP_THREADING_ENABLED
# if ELPP_USE_STD_THREADING
# if ELPP_ASYNC_LOGGING
static void msleep(int ms) {
// Only when async logging enabled - this is because async is strict on compiler
# if defined(ELPP_NO_SLEEP_FOR)
usleep(ms * 1000);
# else
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
# endif // defined(ELPP_NO_SLEEP_FOR)
}
# endif // ELPP_ASYNC_LOGGING
# endif // !ELPP_USE_STD_THREADING
#endif // ELPP_THREADING_ENABLED
} // namespace threading
// el::base
// SubsecondPrecision
void SubsecondPrecision::init(int width) {
if (width < 1 || width > 6) {
width = base::consts::kDefaultSubsecondPrecision;
}
m_width = width;
switch (m_width) {
case 3:
m_offset = 1000;
break;
case 4:
m_offset = 100;
break;
case 5:
m_offset = 10;
break;
case 6:
m_offset = 1;
break;
default:
m_offset = 1000;
break;
}
}
// LogFormat
LogFormat::LogFormat(void) :
m_level(Level::Unknown),
m_userFormat(base::type::string_t()),
m_format(base::type::string_t()),
m_dateTimeFormat(std::string()),
m_flags(0x0),
m_currentUser(base::utils::OS::currentUser()),
m_currentHost(base::utils::OS::currentHost()) {
}
LogFormat::LogFormat(Level level, const base::type::string_t& format)
: m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()),
m_currentHost(base::utils::OS::currentHost()) {
parseFromFormat(m_userFormat);
}
LogFormat::LogFormat(const LogFormat& logFormat):
m_level(logFormat.m_level),
m_userFormat(logFormat.m_userFormat),
m_format(logFormat.m_format),
m_dateTimeFormat(logFormat.m_dateTimeFormat),
m_flags(logFormat.m_flags),
m_currentUser(logFormat.m_currentUser),
m_currentHost(logFormat.m_currentHost) {
}
LogFormat::LogFormat(LogFormat&& logFormat) {
m_level = std::move(logFormat.m_level);
m_userFormat = std::move(logFormat.m_userFormat);
m_format = std::move(logFormat.m_format);
m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
m_flags = std::move(logFormat.m_flags);
m_currentUser = std::move(logFormat.m_currentUser);
m_currentHost = std::move(logFormat.m_currentHost);
}
LogFormat& LogFormat::operator=(const LogFormat& logFormat) {
if (&logFormat != this) {
m_level = logFormat.m_level;
m_userFormat = logFormat.m_userFormat;
m_dateTimeFormat = logFormat.m_dateTimeFormat;
m_flags = logFormat.m_flags;
m_currentUser = logFormat.m_currentUser;
m_currentHost = logFormat.m_currentHost;
}
return *this;
}
bool LogFormat::operator==(const LogFormat& other) {
return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
}
/// @brief Updates format to be used while logging.
/// @param userFormat User provided format
void LogFormat::parseFromFormat(const base::type::string_t& userFormat) {
// We make copy because we will be changing the format
// i.e, removing user provided date format from original format
// and then storing it.
base::type::string_t formatCopy = userFormat;
m_flags = 0x0;
auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
std::size_t foundAt = base::type::string_t::npos;
while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) {
if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
if (hasFlag(flag)) {
// If we already have flag we remove the escape chars so that '%%' is turned to '%'
// even after specifier resolution - this is because we only replaceFirst specifier
formatCopy.erase(foundAt - 1, 1);
++foundAt;
}
} else {
if (!hasFlag(flag)) addFlag(flag);
}
}
};
conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
// For date/time we need to extract user's date format first
std::size_t dateIndex = std::string::npos;
if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
}
if (dateIndex != std::string::npos) {
addFlag(base::FormatFlags::DateTime);
updateDateFormat(dateIndex, formatCopy);
}
}
m_format = formatCopy;
updateFormatSpec();
}
void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) {
if (hasFlag(base::FormatFlags::DateTime)) {
index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
}
const base::type::char_t* ptr = currFormat.c_str() + index;
if ((currFormat.size() > index) && (ptr[0] == '{')) {
// User has provided format for date/time
++ptr;
int count = 1; // Start by 1 in order to remove starting brace
std::stringstream ss;
for (; *ptr; ++ptr, ++count) {
if (*ptr == '}') {
++count; // In order to remove ending brace
break;
}
ss << static_cast<char>(*ptr);
}
currFormat.erase(index, count);
m_dateTimeFormat = ss.str();
} else {
// No format provided, use default
if (hasFlag(base::FormatFlags::DateTime)) {
m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
}
}
}
void LogFormat::updateFormatSpec(void) {
// Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
if (m_level == Level::Debug) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kDebugLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kDebugLevelShortLogValue);
} else if (m_level == Level::Info) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kInfoLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kInfoLevelShortLogValue);
} else if (m_level == Level::Warning) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kWarningLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kWarningLevelShortLogValue);
} else if (m_level == Level::Error) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kErrorLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kErrorLevelShortLogValue);
} else if (m_level == Level::Fatal) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kFatalLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kFatalLevelShortLogValue);
} else if (m_level == Level::Verbose) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kVerboseLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kVerboseLevelShortLogValue);
} else if (m_level == Level::Trace) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
base::consts::kTraceLevelLogValue);
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
base::consts::kTraceLevelShortLogValue);
}
if (hasFlag(base::FormatFlags::User)) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
m_currentUser);
}
if (hasFlag(base::FormatFlags::Host)) {
base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
m_currentHost);
}
// Ignore Level::Global and Level::Unknown
}
// TypedConfigurations
TypedConfigurations::TypedConfigurations(Configurations* configurations,
base::LogStreamsReferenceMap* logStreamsReference) {
m_configurations = configurations;
m_logStreamsReference = logStreamsReference;
build(m_configurations);
}
TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) {
this->m_configurations = other.m_configurations;
this->m_logStreamsReference = other.m_logStreamsReference;
build(m_configurations);
}
bool TypedConfigurations::enabled(Level level) {
return getConfigByVal<bool>(level, &m_enabledMap, "enabled");
}
bool TypedConfigurations::toFile(Level level) {
return getConfigByVal<bool>(level, &m_toFileMap, "toFile");
}
const std::string& TypedConfigurations::filename(Level level) {
return getConfigByRef<std::string>(level, &m_filenameMap, "filename");
}
bool TypedConfigurations::toStandardOutput(Level level) {
return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput");
}
const base::LogFormat& TypedConfigurations::logFormat(Level level) {
return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat");
}
const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) {
return getConfigByRef<base::SubsecondPrecision>(level, &m_subsecondPrecisionMap, "subsecondPrecision");
}
const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) {
return getConfigByRef<base::MillisecondsWidth>(level, &m_subsecondPrecisionMap, "millisecondsWidth");
}
bool TypedConfigurations::performanceTracking(Level level) {
return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking");
}
base::type::fstream_t* TypedConfigurations::fileStream(Level level) {
return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get();
}
std::size_t TypedConfigurations::maxLogFileSize(Level level) {
return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize");
}
std::size_t TypedConfigurations::logFlushThreshold(Level level) {
return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold");
}
void TypedConfigurations::build(Configurations* configurations) {
base::threading::ScopedLock scopedLock(lock());
auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming
base::utils::Str::trim(boolStr);
return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
};
setValue(Level::Global, base::FileStreamPtr(NULL), &m_fileStreamMap);
std::vector<Configuration*> withFileSizeLimit;
for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
Configuration* conf = *it;
// We cannot use switch on strong enums because Intel C++ dont support them yet
if (conf->configurationType() == ConfigurationType::Enabled) {
setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
} else if (conf->configurationType() == ConfigurationType::ToFile) {
setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
} else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
} else if (conf->configurationType() == ConfigurationType::Filename) {
// We do not yet configure filename but we will configure in another
// loop. This is because if file cannot be created, we will force ToFile
// to be false. Because configuring logger is not necessarily performance
// sensative operation, we can live with another loop; (by the way this loop
// is not very heavy either)
} else if (conf->configurationType() == ConfigurationType::Format) {
setValue(conf->level(), base::LogFormat(conf->level(),
base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
} else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) {
setValue(Level::Global,
base::SubsecondPrecision(static_cast<int>(getULong(conf->value()))), &m_subsecondPrecisionMap);
} else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
} else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
auto v = getULong(conf->value());
setValue(conf->level(), static_cast<std::size_t>(v), &m_maxLogFileSizeMap);
if (v != 0) {
withFileSizeLimit.push_back(conf);
}
} else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap);
}
}
// As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files
for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
Configuration* conf = *it;
if (conf->configurationType() == ConfigurationType::Filename) {
insertFile(conf->level(), conf->value());
}
}
for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin();
conf != withFileSizeLimit.end(); ++conf) {
// This is not unsafe as mutex is locked in currect scope
unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
}
}
unsigned long TypedConfigurations::getULong(std::string confVal) {
bool valid = true;
base::utils::Str::trim(confVal);
valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
[](char c) {
return !base::utils::Str::isDigit(c);
}) == confVal.end();
if (!valid) {
valid = false;
ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
return 0;
}
return atol(confVal.c_str());
}
std::string TypedConfigurations::resolveFilename(const std::string& filename) {
std::string resultingFilename = filename;
std::size_t dateIndex = std::string::npos;
std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
}
if (dateIndex != std::string::npos) {
const char* ptr = resultingFilename.c_str() + dateIndex;
// Goto end of specifier
ptr += dateTimeFormatSpecifierStr.size();
std::string fmt;
if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
// User has provided format for date/time
++ptr;
int count = 1; // Start by 1 in order to remove starting brace
std::stringstream ss;
for (; *ptr; ++ptr, ++count) {
if (*ptr == '}') {
++count; // In order to remove ending brace
break;
}
ss << *ptr;
}
resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
fmt = ss.str();
} else {
fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
}
base::SubsecondPrecision ssPrec(3);
std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec);
base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
}
}
return resultingFilename;
}
void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) {
if (fullFilename.empty())
return;
std::string resolvedFilename = resolveFilename(fullFilename);
if (resolvedFilename.empty()) {
std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
<< LevelHelper::convertToString(level) << "]";
}
std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
if (filePath.size() < resolvedFilename.size()) {
base::utils::File::createPath(filePath);
}
auto create = [&](Level level) {
base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
base::type::fstream_t* fs = nullptr;
if (filestreamIter == m_logStreamsReference->end()) {
// We need a completely new stream, nothing to share with
fs = base::utils::File::newFileStream(resolvedFilename);
m_filenameMap.insert(std::make_pair(level, resolvedFilename));
m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
} else {
// Woops! we have an existing one, share it!
m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
fs = filestreamIter->second.get();
}
if (fs == nullptr) {
// We display bad file error from newFileStream()
ELPP_INTERNAL_ERROR("Setting [TO_FILE] of ["
<< LevelHelper::convertToString(level) << "] to FALSE", false);
setValue(level, false, &m_toFileMap);
}
};
// If we dont have file conf for any level, create it for Level::Global first
// otherwise create for specified level
create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
}
bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) {
base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
if (fs == nullptr) {
return true;
}
std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
<< LevelHelper::convertToString(level) << "]");
fs->close();
preRollOutCallback(fname.c_str(), currFileSize);
fs->open(fname, std::fstream::out | std::fstream::trunc);
return true;
}
return false;
}
// RegisteredHitCounters
bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
base::threading::ScopedLock scopedLock(lock());
base::HitCounter* counter = get(filename, lineNumber);
if (counter == nullptr) {
registerNew(counter = new base::HitCounter(filename, lineNumber));
}
counter->validateHitCounts(n);
bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
return result;
}
/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
base::threading::ScopedLock scopedLock(lock());
base::HitCounter* counter = get(filename, lineNumber);
if (counter == nullptr) {
registerNew(counter = new base::HitCounter(filename, lineNumber));
}
// Do not use validateHitCounts here since we do not want to reset counter here
// Note the >= instead of > because we are incrementing
// after this check
if (counter->hitCounts() >= n)
return true;
counter->increment();
return false;
}
/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
base::threading::ScopedLock scopedLock(lock());
base::HitCounter* counter = get(filename, lineNumber);
if (counter == nullptr) {
registerNew(counter = new base::HitCounter(filename, lineNumber));
}
counter->increment();
// Do not use validateHitCounts here since we do not want to reset counter here
if (counter->hitCounts() <= n)
return true;
return false;
}
// RegisteredLoggers
RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
m_defaultLogBuilder(defaultLogBuilder) {
m_defaultConfigurations.setToDefault();
}
Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) {
base::threading::ScopedLock scopedLock(lock());
Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id);
if (logger_ == nullptr && forceCreation) {
bool validId = Logger::isValidId(id);
if (!validId) {
ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
return nullptr;
}
logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
logger_->m_logBuilder = m_defaultLogBuilder;
registerNew(id, logger_);
LoggerRegistrationCallback* callback = nullptr;
for (const std::pair<std::string, base::type::LoggerRegistrationCallbackPtr>& h
: m_loggerRegistrationCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(logger_);
}
}
}
return logger_;
}
bool RegisteredLoggers::remove(const std::string& id) {
if (id == base::consts::kDefaultLoggerId) {
return false;
}
// get has internal lock
Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
if (logger != nullptr) {
// unregister has internal lock
unregister(logger);
}
return true;
}
void RegisteredLoggers::unsafeFlushAll(void) {
ELPP_INTERNAL_INFO(1, "Flushing all log files");
for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
it != m_logStreamsReference.end(); ++it) {
if (it->second.get() == nullptr) continue;
it->second->flush();
}
}
// VRegistry
VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
}
/// @brief Sets verbose level. Accepted range is 0-9
void VRegistry::setLevel(base::type::VerboseLevel level) {
base::threading::ScopedLock scopedLock(lock());
if (level > 9)
m_level = base::consts::kMaxVerboseLevel;
else
m_level = level;
}
void VRegistry::setModules(const char* modules) {
base::threading::ScopedLock scopedLock(lock());
auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
ss.str(std::string(""));
ss << chr;
}
if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
ss.str(std::string(""));
ss << chr;
}
ss << sfx;
};
auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
addSuffix(ss, ".h", nullptr);
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".c", ".h");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".cpp", ".c");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".cc", ".cpp");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".cxx", ".cc");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".-inl.h", ".cxx");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".hxx", ".-inl.h");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".hpp", ".hxx");
m_modules.insert(std::make_pair(ss.str(), level));
addSuffix(ss, ".hh", ".hpp");
}
m_modules.insert(std::make_pair(ss.str(), level));
};
bool isMod = true;
bool isLevel = false;
std::stringstream ss;
int level = -1;
for (; *modules; ++modules) {
switch (*modules) {
case '=':
isLevel = true;
isMod = false;
break;
case ',':
isLevel = false;
isMod = true;
if (!ss.str().empty() && level != -1) {
insert(ss, static_cast<base::type::VerboseLevel>(level));
ss.str(std::string(""));
level = -1;
}
break;
default:
if (isMod) {
ss << *modules;
} else if (isLevel) {
if (isdigit(*modules)) {
level = static_cast<base::type::VerboseLevel>(*modules) - 48;
}
}
break;
}
}
if (!ss.str().empty() && level != -1) {
insert(ss, static_cast<base::type::VerboseLevel>(level));
}
}
// Log levels are sorted in a weird way...
static int priority(Level level) {
if (level == Level::Fatal) return 0;
if (level == Level::Error) return 1;
if (level == Level::Warning) return 2;
if (level == Level::Info) return 3;
if (level == Level::Debug) return 4;
if (level == Level::Verbose) return 5;
if (level == Level::Trace) return 6;
return 7;
}
namespace
{
std::atomic<int> s_lowest_priority{INT_MAX};
}
void VRegistry::clearCategories(void) {
const base::threading::ScopedLock scopedLock(lock());
m_categories.clear();
m_cached_allowed_categories.clear();
s_lowest_priority = INT_MAX;
}
void VRegistry::setCategories(const char* categories, bool clear) {
base::threading::ScopedLock scopedLock(lock());
auto insert = [&](std::stringstream& ss, Level level) {
m_categories.push_back(std::make_pair(ss.str(), level));
m_cached_allowed_categories.clear();
int pri = priority(level);
if (pri > s_lowest_priority)
s_lowest_priority = pri;
};
if (clear) {
s_lowest_priority = 0;
m_categories.clear();
m_cached_allowed_categories.clear();
m_categoriesString.clear();
}
if (!categories)
return;
if (!m_categoriesString.empty())
m_categoriesString += ",";
m_categoriesString += categories;
size_t n_fields = m_categories.size() + 1;
for (const char *ptr = categories; *ptr; ++ptr)
if (*ptr == ',')
++n_fields;
m_categories.reserve(n_fields);
bool isCat = true;
bool isLevel = false;
std::stringstream ss;
Level level = Level::Unknown;
for (; *categories; ++categories) {
switch (*categories) {
case ':':
isLevel = true;
isCat = false;
break;
case ',':
isLevel = false;
isCat = true;
if (!ss.str().empty() && level != Level::Unknown) {
insert(ss, level);
ss.str(std::string(""));
level = Level::Unknown;
}
break;
default:
if (isCat) {
ss << *categories;
} else if (isLevel) {
level = LevelHelper::convertFromStringPrefix(categories);
if (level != Level::Unknown)
categories += strlen(LevelHelper::convertToString(level)) - 1;
}
break;
}
}
if (!ss.str().empty() && level != Level::Unknown) {
insert(ss, level);
}
}
std::string VRegistry::getCategories() {
base::threading::ScopedLock scopedLock(lock());
return m_categoriesString;
}
bool VRegistry::allowed(Level level, const std::string &category) {
return priority_allowed(priority(level), category);
}
bool VRegistry::priority_allowed(const int pri, const std::string &category) {
base::threading::ScopedLock scopedLock(lock());
const std::map<std::string, int>::const_iterator it = m_cached_allowed_categories.find(category);
if (it != m_cached_allowed_categories.end())
return pri <= it->second;
if (m_categories.empty()) {
return false;
} else {
std::vector<std::pair<std::string, Level>>::const_reverse_iterator it = m_categories.rbegin();
for (; it != m_categories.rend(); ++it) {
if (base::utils::Str::wildCardMatch(category.c_str(), it->first.c_str())) {
const int p = priority(it->second);
m_cached_allowed_categories.insert(std::make_pair(category, p));
return pri <= p;
}
}
m_cached_allowed_categories.insert(std::make_pair(category, -1));
return false;
}
}
bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) {
base::threading::ScopedLock scopedLock(lock());
if (m_modules.empty() || file == nullptr) {
return vlevel <= m_level;
} else {
char baseFilename[base::consts::kSourceFilenameMaxLength] = "";
base::utils::File::buildBaseFilename(file, baseFilename);
std::unordered_map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
for (; it != m_modules.end(); ++it) {
if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) {
return vlevel <= it->second;
}
}
if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
return true;
}
return false;
}
}
void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
setLevel(base::consts::kMaxVerboseLevel);
} else if (commandLineArgs->hasParamWithValue("--v")) {
setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--v"))));
} else if (commandLineArgs->hasParamWithValue("--V")) {
setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--V"))));
} else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
setModules(commandLineArgs->getParamValue("-vmodule"));
} else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
setModules(commandLineArgs->getParamValue("-VMODULE"));
}
}
#if !defined(ELPP_DEFAULT_LOGGING_FLAGS)
# define ELPP_DEFAULT_LOGGING_FLAGS 0x0
#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS)
// Storage
el::base::type::StoragePointer &el::base::Storage::getELPP()
{
if (!el::base::elStorage)
el::base::elStorage = new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()));
return el::base::elStorage;
}
static struct EnsureELPP { EnsureELPP() { el::base::Storage::getELPP(); } } ensureELPP;
#if ELPP_ASYNC_LOGGING
Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
#else
Storage::Storage(const LogBuilderPtr& defaultLogBuilder) :
#endif // ELPP_ASYNC_LOGGING
m_registeredHitCounters(new base::RegisteredHitCounters()),
m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
m_flags(ELPP_DEFAULT_LOGGING_FLAGS),
m_vRegistry(new base::VRegistry(0, &m_flags)),
#if ELPP_ASYNC_LOGGING
m_asyncLogQueue(new base::AsyncLogQueue()),
m_asyncDispatchWorker(asyncDispatchWorker),
#endif // ELPP_ASYNC_LOGGING
m_preRollOutCallback(base::defaultPreRollOutCallback) {
// Register default logger
m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
// We register default logger anyway (worse case it's not going to register) just in case
m_registeredLoggers->get("default");
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
// Register performance logger and reconfigure format
Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
m_registeredLoggers->get("performance");
performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
performanceLogger->reconfigure();
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
#if defined(ELPP_SYSLOG)
// Register syslog logger and reconfigure format
Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
sysLogLogger->reconfigure();
#endif // defined(ELPP_SYSLOG)
addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
addFlag(LoggingFlag::CreateLoggerAutomatically);
#if ELPP_ASYNC_LOGGING
installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
#else
installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
#endif // ELPP_ASYNC_LOGGING
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>
(std::string("DefaultPerformanceTrackingCallback"));
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
#if ELPP_ASYNC_LOGGING
m_asyncDispatchWorker->start();
#endif // ELPP_ASYNC_LOGGING
}
Storage::~Storage(void) {
ELPP_INTERNAL_INFO(4, "Destroying storage");
#if ELPP_ASYNC_LOGGING
ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
base::utils::safeDelete(m_asyncDispatchWorker);
ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
base::utils::safeDelete(m_asyncLogQueue);
#endif // ELPP_ASYNC_LOGGING
ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
base::utils::safeDelete(m_registeredHitCounters);
ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
base::utils::safeDelete(m_registeredLoggers);
ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
base::utils::safeDelete(m_vRegistry);
}
bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) {
base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
formatSpecifier) != m_customFormatSpecifiers.end();
}
void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
return;
}
base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
m_customFormatSpecifiers.push_back(customFormatSpecifier);
}
bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) {
base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(),
m_customFormatSpecifiers.end(), formatSpecifier);
if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
m_customFormatSpecifiers.erase(it);
return true;
}
return false;
}
void Storage::setApplicationArguments(int argc, char** argv) {
m_commandLineArgs.setArgs(argc, argv);
m_vRegistry->setFromArgs(commandLineArgs());
// default log file
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
Configurations c;
c.setGlobally(ConfigurationType::Filename,
std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
registeredLoggers()->setDefaultConfigurations(c);
for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
it != registeredLoggers()->end(); ++it) {
it->second->configure(c);
}
}
#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) {
m_flags = userInput;
} else {
base::utils::addFlag<base::type::EnumType>(userInput, &m_flags);
}
}
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
}
} // namespace base
// LogDispatchCallback
void LogDispatchCallback::handle(const LogDispatchData* data) {
#if defined(ELPP_THREAD_SAFE)
base::threading::ScopedLock scopedLock(m_fileLocksMapLock);
std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level());
auto lock = m_fileLocks.find(filename);
if (lock == m_fileLocks.end()) {
m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr<base::threading::Mutex>(new base::threading::Mutex)));
}
#endif
}
base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) {
auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()));
return *(it->second.get());
}
namespace base {
// DefaultLogDispatchCallback
const char* convertToChar(Level level) {
// Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
if (level == Level::Global) return "G";
if (level == Level::Debug) return "D";
if (level == Level::Info) return "I";
if (level == Level::Warning) return "W";
if (level == Level::Error) return "E";
if (level == Level::Fatal) return "F";
if (level == Level::Verbose) return "V";
if (level == Level::Trace) return "T";
return "?";
}
void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
#if defined(ELPP_THREAD_SAFE)
#if 0
LogDispatchCallback::handle(data);
base::threading::ScopedLock scopedLock(fileHandle(data));
#endif
#endif
m_data = data;
base::TypedConfigurations* tc = m_data->logMessage()->logger()->typedConfigurations();
const base::LogFormat* logFormat = &tc->logFormat(m_data->logMessage()->level());
const auto &logmsg = m_data->logMessage();
const auto msg = logmsg->message();
if (strchr(msg.c_str(), '\n'))
{
std::vector<std::string> v;
const char *s = msg.c_str();
while (true)
{
const char *ptr = strchr(s, '\n');
if (!ptr)
{
if (*s)
v.push_back(s);
break;
}
v.push_back(std::string(s, ptr - s));
s = ptr + 1;
}
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));
}
}
template<typename Transform>
static inline void utf8canonical(std::string &s, Transform t = [](wint_t c)->wint_t { return c; })
{
size_t avail = s.size();
const char *ptr = s.data();
wint_t cp = 0;
int rbytes = 1, bytes = -1;
char wbuf[8], *wptr;
size_t w_offset = 0;
while (avail--)
{
if ((*ptr & 0x80) == 0)
{
cp = *ptr++;
rbytes = 1;
}
else if ((*ptr & 0xe0) == 0xc0)
{
if (avail < 1)
throw std::runtime_error("Invalid UTF-8");
cp = (*ptr++ & 0x1f) << 6;
cp |= *ptr++ & 0x3f;
--avail;
rbytes = 2;
}
else if ((*ptr & 0xf0) == 0xe0)
{
if (avail < 2)
throw std::runtime_error("Invalid UTF-8");
cp = (*ptr++ & 0xf) << 12;
cp |= (*ptr++ & 0x3f) << 6;
cp |= *ptr++ & 0x3f;
avail -= 2;
rbytes = 3;
}
else if ((*ptr & 0xf8) == 0xf0)
{
if (avail < 3)
throw std::runtime_error("Invalid UTF-8");
cp = (*ptr++ & 0x7) << 18;
cp |= (*ptr++ & 0x3f) << 12;
cp |= (*ptr++ & 0x3f) << 6;
cp |= *ptr++ & 0x3f;
avail -= 3;
rbytes = 4;
}
else
throw std::runtime_error("Invalid UTF-8");
cp = t(cp);
if (cp <= 0x7f)
bytes = 1;
else if (cp <= 0x7ff)
bytes = 2;
else if (cp <= 0xffff)
bytes = 3;
else if (cp <= 0x10ffff)
bytes = 4;
else
throw std::runtime_error("Invalid code point UTF-8 transformation");
if (bytes > rbytes)
throw std::runtime_error("In place sanitization requires replacements to not take more space than the original code points");
wptr = wbuf;
switch (bytes)
{
case 1: *wptr++ = cp; break;
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
default: throw std::runtime_error("Invalid UTF-8");
}
*wptr = 0;
memcpy(&s[w_offset], wbuf, bytes);
w_offset += bytes;
cp = 0;
bytes = 1;
}
s.resize(w_offset);
}
void sanitize(std::string &s)
{
utf8canonical(s, [](wint_t c)->wint_t {
if (c == 9 || c == 10 || c == 13)
return c;
if (c < 0x20)
return '?';
if (c == 0x7f)
return '?';
if (c >= 0x80 && c <= 0x9f)
return '?';
return c;
});
}
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(
m_data->logMessage()->level());
if (fs != nullptr) {
fs->write(logLine.c_str(), logLine.size());
if (fs->fail()) {
ELPP_INTERNAL_ERROR("Unable to write log to file ["
<< m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n"
<< "Few possible reasons (could be something else):\n" << " * Permission denied\n"
<< " * Disk full\n" << " * Disk is not writable", true);
} else {
if (ELPP->hasFlag(LoggingFlag::ImmediateFlush)
|| (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) {
m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs);
}
}
} else {
ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] "
<< "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: "
<< m_data->logMessage()->logger()->id() << "]", false);
}
}
if (m_data->dispatchAction() != base::DispatchAction::FileOnlyLog) {
if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
const el::Level level = m_data->logMessage()->level();
const el::Color color = m_data->logMessage()->color();
m_data->logMessage()->logger()->logBuilder()->setColor(el::base::utils::colorFromLevel(level), false);
ELPP_COUT << rawLinePrefix;
m_data->logMessage()->logger()->logBuilder()->setColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default);
try { sanitize(rawLinePayload); }
catch (const std::exception &e) { rawLinePayload = "<Invalid UTF-8 in log>"; }
ELPP_COUT << rawLinePayload;
m_data->logMessage()->logger()->logBuilder()->setColor(el::Color::Default, false);
ELPP_COUT << std::flush;
}
}
}
#if defined(ELPP_SYSLOG)
else if (m_data->dispatchAction() == base::DispatchAction::SysLog) {
// Determine syslog priority
int sysLogPriority = 0;
if (m_data->logMessage()->level() == Level::Fatal)
sysLogPriority = LOG_EMERG;
else if (m_data->logMessage()->level() == Level::Error)
sysLogPriority = LOG_ERR;
else if (m_data->logMessage()->level() == Level::Warning)
sysLogPriority = LOG_WARNING;
else if (m_data->logMessage()->level() == Level::Info)
sysLogPriority = LOG_INFO;
else if (m_data->logMessage()->level() == Level::Debug)
sysLogPriority = LOG_DEBUG;
else
sysLogPriority = LOG_NOTICE;
# if defined(ELPP_UNICODE)
char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
syslog(sysLogPriority, "%s", line);
free(line);
# else
syslog(sysLogPriority, "%s", logLine.c_str());
# endif
}
#endif // defined(ELPP_SYSLOG)
}
#if ELPP_ASYNC_LOGGING
// AsyncLogDispatchCallback
void AsyncLogDispatchCallback::handle(const LogDispatchData* data) {
base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(),
data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog);
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()->color());
ELPP_COUT << ELPP_COUT_LINE(logLine);
}
// Save resources and only queue if we want to write to file otherwise just ignore handler
if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) {
ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine));
}
}
// AsyncDispatchWorker
AsyncDispatchWorker::AsyncDispatchWorker() {
setContinueRunning(false);
}
AsyncDispatchWorker::~AsyncDispatchWorker() {
setContinueRunning(false);
ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue");
clean();
ELPP_INTERNAL_INFO(6, "Log queue cleaned");
}
bool AsyncDispatchWorker::clean(void) {
std::mutex m;
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); });
emptyQueue();
lk.unlock();
cv.notify_one();
return ELPP->asyncLogQueue()->empty();
}
void AsyncDispatchWorker::emptyQueue(void) {
while (!ELPP->asyncLogQueue()->empty()) {
AsyncLogItem data = ELPP->asyncLogQueue()->next();
handle(&data);
base::threading::msleep(100);
}
}
void AsyncDispatchWorker::start(void) {
base::threading::msleep(5000); // 5s (why?)
setContinueRunning(true);
std::thread t1(&AsyncDispatchWorker::run, this);
t1.join();
}
void AsyncDispatchWorker::handle(AsyncLogItem* logItem) {
LogDispatchData* data = logItem->data();
LogMessage* logMessage = logItem->logMessage();
Logger* logger = logMessage->logger();
base::TypedConfigurations* conf = logger->typedConfigurations();
base::type::string_t logLine = logItem->logLine();
if (data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog) {
if (conf->toFile(logMessage->level())) {
base::type::fstream_t* fs = conf->fileStream(logMessage->level());
if (fs != nullptr) {
fs->write(logLine.c_str(), logLine.size());
if (fs->fail()) {
ELPP_INTERNAL_ERROR("Unable to write log to file ["
<< conf->filename(logMessage->level()) << "].\n"
<< "Few possible reasons (could be something else):\n" << " * Permission denied\n"
<< " * Disk full\n" << " * Disk is not writable", true);
} else {
if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) {
logger->flush(logMessage->level(), fs);
}
}
} else {
ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] "
<< "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false);
}
}
}
# if defined(ELPP_SYSLOG)
else if (data->dispatchAction() == base::DispatchAction::SysLog) {
// Determine syslog priority
int sysLogPriority = 0;
if (logMessage->level() == Level::Fatal)
sysLogPriority = LOG_EMERG;
else if (logMessage->level() == Level::Error)
sysLogPriority = LOG_ERR;
else if (logMessage->level() == Level::Warning)
sysLogPriority = LOG_WARNING;
else if (logMessage->level() == Level::Info)
sysLogPriority = LOG_INFO;
else if (logMessage->level() == Level::Debug)
sysLogPriority = LOG_DEBUG;
else
sysLogPriority = LOG_NOTICE;
# if defined(ELPP_UNICODE)
char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
syslog(sysLogPriority, "%s", line);
free(line);
# else
syslog(sysLogPriority, "%s", logLine.c_str());
# endif
}
# endif // defined(ELPP_SYSLOG)
}
void AsyncDispatchWorker::run(void) {
while (continueRunning()) {
emptyQueue();
base::threading::msleep(10); // 10ms
}
}
#endif // ELPP_ASYNC_LOGGING
// DefaultLogBuilder
base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const {
base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations();
const base::LogFormat* logFormat = &tc->logFormat(logMessage->level());
base::type::string_t logLine = logFormat->format();
char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = "";
const char* bufLim = buff + sizeof(buff);
if (logFormat->hasFlag(base::FormatFlags::AppName)) {
// App name
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier,
logMessage->logger()->parentApplicationName());
}
if (logFormat->hasFlag(base::FormatFlags::ThreadId)) {
// Thread ID
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier,
ELPP->getThreadName(base::threading::getCurrentThreadId()));
}
if (logFormat->hasFlag(base::FormatFlags::DateTime)) {
// DateTime
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier,
base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(),
&tc->subsecondPrecision(logMessage->level())));
}
if (logFormat->hasFlag(base::FormatFlags::Function)) {
// Function
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func());
}
if (logFormat->hasFlag(base::FormatFlags::File)) {
// File
base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix());
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff));
}
if (logFormat->hasFlag(base::FormatFlags::FileBase)) {
// FileBase
base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
base::utils::File::buildBaseFilename(logMessage->file(), buff);
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff));
}
if (logFormat->hasFlag(base::FormatFlags::Line)) {
// Line
char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength);
buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false);
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff));
}
if (logFormat->hasFlag(base::FormatFlags::Location)) {
// Location
char* buf = base::utils::Str::clearBuff(buff,
base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength);
base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix());
buf = base::utils::Str::addToBuff(buff, buf, bufLim);
buf = base::utils::Str::addToBuff(":", buf, bufLim);
buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim,
false);
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff));
}
if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) {
// Verbose level
char* buf = base::utils::Str::clearBuff(buff, 1);
buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false);
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff));
}
if (logFormat->hasFlag(base::FormatFlags::LogMessage)) {
// Log message
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
}
#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock());
ELPP_UNUSED(lock_);
for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
it != ELPP->customFormatSpecifiers()->end(); ++it) {
std::string fs(it->formatSpecifier());
base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage));
}
#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
if (appendNewLine) logLine += ELPP_LITERAL("\n");
return logLine;
}
// LogDispatcher
void LogDispatcher::dispatch(void) {
if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
m_proceed = false;
}
if (!m_proceed) {
return;
}
#ifndef ELPP_NO_GLOBAL_LOCK
// see https://github.com/muflihun/easyloggingpp/issues/580
// global lock is turned off by default unless
// ELPP_NO_GLOBAL_LOCK is defined
base::threading::ScopedLock scopedLock(ELPP->lock());
#endif
base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations;
if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback());
}
LogDispatchCallback* callback = nullptr;
LogDispatchData data;
for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h
: ELPP->m_logDispatchCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
data.setLogMessage(m_logMessage);
data.setDispatchAction(m_dispatchAction);
callback->handle(&data);
}
}
}
// MessageBuilder
void MessageBuilder::initialize(Logger* logger) {
m_logger = logger;
m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ?
ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");
}
MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) {
if (msg == nullptr) {
m_logger->stream() << base::consts::kNullPointer;
return *this;
}
# if defined(ELPP_UNICODE)
m_logger->stream() << msg;
# else
char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
m_logger->stream() << buff_;
free(buff_);
# endif
if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
m_logger->stream() << " ";
}
return *this;
}
// Writer
Writer& Writer::construct(Logger* logger, bool needLock) {
m_logger = logger;
initializeLogger(logger->id(), false, needLock);
m_messageBuilder.initialize(m_logger);
return *this;
}
Writer& Writer::construct(int count, const char* loggerIds, ...) {
if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
va_list loggersList;
va_start(loggersList, loggerIds);
const char* id = loggerIds;
m_loggerIds.reserve(count);
for (int i = 0; i < count; ++i) {
m_loggerIds.push_back(std::string(id));
id = va_arg(loggersList, const char*);
}
va_end(loggersList);
initializeLogger(m_loggerIds.at(0));
} else {
initializeLogger(std::string(loggerIds));
}
m_messageBuilder.initialize(m_logger);
return *this;
}
Writer& Writer::construct(const char *loggerId) {
initializeLogger(ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)));
m_messageBuilder.initialize(m_logger);
return *this;
}
void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) {
if (lookup) {
m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
}
if (m_logger == nullptr) {
{
if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
// Somehow default logger has been unregistered. Not good! Register again
ELPP->registeredLoggers()->get(std::string(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 {
if (needLock) {
m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because
// m_proceed can be changed by lines below
}
if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) {
m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) :
ELPP->vRegistry()->allowed(m_level, loggerId);
} else {
m_proceed = m_logger->enabled(m_level);
}
}
}
void Writer::initializeLogger(Logger *logger, bool needLock) {
m_logger = logger;
if (m_logger == nullptr) {
m_proceed = false;
} else {
if (needLock) {
m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because
// m_proceed can be changed by lines below
}
m_proceed = true;
}
}
void Writer::processDispatch() {
static __thread bool in_dispatch = false;
if (in_dispatch)
{
if (m_proceed && m_logger != NULL)
{
m_logger->stream().str(ELPP_LITERAL(""));
m_logger->releaseLock();
}
return;
}
in_dispatch = true;
#if ELPP_LOGGING_ENABLED
if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
bool firstDispatched = false;
base::type::string_t logMessage;
std::size_t i = 0;
do {
if (m_proceed) {
if (firstDispatched) {
m_logger->stream() << logMessage;
} else {
firstDispatched = true;
if (m_loggerIds.size() > 1) {
logMessage = m_logger->stream().str();
}
}
triggerDispatch();
} else if (m_logger != nullptr) {
m_logger->stream().str(ELPP_LITERAL(""));
m_logger->releaseLock();
}
if (i + 1 < m_loggerIds.size()) {
initializeLogger(m_loggerIds.at(i + 1));
}
} while (++i < m_loggerIds.size());
} else {
if (m_proceed) {
triggerDispatch();
} else if (m_logger != nullptr) {
m_logger->stream().str(ELPP_LITERAL(""));
m_logger->releaseLock();
}
}
#else
if (m_logger != nullptr) {
m_logger->stream().str(ELPP_LITERAL(""));
m_logger->releaseLock();
}
#endif // ELPP_LOGGING_ENABLED
in_dispatch = false;
}
void Writer::triggerDispatch(void) {
if (m_proceed) {
if (m_msg == nullptr) {
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 {
base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch();
}
}
if (m_logger != nullptr) {
m_logger->stream().str(ELPP_LITERAL(""));
m_logger->releaseLock();
}
if (m_proceed && m_level == Level::Fatal
&& !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
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 << "]"
<< " If you wish to disable 'abort on fatal log' please use "
<< "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
base::utils::abort(1, reasonStream.str());
}
m_proceed = false;
}
// PErrorWriter
PErrorWriter::~PErrorWriter(void) {
if (m_proceed) {
#if ELPP_COMPILER_MSVC
char buff[256];
strerror_s(buff, 256, errno);
m_logger->stream() << ": " << buff << " [" << errno << "]";
#else
m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
#endif
}
}
// PerformanceTracker
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
PerformanceTracker::PerformanceTracker(const std::string& blockName,
base::TimestampUnit timestampUnit,
const std::string& loggerId,
bool scopedLog, Level level) :
m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
// We store it locally so that if user happen to change configuration by the end of scope
// or before calling checkpoint, we still depend on state of configuraton at time of construction
el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
if (m_enabled) {
base::utils::DateTime::gettimeofday(&m_startTime);
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
}
PerformanceTracker::~PerformanceTracker(void) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
if (m_enabled) {
base::threading::ScopedLock scopedLock(lock());
if (m_scopedLog) {
base::utils::DateTime::gettimeofday(&m_endTime);
base::type::string_t formattedTime = getFormattedTimeTaken();
PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
data.init(this);
data.m_formattedTimeTaken = formattedTime;
PerformanceTrackingCallback* callback = nullptr;
for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
: ELPP->m_performanceTrackingCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(&data);
}
}
}
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
}
void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line,
const char* func) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
if (m_enabled) {
base::threading::ScopedLock scopedLock(lock());
base::utils::DateTime::gettimeofday(&m_endTime);
base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
data.init(this);
data.m_checkpointId = id;
data.m_file = file;
data.m_line = line;
data.m_func = func;
data.m_formattedTimeTaken = formattedTime;
PerformanceTrackingCallback* callback = nullptr;
for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
: ELPP->m_performanceTrackingCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(&data);
}
}
base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
m_hasChecked = true;
m_lastCheckpointId = id;
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
ELPP_UNUSED(id);
ELPP_UNUSED(file);
ELPP_UNUSED(line);
ELPP_UNUSED(func);
}
const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const {
if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
base::type::stringstream_t ss;
ss << base::utils::DateTime::getTimeDifference(m_endTime,
startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>
(m_timestampUnit)].unit;
return ss.str();
}
return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
startTime, m_timestampUnit), m_timestampUnit);
}
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
namespace debug {
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
// StackTrace
StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang,
const std::string& hex,
const std::string& addr) :
m_index(index),
m_location(loc),
m_demangled(demang),
m_hex(hex),
m_addr(addr) {
}
std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) {
ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr <<
(si.m_demangled.empty() ? "" : ":") << si.m_demangled;
return ss;
}
std::ostream& operator<<(std::ostream& os, const StackTrace& st) {
std::vector<StackTrace::StackTraceEntry>::const_iterator it = st.m_stack.begin();
while (it != st.m_stack.end()) {
os << " " << *it++ << "\n";
}
return os;
}
void StackTrace::generateNew(void) {
#if ELPP_STACKTRACE
m_stack.clear();
void* stack[kMaxStack];
unsigned int size = backtrace(stack, kMaxStack);
char** strings = backtrace_symbols(stack, size);
if (size > kStackStart) { // Skip StackTrace c'tor and generateNew
for (std::size_t i = kStackStart; i < size; ++i) {
std::string mangName;
std::string location;
std::string hex;
std::string addr;
// entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21
const std::string line(strings[i]);
auto p = line.find("_");
if (p != std::string::npos) {
mangName = line.substr(p);
mangName = mangName.substr(0, mangName.find(" +"));
}
p = line.find("0x");
if (p != std::string::npos) {
addr = line.substr(p);
addr = addr.substr(0, addr.find("_"));
}
// Perform demangling if parsed properly
if (!mangName.empty()) {
int status = 0;
char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status);
// if demangling is successful, output the demangled function name
if (status == 0) {
// Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
StackTraceEntry entry(i - 1, location, demangName, hex, addr);
m_stack.push_back(entry);
} else {
// Not successful - we will use mangled name
StackTraceEntry entry(i - 1, location, mangName, hex, addr);
m_stack.push_back(entry);
}
free(demangName);
} else {
StackTraceEntry entry(i - 1, line);
m_stack.push_back(entry);
}
}
}
free(strings);
#else
ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler");
#endif // ELPP_STACKTRACE
}
// Static helper functions
static std::string crashReason(int sig) {
std::stringstream ss;
bool foundReason = false;
for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) {
if (base::consts::kCrashSignals[i].numb == sig) {
ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal";
if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) {
ss << std::endl <<
" " << base::consts::kCrashSignals[i].brief << std::endl <<
" " << base::consts::kCrashSignals[i].detail;
}
foundReason = true;
}
}
if (!foundReason) {
ss << "Application has crashed due to unknown signal [" << sig << "]";
}
return ss.str();
}
/// @brief Logs reason of crash from sig
static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) {
return;
}
std::stringstream ss;
ss << "CRASH HANDLED; ";
ss << crashReason(sig);
#if ELPP_STACKTRACE
if (stackTraceIfAvailable) {
ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace();
}
#else
ELPP_UNUSED(stackTraceIfAvailable);
#endif // ELPP_STACKTRACE
ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str();
}
static inline void crashAbort(int sig) {
base::utils::abort(sig, std::string());
}
/// @brief Default application crash handler
///
/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program.
static inline void defaultCrashHandler(int sig) {
base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId);
base::debug::crashAbort(sig);
}
// CrashHandler
CrashHandler::CrashHandler(bool useDefault) {
if (useDefault) {
setHandler(defaultCrashHandler);
}
}
void CrashHandler::setHandler(const Handler& cHandler) {
m_handler = cHandler;
#if defined(ELPP_HANDLE_SIGABRT)
int i = 0; // SIGABRT is at base::consts::kCrashSignals[0]
#else
int i = 1;
#endif // defined(ELPP_HANDLE_SIGABRT)
for (; i < base::consts::kCrashSignalsCount; ++i) {
m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler);
}
}
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
} // namespace debug
} // namespace base
// el
// Helpers
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) {
std::stringstream ss;
ss << base::debug::crashReason(sig).c_str();
ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
if (sourceFile != nullptr && strlen(sourceFile) > 0) {
ss << " - Source: " << sourceFile;
if (line > 0)
ss << ":" << line;
else
ss << " (line number not specified)";
}
base::utils::abort(sig, ss.str());
}
void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
}
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
// Loggers
bool Loggers::allowed(Level level, const char* cat)
{
const int pri = base::priority(level);
if (pri > base::s_lowest_priority)
return false;
return ELPP->vRegistry()->priority_allowed(pri, std::string{cat});
}
Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) {
return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
}
void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) {
ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr);
}
bool Loggers::unregisterLogger(const std::string& identity) {
return ELPP->registeredLoggers()->remove(identity);
}
bool Loggers::hasLogger(const std::string& identity) {
return ELPP->registeredLoggers()->has(identity);
}
Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) {
if (!logger) return nullptr;
logger->configure(configurations);
return logger;
}
Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) {
return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
}
Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType,
const std::string& value) {
Logger* logger = Loggers::getLogger(identity);
if (logger == nullptr) {
return nullptr;
}
logger->configurations()->set(Level::Global, configurationType, value);
logger->reconfigure();
return logger;
}
void Loggers::reconfigureAllLoggers(const Configurations& configurations) {
for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
it != ELPP->registeredLoggers()->end(); ++it) {
Loggers::reconfigureLogger(it->second, configurations);
}
}
void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType,
const std::string& value) {
for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
it != ELPP->registeredLoggers()->end(); ++it) {
Logger* logger = it->second;
logger->configurations()->set(level, configurationType, value);
logger->reconfigure();
}
}
void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) {
ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
if (reconfigureExistingLoggers) {
Loggers::reconfigureAllLoggers(configurations);
}
}
const Configurations* Loggers::defaultConfigurations(void) {
return ELPP->registeredLoggers()->defaultConfigurations();
}
const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) {
return ELPP->registeredLoggers()->logStreamsReference();
}
base::TypedConfigurations Loggers::defaultTypedConfigurations(void) {
return base::TypedConfigurations(
ELPP->registeredLoggers()->defaultConfigurations(),
ELPP->registeredLoggers()->logStreamsReference());
}
std::vector<std::string>* Loggers::populateAllLoggerIds(std::vector<std::string>* targetList) {
targetList->clear();
for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
it != ELPP->registeredLoggers()->list().end(); ++it) {
targetList->push_back(it->first);
}
return targetList;
}
void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) {
std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath
<< "] for parsing.");
std::string line = std::string();
std::stringstream ss;
Logger* logger = nullptr;
auto configure = [&](void) {
ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str()
<< "\n--------------");
Configurations c;
c.parseFromText(ss.str());
logger->configure(c);
};
while (gcfStream.good()) {
std::getline(gcfStream, line);
ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
base::utils::Str::trim(line);
if (Configurations::Parser::isComment(line)) continue;
Configurations::Parser::ignoreComments(&line);
base::utils::Str::trim(line);
if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) {
if (!ss.str().empty() && logger != nullptr) {
configure();
}
ss.str(std::string(""));
line = line.substr(2);
base::utils::Str::trim(line);
if (line.size() > 1) {
ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
logger = getLogger(line);
}
} else {
ss << line << "\n";
}
}
if (!ss.str().empty() && logger != nullptr) {
configure();
}
}
bool Loggers::configureFromArg(const char* argKey) {
#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
ELPP_UNUSED(argKey);
#else
if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) {
return false;
}
configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
return true;
}
void Loggers::flushAll(void) {
ELPP->registeredLoggers()->flushAll();
}
void Loggers::setVerboseLevel(base::type::VerboseLevel level) {
ELPP->vRegistry()->setLevel(level);
}
base::type::VerboseLevel Loggers::verboseLevel(void) {
return ELPP->vRegistry()->level();
}
void Loggers::setVModules(const char* modules) {
if (ELPP->vRegistry()->vModulesEnabled()) {
ELPP->vRegistry()->setModules(modules);
}
}
void Loggers::clearVModules(void) {
ELPP->vRegistry()->clearModules();
}
void Loggers::setCategories(const char* categories, bool clear) {
ELPP->vRegistry()->setCategories(categories, clear);
}
std::string Loggers::getCategories() {
return ELPP->vRegistry()->getCategories();
}
void Loggers::clearCategories(void) {
ELPP->vRegistry()->clearCategories();
}
void Loggers::setFilenameCommonPrefix(const std::string &prefix) {
ELPP->vRegistry()->setFilenameCommonPrefix(prefix);
}
const std::string &Loggers::getFilenameCommonPrefix() {
return ELPP->vRegistry()->getFilenameCommonPrefix();
}
// VersionInfo
const std::string VersionInfo::version(void) {
return std::string("9.96.7");
}
/// @brief Release date of current version
const std::string VersionInfo::releaseDate(void) {
return std::string("24-11-2018 0728hrs");
}
} // namespace el