aboutsummaryrefslogtreecommitdiff
path: root/external/easylogging++/easylogging++.cc
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2017-03-25 19:06:14 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2017-04-10 21:05:02 +0100
commita8ac4f0a70cc433723cfc171d8a1b8f2b3287a33 (patch)
tree341e24925517e9081682fdb799a2b0c4dfc83d86 /external/easylogging++/easylogging++.cc
parentMerge pull request #1930 (diff)
downloadmonero-a8ac4f0a70cc433723cfc171d8a1b8f2b3287a33.tar.xz
update easylogging++ to latest upstream
Diffstat (limited to 'external/easylogging++/easylogging++.cc')
-rw-r--r--external/easylogging++/easylogging++.cc2979
1 files changed, 2979 insertions, 0 deletions
diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc
new file mode 100644
index 000000000..cf6958a31
--- /dev/null
+++ b/external/easylogging++/easylogging++.cc
@@ -0,0 +1,2979 @@
+//
+// Bismillah ar-Rahmaan ar-Raheem
+//
+// Easylogging++ v9.94.1
+// Cross-platform logging library for C++ applications
+//
+// Copyright (c) 2017 muflihun.com
+//
+// This library is released under the MIT Licence.
+// http://labs.muflihun.com/easyloggingpp/licence.php
+//
+// https://github.com/muflihun/easyloggingpp
+// https://muflihun.github.io/easyloggingpp
+// http://muflihun.com
+//
+
+#include "easylogging++.h"
+
+#if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
+INITIALIZE_EASYLOGGINGPP
+#endif
+
+namespace el {
+
+// el::base::utils
+namespace base {
+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
+ ::abort();
+#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+}
+
+} // 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;
+}
+
+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);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
+#else
+ ELPP_UNUSED(base::consts::kDefaultLogFile);
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+#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)
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ 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) {
+ if (!m_termSupportsColor) return;
+ const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
+ if (level == Level::Error || level == Level::Fatal)
+ *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
+ else if (level == Level::Warning)
+ *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
+ else if (level == Level::Debug)
+ *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
+ else if (level == Level::Info)
+ *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
+ else if (level == Level::Trace)
+ *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
+}
+
+// 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) {
+ base::utils::safeDelete(m_typedConfigurations);
+ m_id = logger.m_id;
+ m_typedConfigurations = logger.m_typedConfigurations;
+ 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;
+ 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)) {
+ // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE
+ 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();
+ m_unflushedCount.find(level)->second = 0;
+ }
+}
+
+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;
+ }
+ std::streampos currPos = fs->tellg();
+ fs->seekg(0, fs->end);
+ std::size_t size = static_cast<std::size_t>(fs->tellg());
+ fs->seekg(currPos);
+ 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[], std::size_t limit) {
+ 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 > 0 ? foundAt - 1 : 0, 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(), ::toupper);
+ 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);
+}
+
+static 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);
+ if (hBuff[strlen(hBuff) - 1] == '\n') {
+ hBuff[strlen(hBuff) - 1] = '\0';
+ }
+ return std::string(hBuff);
+ }
+ 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";
+}
+
+// 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;
+ ::localtime_r(&rawTime, timeInfo);
+ return timeInfo;
+#else
+# if ELPP_COMPILER_MSVC
+ ELPP_UNUSED(currTime);
+ time_t t;
+ _time64(&t);
+ localtime_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 = localtime(&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 {
+ return m_paramsWithValue.find(std::string(paramKey))->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 > 0 ? foundAt - 1 : 0, 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");
+ };
+ 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) {
+ setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ withFileSizeLimit.push_back(conf);
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ } 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 defined(ELPP_NO_LOG_TO_FILE)
+ setValue(level, false, &m_toFileMap);
+ ELPP_UNUSED(fullFilename);
+ m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr)));
+ return;
+#endif
+ 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;
+ }
+ Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
+ if (logger != nullptr) {
+ 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));
+ }
+}
+
+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 {
+ std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
+ for (; it != m_modules.end(); ++it) {
+ if (base::utils::Str::wildCardMatch(file, 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
+#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");
+ // 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();
+#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);
+#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(lock());
+ 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(lock());
+ m_customFormatSpecifiers.push_back(customFormatSpecifier);
+}
+
+bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+ base::threading::ScopedLock scopedLock(lock());
+ 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)
+}
+
+// DefaultLogDispatchCallback
+
+void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
+ m_data = data;
+ dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
+ m_data->dispatchAction() == base::DispatchAction::NormalLog));
+}
+
+void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) {
+ if (m_data->dispatchAction() == base::DispatchAction::NormalLog) {
+ 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->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
+ if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+ m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level());
+ ELPP_COUT << ELPP_COUT_LINE(logLine);
+ }
+ }
+#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);
+ if (data->dispatchAction() == base::DispatchAction::NormalLog
+ && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
+ if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+ data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
+ 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) {
+ 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);
+ 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);
+ 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)
+ 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;
+ }
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ 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;
+ 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;
+}
+
+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) {
+ ELPP->acquireLock();
+ 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));
+ }
+ ELPP->releaseLock(); // Need to unlock it for next writer
+ Writer(Level::Debug, 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) :
+ LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel);
+ } else {
+ m_proceed = m_logger->enabled(m_level);
+ }
+ }
+}
+
+void Writer::processDispatch() {
+#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
+}
+
+void Writer::triggerDispatch(void) {
+ if (m_proceed) {
+ base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel,
+ m_logger), 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, 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::Helpers::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 char* loc, const char* demang, const char* hex,
+ const char* addr) {
+ m_index = index;
+ m_location = std::string(loc);
+ m_demangled = std::string(demang);
+ m_hex = std::string(hex);
+ m_addr = std::string(addr);
+}
+
+std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) {
+ ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled
+ << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr;
+ 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) {
+ char* mangName = nullptr;
+ char* hex = nullptr;
+ char* addr = nullptr;
+ for (char* c = strings[i]; *c; ++c) {
+ switch (*c) {
+ case '(':
+ mangName = c;
+ break;
+ case '+':
+ hex = c;
+ break;
+ case ')':
+ addr = c;
+ break;
+ default:
+ break;
+ }
+ }
+ // Perform demangling if parsed properly
+ if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) {
+ *mangName++ = '\0';
+ *hex++ = '\0';
+ *addr++ = '\0';
+ int status = 0;
+ char* demangName = abi::__cxa_demangle(mangName, 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, strings[i], demangName, hex, addr);
+ m_stack.push_back(entry);
+ } else {
+ // Not successful - we will use mangled name
+ StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr);
+ m_stack.push_back(entry);
+ }
+ free(demangName);
+ } else {
+ StackTraceEntry entry(i - 1, strings[i]);
+ 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) {
+ 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
+
+Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
+}
+
+void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) {
+ ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr);
+}
+
+bool Loggers::unregisterLogger(const std::string& identity) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ return ELPP->registeredLoggers()->remove(identity);
+}
+
+bool Loggers::hasLogger(const std::string& identity) {
+ base::threading::ScopedLock scopedLock(ELPP->lock());
+ 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();
+}
+
+// VersionInfo
+
+const std::string VersionInfo::version(void) {
+ return std::string("9.94.1");
+}
+/// @brief Release date of current version
+const std::string VersionInfo::releaseDate(void) {
+ return std::string("25-02-2017 0813hrs");
+}
+
+} // namespace el