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