diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | contrib/epee/include/misc_log_ex.h | 9 | ||||
-rw-r--r-- | contrib/epee/src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | external/CMakeLists.txt | 1 | ||||
-rw-r--r-- | external/easylogging++/CMakeLists.txt | 45 | ||||
-rw-r--r-- | external/easylogging++/ea_config.h | 11 | ||||
-rw-r--r-- | external/easylogging++/easylogging++.cc | 2979 | ||||
-rw-r--r-- | external/easylogging++/easylogging++.h | 9218 |
8 files changed, 6504 insertions, 5765 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b25e5c4..1061da413 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,8 +348,9 @@ endif() include_directories(${UNBOUND_INCLUDE}) link_directories(${UNBOUND_LIBRARY_DIRS}) -# Final setup for rapidjson -include_directories(external/rapidjson) +# Final setup for easylogging++ +include_directories(${EASYLOGGING_INCLUDE}) +link_directories(${EASYLOGGING_LIBRARY_DIRS}) # Final setup for liblmdb include_directories(${LMDB_INCLUDE}) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index ae61965fa..f4b28f9b1 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -49,15 +49,6 @@ #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> -#define ELPP_THREAD_SAFE -#define ELPP_DEFAULT_LOG_FILE "" -#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ -#else -#define ELPP_STACKTRACE_ON_CRASH 1 -#endif -#define ELPP_DISABLE_DEFAULT_CRASH_HANDLING -#define ELPP_FEATURE_CRASH_LOG 1 -#define ELPP_DISABLE_CHECK_MACROS #include "easylogging++.h" #define MONERO_DEFAULT_LOG_CATEGORY "default" diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 1e79755e5..9db4c46ce 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -41,6 +41,7 @@ endif() target_link_libraries(epee PUBLIC crypto + easylogging ${Boost_FILESYSTEM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 3c0c0d68e..d67a1a875 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -98,3 +98,4 @@ else() endif() add_subdirectory(db_drivers) +add_subdirectory(easylogging++) diff --git a/external/easylogging++/CMakeLists.txt b/external/easylogging++/CMakeLists.txt new file mode 100644 index 000000000..d8d061db0 --- /dev/null +++ b/external/easylogging++/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 2.8.7) + +project(easylogging CXX) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +find_package(Threads) + +add_library(easylogging + easylogging++.cc) + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +target_link_libraries(easylogging + PRIVATE + ${CMAKE_THREAD_LIBS_INIT}) + diff --git a/external/easylogging++/ea_config.h b/external/easylogging++/ea_config.h new file mode 100644 index 000000000..3e6ed6e8c --- /dev/null +++ b/external/easylogging++/ea_config.h @@ -0,0 +1,11 @@ +#pragma once + +#define ELPP_THREAD_SAFE +#define ELPP_DEFAULT_LOG_FILE "" +#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ +#else +#define ELPP_STACKTRACE_ON_CRASH 1 +#endif +#define ELPP_DISABLE_DEFAULT_CRASH_HANDLING +#define ELPP_FEATURE_CRASH_LOG 1 +#define ELPP_NO_CHECK_MACROS diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc new file mode 100644 index 000000000..cf6958a31 --- /dev/null +++ b/external/easylogging++/easylogging++.cc @@ -0,0 +1,2979 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.94.1 +// Cross-platform logging library for C++ applications +// +// Copyright (c) 2017 muflihun.com +// +// This library is released under the MIT Licence. +// http://labs.muflihun.com/easyloggingpp/licence.php +// +// https://github.com/muflihun/easyloggingpp +// https://muflihun.github.io/easyloggingpp +// http://muflihun.com +// + +#include "easylogging++.h" + +#if defined(AUTO_INITIALIZE_EASYLOGGINGPP) +INITIALIZE_EASYLOGGINGPP +#endif + +namespace el { + +// el::base::utils +namespace base { +namespace utils { + +/// @brief Aborts application due with user-defined status +static void abort(int status, const std::string& reason) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} + +} // namespace utils +} // namespace base + +// el + +// LevelHelper + +const char* LevelHelper::convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; +} + +struct StringToLevelItem { + const char* levelString; + Level level; +}; + +static struct StringToLevelItem stringToLevelMap[] = { + { "global", Level::Global }, + { "debug", Level::Debug }, + { "info", Level::Info }, + { "warning", Level::Warning }, + { "error", Level::Error }, + { "fatal", Level::Fatal }, + { "verbose", Level::Verbose }, + { "trace", Level::Trace } +}; + +Level LevelHelper::convertFromString(const char* levelStr) { + for (auto& item : stringToLevelMap) { + if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { + return item.level; + } + } + return Level::Unknown; +} + +void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); + } while (*startIndex <= lIndexMax); +} + +// ConfigurationTypeHelper + +const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; +} + +struct ConfigurationStringToTypeItem { + const char* configString; + ConfigurationType configType; +}; + +static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { + { "enabled", ConfigurationType::Enabled }, + { "to_file", ConfigurationType::ToFile }, + { "to_standard_output", ConfigurationType::ToStandardOutput }, + { "format", ConfigurationType::Format }, + { "filename", ConfigurationType::Filename }, + { "subsecond_precision", ConfigurationType::SubsecondPrecision }, + { "milliseconds_width", ConfigurationType::MillisecondsWidth }, + { "performance_tracking", ConfigurationType::PerformanceTracking }, + { "max_log_file_size", ConfigurationType::MaxLogFileSize }, + { "log_flush_threshold", ConfigurationType::LogFlushThreshold }, +}; + +ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) { + for (auto& item : configStringToTypeMap) { + if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { + return item.configType; + } + } + return ConfigurationType::Unknown; +} + +void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); + } while (*startIndex <= cIndexMax); +} + +// Configuration + +Configuration::Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { +} + +Configuration& Configuration::operator=(const Configuration& c) { + if (&c != this) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + } + return *this; +} + +/// @brief Full constructor used to sets value of configuration +Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { +} + +void Configuration::log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); +} + +/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. +Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { +} + +bool Configuration::Predicate::operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); +} + +// Configurations + +Configurations::Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { +} + +Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, + Configurations* base) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } +} + +bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { + // We initial assertion with true because if we have assertion diabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; +} + +bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; +} + +void Configurations::setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } +} + +bool Configurations::hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; +} + +bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL +} + +void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } +} + +void Configurations::set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); +} + +void Configurations::setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#else + ELPP_UNUSED(base::consts::kDefaultLogFile); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) +#if defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToFile, std::string("false"), true); +#else + setGlobally(ConfigurationType::ToFile, std::string("true"), true); +#endif // defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +void Configurations::setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); +#if defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); +#else + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#endif // defined(ELPP_NO_LOG_TO_FILE) +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +void Configurations::Parser::ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } +} + +bool Configurations::Parser::isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); +} + +bool Configurations::Parser::isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); +} + +bool Configurations::Parser::isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && + (assignment != std::string::npos) && + (line.size() > assignment); +} + +bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, + Level* currLevel, + Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; +} + +void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } +} + +void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } +} + +void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +// LogBuilder + +void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!m_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + else if (level == Level::Debug) + *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; + else if (level == Level::Info) + *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; + else if (level == Level::Trace) + *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; +} + +// Logger + +Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); +} + +Logger::Logger(const std::string& id, const Configurations& configurations, + base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); +} + +Logger::Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; +} + +Logger& Logger::operator=(const Logger& logger) { + if (&logger != this) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + return *this; +} + +void Logger::configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast<Configurations*>(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; +} + +void Logger::reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); +} + +bool Logger::isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; +} + +void Logger::flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); +} + +void Logger::flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + m_unflushedCount.find(level)->second = 0; + } +} + +void Logger::initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); +} + +void Logger::resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); +} + +// el::base +namespace base { + +// el::base::utils +namespace utils { + +// File + +base::type::fstream_t* File::newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out +#if !defined(ELPP_FRESH_LOG_FILE) + | base::type::fstream_t::app +#endif + ); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +# if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>); + elppUnicodeLocale = elppUnicodeLocaleWindows; +# endif // ELPP_OS_WINDOWS + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; +} + +std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + std::streampos currPos = fs->tellg(); + fs->seekg(0, fs->end); + std::size_t size = static_cast<std::size_t>(fs->tellg()); + fs->seekg(currPos); + return size; +} + +bool File::pathExists(const char* path, bool considerFile) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX +} + +bool File::createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast<char*>(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeperator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; +} + +std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) { + if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(separator); + if (lastSlashAt == 0) { + return std::string(separator); + } + return fullPath.substr(0, lastSlashAt + 1); +} + +void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(separator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +// Str + +bool Str::wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; +} + +std::string& Str::ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { + return !std::isspace(c); + } )); + return str; +} + +std::string& Str::rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { + return !std::isspace(c); + }).base(), str.end()); + return str; +} + +std::string& Str::trim(std::string& str) { + return ltrim(rtrim(str)); +} + +bool Str::startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); +} + +bool Str::endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); +} + +std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; +} + +std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; +} + +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } +} +#if defined(ELPP_UNICODE) +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); +} +#endif // defined(ELPP_UNICODE) + +std::string& Str::toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return str; +} + +bool Str::cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; +} + +bool Str::cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + + // With thanks to cygwin for this code + int d = 0; + + while (true) { + const int c1 = toupper(*s1++); + const int c2 = toupper(*s2++); + + if (((d = c1 - c2) != 0) || (c2 == '\0')) { + break; + } + } + + return d == 0; +} + +bool Str::contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; +} + +char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast<char>(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast<char>('0'); + return addToBuff(p, buf, bufLim); +} + +char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; +} + +char* Str::clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; +} + +/// @brief Converst wchar* to char* +/// NOTE: Need to free return value after use! +char* Str::wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast<char*>(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; +} + +// OS + +#if ELPP_OS_WINDOWS +/// @brief Gets environment variables for Windows based OS. +/// We are not using <code>getenv(const char*)</code> because of CRT deprecation +/// @param varname Variable name to get environment variable value for +/// @return If variable exist the value of it otherwise nullptr +const char* OS::getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; +} +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID +std::string OS::getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); +} + +static std::string OS::getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); +} +#endif // ELPP_OS_ANDROID + +const std::string OS::getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + if (hBuff[strlen(hBuff) - 1] == '\n') { + hBuff[strlen(hBuff) - 1] = '\0'; + } + return std::string(hBuff); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) +} + +std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); +} + +std::string OS::currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +std::string OS::currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +bool OS::termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" + || term == "screen" || term == "linux" || term == "cygwin" + || term == "screen-256color"; +} + +// DateTime + +void DateTime::gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast<long>(present * secOffSet); + tv->tv_usec = static_cast<long>(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS +} + +std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { + struct timeval currTime; + gettimeofday(&currTime); + return timevalToString(currTime, format, ssPrec); +} + +std::string DateTime::timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec) { + struct ::tm timeInfo; + buildTimeInfo(&tval, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset), + ssPrec); + return std::string(buff_); +} + +base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (time <= base::consts::kTimeFormats[i].value) { + break; + } + if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { + break; + } + time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value); + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << time << " " << unit; + return ss.str(); +} + +unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec)); + } + // milliseconds + auto conv = [](const struct timeval& tim) { + return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); + }; + return static_cast<unsigned long long>(conv(endTime) - conv(startTime)); +} + +struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::localtime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; + _time64(&t); + localtime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = localtime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX +} + +char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // subsecond part + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; +} + +// CommandLineArgs + +void CommandLineArgs::setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } +} + +bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); +} + +const char* CommandLineArgs::getParamValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); +} + +bool CommandLineArgs::hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); +} + +bool CommandLineArgs::empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); +} + +std::size_t CommandLineArgs::size(void) const { + return m_params.size() + m_paramsWithValue.size(); +} + +base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; +} + +} // namespace utils + +// el::base::threading +namespace threading { + +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# if ELPP_ASYNC_LOGGING +static void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +# if defined(ELPP_NO_SLEEP_FOR) + usleep(ms * 1000); +# else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +# endif // defined(ELPP_NO_SLEEP_FOR) +} +# endif // ELPP_ASYNC_LOGGING +# endif // !ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED + +} // namespace threading + +// el::base + +// SubsecondPrecision + +void SubsecondPrecision::init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultSubsecondPrecision; + } + m_width = width; + switch (m_width) { + case 3: + m_offset = 1000; + break; + case 4: + m_offset = 100; + break; + case 5: + m_offset = 10; + break; + case 6: + m_offset = 1; + break; + default: + m_offset = 1000; + break; + } +} + +// LogFormat + +LogFormat::LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0), + m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { +} + +LogFormat::LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { + parseFromFormat(m_userFormat); +} + +LogFormat::LogFormat(const LogFormat& logFormat): + m_level(logFormat.m_level), + m_userFormat(logFormat.m_userFormat), + m_format(logFormat.m_format), + m_dateTimeFormat(logFormat.m_dateTimeFormat), + m_flags(logFormat.m_flags), + m_currentUser(logFormat.m_currentUser), + m_currentHost(logFormat.m_currentHost) { +} + +LogFormat::LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + m_currentUser = std::move(logFormat.m_currentUser); + m_currentHost = std::move(logFormat.m_currentHost); +} + +LogFormat& LogFormat::operator=(const LogFormat& logFormat) { + if (&logFormat != this) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + m_currentUser = logFormat.m_currentUser; + m_currentHost = logFormat.m_currentHost; + } + return *this; +} + +bool LogFormat::operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; +} + +/// @brief Updates format to be used while logging. +/// @param userFormat User provided format +void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); +} + +void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << static_cast<char>(*ptr); + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } +} + +void LogFormat::updateFormatSpec(void) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + m_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + m_currentHost); + } + // Ignore Level::Global and Level::Unknown +} + +// TypedConfigurations + +TypedConfigurations::TypedConfigurations(Configurations* configurations, + base::LogStreamsReferenceMap* logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); +} + +TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); +} + +bool TypedConfigurations::enabled(Level level) { + return getConfigByVal<bool>(level, &m_enabledMap, "enabled"); +} + +bool TypedConfigurations::toFile(Level level) { + return getConfigByVal<bool>(level, &m_toFileMap, "toFile"); +} + +const std::string& TypedConfigurations::filename(Level level) { + return getConfigByRef<std::string>(level, &m_filenameMap, "filename"); +} + +bool TypedConfigurations::toStandardOutput(Level level) { + return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput"); +} + +const base::LogFormat& TypedConfigurations::logFormat(Level level) { + return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat"); +} + +const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { + return getConfigByRef<base::SubsecondPrecision>(level, &m_subsecondPrecisionMap, "subsecondPrecision"); +} + +const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { + return getConfigByRef<base::MillisecondsWidth>(level, &m_subsecondPrecisionMap, "millisecondsWidth"); +} + +bool TypedConfigurations::performanceTracking(Level level) { + return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking"); +} + +base::type::fstream_t* TypedConfigurations::fileStream(Level level) { + return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get(); +} + +std::size_t TypedConfigurations::maxLogFileSize(Level level) { + return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize"); +} + +std::size_t TypedConfigurations::logFlushThreshold(Level level) { + return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold"); +} + +void TypedConfigurations::build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector<Configuration*> withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensative operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { + setValue(Level::Global, + base::SubsecondPrecision(static_cast<int>(getULong(conf->value()))), &m_subsecondPrecisionMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + withFileSizeLimit.push_back(conf); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } +} + +unsigned long TypedConfigurations::getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { + return !base::utils::Str::isDigit(c); + }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); +} + +std::string TypedConfigurations::resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::SubsecondPrecision ssPrec(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; +} + +void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { +#if defined(ELPP_NO_LOG_TO_FILE) + setValue(level, false, &m_toFileMap); + ELPP_UNUSED(fullFilename); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr))); + return; +#endif + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); +} + +bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + preRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; +} + +// RegisteredHitCounters + +bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; +} + +/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; +} + +/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; +} + +// RegisteredLoggers + +RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); +} + +Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + LoggerRegistrationCallback* callback = nullptr; + for (const std::pair<std::string, base::type::LoggerRegistrationCallbackPtr>& h + : m_loggerRegistrationCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(logger_); + } + } + } + return logger_; +} + +bool RegisteredLoggers::remove(const std::string& id) { + if (id == base::consts::kDefaultLoggerId) { + return false; + } + Logger* logger = base::utils::Registry<Logger, std::string>::get(id); + if (logger != nullptr) { + unregister(logger); + } + return true; +} + +void RegisteredLoggers::unsafeFlushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); + it != m_logStreamsReference.end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } +} + +// VRegistry + +VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { +} + +/// @brief Sets verbose level. Accepted range is 0-9 +void VRegistry::setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; +} + +void VRegistry::setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast<base::type::VerboseLevel>(level)); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast<base::type::VerboseLevel>(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast<base::type::VerboseLevel>(level)); + } +} + +bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } +} + +void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--v")))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--V")))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } +} + +#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) +# define ELPP_DEFAULT_LOGGING_FLAGS 0x0 +#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) +// Storage +#if ELPP_ASYNC_LOGGING +Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else +Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(ELPP_DEFAULT_LOGGING_FLAGS), + m_vRegistry(new base::VRegistry(0, &m_flags)), +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // We register default logger anyway (worse case it's not going to register) just in case + m_registeredLoggers->get("default"); + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + m_registeredLoggers->get("performance"); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback> + (std::string("DefaultPerformanceTrackingCallback")); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING +} + +Storage::~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); +} + +bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); +} + +void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(lock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); +} + +bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; +} + +void Storage::setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, + std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { + m_flags = userInput; + } else { + base::utils::addFlag<base::type::EnumType>(userInput, &m_flags); + } + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +} + +// DefaultLogDispatchCallback + +void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { + m_data = data; + dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog)); +} + +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( + m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) + || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) +} + +#if ELPP_ASYNC_LOGGING + +// AsyncLogDispatchCallback + +void AsyncLogDispatchCallback::handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), + data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog + && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } +} + +// AsyncDispatchWorker +AsyncDispatchWorker::AsyncDispatchWorker() { + setContinueRunning(false); +} + +AsyncDispatchWorker::~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); +} + +bool AsyncDispatchWorker::clean(void) { + std::mutex m; + std::unique_lock<std::mutex> lk(m); + cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); +} + +void AsyncDispatchWorker::emptyQueue(void) { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } +} + +void AsyncDispatchWorker::start(void) { + base::threading::msleep(5000); // 5s (why?) + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::run, this); + t1.join(); +} + +void AsyncDispatchWorker::handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) +} + +void AsyncDispatchWorker::run(void) { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } +} +#endif // ELPP_ASYNC_LOGGING + +// DefaultLogBuilder + +base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + ELPP->getThreadName(base::threading::getCurrentThreadId())); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->subsecondPrecision(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, + false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; +} + +// LogDispatcher + +void LogDispatcher::dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } + base::threading::ScopedLock scopedLock(ELPP->lock()); + base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(&m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->handle(&data); + } + } +} + +// MessageBuilder + +void MessageBuilder::initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); +} + +MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; +} + +// Writer + +Writer& Writer::construct(Logger* logger, bool needLock) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; +} + +Writer& Writer::construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; +} + +void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + ELPP->acquireLock(); + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + ELPP->releaseLock(); // Need to unlock it for next writer + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } +} + +void Writer::processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED +} + +void Writer::triggerDispatch(void) { + if (m_proceed) { + base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger), m_dispatchAction).dispatch(); + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; +} + +// PErrorWriter + +PErrorWriter::~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } +} + +// PerformanceTracker + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +PerformanceTracker::PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit, + const std::string& loggerId, + bool scopedLog, Level level) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuraton at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED +} + +PerformanceTracker::~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) +} + +void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, + const char* func) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); +} + +const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType> + (m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// StackTrace + +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, + const char* addr) { + m_index = index; + m_location = std::string(loc); + m_demangled = std::string(demang); + m_hex = std::string(hex); + m_addr = std::string(addr); +} + +std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled + << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + return ss; +} + +std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector<StackTrace::StackTraceEntry>::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; +} + +void StackTrace::generateNew(void) { +#if ELPP_STACKTRACE + m_stack.clear(); + void* stack[kMaxStack]; + unsigned int size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + char* mangName = nullptr; + char* hex = nullptr; + char* addr = nullptr; + for (char* c = strings[i]; *c; ++c) { + switch (*c) { + case '(': + mangName = c; + break; + case '+': + hex = c; + break; + case ')': + addr = c; + break; + default: + break; + } + } + // Perform demangling if parsed properly + if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { + *mangName++ = '\0'; + *hex++ = '\0'; + *addr++ = '\0'; + int status = 0; + char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, strings[i]); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE +} + +// Static helper functions + +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} + +static inline void crashAbort(int sig) { + base::utils::abort(sig, std::string()); +} + +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} + +// CrashHandler + +CrashHandler::CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } +} + +void CrashHandler::setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base + +// el + +// Helpers + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); +} + +void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// Loggers + +Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); +} + +void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { + ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); +} + +bool Loggers::unregisterLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->remove(identity); +} + +bool Loggers::hasLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->has(identity); +} + +Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; +} + +void Loggers::reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } +} + +void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } +} + +void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } +} + +const Configurations* Loggers::defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); +} + +const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); +} + +base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); +} + +std::vector<std::string>* Loggers::populateAllLoggerIds(std::vector<std::string>* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; +} + +void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } +} + +bool Loggers::configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; +} + +void Loggers::flushAll(void) { + ELPP->registeredLoggers()->flushAll(); +} + +void Loggers::setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); +} + +base::type::VerboseLevel Loggers::verboseLevel(void) { + return ELPP->vRegistry()->level(); +} + +void Loggers::setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } +} + +void Loggers::clearVModules(void) { + ELPP->vRegistry()->clearModules(); +} + +// VersionInfo + +const std::string VersionInfo::version(void) { + return std::string("9.94.1"); +} +/// @brief Release date of current version +const std::string VersionInfo::releaseDate(void) { + return std::string("25-02-2017 0813hrs"); +} + +} // namespace el diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 1cf9dbac8..8f8a71733 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -1,227 +1,222 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.84 +// Easylogging++ v9.94.1 // Single-header only, cross-platform logging library for C++ applications // -// Copyright (c) 2016 muflihun.com +// Copyright (c) 2017 muflihun.com // // This library is released under the MIT Licence. -// http://easylogging.muflihun.com/licence.php +// http://labs.muflihun.com/easyloggingpp/licence.php // -// easylogging@muflihun.com -// -// https://github.com/easylogging/easyloggingpp -// http://easylogging.muflihun.com +// https://github.com/muflihun/easyloggingpp +// https://muflihun.github.io/easyloggingpp // http://muflihun.com // #ifndef EASYLOGGINGPP_H #define EASYLOGGINGPP_H +#include "ea_config.h" // Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L #if (defined(__GNUC__)) -# define ELPP_COMPILER_GCC 1 +# define ELPP_COMPILER_GCC 1 #else -# define ELPP_COMPILER_GCC 0 +# define ELPP_COMPILER_GCC 0 #endif #if ELPP_COMPILER_GCC # define ELPP_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) -# if defined(__GXX_EXPERIMENTAL_CXX0X__) -# define ELPP_CXX0X 1 -# elif(ELPP_GCC_VERSION >= 40801) -# define ELPP_CXX11 1 -# endif +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif #endif // Visual C++ #if defined(_MSC_VER) -# define ELPP_COMPILER_MSVC 1 +# define ELPP_COMPILER_MSVC 1 #else -# define ELPP_COMPILER_MSVC 0 +# define ELPP_COMPILER_MSVC 0 #endif #define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC #if ELPP_COMPILER_MSVC -# if (_MSC_VER == 1600) -# define ELPP_CXX0X 1 -# elif(_MSC_VER >= 1700) -# define ELPP_CXX11 1 -# endif +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif #endif // Clang++ #if (defined(__clang__) && (__clang__ == 1)) -# define ELPP_COMPILER_CLANG 1 +# define ELPP_COMPILER_CLANG 1 #else -# define ELPP_COMPILER_CLANG 0 +# define ELPP_COMPILER_CLANG 0 #endif #if ELPP_COMPILER_CLANG -# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \ -+ __clang_minor__ * 100 \ -+ __clang_patchlevel__) -# if (ELPP_CLANG_VERSION >= 30300) -# define ELPP_CXX11 1 -# endif // (ELPP_CLANG_VERSION >= 30300) +# if __has_include(<thread>) +# include <cstddef> // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include(<thread>) #endif #if (defined(__MINGW32__) || defined(__MINGW64__)) -# define ELPP_MINGW 1 +# define ELPP_MINGW 1 #else -# define ELPP_MINGW 0 +# define ELPP_MINGW 0 #endif #if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) -# define ELPP_CYGWIN 1 +# define ELPP_CYGWIN 1 #else -# define ELPP_CYGWIN 0 +# define ELPP_CYGWIN 0 #endif #if (defined(__INTEL_COMPILER)) -# define ELPP_COMPILER_INTEL 1 +# define ELPP_COMPILER_INTEL 1 #else -# define ELPP_COMPILER_INTEL 0 +# define ELPP_COMPILER_INTEL 0 #endif // Operating System Evaluation // Windows #if (defined(_WIN32) || defined(_WIN64)) -# define ELPP_OS_WINDOWS 1 +# define ELPP_OS_WINDOWS 1 #else -# define ELPP_OS_WINDOWS 0 +# define ELPP_OS_WINDOWS 0 #endif // Linux #if (defined(__linux) || defined(__linux__)) -# define ELPP_OS_LINUX 1 +# define ELPP_OS_LINUX 1 #else -# define ELPP_OS_LINUX 0 +# define ELPP_OS_LINUX 0 #endif #if (defined(__APPLE__)) -# define ELPP_OS_MAC 1 +# define ELPP_OS_MAC 1 #else -# define ELPP_OS_MAC 0 +# define ELPP_OS_MAC 0 #endif #if (defined(__FreeBSD__)) -# define ELPP_OS_FREEBSD 1 +# define ELPP_OS_FREEBSD 1 #else -# define ELPP_OS_FREEBSD 0 +# define ELPP_OS_FREEBSD 0 #endif #if (defined(__sun)) -# define ELPP_OS_SOLARIS 1 -#else -# define ELPP_OS_SOLARIS 0 -#endif -#if (defined(__DragonFly__)) -# define ELPP_OS_DRAGONFLY 1 +# define ELPP_OS_SOLARIS 1 #else -# define ELPP_OS_DRAGONFLY 0 +# define ELPP_OS_SOLARIS 0 #endif // Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY) && (!ELPP_OS_WINDOWS)) -# define ELPP_OS_UNIX 1 +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 #else -# define ELPP_OS_UNIX 0 +# define ELPP_OS_UNIX 0 #endif #if (defined(__ANDROID__)) -# define ELPP_OS_ANDROID 1 +# define ELPP_OS_ANDROID 1 #else -# define ELPP_OS_ANDROID 0 +# define ELPP_OS_ANDROID 0 #endif // Evaluating Cygwin as *nix OS #if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN -# undef ELPP_OS_UNIX -# undef ELPP_OS_LINUX -# define ELPP_OS_UNIX 1 -# define ELPP_OS_LINUX 1 +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 #endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN #if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) -# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) -# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) -# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_MSG) -# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) // Internal Assertions and errors #if !defined(ELPP_DISABLE_ASSERT) -# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) -# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ std::stringstream internalInfoStream; internalInfoStream << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } -# else -# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ std::stringstream internalInfoStream; internalInfoStream << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ << ELPP_INTERNAL_DEBUGGING_ENDL; } -# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) #else -# define ELPP_ASSERT(x, y) +# define ELPP_ASSERT(x, y) #endif //(!defined(ELPP_DISABLE_ASSERT) #if ELPP_COMPILER_MSVC -# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ { char buff[256]; strerror_s(buff, 256, errno); \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 #else -# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 #endif // ELPP_COMPILER_MSVC #if defined(ELPP_DEBUG_ERRORS) -# if !defined(ELPP_INTERNAL_ERROR) -# define ELPP_INTERNAL_ERROR(msg, pe) { \ +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 -# endif +# endif #else -# undef ELPP_INTERNAL_INFO -# define ELPP_INTERNAL_ERROR(msg, pe) +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) #endif // defined(ELPP_DEBUG_ERRORS) #if (defined(ELPP_DEBUG_INFO)) -# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) -# define ELPP_INTERNAL_INFO_LEVEL 9 -# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) -# if !defined(ELPP_INTERNAL_INFO) -# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ << ELPP_INTERNAL_DEBUGGING_ENDL; }} -# endif +# endif #else -# undef ELPP_INTERNAL_INFO -# define ELPP_INTERNAL_INFO(lvl, msg) +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) -#if defined(ELPP_STACKTRACE_ON_CRASH) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW) -# define ELPP_STACKTRACE 1 -# else -# define ELPP_STACKTRACE 0 +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW) +# define ELPP_STACKTRACE 1 +# else # if ELPP_COMPILER_MSVC # pragma message("Stack trace not available for this compiler") # else # warning "Stack trace not available for this compiler"; # endif // ELPP_COMPILER_MSVC -# endif // ELPP_COMPILER_GCC +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC #else -# define ELPP_STACKTRACE 0 -#endif // (defined(ELPP_STACKTRACE_ON_CRASH)) +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) // Miscellaneous macros #define ELPP_UNUSED(x) (void)x #if ELPP_OS_UNIX // Log file permissions for unix-based systems -# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH #endif // ELPP_OS_UNIX #if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC -# if defined(ELPP_EXPORT_SYMBOLS) -# define ELPP_EXPORT __declspec(dllexport) -# else -# define ELPP_EXPORT __declspec(dllimport) -# endif // defined(ELPP_EXPORT_SYMBOLS) +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) #else -# define ELPP_EXPORT +# define ELPP_EXPORT #endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC // Some special functions that are VC++ specific #undef STRTOK @@ -229,54 +224,60 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #undef STRCAT #undef STRCPY #if ELPP_CRT_DBG_WARNINGS -# define STRTOK(a, b, c) strtok_s(a, b, c) -# define STRERROR(a, b, c) strerror_s(a, b, c) -# define STRCAT(a, b, len) strcat_s(a, len, b) -# define STRCPY(a, b, len) strcpy_s(a, len, b) +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) #else -# define STRTOK(a, b, c) strtok(a, b) -# define STRERROR(a, b, c) strerror(c) -# define STRCAT(a, b, len) strcat(a, b) -# define STRCPY(a, b, len) strcpy(a, b) +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) #endif // Compiler specific support evaluations -#if ((!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)) -# define ELPP_USE_STD_THREADING 1 +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 #else -# define ELPP_USE_STD_THREADING 0 +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif #endif #undef ELPP_FINAL #if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) -# define ELPP_FINAL +# define ELPP_FINAL #else -# define ELPP_FINAL final +# define ELPP_FINAL final #endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) #if defined(ELPP_EXPERIMENTAL_ASYNC) -# define ELPP_ASYNC_LOGGING 1 +# define ELPP_ASYNC_LOGGING 1 #else -# define ELPP_ASYNC_LOGGING 0 +# define ELPP_ASYNC_LOGGING 0 #endif // defined(ELPP_EXPERIMENTAL_ASYNC) #if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING -# define ELPP_THREADING_ENABLED 1 +# define ELPP_THREADING_ENABLED 1 #else -# define ELPP_THREADING_ENABLED 0 +# define ELPP_THREADING_ENABLED 0 #endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING // Function macro ELPP_FUNC #undef ELPP_FUNC #if ELPP_COMPILER_MSVC // Visual C++ -# define ELPP_FUNC __FUNCSIG__ +# define ELPP_FUNC __FUNCSIG__ #elif ELPP_COMPILER_GCC // GCC -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #elif ELPP_COMPILER_INTEL // Intel C++ -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #elif ELPP_COMPILER_CLANG // Clang++ -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #else -# if defined(__func__) -# define ELPP_FUNC __func__ -# else -# define ELPP_FUNC "" -# endif // defined(__func__) +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) #endif // defined(_MSC_VER) #undef ELPP_VARIADIC_TEMPLATES_SUPPORTED // Keep following line commented until features are fixed @@ -284,43 +285,43 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre (ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) // Logging Enable/Disable macros #define ELPP_LOGGING_ENABLED (!defined(ELPP_DISABLE_LOGS)) -#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_DEBUG_LOG 1 +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +# define ELPP_DEBUG_LOG 1 #else -# define ELPP_DEBUG_LOG 0 -#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) #if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_INFO_LOG 1 +# define ELPP_INFO_LOG 1 #else -# define ELPP_INFO_LOG 0 +# define ELPP_INFO_LOG 0 #endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_WARNING_LOG 1 +# define ELPP_WARNING_LOG 1 #else -# define ELPP_WARNING_LOG 0 +# define ELPP_WARNING_LOG 0 #endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_ERROR_LOG 1 +# define ELPP_ERROR_LOG 1 #else -# define ELPP_ERROR_LOG 0 +# define ELPP_ERROR_LOG 0 #endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_FATAL_LOG 1 +# define ELPP_FATAL_LOG 1 #else -# define ELPP_FATAL_LOG 0 +# define ELPP_FATAL_LOG 0 #endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_TRACE_LOG 1 +# define ELPP_TRACE_LOG 1 #else -# define ELPP_TRACE_LOG 0 +# define ELPP_TRACE_LOG 0 #endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_VERBOSE_LOG 1 +# define ELPP_VERBOSE_LOG 1 #else -# define ELPP_VERBOSE_LOG 0 +# define ELPP_VERBOSE_LOG 0 #endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!(ELPP_CXX0X || ELPP_CXX11)) -# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler" +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" #endif // (!(ELPP_CXX0X || ELPP_CXX11)) // Headers #if defined(ELPP_SYSLOG) @@ -336,9 +337,9 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include <cstdarg> #if defined(ELPP_UNICODE) # include <locale> -# if ELPP_OS_WINDOWS +# if ELPP_OS_WINDOWS # include <codecvt> -# endif // ELPP_OS_WINDOWS +# endif // ELPP_OS_WINDOWS #endif // defined(ELPP_UNICODE) #if ELPP_STACKTRACE # include <cxxabi.h> @@ -353,41 +354,39 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #elif ELPP_OS_WINDOWS # include <direct.h> # include <windows.h> -# if defined(WIN32_LEAN_AND_MEAN) +# if defined(WIN32_LEAN_AND_MEAN) # if defined(ELPP_WINSOCK2) # include <winsock2.h> # else # include <winsock.h> # endif // defined(ELPP_WINSOCK2) -# endif // defined(WIN32_LEAN_AND_MEAN) +# endif // defined(WIN32_LEAN_AND_MEAN) #endif // ELPP_OS_UNIX #include <string> #include <vector> #include <map> -#include <deque> #include <utility> #include <functional> #include <algorithm> #include <fstream> #include <iostream> #include <sstream> -#include <iomanip> #include <memory> #include <type_traits> #if ELPP_THREADING_ENABLED -# if ELPP_USE_STD_THREADING +# if ELPP_USE_STD_THREADING # include <mutex> # include <thread> -# else +# else # if ELPP_OS_UNIX # include <pthread.h> # endif // ELPP_OS_UNIX -# endif // ELPP_USE_STD_THREADING +# endif // ELPP_USE_STD_THREADING #endif // ELPP_THREADING_ENABLED #if ELPP_ASYNC_LOGGING -# if defined(ELPP_NO_SLEEP_FOR) +# if defined(ELPP_NO_SLEEP_FOR) # include <unistd.h> -# endif // defined(ELPP_NO_SLEEP_FOR) +# endif // defined(ELPP_NO_SLEEP_FOR) # include <thread> # include <queue> # include <condition_variable> @@ -400,15 +399,15 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # include <set> # include <bitset> # include <stack> -# if defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_STD_ARRAY) # include <array> -# endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) # include <unordered_map> -# endif // defined(ELPP_LOG_UNORDERED_MAP) -# if defined(ELPP_LOG_UNORDERED_SET) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) # include <unordered_set> -# endif // defined(ELPP_UNORDERED_SET) +# endif // defined(ELPP_UNORDERED_SET) #endif // defined(ELPP_STL_LOGGING) #if defined(ELPP_QT_LOGGING) // For logging Qt based classes & templates @@ -442,1565 +441,960 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #endif // defined(ELPP_WXWIDGETS_LOGGING) // Forward declarations namespace el { - class Logger; - class LogMessage; - class PerformanceTrackingData; - class Loggers; - class Helpers; - template <typename T> class Callback; - class LogDispatchCallback; - class PerformanceTrackingCallback; - class LogDispatchData; - namespace base { - class Storage; - class RegisteredLoggers; - class PerformanceTracker; - class MessageBuilder; - class Writer; - class PErrorWriter; - class LogDispatcher; - class DefaultLogBuilder; - class DefaultLogDispatchCallback; +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template <typename T> class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; #if ELPP_ASYNC_LOGGING - class AsyncLogDispatchCallback; - class AsyncDispatchWorker; +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; #endif // ELPP_ASYNC_LOGGING - class DefaultPerformanceTrackingCallback; - } // namespace base +class DefaultPerformanceTrackingCallback; +} // namespace base } // namespace el /// @brief Easylogging++ entry namespace namespace el { - /// @brief Namespace containing base/internal functionality used by Easylogging++ - namespace base { - /// @brief Data types used by Easylogging++ - namespace type { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { #undef ELPP_LITERAL #undef ELPP_STRLEN #undef ELPP_COUT #if defined(ELPP_UNICODE) -# define ELPP_LITERAL(txt) L##txt -# define ELPP_STRLEN wcslen -# if defined ELPP_CUSTOM_COUT -# define ELPP_COUT ELPP_CUSTOM_COUT -# else -# define ELPP_COUT std::wcout -# endif // defined ELPP_CUSTOM_COUT - typedef wchar_t char_t; - typedef std::wstring string_t; - typedef std::wstringstream stringstream_t; - typedef std::wfstream fstream_t; - typedef std::wostream ostream_t; +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; #else -# define ELPP_LITERAL(txt) txt -# define ELPP_STRLEN strlen -# if defined ELPP_CUSTOM_COUT -# define ELPP_COUT ELPP_CUSTOM_COUT -# else -# define ELPP_COUT std::cout -# endif // defined ELPP_CUSTOM_COUT - typedef char char_t; - typedef std::string string_t; - typedef std::stringstream stringstream_t; - typedef std::fstream fstream_t; - typedef std::ostream ostream_t; +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; #endif // defined(ELPP_UNICODE) #if defined(ELPP_CUSTOM_COUT_LINE) -# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) #else -# define ELPP_COUT_LINE(logLine) logLine << std::flush +# define ELPP_COUT_LINE(logLine) logLine << std::flush #endif // defined(ELPP_CUSTOM_COUT_LINE) - typedef unsigned short EnumType; - typedef std::shared_ptr<base::Storage> StoragePointer; - typedef int VerboseLevel; - typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr; - typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr; - } // namespace type - /// @brief Internal helper class that prevent copy constructor for class - /// - /// @detail When using this class simply inherit it privately - class NoCopy { - protected: - NoCopy(void) {} - private: - NoCopy(const NoCopy&); - NoCopy& operator=(const NoCopy&); - }; - /// @brief Internal helper class that makes all default constructors private. - /// - /// @detail This prevents initializing class making it static unless an explicit constructor is declared. - /// When using this class simply inherit it privately - class StaticClass { - private: - StaticClass(void); - StaticClass(const StaticClass&); - StaticClass& operator=(const StaticClass&); - }; - } // namespace base - /// @brief Represents enumeration for severity level used to determine level of logging - /// - /// @detail With Easylogging++, developers may disable or enable any level regardless of - /// what the severity is. Or they can choose to log using hierarchical logging flag - enum class Level : base::type::EnumType { - /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels - Global = 1, - /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. - Trace = 2, - /// @brief Informational events most useful for developers to debug application - Debug = 4, - /// @brief Severe error information that will presumably abort application - Fatal = 8, - /// @brief Information representing errors in application but application will keep running - Error = 16, - /// @brief Useful when application has potentially harmful situtaions - Warning = 32, - /// @brief Information that can be highly useful and vary with verbose logging level. - Verbose = 64, - /// @brief Mainly useful to represent current progress of application - Info = 128, - /// @brief Represents unknown level - Unknown = 1010 - }; - /// @brief Static class that contains helper functions for el::Level - class LevelHelper : base::StaticClass { - public: - /// @brief Represents minimum valid level. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace); - /// @brief Represents maximum valid level. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info); - /// @brief Casts level to int, useful for iterating through enum. - static base::type::EnumType castToInt(Level level) { - return static_cast<base::type::EnumType>(level); - } - /// @brief Casts int(ushort) to level, useful for iterating through enum. - static Level castFromInt(base::type::EnumType l) { - return static_cast<Level>(l); - } - /// @brief Converts level to associated const char* - /// @return Upper case string based level. - static const char* 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"; - } - /// @brief Converts from levelStr to Level - /// @param levelStr Upper case string based level. - /// Lower case is also valid but providing upper case is recommended. - static Level convertFromString(const char* levelStr) { - if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0)) - return Level::Global; - if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0)) - return Level::Debug; - if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0)) - return Level::Info; - if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0)) - return Level::Warning; - if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0)) - return Level::Error; - if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0)) - return Level::Fatal; - if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0)) - return Level::Verbose; - if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0)) - return Level::Trace; - return Level::Unknown; - } - /// @brief Converts from prefix of levelStr to Level - /// @param levelStr Upper case string based level. - /// Lower case is also valid but providing upper case is recommended. - static Level 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; - } - /// @brief Applies specified function to each level starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed as pointer and - /// is left-shifted so this can be used inside function (fn) to represent current level. - /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. - static inline void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { - base::type::EnumType lIndexMax = LevelHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); - } while (*startIndex <= lIndexMax); - } - }; - /// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect - /// of logging - enum class ConfigurationType : base::type::EnumType { - /// @brief Determines whether or not corresponding level and logger of logging is enabled - /// You may disable all logs by using el::Level::Global - Enabled = 1, - /// @brief Whether or not to write corresponding log to log file - ToFile = 2, - /// @brief Whether or not to write corresponding level and logger log to standard output. - /// By standard output meaning termnal, command prompt etc - ToStandardOutput = 4, - /// @brief Determines format of logging corresponding level and logger. - Format = 8, - /// @brief Determines log file (full path) to write logs to for correponding level and logger - Filename = 16, - /// @brief Specifies milliseconds width. Width can be within range (1-6) - MillisecondsWidth = 32, - /// @brief Determines whether or not performance tracking is enabled. - /// - /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger - PerformanceTracking = 64, - /// @brief Specifies log file max size. - /// - /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will - /// be truncated and re-initiated. - MaxLogFileSize = 128, - /// @brief Specifies number of log entries to hold until we flush pending log data - LogFlushThreshold = 256, - /// @brief Represents unknown configuration - Unknown = 1010 - }; - /// @brief Static class that contains helper functions for el::ConfigurationType - class ConfigurationTypeHelper : base::StaticClass { - public: - /// @brief Represents minimum valid configuration type. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled); - /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize); - /// @brief Casts configuration type to int, useful for iterating through enum. - static base::type::EnumType castToInt(ConfigurationType configurationType) { - return static_cast<base::type::EnumType>(configurationType); - } - /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. - static ConfigurationType castFromInt(base::type::EnumType c) { - return static_cast<ConfigurationType>(c); - } - /// @brief Converts configuration type to associated const char* - /// @returns Upper case string based configuration type. - static const char* 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::MillisecondsWidth) return "MILLISECONDS_WIDTH"; - 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"; - } - /// @brief Converts from configStr to ConfigurationType - /// @param configStr Upper case string based configuration type. - /// Lower case is also valid but providing upper case is recommended. - static ConfigurationType convertFromString(const char* configStr) { - if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0)) - return ConfigurationType::Enabled; - if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0)) - return ConfigurationType::ToFile; - if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0)) - return ConfigurationType::ToStandardOutput; - if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0)) - return ConfigurationType::Format; - if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0)) - return ConfigurationType::Filename; - if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0)) - return ConfigurationType::MillisecondsWidth; - if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0)) - return ConfigurationType::PerformanceTracking; - if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0)) - return ConfigurationType::MaxLogFileSize; - if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0)) - return ConfigurationType::LogFlushThreshold; - return ConfigurationType::Unknown; - } - /// @brief Applies specified function to each configuration type starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted - /// so this can be used inside function (fn) to represent current configuration type. - /// @param fn function to apply with each configuration type. - /// This bool represent whether or not to stop iterating through configurations. - static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { - base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); - } while (*startIndex <= cIndexMax); - } - }; - /// @brief Flags used while writing logs. This flags are set by user - enum class LoggingFlag : base::type::EnumType { - /// @brief Makes sure we have new line for each container log entry - NewLineForContainer = 1, - /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose - /// logging is allowed via that module. - AllowVerboseIfModuleNotSpecified = 2, - /// @brief When handling crashes by default, detailed crash reason will be logged as well - LogDetailedCrashReason = 4, - /// @brief Allows to disable application abortion when logged using FATAL level - DisableApplicationAbortOnFatalLog = 8, - /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default - ImmediateFlush = 16, - /// @brief Enables strict file rolling - StrictLogFileSizeCheck = 32, - /// @brief Make terminal output colorful for supported terminals - ColoredTerminalOutput = 64, - /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") - MultiLoggerSupport = 128, - /// @brief Disables comparing performance tracker's checkpoints - DisablePerformanceTrackingCheckpointComparison = 256, - /// @brief Disable VModules - DisableVModules = 512, - /// @brief Disable VModules extensions - DisableVModulesExtensions = 1024, - /// @brief Enables hierarchical logging - HierarchicalLogging = 2048, - /// @brief Creates logger automatically when not available - CreateLoggerAutomatically = 4096, - /// @brief Adds spaces b/w logs that separated by left-shift operator - AutoSpacing = 8192, - /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) - FixedTimeFormat = 16384 - }; - namespace base { - /// @brief Namespace containing constants used internally. - namespace consts { - // Level log values - These are values that are replaced in place of %level format specifier - 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("WARN "); - 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("VER"); - 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* kDefaultLoggerId = "default"; - static const char* kPerformanceLoggerId = "performance"; +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr<base::Storage> StoragePointer; +typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr; +typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr; +typedef std::shared_ptr<LoggerRegistrationCallback> LoggerRegistrationCallbackPtr; +typedef std::unique_ptr<el::base::PerformanceTracker> PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast<base::type::EnumType>(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast<Level>(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast<base::type::EnumType>(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast<ConfigurationType>(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384 +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif +// Level log values - These are values that are replaced in place of %level format specifier +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("WARN "); +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("VER"); +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 +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif #if defined(ELPP_SYSLOG) - static const char* kSysLogLoggerId = "syslog"; +static const char* kSysLogLoggerId = "syslog"; #endif // defined(ELPP_SYSLOG) - static const char* kNullPointer = "nullptr"; - static const char kFormatSpecifierChar = '%'; +static const char* kNullPointer = "nullptr"; +static const char kFormatSpecifierChar = '%'; #if ELPP_VARIADIC_TEMPLATES_SUPPORTED - static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierCharValue = 'v'; #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - static const unsigned int kMaxLogPerContainer = 100; - static const unsigned int kMaxLogPerCounter = 100000; - static const unsigned int kDefaultMillisecondsWidth = 3; - static const base::type::VerboseLevel kMaxVerboseLevel = 9; - static const char* kUnknownUser = "user"; - static const char* kUnknownHost = "unknown-host"; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kDefaultSubsecondPrecision = 3; +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "user"; +static const char* kUnknownHost = "unknown-host"; #if defined(ELPP_DEFAULT_LOG_FILE) - static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; #else -# if ELPP_OS_UNIX +# if ELPP_OS_UNIX # if ELPP_OS_ANDROID - static const char* kDefaultLogFile = "logs/myeasylog.log"; +static const char* kDefaultLogFile = "logs/myeasylog.log"; # else - static const char* kDefaultLogFile = "logs/myeasylog.log"; +static const char* kDefaultLogFile = "logs/myeasylog.log"; # endif // ELPP_OS_ANDROID -# elif ELPP_OS_WINDOWS - static const char* kDefaultLogFile = "logs\\myeasylog.log"; -# endif // ELPP_OS_UNIX +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "logs\\myeasylog.log"; +# endif // ELPP_OS_UNIX #endif // defined(ELPP_DEFAULT_LOG_FILE) #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) - static const char* kDefaultLogFileParam = "--default-log-file"; +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"; +static const char* kLoggingFlagsParam = "--logging-flags"; #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) #if ELPP_OS_WINDOWS - static const char* kFilePathSeperator = "\\"; +static const char* kFilePathSeperator = "\\"; #else - static const char* kFilePathSeperator = "/"; +static const char* kFilePathSeperator = "/"; #endif // ELPP_OS_WINDOWS - static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; - static const char* kConfigurationComment = "##"; - static const char* kConfigurationLevel = "*"; - static const char* kConfigurationLoggerId = "--"; - static const std::size_t kSourceFilenameMaxLength = 100; - static const std::size_t kSourceLineMaxLength = 10; - static const Level kPerformanceTrackerDefaultLevel = Level::Info; - const struct { - double value; - const base::type::char_t* unit; - } kTimeFormats[] = { - { 1000.0f, ELPP_LITERAL("mis") }, - { 1000.0f, ELPP_LITERAL("ms") }, - { 60.0f, ELPP_LITERAL("seconds") }, - { 60.0f, ELPP_LITERAL("minutes") }, - { 24.0f, ELPP_LITERAL("hours") }, - { 7.0f, ELPP_LITERAL("days") } - }; - static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); - const struct { - int numb; - const char* name; - const char* brief; - const char* detail; - } kCrashSignals[] = { - // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) - { SIGABRT, "SIGABRT", "Abnormal termination", - "Program was abnormally terminated." }, - { SIGFPE, "SIGFPE", "Erroneous arithmetic operation", - "Arithemetic operation issue such as division by zero or operation resulting in overflow." }, - { SIGILL, "SIGILL", "Illegal instruction", - "Generally due to a corruption in the code or to an attempt to execute data."}, - { SIGSEGV, "SIGSEGV", "Invalid access to memory", - "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." }, - { SIGINT, "SIGINT", "Interactive attention signal", - "Interruption generated (generally) by user or operating system." }, - }; - static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); - } // namespace consts - } // namespace base - typedef std::function<void(const char*, std::size_t)> PreRollOutCallback; - namespace base { - static inline void defaultPreRollOutCallback(const char*, std::size_t) {} - /// @brief Enum to represent timestamp unit - enum class TimestampUnit : base::type::EnumType { - Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 - }; - /// @brief Format flags used to determine specifiers that are active for performance improvements. - enum class FormatFlags : base::type::EnumType { - DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6, - User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12, - Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15 - }; - /// @brief A milliseconds width class containing actual width and offset for date/time - class MillisecondsWidth { - public: - MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); } - explicit MillisecondsWidth(int width) { init(width); } - bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; } - int m_width; unsigned int m_offset; - private: - void init(int width) { - if (width < 1 || width > 6) { - width = base::consts::kDefaultMillisecondsWidth; - } - 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; - } - } - }; - /// @brief Namespace containing utility functions/static classes used internally - namespace utils { - /// @brief Deletes memory safely and points to null - template <typename T> - static inline - typename std::enable_if<std::is_pointer<T*>::value, void>::type - safeDelete(T*& pointer) { - if (pointer == nullptr) - return; - delete pointer; - pointer = nullptr; - } - /// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned - static inline const char* charPtrVal(const char* pointer) { - return pointer == nullptr ? base::consts::kNullPointer : pointer; - } - /// @brief Aborts application due with user-defined status - static inline void abort(int status, const std::string& reason = std::string()) { - // 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 +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +} // namespace consts +} // namespace base +typedef std::function<void(const char*, std::size_t)> PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template <typename T> +static +typename std::enable_if<std::is_pointer<T*>::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre> +namespace bitwise { +template <typename Enum> +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e); +} +template <typename Enum> +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e)); +} +template <typename Enum> +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e); +} +} // namespace bitwise +template <typename Enum> +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or<Enum>(e, *flag); +} +template <typename Enum> +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not<Enum>(e, *flag); +} +template <typename Enum> +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And<Enum>(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template <typename M> +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard<base::threading::Mutex> ScopedLock; +# endif // !ELPP_USE_STD_THREADING #else - ::abort(); -#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) - } - /// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation - /// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre> - namespace bitwise { - template <typename Enum> - static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e); - } - template <typename Enum> - static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e)); - } - template <typename Enum> - static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e); - } - } // namespace bitwise - template <typename Enum> - static inline void addFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Or<Enum>(e, *flag); - } - template <typename Enum> - static inline void removeFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Not<Enum>(e, *flag); - } - template <typename Enum> - static inline bool hasFlag(Enum e, base::type::EnumType flag) { - return base::utils::bitwise::And<Enum>(e, flag) > 0x0; - } - } // namespace utils - namespace threading { +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template <typename Mutex> +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + #if ELPP_THREADING_ENABLED -# if !ELPP_USE_STD_THREADING - namespace internal { - /// @brief A mutex wrapper for compiler that dont yet support std::mutex - class Mutex : base::NoCopy { - public: - Mutex(void) { -# if ELPP_OS_UNIX - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m_underlyingMutex, &attr); - pthread_mutexattr_destroy(&attr); -# elif ELPP_OS_WINDOWS - InitializeCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - virtual ~Mutex(void) { -# if ELPP_OS_UNIX - pthread_mutex_destroy(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - DeleteCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline void lock(void) { -# if ELPP_OS_UNIX - pthread_mutex_lock(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - EnterCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline bool try_lock(void) { -# if ELPP_OS_UNIX - return (pthread_mutex_trylock(&m_underlyingMutex) == 0); -# elif ELPP_OS_WINDOWS - return TryEnterCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline void unlock(void) { -# if ELPP_OS_UNIX - pthread_mutex_unlock(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - LeaveCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - private: -# if ELPP_OS_UNIX - pthread_mutex_t m_underlyingMutex; -# elif ELPP_OS_WINDOWS - CRITICAL_SECTION m_underlyingMutex; -# endif // ELPP_OS_UNIX - }; - /// @brief Scoped lock for compiler that dont yet support std::lock_guard - template <typename M> - class ScopedLock : base::NoCopy { - public: - explicit ScopedLock(M& mutex) { - m_mutex = &mutex; - m_mutex->lock(); - } - - virtual ~ScopedLock(void) { - m_mutex->unlock(); - } - private: - M* m_mutex; - ScopedLock(void); - }; - } // namespace internal - /// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. - static inline std::string getCurrentThreadId(void) { - std::stringstream ss; +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; # if (ELPP_OS_WINDOWS) - ss << GetCurrentThreadId(); + ss << GetCurrentThreadId(); # endif // (ELPP_OS_WINDOWS) - return ss.str(); - } - static inline void msleep(int) { - // No implementation for non std::thread version - } - typedef base::threading::internal::Mutex Mutex; - typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock; -# else - /// @brief Gets ID of currently running threading using std::this_thread::get_id() - static inline std::string getCurrentThreadId(void) { - std::stringstream ss; - ss << std::setfill(' ') << std::setw(16) << std::hex << std::this_thread::get_id(); - return ss.str(); - } - static inline void msleep(int ms) { - // Only when async logging enabled - this is because async is strict on compiler -# if ELPP_ASYNC_LOGGING -# 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) -# else - ELPP_UNUSED(ms); -# endif // ELPP_ASYNC_LOGGING - } - typedef std::recursive_mutex Mutex; - typedef std::lock_guard<std::recursive_mutex> ScopedLock; -# endif // !ELPP_USE_STD_THREADING + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING #else - namespace internal { - /// @brief Mutex wrapper used when multi-threading is disabled. - class NoMutex : base::NoCopy { - public: - NoMutex(void) {} - inline void lock(void) {} - inline bool try_lock(void) { return true; } - inline void unlock(void) {} - }; - /// @brief Lock guard wrapper used when multi-threading is disabled. - template <typename Mutex> - class NoScopedLock : base::NoCopy { - public: - explicit NoScopedLock(Mutex&) { - } - virtual ~NoScopedLock(void) { - } - private: - NoScopedLock(void); - }; - } // namespace internal - static inline std::string getCurrentThreadId(void) { - return std::string(); - } - static inline void msleep(int) { - // No custom implementation - } - typedef base::threading::internal::NoMutex Mutex; - typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock; +static inline std::string getCurrentThreadId(void) { + return std::string(); +} #endif // ELPP_THREADING_ENABLED - /// @brief Base of thread safe class, this class is inheritable-only - class ThreadSafe { - public: - virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } - virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } - virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } - protected: - ThreadSafe(void) {} - virtual ~ThreadSafe(void) {} - private: - base::threading::Mutex m_mutex; - }; - } // namespace threading - namespace utils { - class File : base::StaticClass { - public: - /// @brief Creates new out file stream for specified filename. - /// @return Pointer to newly created fstream or nullptr - static base::type::fstream_t* newFileStream(const std::string& filename) { - base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), - base::type::fstream_t::out -#if !defined(ELPP_FRESH_LOG_FILE) - | base::type::fstream_t::app -#endif - ); -#if defined(ELPP_UNICODE) - std::locale elppUnicodeLocale(""); -# if ELPP_OS_WINDOWS - std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>); - elppUnicodeLocale = elppUnicodeLocaleWindows; -# endif // ELPP_OS_WINDOWS - fs->imbue(elppUnicodeLocale); -#endif // defined(ELPP_UNICODE) - if (fs->is_open()) { - fs->flush(); - } else { - base::utils::safeDelete(fs); - ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); - } - return fs; - } - - /// @brief Gets size of file provided in stream - static std::size_t getSizeOfFile(base::type::fstream_t* fs) { - if (fs == nullptr) { - return 0; - } - std::streampos currPos = fs->tellg(); - fs->seekg(0, fs->end); - std::size_t size = static_cast<std::size_t>(fs->tellg()); - fs->seekg(currPos); - return size; - } - - /// @brief Determines whether or not provided path exist in current file system - static inline bool pathExists(const char* path, bool considerFile = false) { - 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 - } - - /// @brief Creates specified path on file system - /// @param path Path to create. - static bool createPath(const std::string& path) { - if (path.empty()) { - return false; - } - if (base::utils::File::pathExists(path.c_str())) { - return true; - } - int status = -1; - - char* currPath = const_cast<char*>(path.c_str()); - std::string builtPath = std::string(); -#if ELPP_OS_UNIX - if (path[0] == '/') { - builtPath = "/"; - } - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - // Use secure functions API - char* nextTok_ = nullptr; - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); - ELPP_UNUSED(nextTok_); -#endif // ELPP_OS_UNIX - while (currPath != nullptr) { - builtPath.append(currPath); - builtPath.append(base::consts::kFilePathSeperator); -#if ELPP_OS_UNIX - status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - status = _mkdir(builtPath.c_str()); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); -#endif // ELPP_OS_UNIX - } - if (status == -1) { - ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); - return false; - } - return true; - } - /// @brief Extracts path of filename with leading slash - static std::string extractPathFromFilename(const std::string& fullPath, - const char* seperator = base::consts::kFilePathSeperator) { - if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) { - return fullPath; - } - std::size_t lastSlashAt = fullPath.find_last_of(seperator); - if (lastSlashAt == 0) { - return std::string(seperator); - } - return fullPath.substr(0, lastSlashAt + 1); - } - /// @brief builds stripped filename and puts it in buff - static void buildStrippedFilename(const char* filename, char buff[], const std::string &commonPrefix = NULL, - std::size_t limit = base::consts::kSourceFilenameMaxLength) { - 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); - } - /// @brief builds base filename and puts it in buff - static void buildBaseFilename(const std::string& fullPath, char buff[], - std::size_t limit = base::consts::kSourceFilenameMaxLength, - const char* seperator = base::consts::kFilePathSeperator) { - const char *filename = fullPath.c_str(); - std::size_t lastSlashAt = fullPath.find_last_of(seperator); - 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); - } - }; - /// @brief String utilities helper class used internally. You should not use it. - class Str : base::StaticClass { - public: - /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. - static inline bool isDigit(char c) { - return c >= '0' && c <= '9'; - } - - /// @brief Matches wildcards, '*' and '?' only supported. - static bool 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; - } - - /// @brief Trims string from start - /// @param [in,out] str String to trim - static inline std::string& ltrim(std::string& str) { - str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(&std::isspace)))); - return str; - } - - /// @brief Trim string from end - /// @param [in,out] str String to trim - static inline std::string& rtrim(std::string& str) { - str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(&std::isspace))).base(), str.end()); - return str; - } - - /// @brief Trims string from left and right - /// @param [in,out] str String to trim - static inline std::string& trim(std::string& str) { - return ltrim(rtrim(str)); - } - - /// @brief Determines whether or not str starts with specified string - /// @param str String to check - /// @param start String to check against - /// @return Returns true if starts with specified string, false otherwise - static inline bool startsWith(const std::string& str, const std::string& start) { - return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); - } - - /// @brief Determines whether or not str ends with specified string - /// @param str String to check - /// @param end String to check against - /// @return Returns true if ends with specified string, false otherwise - static inline bool endsWith(const std::string& str, const std::string& end) { - return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); - } - - /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. - /// @param [in,out] str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified version of str - static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) { - std::replace(str.begin(), str.end(), replaceWhat, replaceWith); - return str; - } - - /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place - /// @param str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified (original) str - static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT - 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; - } - - static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT - const base::type::string_t& replaceWith) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { - if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { - str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); - ++foundAt; - } else { - str.replace(foundAt, replaceWhat.length(), replaceWith); - return; - } - } - } +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); #if defined(ELPP_UNICODE) - static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT - const std::string& replaceWith) { - replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); - } + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); #endif // defined(ELPP_UNICODE) - /// @brief Converts string to uppercase - /// @param str String to convert - /// @return Uppercase string - static inline std::string& toUpper(std::string& str) { - std::transform(str.begin(), str.end(), str.begin(), ::toupper); - return str; - } - - /// @brief Compares cstring equality - uses strcmp - static inline bool 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; - } - - /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) - /// Dont use strcasecmp because of CRT (VC++) - static bool cStringCaseEq(const char* s1, const char* s2) { - if (s1 == nullptr && s2 == nullptr) return true; - if (s1 == nullptr || s2 == nullptr) return false; - if (strlen(s1) != strlen(s2)) return false; - while (*s1 != '\0' && *s2 != '\0') { - if (::toupper(*s1) != ::toupper(*s2)) return false; - ++s1; - ++s2; - } - return true; - } - - /// @brief Returns true if c exist in str - static inline bool contains(const char* str, char c) { - for (; *str; ++str) { - if (*str == c) - return true; - } - return false; - } - - static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) { - char localBuff[10] = ""; - char* p = localBuff + sizeof(localBuff) - 2; - if (n > 0) { - for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) - *--p = static_cast<char>(n % 10 + '0'); - } else { - *--p = '0'; - --len; - } - if (zeroPadded) - while (p > localBuff && len-- > 0) *--p = static_cast<char>('0'); - return addToBuff(p, buf, bufLim); - } - - static inline char* addToBuff(const char* str, char* buf, const char* bufLim) { - while ((buf < bufLim) && ((*buf = *str++) != '\0')) - ++buf; - return buf; - } - - static inline char* 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! - static char* wcharPtrToCharPtr(const wchar_t* line) { - std::size_t len_ = wcslen(line) + 1; - char* buff_ = static_cast<char*>(malloc(len_ + 1)); -# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - std::wcstombs(buff_, line, len_); -# elif ELPP_OS_WINDOWS - std::size_t convCount_ = 0; - mbstate_t mbState_; - ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_)); - wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); -# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - return buff_; - } - }; - /// @brief Operating System helper static class used internally. You should not use it. - class OS : base::StaticClass { - public: + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: #if ELPP_OS_WINDOWS - /// @brief Gets environment variables for Windows based OS. - /// We are not using <code>getenv(const char*)</code> because of CRT deprecation - /// @param varname Variable name to get environment variable value for - /// @return If variable exist the value of it otherwise nullptr - static const char* getWindowsEnvironmentVariable(const char* varname) { - const DWORD bufferLen = 50; - static char buffer[bufferLen]; - if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { - return buffer; - } - return nullptr; - } + /// @brief Gets environment variables for Windows based OS. + /// We are not using <code>getenv(const char*)</code> because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); #endif // ELPP_OS_WINDOWS #if ELPP_OS_ANDROID - /// @brief Reads android property value - static inline std::string 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); - } - - /// @brief Reads android device name - static std::string 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(); - } + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); #endif // ELPP_OS_ANDROID - - /// @brief Runs command on terminal and returns the output. - /// - /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. - /// @param command Bash command - /// @return Result of bash output or empty string if no result found. - static const std::string getBashOutput(const char* command) { -#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) - if (command == nullptr) { - return std::string(); - } - FILE* proc = nullptr; - if ((proc = popen(command, "r")) == nullptr) { - ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); - return std::string(); - } - char hBuff[4096]; - if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { - pclose(proc); - if (hBuff[strlen(hBuff) - 1] == '\n') { - hBuff[strlen(hBuff) - 1] = '\0'; - } - return std::string(hBuff); - } - return std::string(); -#else - ELPP_UNUSED(command); - return std::string(); -#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) - } - - /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) - /// @param variableName Environment variable name - /// @param defaultVal If no environment variable or value found the value to return by default - /// @param alternativeBashCommand If environment variable not found what would be alternative bash command - /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' - static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) { -#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); - } - /// @brief Gets current username. - static inline std::string 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 - } - - /// @brief Gets current host name or computer name. - /// - /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen - static inline std::string 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 - } - /// @brief Whether or not terminal supports colors - static inline bool 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"; - } - }; - extern std::string s_currentUser; - extern std::string s_currentHost; - extern bool s_termSupportsColor; -#define ELPP_INITI_BASIC_DECLR \ -namespace el {\ -namespace base {\ -namespace utils {\ -std::string s_currentUser = el::base::utils::OS::currentUser(); \ -std::string s_currentHost = el::base::utils::OS::currentHost(); \ -bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \ -}\ -}\ -} - /// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str - class DateTime : base::StaticClass { - public: - /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond. - /// - /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided - /// @param [in,out] tv Pointer that gets updated - static void gettimeofday(struct timeval* tv) { -#if ELPP_OS_WINDOWS - if (tv != nullptr) { -# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const unsigned __int64 delta_ = 11644473600000000Ui64; -# else - const unsigned __int64 delta_ = 11644473600000000ULL; -# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const double secOffSet = 0.000001; - const unsigned long usecOffSet = 1000000; - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - unsigned __int64 present = 0; - present |= fileTime.dwHighDateTime; - present = present << 32; - present |= fileTime.dwLowDateTime; - present /= 10; // mic-sec - // Subtract the difference - present -= delta_; - tv->tv_sec = static_cast<long>(present * secOffSet); - tv->tv_usec = static_cast<long>(present % usecOffSet); - } -#else - ::gettimeofday(tv, nullptr); -#endif // ELPP_OS_WINDOWS - } - - /// @brief Gets current date and time with milliseconds. - /// @param format User provided date/time format - /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null) - /// @returns string based date time in specified format. - static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) { - struct timeval currTime; - gettimeofday(&currTime); - struct ::tm timeInfo; - buildTimeInfo(&currTime, &timeInfo); - const int kBuffSize = 30; - char buff_[kBuffSize] = ""; - parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(currTime.tv_usec / msWidth->m_offset), msWidth); - return std::string(buff_); - } - - /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc - static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { - double result = static_cast<double>(time); - base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit); - const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; - for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { - if (result <= base::consts::kTimeFormats[i].value) { - break; - } - result /= base::consts::kTimeFormats[i].value; - unit = base::consts::kTimeFormats[i + 1].unit; - } - base::type::stringstream_t ss; - ss << result << " " << unit; - return ss.str(); - } - - /// @brief Gets time difference in milli/micro second depending on timestampUnit - static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) { - if (timestampUnit == base::TimestampUnit::Microsecond) { - return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) - - static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec)); - } else { - return static_cast<unsigned long long>((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000); - } - } - - private: - static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { -#if ELPP_OS_UNIX - time_t rawTime = currTime->tv_sec; - ::localtime_r(&rawTime, timeInfo); - return timeInfo; -#else -# if ELPP_COMPILER_MSVC - ELPP_UNUSED(currTime); - time_t t; - _time64(&t); - localtime_s(timeInfo, &t); - return timeInfo; -# else - // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method - time_t rawTime = currTime->tv_sec; - struct tm* tmInf = localtime(&rawTime); - *timeInfo = *tmInf; - return timeInfo; -# endif // ELPP_COMPILER_MSVC -#endif // ELPP_OS_UNIX - } - static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, - std::size_t msec, const base::MillisecondsWidth* msWidth) { - 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': // milliseconds - case 'g': - buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->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; - } - }; - /// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) - class CommandLineArgs { - public: - CommandLineArgs(void) { - setArgs(0, static_cast<char**>(nullptr)); - } - CommandLineArgs(int argc, const char** argv) { - setArgs(argc, argv); - } - CommandLineArgs(int argc, char** argv) { - setArgs(argc, argv); - } - virtual ~CommandLineArgs(void) {} - /// @brief Sets arguments and parses them - inline void setArgs(int argc, const char** argv) { - setArgs(argc, const_cast<char**>(argv)); - } - /// @brief Sets arguments and parses them - inline void 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])); - } - } - } - } - /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') - inline bool hasParamWithValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); - } - /// @brief Returns value of arguments - /// @see hasParamWithValue(const char*) - inline const char* getParamValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); - } - /// @brief Return true if arguments has a param (not having a value) i,e without '=' - inline bool hasParam(const char* paramKey) const { - return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); - } - /// @brief Returns true if no params available. This exclude argv[0] - inline bool empty(void) const { - return m_params.empty() && m_paramsWithValue.empty(); - } - /// @brief Returns total number of arguments. This exclude argv[0] - inline std::size_t size(void) const { - return m_params.size() + m_paramsWithValue.size(); - } - inline friend 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; - } - - private: - int m_argc; - char** m_argv; - std::map<std::string, std::string> m_paramsWithValue; - std::vector<std::string> m_params; - }; - /// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. - /// - /// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement - /// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container - /// and few more methods; get() to find element, unregister() to unregister single entry. - /// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. - template <typename T_Ptr, typename Container> - class AbstractRegistry : public base::threading::ThreadSafe { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - - /// @brief Default constructor - AbstractRegistry(void) {} - - /// @brief Move constructor that is useful for base classes - AbstractRegistry(AbstractRegistry&& sr) { - if (this == &sr) { - return; - } - unregisterAll(); - m_list = std::move(sr.m_list); - } - - bool operator==(const AbstractRegistry<T_Ptr, Container>& other) { - if (size() != other.size()) { - return false; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return false; - } - } - return true; - } - - bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) { - if (size() != other.size()) { - return true; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return true; - } - } - return false; - } - - /// @brief Assignment move operator - AbstractRegistry& operator=(AbstractRegistry&& sr) { - if (this == &sr) { - return *this; - } - unregisterAll(); - m_list = std::move(sr.m_list); - return *this; - } - - virtual ~AbstractRegistry(void) { - } - - /// @return Iterator pointer from start of repository - virtual inline iterator begin(void) ELPP_FINAL { - return m_list.begin(); - } - - /// @return Iterator pointer from end of repository - virtual inline iterator end(void) ELPP_FINAL { - return m_list.end(); - } - - - /// @return Constant iterator pointer from start of repository - virtual inline const_iterator cbegin(void) const ELPP_FINAL { - return m_list.cbegin(); - } - - /// @return End of repository - virtual inline const_iterator cend(void) const ELPP_FINAL { - return m_list.cend(); - } - - /// @return Whether or not repository is empty - virtual inline bool empty(void) const ELPP_FINAL { - return m_list.empty(); - } - - /// @return Size of repository - virtual inline std::size_t size(void) const ELPP_FINAL { - return m_list.size(); - } - - /// @brief Returns underlying container by reference - virtual inline Container& list(void) ELPP_FINAL { - return m_list; - } - - /// @brief Returns underlying container by constant reference. - virtual inline const Container& list(void) const ELPP_FINAL { - return m_list; + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + private: + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast<char**>(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast<char**>(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::map<std::string, std::string> m_paramsWithValue; + std::vector<std::string> m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template <typename T_Ptr, typename Container> +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; } - - /// @brief Unregisters all the pointers from current repository. - virtual void unregisterAll(void) = 0; - -protected: - virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0; - void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) { - unregisterAll(); - deepCopy(sr); + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry<T_Ptr, Container>& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } } - -private: - Container m_list; + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0; + void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; }; /// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) @@ -2010,74 +1404,74 @@ private: /// explicitly (by using lock functions) template <typename T_Ptr, typename T_Key = const char*> class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> { -public: - typedef typename Registry<T_Ptr, T_Key>::iterator iterator; - typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator; - - Registry(void) {} - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); - } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - Registry& operator=(const Registry& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; + public: + typedef typename Registry<T_Ptr, T_Key>::iterator iterator; + typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { + if (this == &sr) { + return; } - - virtual ~Registry(void) { - unregisterAll(); + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; } - -protected: - virtual inline void unregisterAll(void) ELPP_FINAL { + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr.second); - } - this->list().clear(); + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); } -} + } /// @brief Registers new registry to repository. -virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { -unregister(uniqKey); -this->list().insert(std::make_pair(uniqKey, ptr)); -} + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } /// @brief Unregisters single entry mapped to specified unique key -inline void unregister(const T_Key& uniqKey) { + void unregister(const T_Key& uniqKey) { T_Ptr* existing = get(uniqKey); if (existing != nullptr) { - base::utils::safeDelete(existing); - this->list().erase(uniqKey); + base::utils::safeDelete(existing); + this->list().erase(uniqKey); } -} + } /// @brief Gets pointer from repository. If none found, nullptr is returned. -inline T_Ptr* get(const T_Key& uniqKey) { + T_Ptr* get(const T_Key& uniqKey) { iterator it = this->list().find(uniqKey); return it == this->list().end() - ? nullptr - : it->second; -} + ? nullptr + : it->second; + } -private: -virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL { -for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { - registerNew(it->first, new T_Ptr(*it->second)); -} -} + private: + virtual void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } }; /// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) @@ -2086,348 +1480,222 @@ for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { /// should be made thread-safe explicitly template <typename T_Ptr, typename Pred> class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> { -public: - typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator; - typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator; - - RegistryWithPred(void) { - } - - virtual ~RegistryWithPred(void) { - unregisterAll(); - } - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); + public: + typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator; + typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { + if (this == &sr) { + return; } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - RegistryWithPred& operator=(const RegistryWithPred& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; } - - friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { - for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); - } - return os; + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); } - -protected: - virtual inline void unregisterAll(void) ELPP_FINAL { + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr); - } - this->list().clear(); + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); } -} + } -virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { -if (ptr) { - iterator iter = this->begin(); - for (; iter != this->end(); ++iter) { + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { if (ptr == *iter) { - break; + break; } - } - if (iter != this->end() && *iter != nullptr) { + } + if (iter != this->end() && *iter != nullptr) { this->list().erase(iter); base::utils::safeDelete(*iter); + } } -} -} + } -virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { -this->list().push_back(ptr); -} + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate /// in order to validate pointer. -template <typename T, typename T2> -inline T_Ptr* get(const T& arg1, const T2 arg2) { + template <typename T, typename T2> + T_Ptr* get(const T& arg1, const T2 arg2) { iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); if (iter != this->list().end() && *iter != nullptr) { - return *iter; + return *iter; } return nullptr; -} + } -private: -virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) { + private: + virtual void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) { for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - registerNew(new T_Ptr(**it)); + registerNew(new T_Ptr(**it)); } -} + } }; +class Utils { + public: + template <typename T, typename TPtr> + static bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template <typename T, typename TPtr> + static void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + template <typename T, typename TPtr> + static T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) { + typename std::map<std::string, TPtr>::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast<T*>(iter->second.get()); + } + return nullptr; + } +}; } // namespace utils } // namespace base /// @brief Base of Easylogging++ friendly class /// /// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` class Loggable { -public: - virtual ~Loggable(void) {} - virtual void log(el::base::type::ostream_t&) const = 0; -private: - friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { - loggable.log(os); - return os; - } + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } }; namespace base { - /// @brief Represents log format containing flags and date format. This is used internally to start initial log - class LogFormat : public Loggable { - public: - 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) { - } - - LogFormat(Level level, const base::type::string_t& format) - : m_level(level), m_userFormat(format) { - parseFromFormat(m_userFormat); - } - - 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; - } - - 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); - } - - LogFormat& operator=(const LogFormat& logFormat) { - m_level = logFormat.m_level; - m_userFormat = logFormat.m_userFormat; - m_dateTimeFormat = logFormat.m_dateTimeFormat; - m_flags = logFormat.m_flags; - return *this; - } - - virtual ~LogFormat(void) { - } - - inline bool 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 parseFromFormat(const base::type::string_t& userFormat) { - // We make copy because we will be changing the format - // i.e, removing user provided date format from original format - // and then storing it. - base::type::string_t formatCopy = userFormat; - m_flags = 0x0; - auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){ - if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { - if (hasFlag(flag)) { - // If we already have flag we remove the escape chars so that '%%' is turned to '%' - // even after specifier resolution - this is because we only replaceFirst specifier - formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); - ++foundAt; - } - } else { - if (!hasFlag(flag)) addFlag(flag); - } - } - }; - conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); - conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); - conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); - conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); - conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); - conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); - conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); - conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); - conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); - conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); - conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); - conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); - conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); - conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); - // For date/time we need to extract user's date format first - std::size_t dateIndex = std::string::npos; - if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { - while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { - dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); - } - if (dateIndex != std::string::npos) { - addFlag(base::FormatFlags::DateTime); - updateDateFormat(dateIndex, formatCopy); - } - } - m_format = formatCopy; - updateFormatSpec(); - } - - inline Level level(void) const { - return m_level; - } - - inline const base::type::string_t& userFormat(void) const { - return m_userFormat; - } - - inline const base::type::string_t& format(void) const { - return m_format; - } - - inline const std::string& dateTimeFormat(void) const { - return m_dateTimeFormat; - } - - inline base::type::EnumType flags(void) const { - return m_flags; - } - - inline bool hasFlag(base::FormatFlags flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - virtual void log(el::base::type::ostream_t& os) const { - os << m_format; - } - - protected: - /// @brief Updates date time format if available in currFormat. - /// @param index Index where %datetime, %date or %time was found - /// @param [in,out] currFormat current format that is being used to format - virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL { - 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 << *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); - } - } - } - - /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level - virtual void updateFormatSpec(void) ELPP_FINAL { - // 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)) { - std::string s = base::utils::s_currentUser; - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, - base::utils::s_currentUser); - } - if (hasFlag(base::FormatFlags::Host)) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, - base::utils::s_currentHost); - } - // Ignore Level::Global and Level::Unknown - } - - inline void addFlag(base::FormatFlags flag) { - base::utils::addFlag(flag, &m_flags); - } - - private: - Level m_level; - base::type::string_t m_userFormat; - base::type::string_t m_format; - std::string m_dateTimeFormat; - base::type::EnumType m_flags; - friend class el::Logger; // To resolve loggerId format specifier easily - }; +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; } // namespace base /// @brief Resolving function for format specifier -typedef std::function<const char*(void)> FormatSpecifierValueResolver; +typedef std::function<std::string(const LogMessage*)> FormatSpecifierValueResolver; /// @brief User-provided custom format specifier /// @see el::Helpers::installCustomFormatSpecifier /// @see FormatSpecifierValueResolver class CustomFormatSpecifier { -public: - CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} - inline const char* formatSpecifier(void) const { return m_formatSpecifier; } - inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; } - inline bool operator==(const char* formatSpecifier) { - return strcmp(m_formatSpecifier, formatSpecifier) == 0; - } - -private: - const char* m_formatSpecifier; - FormatSpecifierValueResolver m_resolver; + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; }; /// @brief Represents single configuration that has representing level, configuration type and a string based value. /// @@ -2439,2375 +1707,1221 @@ private: /// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); /// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); class Configuration : public Loggable { -public: - Configuration(const Configuration& c) : - m_level(c.m_level), - m_configurationType(c.m_configurationType), - m_value(c.m_value) { - } - - Configuration& operator=(const Configuration& c) { - m_level = c.m_level; - m_configurationType = c.m_configurationType; - m_value = c.m_value; - return *this; - } - - virtual ~Configuration(void) { - } - - /// @brief Full constructor used to sets value of configuration - Configuration(Level level, ConfigurationType configurationType, const std::string& value) : - m_level(level), - m_configurationType(configurationType), - m_value(value) { - } - - /// @brief Gets level of current configuration - inline Level level(void) const { - return m_level; - } - - /// @brief Gets configuration type of current configuration - inline ConfigurationType configurationType(void) const { - return m_configurationType; - } - - /// @brief Gets string based configuration value - inline const std::string& value(void) const { - return m_value; - } - - /// @brief Set string based configuration value - /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values - /// use them in quotes. They will be parsed when configuring - inline void setValue(const std::string& value) { - m_value = value; - } - - virtual inline void 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. - class Predicate { - public: - Predicate(Level level, ConfigurationType configurationType) : - m_level(level), - m_configurationType(configurationType) { - } - - inline bool operator()(const Configuration* conf) const { - return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); - } - - private: - Level m_level; - ConfigurationType m_configurationType; - }; - -private: + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: Level m_level; ConfigurationType m_configurationType; - std::string m_value; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; }; /// @brief Thread-safe Configuration repository /// /// @detail This repository represents configurations for all the levels and configuration type mapped to a value. class Configurations : public base::utils::RegistryWithPred<Configuration, Configuration::Predicate> { -public: - /// @brief Default constructor with empty repository - Configurations(void) : - m_configurationFile(std::string()), - m_isFromFile(false) { - } - - /// @brief Constructor used to set configurations using configuration file. - /// @param configurationFile Full path to configuration file - /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. - /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. - /// @see parseFromFile(const std::string&, Configurations* base) - /// @see setRemainingToDefault() - Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) : - m_configurationFile(configurationFile), - m_isFromFile(false) { - parseFromFile(configurationFile, base); - if (useDefaultsForRemaining) { - setRemainingToDefault(); - } - } - - virtual ~Configurations(void) { - } - + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: /// @brief Parses configuration from file. /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class /// @param base Configurations to base new configuration repository off. This value is used when you want to use /// existing Configurations to base all the values and then set rest of configuration via configuration file. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you /// do not proceed without successful parse. - inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) { - // 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)), - "Configuration file [" << configurationFile << "] does not exist!"); - if (!assertionPassed) { - return false; - } - bool success = Parser::parseFromFile(configurationFile, this, base); - m_isFromFile = success; - return success; - } - + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + /// @brief Parse configurations from configuration string. /// /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary - /// new line characters are provided. + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class /// @param base Configurations to base new configuration repository off. This value is used when you want to use /// existing Configurations to base all the values and then set rest of configuration via configuration text. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you - /// do not proceed without successful parse. - inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) { - bool success = Parser::parseFromText(configurationsString, this, base); - if (success) { - m_isFromFile = false; - } - return success; - } - - /// @brief Sets configuration based-off an existing configurations. - /// @param base Pointer to existing configurations. - inline void setFromBase(Configurations* base) { - if (base == nullptr || base == this) { - return; - } - base::threading::ScopedLock scopedLock(base->lock()); - for (Configuration*& conf : base->list()) { - set(conf); - } - } - - /// @brief Determines whether or not specified configuration type exists in the repository. - /// - /// @detail Returns as soon as first level is found. - /// @param configurationType Type of configuration to check existence for. - bool 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; - } - - /// @brief Determines whether or not specified configuration type exists for specified level - /// @param level Level to check - /// @param configurationType Type of configuration to check existence for. - inline bool hasConfiguration(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); -#if ELPP_COMPILER_INTEL - // We cant specify template types here, Intel C++ throws compilation error - // "error: type name is not allowed" - return RegistryWithPred::get(level, configurationType) != nullptr; -#else - return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr; -#endif // ELPP_COMPILER_INTEL - } - - /// @brief Sets value of configuration for specified level. - /// - /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types - /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for - /// Level::Global because these configurations are not dependant on level. - /// @param level Level to set configuration for (el::Level). - /// @param configurationType Type of configuration (el::ConfigurationType) - /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string - /// from users' point of view. This is then parsed later to be used internally. - /// @see Configuration::setValue(const std::string& value) - /// @see el::Level - /// @see el::ConfigurationType - inline void 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 - } - } - - /// @brief Sets single configuration based on other single configuration. - /// @see set(Level level, ConfigurationType configurationType, const std::string& value) - inline void set(Configuration* conf) { - if (conf == nullptr) { - return; - } - set(conf->level(), conf->configurationType(), conf->value()); - } - - inline Configuration* get(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); - return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - } - - /// @brief Sets configuration for all levels. - /// @param configurationType Type of configuration - /// @param value String based value - /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) - inline void setGlobally(ConfigurationType configurationType, const std::string& value) { - setGlobally(configurationType, value, false); + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr; +typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::map<Level, bool> m_enabledMap; + std::map<Level, bool> m_toFileMap; + std::map<Level, std::string> m_filenameMap; + std::map<Level, bool> m_toStandardOutputMap; + std::map<Level, base::LogFormat> m_logFormatMap; + std::map<Level, base::SubsecondPrecision> m_subsecondPrecisionMap; + std::map<Level, bool> m_performanceTrackingMap; + std::map<Level, base::FileStreamPtr> m_fileStreamMap; + std::map<Level, std::size_t> m_maxLogFileSizeMap; + std::map<Level, std::size_t> m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template <typename Conf_T> + inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template <typename Conf_T> + inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template <typename Conf_T> + Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } } - - /// @brief Clears repository so that all the configurations are unset - inline void clear(void) { - base::threading::ScopedLock scopedLock(lock()); - unregisterAll(); + return it->second; + } + + template <typename Conf_T> + Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map<Level, Conf_T>::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } } - - /// @brief Gets configuration file used in parsing this configurations. - /// - /// @detail If this repository was set manually or by text this returns empty string. - inline const std::string& configurationFile(void) const { - return m_configurationFile; + return it->second; + } + + template <typename Conf_T> + void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; } - - /// @brief Sets configurations to "factory based" configurations. - void setToDefault(void) { - setGlobally(ConfigurationType::Enabled, std::string("true"), true); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); -#else - ELPP_UNUSED(base::consts::kDefaultLogFile); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - setGlobally(ConfigurationType::ToFile, std::string("true"), true); - setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); - setGlobally(ConfigurationType::MillisecondsWidth, 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")); + // If same value exist in generic level already, dont add it to explicit level + typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; } - - /// @brief Lets you set the remaining configurations to default. - /// - /// @detail By remaining, it means that the level/type a configuration does not exist for. - /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets - /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, - /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor - /// and try to access a value, an error is thrown - void setRemainingToDefault(void) { - base::threading::ScopedLock scopedLock(lock()); - unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, 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")); + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; } - - /// @brief Parser used internally to parse configurations from file or text. - /// - /// @detail This class makes use of base::utils::Str. - /// You should not need this unless you are working on some tool for Easylogging++ - class Parser : base::StaticClass { - public: - /// @brief Parses configuration from file. - /// @param configurationFile Full path to configuration file - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration file. - /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you - /// do not proceed without successful parse. - static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) { - 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; - } - - /// @brief Parse configurations from configuration string. - /// - /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary - /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you - /// do not proceed without successful parse (This is recommended) - /// @param configurationsString - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration text. - /// @return True if successfully parsed, false otherwise. - static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) { - 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; - } - - private: - friend class el::Loggers; - static void 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); - } - } - static inline bool isLevel(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); - } - - static inline bool isComment(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); - } - - static inline bool isConfig(const std::string& line) { - std::size_t assignment = line.find('='); - return line != "" && - ((line[0] >= 65 && line[0] <= 90) || (line[0] >= 97 && line[0] <= 122)) && - (assignment != std::string::npos) && - (line.size() > assignment); - } - - static bool 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; - } - }; - -private: - std::string m_configurationFile; - bool m_isFromFile; - friend class el::Loggers; - - /// @brief Unsafely sets configuration if does not already exist - void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - if (conf == nullptr) { - unsafeSet(level, configurationType, value); - } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; } - - /// @brief Thread unsafe set - void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - if (conf == nullptr) { - registerNew(new Configuration(level, configurationType, value)); - } else { - conf->setValue(value); - } - if (level == Level::Global) { - unsafeSetGlobally(configurationType, value, false); - } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); } - - /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void 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 - }); + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { } - - /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void 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 - }); + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; }; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> { + public: + /// @brief Validates counter for every 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 validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @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 validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @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 validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template <typename T> +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } +}; +class LogDispatchCallback : public Callback<LogDispatchData> { + private: + friend class base::LogDispatcher; +}; +class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback<Logger> { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr<LogBuilder> LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } -namespace base { - typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr; - typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap; - /// @brief Configurations with data types. - /// - /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. - /// This is to perform faster while writing logs using correct configurations. - /// - /// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) - class TypedConfigurations : public base::threading::ThreadSafe { - public: - /// @brief Constructor to initialize (construct) the object off el::Configurations - /// @param configurations Configurations pointer/reference to base this typed configurations off. - /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() - TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) { - m_configurations = configurations; - m_logStreamsReference = logStreamsReference; - build(m_configurations); - } - - TypedConfigurations(const TypedConfigurations& other) { - this->m_configurations = other.m_configurations; - this->m_logStreamsReference = other.m_logStreamsReference; - build(m_configurations); - } - - virtual ~TypedConfigurations(void) { - } - - const Configurations* configurations(void) const { - return m_configurations; - } - - inline bool enabled(Level level) { - return getConfigByVal<bool>(level, &m_enabledMap, "enabled"); - } - - inline bool toFile(Level level) { - return getConfigByVal<bool>(level, &m_toFileMap, "toFile"); - } - - inline const std::string& filename(Level level) { - return getConfigByRef<std::string>(level, &m_filenameMap, "filename"); - } - - inline bool toStandardOutput(Level level) { - return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput"); - } - - inline const base::LogFormat& logFormat(Level level) { - return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat"); - } - - inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) { - return getConfigByRef<base::MillisecondsWidth>(level, &m_millisecondsWidthMap, "millisecondsWidth"); - } - - inline bool performanceTracking(Level level = Level::Global) { - return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking"); - } - - inline base::type::fstream_t* fileStream(Level level) { - return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get(); - } - - inline std::size_t maxLogFileSize(Level level) { - return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize"); - } - - inline std::size_t logFlushThreshold(Level level) { - return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold"); - } - - private: - Configurations* m_configurations; - std::map<Level, bool> m_enabledMap; - std::map<Level, bool> m_toFileMap; - std::map<Level, std::string> m_filenameMap; - std::map<Level, bool> m_toStandardOutputMap; - std::map<Level, base::LogFormat> m_logFormatMap; - std::map<Level, base::MillisecondsWidth> m_millisecondsWidthMap; - std::map<Level, bool> m_performanceTrackingMap; - std::map<Level, base::FileStreamPtr> m_fileStreamMap; - std::map<Level, std::size_t> m_maxLogFileSizeMap; - std::map<Level, std::size_t> m_logFlushThresholdMap; - base::LogStreamsReferenceMap* m_logStreamsReference; - - friend class el::Helpers; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::LogDispatcher; - - template <typename Conf_T> - inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template <typename Conf_T> - inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template <typename Conf_T> - inline Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" - << LevelHelper::convertToString(level) << "]" - << std::endl << "Please ensure you have properly configured logger.", false); - return Conf_T(); - } - } - return it->second; - } - - template <typename Conf_T> - inline Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::map<Level, Conf_T>::iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" - << LevelHelper::convertToString(level) << "]" - << std::endl << "Please ensure you have properly configured logger.", false); - } - } - return it->second; - } - - template <typename Conf_T> - void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) { - // If map is empty and we are allowed to add into generic level (Level::Global), do it! - if (confMap->empty() && includeGlobalLevel) { - confMap->insert(std::make_pair(Level::Global, value)); - return; - } - // If same value exist in generic level already, dont add it to explicit level - typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global); - if (it != confMap->end() && it->second == value) { - return; - } - // Now make sure we dont double up values if we really need to add it to explicit level - it = confMap->find(level); - if (it == confMap->end()) { - // Value not found for level, add new - confMap->insert(std::make_pair(level, value)); - } else { - // Value found, just update value - confMap->at(level) = value; - } - } - - void build(Configurations* configurations) { - base::threading::ScopedLock scopedLock(lock()); - auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming - base::utils::Str::trim(boolStr); - return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); - }; - setValue(Level::Global, base::FileStreamPtr(NULL), &m_fileStreamMap); - std::vector<Configuration*> withFileSizeLimit; - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - // We cannot use switch on strong enums because Intel C++ dont support them yet - if (conf->configurationType() == ConfigurationType::Enabled) { - setValue(conf->level(), getBool(conf->value()), &m_enabledMap); - } else if (conf->configurationType() == ConfigurationType::ToFile) { - setValue(conf->level(), getBool(conf->value()), &m_toFileMap); - } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { - setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); - } else if (conf->configurationType() == ConfigurationType::Filename) { - // We do not yet configure filename but we will configure in another - // loop. This is because if file cannot be created, we will force ToFile - // to be false. Because configuring logger is not necessarily performance - // sensative operation, we can live with another loop; (by the way this loop - // is not very heavy either) - } else if (conf->configurationType() == ConfigurationType::Format) { - setValue(conf->level(), base::LogFormat(conf->level(), - base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); - } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) { - setValue(Level::Global, - base::MillisecondsWidth(static_cast<int>(getULong(conf->value()))), &m_millisecondsWidthMap); - } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { - setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); - } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { - setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - withFileSizeLimit.push_back(conf); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { - setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap); - } - } - // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - if (conf->configurationType() == ConfigurationType::Filename) { - insertFile(conf->level(), conf->value()); - } - } - for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin(); - conf != withFileSizeLimit.end(); ++conf) { - // This is not unsafe as mutex is locked in currect scope - unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); - } - } - - unsigned long 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 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::MillisecondsWidth msWidth(3); - std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth); - base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename - base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); - } - } - return resultingFilename; - } - - void 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 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; - } - - bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeValidateFileRolling(level, PreRollOutCallback); - } - }; - /// @brief Class that keeps record of current line hit for occasional logging - class HitCounter { - public: - HitCounter(void) : - m_filename(""), - m_lineNumber(0), - m_hitCounts(0) { - } - - HitCounter(const char* filename, unsigned long int lineNumber) : - m_filename(filename), - m_lineNumber(lineNumber), - m_hitCounts(0) { - } - - HitCounter(const HitCounter& hitCounter) : - m_filename(hitCounter.m_filename), - m_lineNumber(hitCounter.m_lineNumber), - m_hitCounts(hitCounter.m_hitCounts) { - } - - HitCounter& operator=(const HitCounter& hitCounter) { - m_filename = hitCounter.m_filename; - m_lineNumber = hitCounter.m_lineNumber; - m_hitCounts = hitCounter.m_hitCounts; - return *this; - } - - virtual ~HitCounter(void) { - } - - /// @brief Resets location of current hit counter - inline void resetLocation(const char* filename, unsigned long int lineNumber) { - m_filename = filename; - m_lineNumber = lineNumber; - } - - /// @brief Validates hit counts and resets it if necessary - inline void validateHitCounts(std::size_t n) { - if (m_hitCounts >= base::consts::kMaxLogPerCounter) { - m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); - } - ++m_hitCounts; - } - - inline const char* filename(void) const { - return m_filename; - } - - inline unsigned long int lineNumber(void) const { - return m_lineNumber; - } - - inline std::size_t hitCounts(void) const { - return m_hitCounts; - } - - inline void increment(void) { - ++m_hitCounts; - } - - class Predicate { - public: - Predicate(const char* filename, unsigned long int lineNumber) - : m_filename(filename), - m_lineNumber(lineNumber) { - } - inline bool operator()(const HitCounter* counter) { - return ((counter != nullptr) && - (strcmp(counter->m_filename, m_filename) == 0) && - (counter->m_lineNumber == m_lineNumber)); - } - - private: - const char* m_filename; - unsigned long int m_lineNumber; - }; - - private: - const char* m_filename; - unsigned long int m_lineNumber; - std::size_t m_hitCounts; - }; - /// @brief Repository for hit counters used across the application - class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> { - public: - /// @brief Validates counter for every 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 validateEveryN(const char* filename, unsigned long int 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 validateAfterN(const char* filename, unsigned long int 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 validateNTimes(const char* filename, unsigned long int 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; - } - - /// @brief Gets hit counter registered at specified position - inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) { - base::threading::ScopedLock scopedLock(lock()); - return get(filename, lineNumber); - } - }; - /// @brief Action to be taken for dispatching - enum class DispatchAction : base::type::EnumType { - None = 1, NormalLog = 2, SysLog = 4, FileOnlyLog = 8, - }; - } // namespace base - template <typename T> - class Callback : protected base::threading::ThreadSafe { - public: - Callback(void) : m_enabled(true) {} - inline bool enabled(void) const { return m_enabled; } - inline void setEnabled(bool enabled) { - base::threading::ScopedLock scopedLock(lock()); - m_enabled = enabled; - } - protected: - virtual void handle(const T* handlePtr) = 0; - private: - bool m_enabled; - }; - class LogDispatchData { - public: - LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} - inline const LogMessage* logMessage(void) const { return m_logMessage; } - inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } - private: - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; - friend class base::LogDispatcher; - - inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } - inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } - }; - class LogDispatchCallback : public Callback<LogDispatchData> { - private: - friend class base::LogDispatcher; - }; - class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> { - private: - friend class base::PerformanceTracker; - }; - class LogBuilder : base::NoCopy { - public: - virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")} - virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; - void convertToColoredOutput(base::type::string_t* logLine, Level level) { - if (!base::utils::s_termSupportsColor) return; - const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); - if (level == Level::Error || level == Level::Fatal) - *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; - else if (level == Level::Warning) - *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; - else if (level == Level::Debug) - *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; - else if (level == Level::Info) - *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; - else if (level == Level::Trace) - *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; - } - private: - friend class el::base::DefaultLogDispatchCallback; - }; - typedef std::shared_ptr<LogBuilder> LogBuilderPtr; - /// @brief Represents a logger holding ID and configurations we need to write logs - /// - /// @detail This class does not write logs itself instead its used by writer to read configuations from. - class Logger : public base::threading::ThreadSafe, public Loggable { - public: - 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(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(const Logger& logger) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; - } - - Logger& operator=(const Logger& logger) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; - return *this; - } - - virtual ~Logger(void) { - base::utils::safeDelete(m_typedConfigurations); - } - - virtual inline void log(el::base::type::ostream_t& os) const { - os << m_id.c_str(); - } - - /// @brief Configures the logger using specified configurations. - void configure(const Configurations& configurations) { - m_isConfigured = false; // we set it to false in case if we fail - initUnflushedCount(); - if (m_typedConfigurations != nullptr) { - Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations()); - if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { - // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE - flush(); - } - } - base::threading::ScopedLock scopedLock(lock()); - if (m_configurations != configurations) { - m_configurations.setFromBase(const_cast<Configurations*>(&configurations)); - } - base::utils::safeDelete(m_typedConfigurations); - m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); - resolveLoggerFormatSpec(); - m_isConfigured = true; - } - - /// @brief Reconfigures logger using existing configurations - inline void reconfigure(void) { - ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); - configure(m_configurations); - } - - inline const std::string& id(void) const { - return m_id; - } - - inline const std::string& parentApplicationName(void) const { - return m_parentApplicationName; - } - - inline void setParentApplicationName(const std::string& parentApplicationName) { - m_parentApplicationName = parentApplicationName; - } - - inline Configurations* configurations(void) { - return &m_configurations; - } - - inline base::TypedConfigurations* typedConfigurations(void) { - return m_typedConfigurations; - } - - static inline bool 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; - } - /// @brief Flushes logger to sync all log files for all levels - inline void 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; - }); - } - - inline void flush(Level level, base::type::fstream_t* fs) { - if (fs == nullptr && m_typedConfigurations->toFile(level)) { - fs = m_typedConfigurations->fileStream(level); - } - if (fs != nullptr) { - fs->flush(); - m_unflushedCount.find(level)->second = 0; - } - } - - inline bool isFlushNeeded(Level level) { - return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); - } - - inline LogBuilder* logBuilder(void) const { - return m_logBuilder.get(); - } - - inline void setLogBuilder(const LogBuilderPtr& logBuilder) { - m_logBuilder = logBuilder; - } - - inline bool enabled(Level level) const { - return m_typedConfigurations->enabled(level); - } - #if ELPP_VARIADIC_TEMPLATES_SUPPORTED -# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ template <typename T, typename... Args>\ inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ template <typename T>\ inline void FUNCTION_NAME(const T&); - - template <typename T, typename... Args> - inline void verbose(int, const char*, const T&, const Args&...); - - template <typename T> - inline void verbose(int, const T&); - - LOGGER_LEVEL_WRITERS_SIGNATURES(info) - LOGGER_LEVEL_WRITERS_SIGNATURES(debug) - LOGGER_LEVEL_WRITERS_SIGNATURES(warn) - LOGGER_LEVEL_WRITERS_SIGNATURES(error) - LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) - LOGGER_LEVEL_WRITERS_SIGNATURES(trace) -# undef LOGGER_LEVEL_WRITERS_SIGNATURES + + template <typename T, typename... Args> + inline void verbose(int, const char*, const T&, const Args&...); + + template <typename T> + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - private: - std::string m_id; - base::TypedConfigurations* m_typedConfigurations; - base::type::stringstream_t m_stream; - std::string m_parentApplicationName; - bool m_isConfigured; - Configurations m_configurations; - std::map<Level, unsigned int> m_unflushedCount; - base::LogStreamsReferenceMap* m_logStreamsReference; - LogBuilderPtr m_logBuilder; - - friend class el::LogMessage; - friend class el::Loggers; - friend class el::Helpers; - friend class el::base::RegisteredLoggers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PErrorWriter; - friend class el::base::Storage; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - Logger(void); - + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::map<Level, unsigned int> m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + #if ELPP_VARIADIC_TEMPLATES_SUPPORTED - template <typename T, typename... Args> - void log_(Level, int, const char*, const T&, const Args&...); - - template <typename T> - inline void log_(Level, int, const T&); - - template <typename T, typename... Args> - void log(Level, const char*, const T&, const Args&...); - - template <typename T> - inline void log(Level, const T&); + template <typename T, typename... Args> + void log_(Level, int, const char*, const T&, const Args&...); + + template <typename T> + inline void log_(Level, int, const T&); + + template <typename T, typename... Args> + void log(Level, const char*, const T&, const Args&...); + + template <typename T> + inline void log(Level, const T&); #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - - void 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; - }); - } - - inline base::type::stringstream_t& stream(void) { - return m_stream; - } - - void resolveLoggerFormatSpec(void) const { - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - base::LogFormat* logFormat = - const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); - base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); - return false; - }); - } - }; - namespace base { - /// @brief Loggers repository - class RegisteredLoggers : public base::utils::Registry<Logger, std::string> { - public: - explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : - m_defaultLogBuilder(defaultLogBuilder) { - m_defaultConfigurations.setToDefault(); - } - - virtual ~RegisteredLoggers(void) { - unsafeFlushAll(); - } - - inline void setDefaultConfigurations(const Configurations& configurations) { - base::threading::ScopedLock scopedLock(lock()); - m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations)); - } - - inline Configurations* defaultConfigurations(void) { - return &m_defaultConfigurations; - } - - Logger* get(const std::string& id, bool forceCreation = true) { - base::threading::ScopedLock scopedLock(lock()); - Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id); - if (logger_ == nullptr && forceCreation) { - bool validId = Logger::isValidId(id); - if (!validId) { - ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); - return nullptr; - } - logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); - logger_->m_logBuilder = m_defaultLogBuilder; - registerNew(id, logger_); - } - return logger_; - } - - bool remove(const std::string& id) { - if (id == "default") { - return false; - } - Logger* logger = base::utils::Registry<Logger, std::string>::get(id); - if (logger != nullptr) { - unregister(logger); - } - return true; - } - - inline bool has(const std::string& id) { - return get(id, false) != nullptr; - } - - inline void unregister(Logger*& logger) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::Registry<Logger, std::string>::unregister(logger->id()); - } - - inline base::LogStreamsReferenceMap* logStreamsReference(void) { - return &m_logStreamsReference; - } - - inline void flushAll(void) { - base::threading::ScopedLock scopedLock(lock()); - unsafeFlushAll(); - } - - private: - LogBuilderPtr m_defaultLogBuilder; - Configurations m_defaultConfigurations; - base::LogStreamsReferenceMap m_logStreamsReference; - friend class el::base::Storage; - - inline void 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(); - } - } - }; - /// @brief Represents registries for verbose logging - class VRegistry : base::NoCopy, public base::threading::ThreadSafe { - public: - explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { - } - - /// @brief Sets verbose level. Accepted range is 0-9 - inline void setLevel(base::type::VerboseLevel level) { - base::threading::ScopedLock scopedLock(lock()); - if (level < 0) - m_level = 0; - else if (level > 9) - m_level = base::consts::kMaxVerboseLevel; - else - m_level = level; - } - - inline base::type::VerboseLevel level(void) const { - return m_level; - } - - inline void clearModules(void) { - base::threading::ScopedLock scopedLock(lock()); - m_modules.clear(); - } - - inline void clearCategories(void) { - base::threading::ScopedLock scopedLock(lock()); - m_categories.clear(); - } - - void 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, level); - ss.str(std::string("")); - level = -1; - } - break; - default: - if (isMod) { - ss << *modules; - } else if (isLevel) { - if (isdigit(*modules)) { - level = static_cast<base::type::VerboseLevel>(*modules) - 48; - } - } - break; - } - } - if (!ss.str().empty() && level != -1) { - insert(ss, level); - } - } - - void setCategories(const char* categories, bool clear = true) { - base::threading::ScopedLock scopedLock(lock()); - auto insert = [&](std::stringstream& ss, Level level) { - m_categories.push_back(std::make_pair(ss.str(), level)); - }; - - if (clear) - m_categories.clear(); - if (!categories) - return; - - 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); - } - } - - bool allowed(base::type::VerboseLevel vlevel, const char* file) { - base::threading::ScopedLock scopedLock(lock()); - if (m_modules.empty() || file == nullptr) { - return vlevel <= m_level; - } else { - std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin(); - for (; it != m_modules.end(); ++it) { - if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { - return vlevel <= it->second; - } - } - if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { - return true; - } - return false; - } - } - - // Log levels are sorted in a weird way... - 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; - } - - bool allowed(Level level, const char* category) { - base::threading::ScopedLock scopedLock(lock()); - if (m_categories.empty() || category == nullptr) { - return false; - } else { - std::deque<std::pair<std::string, Level>>::const_reverse_iterator it = m_categories.rbegin(); - for (; it != m_categories.rend(); ++it) { - if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { - return priority(level) <= priority(it->second); - } - } - return false; - } - } - - inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const { - return m_modules; - } - - void 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(atoi(commandLineArgs->getParamValue("--v"))); - } else if (commandLineArgs->hasParamWithValue("--V")) { - setLevel(atoi(commandLineArgs->getParamValue("--V"))); - } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-vmodule")); - } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-VMODULE")); - } - } - - /// @brief Whether or not vModules enabled - inline bool vModulesEnabled(void) { - return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); - } - - void setThreadName(const std::string &name) { - const base::threading::ScopedLock scopedLock(lock()); - m_threadNames[base::threading::getCurrentThreadId()] = name; - } - - std::string getThreadName(const std::string& name) { - const base::threading::ScopedLock scopedLock(lock()); - std::map<std::string, std::string>::const_iterator it = m_threadNames.find(name); - if (it == m_threadNames.end()) - return name; - return it->second; - } - - void setFilenameCommonPrefix(const std::string &prefix) { - m_filenameCommonPrefix = prefix; - } - - std::string getFilenameCommonPrefix() { - return m_filenameCommonPrefix; - } - private: - base::type::VerboseLevel m_level; - base::type::EnumType* m_pFlags; - std::map<std::string, base::type::VerboseLevel> m_modules; - std::deque<std::pair<std::string, Level>> m_categories; - std::map<std::string, std::string> m_threadNames; - std::string m_filenameCommonPrefix; - }; - } // namespace base - class LogMessage { - public: - LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func, - base::type::VerboseLevel verboseLevel, Logger* logger) : - m_level(level), m_file(file), m_line(line), m_func(func), - m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { - } - inline Level level(void) const { return m_level; } - inline const std::string& file(void) const { return m_file; } - inline unsigned long int line(void) const { return m_line; } // NOLINT - inline const std::string& func(void) const { return m_func; } - inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; } - inline Logger* logger(void) const { return m_logger; } - inline const base::type::string_t& message(void) const { return m_message; } - private: - Level m_level; - std::string m_file; - unsigned long int m_line; - std::string m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - base::type::string_t m_message; - }; - namespace base { + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry<Logger, std::string> { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template <typename T> + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, + &m_loggerRegistrationCallbacks); + } + + template <typename T> + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); + } + + template <typename T> + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry<Logger, std::string>::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + std::map<std::string, base::type::LoggerRegistrationCallbackPtr> m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::map<std::string, base::type::VerboseLevel> m_modules; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { #if ELPP_ASYNC_LOGGING - class AsyncLogItem { - public: - explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) - : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} - virtual ~AsyncLogItem() {} - inline LogMessage* logMessage(void) { return &m_logMessage; } - inline LogDispatchData* data(void) { return &m_dispatchData; } - inline base::type::string_t logLine(void) { return m_logLine; } - private: - LogMessage m_logMessage; - LogDispatchData m_dispatchData; - base::type::string_t m_logLine; - }; - class AsyncLogQueue : public base::threading::ThreadSafe { - public: - virtual ~AsyncLogQueue() { - ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); - } - - inline AsyncLogItem next(void) { - base::threading::ScopedLock scopedLock(lock()); - AsyncLogItem result = m_queue.front(); - m_queue.pop(); - return result; - } - - inline void push(const AsyncLogItem& item) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.push(item); - } - inline void pop(void) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.pop(); - } - inline AsyncLogItem front(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.front(); - } - inline bool empty(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.empty(); - } - private: - std::queue<AsyncLogItem> m_queue; - }; - class IWorker { - public: - virtual ~IWorker() {} - virtual void start() = 0; - }; +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue<AsyncLogItem> m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; #endif // ELPP_ASYNC_LOGGING - /// @brief Easylogging++ management storage - class Storage : base::NoCopy, public base::threading::ThreadSafe { - public: +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: #if ELPP_ASYNC_LOGGING - Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); #else - explicit Storage(const LogBuilderPtr& defaultLogBuilder) : + explicit Storage(const LogBuilderPtr& defaultLogBuilder); #endif // ELPP_ASYNC_LOGGING - m_registeredHitCounters(new base::RegisteredHitCounters()), - m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), - m_flags(0x0), - 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)); - // Register performance logger and reconfigure format - Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); - performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); - performanceLogger->reconfigure(); -#if defined(ELPP_SYSLOG) - // Register syslog logger and reconfigure format - Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); - sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); - sysLogLogger->reconfigure(); -#endif // defined(ELPP_SYSLOG) - addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); -#if ELPP_ASYNC_LOGGING - installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); -#else - installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); -#endif // ELPP_ASYNC_LOGGING - installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>(std::string("DefaultPerformanceTrackingCallback")); - ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); -#if ELPP_ASYNC_LOGGING - m_asyncDispatchWorker->start(); -#endif // ELPP_ASYNC_LOGGING - } - - virtual ~Storage(void) { - ELPP_INTERNAL_INFO(4, "Destroying storage"); -#if ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); - uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); - installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); - ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); - base::utils::safeDelete(m_asyncDispatchWorker); - ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); - base::utils::safeDelete(m_asyncLogQueue); -#endif // ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); - base::utils::safeDelete(m_registeredHitCounters); - ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); - base::utils::safeDelete(m_registeredLoggers); - ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); - base::utils::safeDelete(m_vRegistry); - } - - inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) { - return hitCounters()->validateEveryN(filename, lineNumber, occasion); - } - - inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT - return hitCounters()->validateAfterN(filename, lineNumber, n); - } - - inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT - return hitCounters()->validateNTimes(filename, lineNumber, n); - } - - inline base::RegisteredHitCounters* hitCounters(void) const { - return m_registeredHitCounters; - } - - inline base::RegisteredLoggers* registeredLoggers(void) const { - return m_registeredLoggers; - } - - inline base::VRegistry* vRegistry(void) const { - return m_vRegistry; - } - + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + #if ELPP_ASYNC_LOGGING - inline base::AsyncLogQueue* asyncLogQueue(void) const { - return m_asyncLogQueue; - } + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } #endif // ELPP_ASYNC_LOGGING - - inline const base::utils::CommandLineArgs* commandLineArgs(void) const { - return &m_commandLineArgs; - } - - inline void addFlag(LoggingFlag flag) { - base::utils::addFlag(flag, &m_flags); - } - - inline void removeFlag(LoggingFlag flag) { - base::utils::removeFlag(flag, &m_flags); - } - - inline bool hasFlag(LoggingFlag flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - inline base::type::EnumType flags(void) const { - return m_flags; - } - - inline void setFlags(base::type::EnumType flags) { - m_flags = flags; - } - - inline void setPreRollOutCallback(const PreRollOutCallback& callback) { - m_preRollOutCallback = callback; - } - - inline void unsetPreRollOutCallback(void) { - m_preRollOutCallback = base::defaultPreRollOutCallback; - } - - inline PreRollOutCallback& preRollOutCallback(void) { - return m_preRollOutCallback; - } - - inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); - return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), - formatSpecifier) != m_customFormatSpecifiers.end(); - } - - inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { - return; - } - base::threading::ScopedLock scopedLock(lock()); - m_customFormatSpecifiers.push_back(customFormatSpecifier); - } - - inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); - std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(), - m_customFormatSpecifiers.end(), formatSpecifier); - if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { - m_customFormatSpecifiers.erase(it); - return true; - } - return false; - } - - const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const { - return &m_customFormatSpecifiers; - } - - inline void setLoggingLevel(Level level) { - m_loggingLevel = level; - } - - template <typename T> - inline bool installLogDispatchCallback(const std::string& id) { - return installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - - template <typename T> - inline void uninstallLogDispatchCallback(const std::string& id) { - uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - template <typename T> - inline T* logDispatchCallback(const std::string& id) { - return callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - - template <typename T> - inline bool installPerformanceTrackingCallback(const std::string& id) { - return installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - - template <typename T> - inline void uninstallPerformanceTrackingCallback(const std::string& id) { - uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - - template <typename T> - inline T* performanceTrackingCallback(const std::string& id) { - return callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - private: - base::RegisteredHitCounters* m_registeredHitCounters; - base::RegisteredLoggers* m_registeredLoggers; - base::type::EnumType m_flags; - base::VRegistry* m_vRegistry; + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template <typename T> + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + + template <typename T> + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + template <typename T> + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template <typename T> + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, + &m_performanceTrackingCallbacks); + } + + template <typename T> + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, + &m_performanceTrackingCallbacks); + } + + template <typename T> + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(lock()); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(lock()); + std::map<std::string, std::string>::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; #if ELPP_ASYNC_LOGGING - base::AsyncLogQueue* m_asyncLogQueue; - base::IWorker* m_asyncDispatchWorker; + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; #endif // ELPP_ASYNC_LOGGING - base::utils::CommandLineArgs m_commandLineArgs; - PreRollOutCallback m_preRollOutCallback; - std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks; - std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks; - std::vector<CustomFormatSpecifier> m_customFormatSpecifiers; - Level m_loggingLevel; - - friend class el::Helpers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::LogBuilder; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - void 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)) { - m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); - } -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) - } - - inline void setApplicationArguments(int argc, const char** argv) { - setApplicationArguments(argc, const_cast<char**>(argv)); - } - - template <typename T, typename TPtr> - inline bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { - if (mapT->find(id) == mapT->end()) { - mapT->insert(std::make_pair(id, TPtr(new T()))); - return true; - } - return false; - } - - template <typename T, typename TPtr> - inline void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { - if (mapT->find(id) != mapT->end()) { - mapT->erase(id); - } - } - - template <typename T, typename TPtr> - inline T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) { - typename std::map<std::string, TPtr>::iterator iter = mapT->find(id); - if (iter != mapT->end()) { - return static_cast<T*>(iter->second.get()); - } - return nullptr; - } - }; - extern ELPP_EXPORT base::type::StoragePointer elStorage; + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks; + std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks; + std::map<std::string, std::string> m_threadNames; + std::vector<CustomFormatSpecifier> m_customFormatSpecifiers; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast<char**>(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; #define ELPP el::base::elStorage - class DefaultLogDispatchCallback : public LogDispatchCallback { - protected: - void handle(const LogDispatchData* data) { - m_data = data; - dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), - m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog)); - } - private: - const LogDispatchData* m_data; - void dispatch(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())) { - if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); - } - } - } -#if defined(ELPP_SYSLOG) - else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { - // Determine syslog priority - int sysLogPriority = 0; - if (m_data->logMessage()->level() == Level::Fatal) - sysLogPriority = LOG_EMERG; - else if (m_data->logMessage()->level() == Level::Error) - sysLogPriority = LOG_ERR; - else if (m_data->logMessage()->level() == Level::Warning) - sysLogPriority = LOG_WARNING; - else if (m_data->logMessage()->level() == Level::Info) - sysLogPriority = LOG_INFO; - else if (m_data->logMessage()->level() == Level::Debug) - sysLogPriority = LOG_DEBUG; - else - sysLogPriority = LOG_NOTICE; -# if defined(ELPP_UNICODE) - char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); - syslog(sysLogPriority, "%s", line); - free(line); -# else - syslog(sysLogPriority, "%s", logLine.c_str()); -# endif - } -#endif // defined(ELPP_SYSLOG) - } - }; +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; #if ELPP_ASYNC_LOGGING - class AsyncLogDispatchCallback : public LogDispatchCallback { - protected: - void 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()); - 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)); - } - } - }; - class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { - public: - AsyncDispatchWorker() { - setContinueRunning(false); - } - - virtual ~AsyncDispatchWorker() { - setContinueRunning(false); - ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); - clean(); - ELPP_INTERNAL_INFO(6, "Log queue cleaned"); - } - - inline bool clean(void) { - std::mutex m; - std::unique_lock<std::mutex> lk(m); - cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); }); - emptyQueue(); - lk.unlock(); - cv.notify_one(); - return ELPP->asyncLogQueue()->empty(); - } - - inline void emptyQueue(void) { - while (!ELPP->asyncLogQueue()->empty()) { - AsyncLogItem data = ELPP->asyncLogQueue()->next(); - handle(&data); - base::threading::msleep(100); - } - } - - virtual inline void start(void) { - base::threading::msleep(5000); // 5s (why?) - setContinueRunning(true); - std::thread t1(&AsyncDispatchWorker::run, this); - t1.join(); - } - - void 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 run(void) { - while (continueRunning()) { - emptyQueue(); - base::threading::msleep(10); // 10ms - } - } - - void setContinueRunning(bool value) { - base::threading::ScopedLock scopedLock(m_continueRunningMutex); - m_continueRunning = value; - } - - bool continueRunning(void) const { - return m_continueRunning; - } - private: - std::condition_variable cv; - bool m_continueRunning; - base::threading::Mutex m_continueRunningMutex; - }; +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningMutex); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningMutex; +}; #endif // ELPP_ASYNC_LOGGING - } // namespace base - namespace base { - class DefaultLogBuilder : public LogBuilder { - public: - base::type::string_t 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->vRegistry()->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->millisecondsWidth(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) - for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); - it != ELPP->customFormatSpecifiers()->end(); ++it) { - std::string fs(it->formatSpecifier()); - base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); - base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()())); - } -#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) - if (appendNewLine) logLine += ELPP_LITERAL("\n"); - return logLine; - } - }; - /// @brief Dispatches log messages - class LogDispatcher : base::NoCopy { - public: - LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : - m_proceed(proceed), - m_logMessage(std::move(logMessage)), - m_dispatchAction(std::move(dispatchAction)) { - } - - void dispatch(void) { - if (m_proceed && m_dispatchAction == base::DispatchAction::None) { - m_proceed = false; - } - if (!m_proceed) { - return; - } - // We minimize the time of ELPP's lock - this lock is released after log is written - base::threading::ScopedLock scopedLock(ELPP->lock()); - base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; - if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { - tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); - } - LogDispatchCallback* callback = nullptr; - LogDispatchData data; - for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h - : ELPP->m_logDispatchCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - data.setLogMessage(&m_logMessage); - data.setDispatchAction(m_dispatchAction); - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - } - - private: - bool m_proceed; - LogMessage m_logMessage; - base::DispatchAction m_dispatchAction; - }; +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(std::move(logMessage)), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage m_logMessage; + base::DispatchAction m_dispatchAction; +}; #if defined(ELPP_STL_LOGGING) - /// @brief Workarounds to write some STL logs - /// - /// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers - /// of same type and provide iterator interface and pass it on to writeIterator(). - /// Remember, this is passed by value in constructor so that we dont change original containers. - /// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) - namespace workarounds { - /// @brief Abstract IterableContainer template that provides interface for iterable classes of type T - template <typename T, typename Container> - class IterableContainer { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - IterableContainer(void) {} - virtual ~IterableContainer(void) {} - iterator begin(void) { return getContainer().begin(); } - iterator end(void) { return getContainer().end(); } - private: - virtual Container& getContainer(void) = 0; - }; - /// @brief Implements IterableContainer and provides iterable std::priority_queue class - template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>> - class IterablePriorityQueue : public IterableContainer<T, Container>, public std::priority_queue<T, Container, Comparator> { - public: - IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.top()); - queue_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - /// @brief Implements IterableContainer and provides iterable std::queue class - template<typename T, typename Container = std::deque<T>> - class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> { - public: - IterableQueue(std::queue<T, Container> queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.front()); - queue_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - /// @brief Implements IterableContainer and provides iterable std::stack class - template<typename T, typename Container = std::deque<T>> - class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> { - public: - IterableStack(std::stack<T, Container> stack_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { - this->push(stack_.top()); - stack_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - } // namespace workarounds +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template <typename T, typename Container> +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>> +class IterablePriorityQueue : public IterableContainer<T, Container>, + public std::priority_queue<T, Container, Comparator> { + public: + IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template<typename T, typename Container = std::deque<T>> +class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> { + public: + IterableQueue(std::queue<T, Container> queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template<typename T, typename Container = std::deque<T>> +class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> { + public: + IterableStack(std::stack<T, Container> stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds #endif // defined(ELPP_STL_LOGGING) - // Log message builder - class MessageBuilder { - public: - MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} - void initialize(Logger* logger) { - m_logger = logger; - m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? - ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); - } - -# define ELPP_SIMPLE_LOG(LOG_TYPE)\ -inline MessageBuilder& operator<<(LOG_TYPE msg) {\ +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ m_logger->stream() << msg;\ if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ m_logger->stream() << " ";\ }\ return *this;\ } - - inline MessageBuilder& operator<<(const std::string& msg) { - return operator<<(msg.c_str()); - } - ELPP_SIMPLE_LOG(char) - ELPP_SIMPLE_LOG(bool) - ELPP_SIMPLE_LOG(signed short) - ELPP_SIMPLE_LOG(unsigned short) - ELPP_SIMPLE_LOG(signed int) - ELPP_SIMPLE_LOG(unsigned int) - ELPP_SIMPLE_LOG(signed long) - ELPP_SIMPLE_LOG(unsigned long) - ELPP_SIMPLE_LOG(float) - ELPP_SIMPLE_LOG(double) - ELPP_SIMPLE_LOG(char*) - ELPP_SIMPLE_LOG(const char*) - ELPP_SIMPLE_LOG(const void*) - ELPP_SIMPLE_LOG(long double) - inline MessageBuilder& operator<<(const std::wstring& msg) { - return operator<<(msg.c_str()); - } - inline 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; - } - // ostream manipulators - inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { - m_logger->stream() << OStreamMani; - return *this; - } + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } #define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ template <typename T> \ inline MessageBuilder& operator<<(const temp<T>& template_inst) { \ @@ -4833,190 +2947,190 @@ template <typename T1, typename T2, typename T3, typename T4, typename T5> inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4, T5>& template_inst) { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } - + #if defined(ELPP_STL_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) - template <class T, class Container> - inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) { - base::workarounds::IterableQueue<T, Container> iterableQueue_ = - static_cast<base::workarounds::IterableQueue<T, Container> >(queue_); - return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); - } - template <class T, class Container> - inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) { - base::workarounds::IterableStack<T, Container> iterableStack_ = - static_cast<base::workarounds::IterableStack<T, Container> >(stack_); - return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); - } - template <class T, class Container, class Comparator> - inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) { - base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ = - static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_); - return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); - } - template <class First, class Second> - inline MessageBuilder& operator<<(const std::pair<First, Second>& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<First>(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<Second>(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template <std::size_t Size> - inline MessageBuilder& operator<<(const std::bitset<Size>& bitset_) { - m_logger->stream() << ELPP_LITERAL("["); - operator << (bitset_.to_string()); - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } -# if defined(ELPP_LOG_STD_ARRAY) - template <class T, std::size_t Size> - inline MessageBuilder& operator<<(const std::array<T, Size>& array) { - return writeIterator(array.begin(), array.end(), array.size()); - } -# endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) -# endif // defined(ELPP_LOG_UNORDERED_MAP) -# if defined(ELPP_LOG_UNORDERED_SET) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) -# endif // defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template <class T, class Container> + inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) { + base::workarounds::IterableQueue<T, Container> iterableQueue_ = + static_cast<base::workarounds::IterableQueue<T, Container> >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template <class T, class Container> + inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) { + base::workarounds::IterableStack<T, Container> iterableStack_ = + static_cast<base::workarounds::IterableStack<T, Container> >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template <class T, class Container, class Comparator> + inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) { + base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ = + static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template <class First, class Second> + MessageBuilder& operator<<(const std::pair<First, Second>& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<First>(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<Second>(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template <std::size_t Size> + MessageBuilder& operator<<(const std::bitset<Size>& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template <class T, std::size_t Size> + inline MessageBuilder& operator<<(const std::array<T, Size>& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) #endif // defined(ELPP_STL_LOGGING) #if defined(ELPP_QT_LOGGING) - inline MessageBuilder& operator<<(const QString& msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << msg.toStdWString(); -# else - m_logger->stream() << msg.toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(const QByteArray& msg) { - return operator << (QString(msg)); - } - inline MessageBuilder& operator<<(const QStringRef& msg) { - return operator<<(msg.toString()); - } - inline MessageBuilder& operator<<(qint64 msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -# else - m_logger->stream() << QString::number(msg).toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(quint64 msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -# else - m_logger->stream() << QString::number(msg).toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(QChar msg) { - m_logger->stream() << msg.toLatin1(); - return *this; - } - inline MessageBuilder& operator<<(const QLatin1String& msg) { - m_logger->stream() << msg.latin1(); - return *this; - } - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) - template <typename First, typename Second> - inline MessageBuilder& operator<<(const QPair<First, Second>& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<First>(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<Second>(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMap<K, V>& map_) { - m_logger->stream() << ELPP_LITERAL("["); - QList<K> keys = map_.keys(); - typename QList<K>::const_iterator begin = keys.begin(); - typename QList<K>::const_iterator end = keys.end(); - int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // to prevent warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<K>(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<V>(map_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) { - operator << (static_cast<QMap<K, V>>(map_)); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QHash<K, V>& hash_) { - m_logger->stream() << ELPP_LITERAL("["); - QList<K> keys = hash_.keys(); - typename QList<K>::const_iterator begin = keys.begin(); - typename QList<K>::const_iterator end = keys.end(); - int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // prevent type warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<K>(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<V>(hash_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) { - operator << (static_cast<QHash<K, V>>(multiHash_)); - return *this; - } + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template <typename First, typename Second> + MessageBuilder& operator<<(const QPair<First, Second>& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<First>(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<Second>(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template <typename K, typename V> + MessageBuilder& operator<<(const QMap<K, V>& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList<K> keys = map_.keys(); + typename QList<K>::const_iterator begin = keys.begin(); + typename QList<K>::const_iterator end = keys.end(); + int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<K>(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<V>(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template <typename K, typename V> + inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) { + operator << (static_cast<QMap<K, V>>(map_)); + return *this; + } + template <typename K, typename V> + MessageBuilder& operator<<(const QHash<K, V>& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList<K> keys = hash_.keys(); + typename QList<K>::const_iterator begin = keys.begin(); + typename QList<K>::const_iterator end = keys.end(); + int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<K>(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<V>(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template <typename K, typename V> + inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) { + operator << (static_cast<QHash<K, V>>(multiHash_)); + return *this; + } #endif // defined(ELPP_QT_LOGGING) #if defined(ELPP_BOOST_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) #endif // defined(ELPP_BOOST_LOGGING) - - /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly - /// - /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to - /// have begin() and end() methods that return respective iterators - /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets - /// @param SizeMethod Method used to get size of container. - /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro - /// for an example usage + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro + /// for an example usage #define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ @@ -5036,323 +3150,200 @@ ss << ELPP_LITERAL("]");\ return ss;\ } #if defined(ELPP_WXWIDGETS_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) -# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) -# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) -# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") #else -# define ELPP_WX_PTR_ENABLED(ContainerType) -# define ELPP_WX_ENABLED(ContainerType) -# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) #endif // defined(ELPP_WXWIDGETS_LOGGING) - // Other classes - template <class Class> - ELPP_SIMPLE_LOG(const Class&) + // Other classes + template <class Class> + ELPP_SIMPLE_LOG(const Class&) #undef ELPP_SIMPLE_LOG #undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG - private: - Logger* m_logger; - const base::type::char_t* m_containerLogSeperator; - - template<class Iterator> - inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { - m_logger->stream() << ELPP_LITERAL("["); - for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { - operator << (*begin_); - m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin_ != end_) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { - m_logger->stream() << " "; - } - return *this; - } - }; - /// @brief Writes nothing - Used when certain log is disabled - class NullWriter : base::NoCopy { - public: - NullWriter(void) {} - - // Null manipulator - inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { - return *this; - } - - template <typename T> - inline NullWriter& operator<<(const T&) { - return *this; - } - }; - /// @brief Main entry point of each logging - class Writer : base::NoCopy { - public: - Writer(Level level, const char* file, unsigned long int line, - const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) : - m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), - m_proceed(false), m_dispatchAction(dispatchAction) { - } - - virtual ~Writer(void) { - processDispatch(); - } - - template <typename T> - inline typename std::enable_if<std::is_integral<T>::value, Writer&>::type - operator<<(T log) { -#if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } -#endif // ELPP_LOGGING_ENABLED - return *this; - } + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; - template <typename T> - inline typename std::enable_if<!std::is_integral<T>::value, Writer&>::type - operator<<(const T& log) { -#if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } -#endif // ELPP_LOGGING_ENABLED - return *this; - } - - inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { -#if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } -#endif // ELPP_LOGGING_ENABLED - return *this; - } - - Writer& construct(Logger* logger, bool needLock = true) { - m_logger = logger; - initializeLogger(logger->id(), false, needLock); - m_messageBuilder.initialize(m_logger); - return *this; - } - - Writer& construct(int count, const char* loggerIds, ...) { - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - va_list loggersList; - va_start(loggersList, loggerIds); - const char* id = loggerIds; - for (int i = 0; i < count; ++i) { - m_loggerIds.push_back(std::string(id)); - id = va_arg(loggersList, const char*); - } - va_end(loggersList); - initializeLogger(m_loggerIds.at(0)); - } else { - initializeLogger(std::string(loggerIds)); - } - m_messageBuilder.initialize(m_logger); - return *this; - } - protected: - Level m_level; - const char* m_file; - const unsigned long int m_line; - const char* m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - bool m_proceed; - base::MessageBuilder m_messageBuilder; - base::DispatchAction m_dispatchAction; - std::vector<std::string> m_loggerIds; - friend class el::Helpers; - - void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) { - if (lookup) { - m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); - } - if (m_logger == nullptr) { - ELPP->acquireLock(); - if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { - // Somehow default logger has been unregistered. Not good! Register again - ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); - } - ELPP->releaseLock(); // Need to unlock it for next writer - Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Logger [" << loggerId << "] is not registered yet!"; - m_proceed = false; - } else { - if (needLock) { - m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because - // m_proceed can be changed by lines below - } - if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { - m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : - ELPP->vRegistry()->allowed(m_level, loggerId.c_str()); - } else { - m_proceed = m_logger->enabled(m_level); - } - } - } - - void processDispatch() { -#if ELPP_LOGGING_ENABLED - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - bool firstDispatched = false; - base::type::string_t logMessage; - std::size_t i = 0; - do { - if (m_proceed) { - if (firstDispatched) { - m_logger->stream() << logMessage; - } else { - firstDispatched = true; - if (m_loggerIds.size() > 1) { - logMessage = m_logger->stream().str(); - } - } - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (i + 1 < m_loggerIds.size()) { - initializeLogger(m_loggerIds.at(i + 1)); - } - } while (++i < m_loggerIds.size()); - } else { - if (m_proceed) { - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - } -#else - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } -#endif // ELPP_LOGGING_ENABLED - } - - void triggerDispatch(void) { - if (m_proceed) { - base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, - m_logger), m_dispatchAction).dispatch(); - } - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (m_proceed && m_level == Level::Fatal - && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { - base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; - std::stringstream reasonStream; - reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" - << " If you wish to disable 'abort on fatal log' please use " - << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; - base::utils::abort(1, reasonStream.str()); - } - m_proceed = false; - } - }; - class PErrorWriter : public base::Writer { - public: - PErrorWriter(Level level, const char* file, unsigned long int line, - const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) : - base::Writer(level, file, line, func, dispatchAction, verboseLevel) { - } - - virtual ~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 - } - } - }; - } // namespace base - // Logging from Logger class. Why this is here? Because we have Storage and Writer class available -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED - template <typename T, typename... Args> - void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { - base::MessageBuilder b; - b.initialize(this); - while (*s) { - if (*s == base::consts::kFormatSpecifierChar) { - if (*(s + 1) == base::consts::kFormatSpecifierChar) { - ++s; - } else { - if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { - ++s; - b << value; - log_(level, vlevel, ++s, args...); - return; - } - } - } - b << *s++; - } - ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); + template<class Iterator> + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); } - template <typename T> - inline void Logger::log_(Level level, int vlevel, const T& log) { - if (level == Level::Verbose) { - if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { - base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", - base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; - } else { - stream().str(ELPP_LITERAL("")); - } - } else { - base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; - } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); } - template <typename T, typename... Args> - void Logger::log(Level level, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); - log_(level, 0, s, value, args...); + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; } - template <typename T> - inline void Logger::log(Level level, const T& log) { - base::threading::ScopedLock scopedLock(lock()); - log_(level, 0, log); - } -# if ELPP_VERBOSE_LOG - template <typename T, typename... Args> - inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); - log_(el::Level::Verbose, vlevel, s, value, args...); + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template <typename T> + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template <typename T> + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; } - template <typename T> - inline void Logger::verbose(int vlevel, const T& log) { - base::threading::ScopedLock scopedLock(lock()); - log_(el::Level::Verbose, vlevel, log); +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; } -# else - template <typename T, typename... Args> - inline void Logger::verbose(int, const char*, const T&, const Args&...) { - return; +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector<std::string> m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +template <typename T, typename... Args> +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } } - template <typename T> - inline void Logger::verbose(int, const T&) { - return; + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template <typename T> +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); } -# endif // ELPP_VERBOSE_LOG -# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template <typename T, typename... Args> +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, s, value, args...); +} +template <typename T> +inline void Logger::log(Level level, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template <typename T, typename... Args> +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template <typename T> +inline void Logger::verbose(int vlevel, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, log); +} +# else +template <typename T, typename... Args> +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template <typename T> +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ template <typename T, typename... Args>\ inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ log(LOG_LEVEL, s, value, args...);\ @@ -5361,7 +3352,7 @@ template <typename T>\ inline void Logger::FUNCTION_NAME(const T& value) {\ log(LOG_LEVEL, value);\ } -# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ template <typename T, typename... Args>\ inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ return;\ @@ -5370,51 +3361,51 @@ template <typename T>\ inline void Logger::FUNCTION_NAME(const T&) {\ return;\ } - -# if ELPP_INFO_LOG - LOGGER_LEVEL_WRITERS(info, Level::Info) -# else - LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) -# endif // ELPP_INFO_LOG -# if ELPP_DEBUG_LOG - LOGGER_LEVEL_WRITERS(debug, Level::Debug) -# else - LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) -# endif // ELPP_DEBUG_LOG -# if ELPP_WARNING_LOG - LOGGER_LEVEL_WRITERS(warn, Level::Warning) -# else - LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) -# endif // ELPP_WARNING_LOG -# if ELPP_ERROR_LOG - LOGGER_LEVEL_WRITERS(error, Level::Error) -# else - LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) -# endif // ELPP_ERROR_LOG -# if ELPP_FATAL_LOG - LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) -# else - LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) -# endif // ELPP_FATAL_LOG -# if ELPP_TRACE_LOG - LOGGER_LEVEL_WRITERS(trace, Level::Trace) -# else - LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) -# endif // ELPP_TRACE_LOG -# undef LOGGER_LEVEL_WRITERS -# undef LOGGER_LEVEL_WRITERS_DISABLED + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED #if ELPP_COMPILER_MSVC -# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs -# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) -# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #else -# if ELPP_COMPILER_CLANG -# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -# else -# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -# endif // ELPP_COMPILER_CLANG +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG #endif // ELPP_COMPILER_MSVC #define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ @@ -5422,818 +3413,540 @@ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVAL #define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ -if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ -if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ -if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER -#if defined(ELPP_PERFORMANCE_LOGGER) -# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER -#else -# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId -#endif - class PerformanceTrackingData { - public: - enum class DataType : base::type::EnumType { - Checkpoint = 1, Complete = 2 - }; - // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) - explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), - m_dataType(dataType), m_file(""), m_line(0), m_func("") {} - inline const std::string* blockName(void) const; - inline const struct timeval* startTime(void) const; - inline const struct timeval* endTime(void) const; - inline const struct timeval* lastCheckpointTime(void) const; - inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; } - inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; } - inline bool firstCheckpoint(void) const { return m_firstCheckpoint; } - inline std::string checkpointId(void) const { return m_checkpointId; } - inline const char* file(void) const { return m_file; } - inline unsigned long int line(void) const { return m_line; } - inline const char* func(void) const { return m_func; } - inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; } - inline const std::string& loggerId(void) const; - private: - base::PerformanceTracker* m_performanceTracker; - base::type::string_t m_formattedTimeTaken; - PerformanceTrackingData::DataType m_dataType; - bool m_firstCheckpoint; - std::string m_checkpointId; - const char* m_file; - unsigned long int m_line; - const char* m_func; - inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { - m_performanceTracker = performanceTracker; - m_firstCheckpoint = firstCheckpoint; +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); } - - friend class el::base::PerformanceTracker; - }; - namespace base { - /// @brief Represents performanceTracker block of code that conditionally adds performance status to log - /// either when goes outside the scope of when checkpoint() is called - class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { - public: - PerformanceTracker(const std::string& blockName, - base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, - const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), - bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) : - 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 - } - /// @brief Copy constructor - PerformanceTracker(const PerformanceTracker& t) : - m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), - m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), - m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { - } - virtual ~PerformanceTracker(void) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - if (m_scopedLog) { - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = getFormattedTimeTaken(); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); - data.init(this); - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h - : ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - } - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) - } - /// @brief A checkpoint for current performanceTracker block. - void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); - data.init(this); - data.m_checkpointId = id; - data.m_file = file; - data.m_line = line; - data.m_func = func; - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h - : ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - 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); - } - inline Level level(void) const { return m_level; } - private: - std::string m_blockName; - base::TimestampUnit m_timestampUnit; - std::string m_loggerId; - bool m_scopedLog; - Level m_level; - bool m_hasChecked; - std::string m_lastCheckpointId; - bool m_enabled; - struct timeval m_startTime, m_endTime, m_lastCheckpointTime; - - PerformanceTracker(void); - - friend class el::PerformanceTrackingData; - friend class base::DefaultPerformanceTrackingCallback; - - const inline base::type::string_t getFormattedTimeTaken() const { - return getFormattedTimeTaken(m_startTime); - } - - const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const { - if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { - base::type::stringstream_t ss; - ss << base::utils::DateTime::getTimeDifference(m_endTime, - startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>(m_timestampUnit)].unit; - return ss.str(); - } - return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, - startTime, m_timestampUnit), m_timestampUnit); - } - - virtual inline void log(el::base::type::ostream_t& os) const { - os << getFormattedTimeTaken(); - } - }; - class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { - protected: - void handle(const PerformanceTrackingData* data) { - m_data = data; - base::type::stringstream_t ss; - if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { - ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); - } else { - ss << ELPP_LITERAL("Performance checkpoint"); - if (!m_data->checkpointId().empty()) { - ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); - } - ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker(); - if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) { - ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); - if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { - ss << ELPP_LITERAL("last checkpoint"); - } else { - ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); - } - ss << ELPP_LITERAL(")]"); - } else { - ss << ELPP_LITERAL("]"); - } - } - el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str(); - } - private: - const PerformanceTrackingData* m_data; - }; - } // namespace base - inline const std::string* PerformanceTrackingData::blockName() const { - return const_cast<const std::string*>(&m_performanceTracker->m_blockName); - } - inline const struct timeval* PerformanceTrackingData::startTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime); - } - inline const struct timeval* PerformanceTrackingData::endTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime); + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } } - inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime); + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast<const std::string*>(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr); + StackTraceEntry(std::size_t index, char* loc) : + m_index(index), + m_location(loc) { } - inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; } - namespace base { - /// @brief Contains some internal debugging tools like crash handler and stack tracer - namespace debug { - class StackTrace : base::NoCopy { - public: - static const std::size_t kMaxStack = 64; - static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() - class StackTraceEntry { - public: - StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) { - m_index = index; - m_location = std::string(loc); - m_demangled = std::string(demang); - m_hex = std::string(hex); - m_addr = std::string(addr); - } - StackTraceEntry(std::size_t index, char* loc) { - m_index = index; - m_location = std::string(loc); - } - std::size_t m_index; - std::string m_location; - std::string m_demangled; - std::string m_hex; - std::string m_addr; - friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) { - ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled - << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; - return ss; - } - - private: - StackTraceEntry(void); - }; - - StackTrace(void) { - generateNew(); - } - - virtual ~StackTrace(void) { - } - - inline std::vector<StackTraceEntry>& getLatestStack(void) { - return m_stack; - } - - friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) { - std::vector<StackTraceEntry>::const_iterator it = st.m_stack.begin(); - while (it != st.m_stack.end()) { - os << " " << *it++ << "\n"; - } - return os; - } - - private: - std::vector<StackTraceEntry> m_stack; - - void generateNew(void) { -#if ELPP_STACKTRACE - m_stack.clear(); - void* stack[kMaxStack]; - std::size_t size = backtrace(stack, kMaxStack); - char** strings = backtrace_symbols(stack, size); - if (size > kStackStart) { // Skip StackTrace c'tor and generateNew - for (std::size_t i = kStackStart; i < size; ++i) { - char* mangName = nullptr; - char* hex = nullptr; - char* addr = nullptr; - for (char* c = strings[i]; *c; ++c) { - switch (*c) { - case '(': - mangName = c; - break; - case '+': - hex = c; - break; - case ')': - addr = c; - break; - default: - break; - } - } - // Perform demangling if parsed properly - if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { - *mangName++ = '\0'; - *hex++ = '\0'; - *addr++ = '\0'; - int status = 0; - char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); - // if demangling is successful, output the demangled function name - if (status == 0) { - // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) - StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); - m_stack.push_back(entry); - } else { - // Not successful - we will use mangled name - StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); - m_stack.push_back(entry); - } - free(demangName); - } else { - StackTraceEntry entry(i - 1, strings[i]); - m_stack.push_back(entry); - } - } - } - free(strings); -#else - ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); -#endif // ELPP_STACKTRACE - } - }; - static std::string crashReason(int sig) { - std::stringstream ss; - bool foundReason = false; - for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { - if (base::consts::kCrashSignals[i].numb == sig) { - ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; - if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { - ss << std::endl << - " " << base::consts::kCrashSignals[i].brief << std::endl << - " " << base::consts::kCrashSignals[i].detail; - } - foundReason = true; - } - } - if (!foundReason) { - ss << "Application has crashed due to unknown signal [" << sig << "]"; - } - return ss.str(); - } - /// @brief Logs reason of crash from sig - static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { - std::stringstream ss; - ss << "CRASH HANDLED; "; - ss << crashReason(sig); -#if ELPP_STACKTRACE - if (stackTraceIfAvailable) { - ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); - } -#else - ELPP_UNUSED(stackTraceIfAvailable); -#endif // ELPP_STACKTRACE - ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); - } - static inline void crashAbort(int sig) { - base::utils::abort(sig); - } - /// @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); - } - /// @brief Handles unexpected crashes - class CrashHandler : base::NoCopy { - public: - typedef void (*Handler)(int); - - explicit CrashHandler(bool useDefault) { - if (useDefault) { - setHandler(defaultCrashHandler); - } - } - explicit CrashHandler(const Handler& cHandler) { - setHandler(cHandler); - } - void setHandler(const Handler& cHandler) { - m_handler = cHandler; -#if defined(ELPP_HANDLE_SIGABRT) - int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector<StackTraceEntry>& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector<StackTraceEntry> m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; #else - int i = 1; -#endif // defined(ELPP_HANDLE_SIGABRT) - for (; i < base::consts::kCrashSignalsCount; ++i) { - m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); - } - } - - private: - Handler m_handler; - }; - } // namespace debug - } // namespace base - extern base::debug::CrashHandler elCrashHandler; +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; #define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) - /// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor - class SysLogInitializer { - public: - SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { #if defined(ELPP_SYSLOG) - openlog(processIdent, options, facility); + openlog(processIdent, options, facility); #else - ELPP_UNUSED(processIdent); - ELPP_UNUSED(options); - ELPP_UNUSED(facility); + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); #endif // defined(ELPP_SYSLOG) - } - virtual ~SysLogInitializer(void) { + } + virtual ~SysLogInitializer(void) { #if defined(ELPP_SYSLOG) - closelog(); + closelog(); #endif // defined(ELPP_SYSLOG) - } - }; + } +}; #define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) - /// @brief Static helpers for developers - class Helpers : base::StaticClass { - public: - /// @brief Shares logging repository (base::Storage) - static inline void setStorage(base::type::StoragePointer storage) { - ELPP = storage; - } - /// @return Main storage repository - static inline base::type::StoragePointer storage() { - return ELPP; - } - /// @brief Sets application arguments and figures out whats active for logging and whats not. - static inline void setArgs(int argc, char** argv) { - ELPP->setApplicationArguments(argc, argv); - } - /// @copydoc setArgs(int argc, char** argv) - static inline void setArgs(int argc, const char** argv) { - ELPP->setApplicationArguments(argc, const_cast<char**>(argv)); - } - /// @brief Overrides default crash handler and installs custom handler. - /// @param crashHandler A functor with no return type that takes single int argument. - /// Handler is a typedef with specification: void (*Handler)(int) - static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { - el::elCrashHandler.setHandler(crashHandler); - } - /// @brief Abort due to crash with signal in parameter - /// @param sig Crash signal - static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) { - 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()); - } - /// @brief Logs reason of crash as per sig - /// @param sig Crash signal - /// @param stackTraceIfAvailable Includes stack trace if available - /// @param level Logging level - /// @param logger Logger to use for logging - static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false, - Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) { - el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); - } - /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out - /// (can be useful for backing up) - static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { - ELPP->setPreRollOutCallback(callback); - } - /// @brief Uninstalls pre rollout callback - static inline void uninstallPreRollOutCallback(void) { - ELPP->unsetPreRollOutCallback(); - } - /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched - template <typename T> - static inline bool installLogDispatchCallback(const std::string& id) { - return ELPP->installLogDispatchCallback<T>(id); - } - /// @brief Uninstalls log dispatch callback - template <typename T> - static inline void uninstallLogDispatchCallback(const std::string& id) { - ELPP->uninstallLogDispatchCallback<T>(id); - } - template <typename T> - static inline T* logDispatchCallback(const std::string& id) { - return ELPP->logDispatchCallback<T>(id); - } - /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished - template <typename T> - static inline bool installPerformanceTrackingCallback(const std::string& id) { - return ELPP->installPerformanceTrackingCallback<T>(id); - } - /// @brief Uninstalls post performance tracking handler - template <typename T> - static inline void uninstallPerformanceTrackingCallback(const std::string& id) { - ELPP->uninstallPerformanceTrackingCallback<T>(id); - } - template <typename T> - static inline T* performanceTrackingCallback(const std::string& id) { - return ELPP->performanceTrackingCallback<T>(id); - } - /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const - template <typename T> - static std::string convertTemplateToStdString(const T& templ) { - el::Logger* logger = - ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); - if (logger == nullptr) { - return std::string(); - } - base::MessageBuilder b; - b.initialize(logger); - logger->acquireLock(); - b << templ; +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast<char**>(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template <typename T> + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback<T>(id); + } + /// @brief Uninstalls log dispatch callback + template <typename T> + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback<T>(id); + } + template <typename T> + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback<T>(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template <typename T> + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback<T>(id); + } + /// @brief Uninstalls post performance tracking handler + template <typename T> + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback<T>(id); + } + template <typename T> + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback<T>(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template <typename T> + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; #if defined(ELPP_UNICODE) - std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); #else - std::string s = logger->stream().str(); + std::string s = logger->stream().str(); #endif // defined(ELPP_UNICODE) - logger->stream().str(ELPP_LITERAL("")); - logger->releaseLock(); - return s; - } - /// @brief Returns command line arguments (pointer) provided to easylogging++ - static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { - return ELPP->commandLineArgs(); - } - /// @brief Installs user defined format specifier and handler - static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - ELPP->installCustomFormatSpecifier(customFormatSpecifier); - } - /// @brief Uninstalls user defined format specifier and handler - static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); - } - /// @brief Returns true if custom format specifier is installed - static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->hasCustomFormatSpecifier(formatSpecifier); - } - static inline void validateFileRolling(Logger* logger, Level level) { - if (logger == nullptr) return; - logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); - } - }; - /// @brief Static helpers to deal with loggers and their configurations - class Loggers : base::StaticClass { - public: - /// @brief Gets existing or registers new logger - static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); - } - /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister - /// loggers initialized / used by third-party libs. - static inline bool unregisterLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->remove(identity); - } - /// @brief Whether or not logger with id is registered - static inline bool hasLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->has(identity); - } - /// @brief Reconfigures specified logger with new configurations - static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) { - if (!logger) return nullptr; - logger->configure(configurations); - return logger; - } - /// @brief Reconfigures logger with new configurations after looking it up using identity - static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) { - return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); - } - /// @brief Reconfigures logger's single configuration - static inline Logger* 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; - } - /// @brief Reconfigures all the existing loggers with new configurations - static inline void reconfigureAllLoggers(const Configurations& configurations) { - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); - it != ELPP->registeredLoggers()->end(); ++it) { - Loggers::reconfigureLogger(it->second, configurations); - } - } - /// @brief Reconfigures single configuration for all the loggers - static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { - reconfigureAllLoggers(Level::Global, configurationType, value); - } - /// @brief Reconfigures single configuration for all the loggers for specified level - static inline void 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(); - } - } - /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers - static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) { - ELPP->registeredLoggers()->setDefaultConfigurations(configurations); - if (reconfigureExistingLoggers) { - Loggers::reconfigureAllLoggers(configurations); - } - } - /// @brief Returns current default - static inline const Configurations* defaultConfigurations(void) { - return ELPP->registeredLoggers()->defaultConfigurations(); - } - /// @brief Returns log stream reference pointer if needed by user - static inline const base::LogStreamsReferenceMap* logStreamsReference(void) { - return ELPP->registeredLoggers()->logStreamsReference(); - } - /// @brief Default typed configuration based on existing defaultConf - static base::TypedConfigurations defaultTypedConfigurations(void) { - return base::TypedConfigurations( - ELPP->registeredLoggers()->defaultConfigurations(), - ELPP->registeredLoggers()->logStreamsReference()); - } - /// @brief Populates all logger IDs in current repository. - /// @param [out] targetList List of fill up. - static inline std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList) { - targetList->clear(); - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); - it != ELPP->registeredLoggers()->list().end(); ++it) { - targetList->push_back(it->first); - } - return targetList; - } - /// @brief Sets configurations from global configuration file. - static void 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(); - } - } - /// @brief Configures loggers using command line arg. Ensure you have already set command line args, - /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. - /// If true is returned that does not mean it has been configured successfully, it only means that it - /// has attempeted to configure logger using configuration file provided in argument - static inline bool 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; - } - /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered - static inline void flushAll(void) { - ELPP->registeredLoggers()->flushAll(); - } - /// @brief Adds logging flag used internally. - static inline void addFlag(LoggingFlag flag) { - ELPP->addFlag(flag); - } - /// @brief Removes logging flag used internally. - static inline void removeFlag(LoggingFlag flag) { - ELPP->removeFlag(flag); - } - /// @brief Determines whether or not certain flag is active - static inline bool hasFlag(LoggingFlag flag) { - return ELPP->hasFlag(flag); - } - /// @brief Adds flag and removes it when scope goes out - class ScopedAddFlag { - public: - ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); } - ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); } - private: - LoggingFlag m_flag; - }; - /// @brief Removes flag and add it when scope goes out - class ScopedRemoveFlag { - public: - ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); } - ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); } - private: - LoggingFlag m_flag; - }; - /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) - static inline void setLoggingLevel(Level level) { - ELPP->setLoggingLevel(level); - } - /// @brief Sets verbose level on the fly - static inline void setVerboseLevel(base::type::VerboseLevel level) { - ELPP->vRegistry()->setLevel(level); - } - /// @brief Gets current verbose level - static inline base::type::VerboseLevel verboseLevel(void) { - return ELPP->vRegistry()->level(); - } - /// @brief Sets vmodules as specified (on the fly) - static inline void setVModules(const char* modules) { - if (ELPP->vRegistry()->vModulesEnabled()) { - ELPP->vRegistry()->setModules(modules); - } - } - /// @brief Sets categories as specified (on the fly) - static inline void setCategories(const char* categories, bool clear = true) { - ELPP->vRegistry()->setCategories(categories, clear); - } - /// @brief Sets thread name (to replace ID) - static inline void setThreadName(const std::string &name) { - ELPP->vRegistry()->setThreadName(name); - } - /// @brief Clears vmodules - static inline void clearVModules(void) { - ELPP->vRegistry()->clearModules(); - } - /// @brief Clears categories - static inline void clearCategories(void) { - ELPP->vRegistry()->clearCategories(); - } - /// @brief Sets filename common prefix - static inline void setFilenameCommonPrefix(const std::string &prefix) { - ELPP->vRegistry()->setFilenameCommonPrefix(prefix); - } - }; - class VersionInfo : base::StaticClass { - public: - /// @brief Current version number - static inline const std::string version(void) { return std::string("9.84"); } - /// @brief Release date of current version - static inline const std::string releaseDate(void) { return std::string("29-07-2016 1221hrs"); } - }; + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template <typename T> + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback<T>(id); + } + /// @brief Uninstalls log dispatch callback + template <typename T> + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback<T>(id); + } + template <typename T> + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback<T>(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMap* logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Clears vmodules + static void clearVModules(void); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; } // namespace el #undef VLOG_IS_ON /// @brief Determines whether verbose logging is on for specified level current file. #define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) #undef TIMED_BLOCK #undef TIMED_SCOPE +#undef TIMED_SCOPE_IF #undef TIMED_FUNC +#undef TIMED_FUNC_IF #undef ELPP_MIN_UNIT #if defined(ELPP_PERFORMANCE_MICROSECONDS) -# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond #else -# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond #endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) /// @brief Performance tracked scope. Performance gets written when goes out of scope using /// 'performance' logger. /// -/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint // Note: Do not surround this definition with null macro because of obj instance -#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT) -#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \ -el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) /// @brief Performance tracked function. Performance gets written when goes out of scope using /// 'performance' logger. /// -/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) #define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) #undef PERFORMANCE_CHECKPOINT #undef PERFORMANCE_CHECKPOINT_WITH_ID -#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) -#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) #undef ELPP_COUNTER #undef ELPP_COUNTER_POS /// @brief Gets hit counter for file/line @@ -6286,212 +3999,212 @@ el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) #undef CVERBOSE_N_TIMES // Normal logs #if ELPP_INFO_LOG -# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #else -# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // Conditional logs #if ELPP_INFO_LOG -# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #else -# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // Occasional logs #if ELPP_INFO_LOG -# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // After N logs #if ELPP_INFO_LOG -# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // N Times logs #if ELPP_INFO_LOG -# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // // Custom Loggers - Requires (level, dispatchAction, loggerId/s) @@ -6547,9 +4260,9 @@ CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLo #undef VLOG_N_TIMES #undef ELPP_CURR_FILE_LOGGER_ID #if defined(ELPP_DEFAULT_LOGGER) -# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER #else -# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId #endif #undef ELPP_TRACE #define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) @@ -6609,53 +4322,53 @@ C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base: #undef DSYSLOG_AFTER_N #undef DSYSLOG_N_TIMES #if defined(ELPP_SYSLOG) -# define CSYSLOG(LEVEL, ...)\ +# define CSYSLOG(LEVEL, ...)\ C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_IF(condition, LEVEL, ...)\ +# define CSYSLOG_IF(condition, LEVEL, ...)\ C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_IF(condition, LEVEL, ...)\ +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) #else -# define CSYSLOG(LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -# define SYSLOG(LEVEL) el::base::NullWriter() -# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() -# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() -# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -# define DSYSLOG(LEVEL) el::base::NullWriter() -# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() -# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() #endif // defined(ELPP_SYSLOG) // // Custom Debug Only Loggers - Requires (level, loggerId/s) @@ -6688,6 +4401,7 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc // // Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros // +#if !defined(ELPP_NO_DEBUG_MACROS) // undef existing #undef DLOG #undef DVLOG @@ -6712,8 +4426,9 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc #define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) #define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) #define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) // Check macros -#if !defined(ELPP_DISABLE_CHECK_MACROS) #undef CCHECK #undef CPCHECK #undef CCHECK_EQ @@ -6822,15 +4537,14 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc #define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) #define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) #define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#endif // define(ELPP_DISABLE_CHECK_MACROS) +#endif // defined(ELPP_NO_CHECK_MACROS) #if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) -# define ELPP_USE_DEF_CRASH_HANDLER false +# define ELPP_USE_DEF_CRASH_HANDLER false #else -# define ELPP_USE_DEF_CRASH_HANDLER true +# define ELPP_USE_DEF_CRASH_HANDLER true #endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) #define ELPP_CRASH_HANDLER_INIT #define ELPP_INIT_EASYLOGGINGPP(val) \ -ELPP_INITI_BASIC_DECLR \ namespace el { \ namespace base { \ el::base::type::StoragePointer elStorage(val); \ @@ -6839,22 +4553,18 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ } #if ELPP_ASYNC_LOGGING -# define INITIALIZE_EASYLOGGINGPP\ -ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ -new el::base::AsyncDispatchWorker()))\ - +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) #else -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) #endif // ELPP_ASYNC_LOGGING -#define INITIALIZE_NULL_EASYLOGGINGPP\ -ELPP_INITI_BASIC_DECLR\ +#define INITIALIZE_NULL_EASYLOGGINGPP \ namespace el {\ namespace base {\ el::base::type::StoragePointer elStorage;\ }\ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ } -// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols #define SHARE_EASYLOGGINGPP(initializedStorage)\ namespace el {\ namespace base {\ @@ -6864,8 +4574,8 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ } #if defined(ELPP_UNICODE) -# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) #else -# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) #endif // defined(ELPP_UNICODE) #endif // EASYLOGGINGPP_H |