Przeglądaj źródła

ver. 4.0.0 ALPHA

Ivan Arkhipov 6 lat temu
rodzic
commit
d0ab5f9647

+ 11 - 8
.gitignore

@@ -13,11 +13,14 @@ cmake-build-debug
 *.dylib
 
 # Build and stuff dirs
-build*
-debug*
-stuff*
-debug_build
-release_build
-/bin
-/install
-!/bin/LotRO_dat_pat_tester.exe
+build*/*
+debug/*
+stuff/*
+debug_build/*
+release_build/*
+bin/*
+install/*
+
+
+!bin/LotRO_dat_extractor.exe
+!bin/LotRO_dat_patcher.exe

+ 5 - 0
CHANGELOG

@@ -27,3 +27,8 @@ Version 3.2.0
 Version 3.3.0
     * Added check and repair functions for situation, when .dat file was patched by LotRO launcher because of official update
 ----------------------------------------------------------------------
+Version 4.0.0 (ALPHA)
+    * Refactored most functions of DatFile class - they throw much less exceptions and base on returning special error values
+    * Added Easylogging++ library and implememted it fully in DatFile class
+    * Improved stability and constantly of library with using ordered locale sets and maps
+----------------------------------------------------------------------

+ 5 - 4
CMakeLists.txt

@@ -51,6 +51,7 @@ set(SOURCE_FILES
         ${CMAKE_SOURCE_DIR}/src/Subfiles/UnknownSubfile.cpp
 
         ${CMAKE_SOURCE_DIR}/Third_party/SQLite/sqlite3.c
+        ${CMAKE_SOURCE_DIR}/Third_party/EasyLogging++/easylogging++.cc
         )
 
 set(CompilerFlags
@@ -75,11 +76,11 @@ add_library(LotroDat_static STATIC ${SOURCE_FILES} ${HEADER_FILES})
 # SHARED LIBRARY
 add_library(LotroDat SHARED ${SOURCE_FILES} ${HEADER_FILES})
 # CONSOLE EXTRACTOR TOOL
-add_executable(LotRO_dat_extract_tester ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/extractor_example.cpp)
+add_executable(LotRO_dat_extractor ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/extractor_example.cpp)
 # CONSOLE PATCHER TOOL
-add_executable(LotRO_dat_pat_tester ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/patcher_example.cpp)
+add_executable(LotRO_dat_patcher ${SOURCE_FILES} ${HEADER_FILES} ${CMAKE_SOURCE_DIR}/src/Examples/patcher_example.cpp)
 
-foreach(TARGET LotroDat_static LotroDat LotRO_dat_extract_tester LotRO_dat_pat_tester)
+foreach(TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher)
     #if(MSVC)
     #    target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/Zlib/zlibstatic.lib)
     #    target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/Yaml-cpp/libyaml-cppmt.lib)
@@ -89,4 +90,4 @@ foreach(TARGET LotroDat_static LotroDat LotRO_dat_extract_tester LotRO_dat_pat_t
         target_link_libraries(${TARGET} ${CMAKE_SOURCE_DIR}/Third_Party/lib/libyaml-cpp.a)
         target_link_libraries(${TARGET} -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic)
     #endif(MSVC)
-endforeach(TARGET LotroDat_static LotroDat LotRO_dat_extract_tester LotRO_dat_pat_tester)
+endforeach(TARGET LotroDat_static LotroDat LotRO_dat_extractor LotRO_dat_patcher)

+ 2996 - 0
Third_party/EasyLogging++/easylogging++.cc

@@ -0,0 +1,2996 @@
+//
+//  Bismillah ar-Rahmaan ar-Raheem
+//
+//  Easylogging++ v9.95.3
+//  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();
+    std::map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level);
+    if (iter != m_unflushedCount.end()) {
+      iter->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(),
+  [](char c) {
+    return static_cast<char>(::toupper(c));
+  });
+  return str;
+}
+
+bool Str::cStringEq(const char* s1, const char* s2) {
+  if (s1 == nullptr && s2 == nullptr) return true;
+  if (s1 == nullptr || s2 == nullptr) return false;
+  return strcmp(s1, s2) == 0;
+}
+
+bool Str::cStringCaseEq(const char* s1, const char* s2) {
+  if (s1 == nullptr && s2 == nullptr) return true;
+  if (s1 == nullptr || s2 == nullptr) return false;
+
+  // With thanks to cygwin for this code
+  int d = 0;
+
+  while (true) {
+    const int c1 = toupper(*s1++);
+    const int c2 = toupper(*s2++);
+
+    if (((d = c1 - c2) != 0) || (c2 == '\0')) {
+      break;
+    }
+  }
+
+  return d == 0;
+}
+
+bool Str::contains(const char* str, char c) {
+  for (; *str; ++str) {
+    if (*str == c)
+      return true;
+  }
+  return false;
+}
+
+char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) {
+  char localBuff[10] = "";
+  char* p = localBuff + sizeof(localBuff) - 2;
+  if (n > 0) {
+    for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
+      *--p = static_cast<char>(n % 10 + '0');
+  } else {
+    *--p = '0';
+    --len;
+  }
+  if (zeroPadded)
+    while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
+  return addToBuff(p, buf, bufLim);
+}
+
+char* Str::addToBuff(const char* str, char* buf, const char* bufLim) {
+  while ((buf < bufLim) && ((*buf = *str++) != '\0'))
+    ++buf;
+  return buf;
+}
+
+char* Str::clearBuff(char buff[], std::size_t lim) {
+  STRCPY(buff, "", lim);
+  ELPP_UNUSED(lim);  // For *nix we dont have anything using lim in above STRCPY macro
+  return buff;
+}
+
+/// @brief Converst wchar* to char*
+///        NOTE: Need to free return value after use!
+char* Str::wcharPtrToCharPtr(const wchar_t* line) {
+  std::size_t len_ = wcslen(line) + 1;
+  char* buff_ = static_cast<char*>(malloc(len_ + 1));
+#      if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+  std::wcstombs(buff_, line, len_);
+#      elif ELPP_OS_WINDOWS
+  std::size_t convCount_ = 0;
+  mbstate_t mbState_;
+  ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
+  wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
+#      endif  // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+  return buff_;
+}
+
+// OS
+
+#if ELPP_OS_WINDOWS
+/// @brief Gets environment variables for Windows based OS.
+///        We are not using <code>getenv(const char*)</code> because of CRT deprecation
+/// @param varname Variable name to get environment variable value for
+/// @return If variable exist the value of it otherwise nullptr
+const char* OS::getWindowsEnvironmentVariable(const char* varname) {
+  const DWORD bufferLen = 50;
+  static char buffer[bufferLen];
+  if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
+    return buffer;
+  }
+  return nullptr;
+}
+#endif  // ELPP_OS_WINDOWS
+#if ELPP_OS_ANDROID
+std::string OS::getProperty(const char* prop) {
+  char propVal[PROP_VALUE_MAX + 1];
+  int ret = __system_property_get(prop, propVal);
+  return ret == 0 ? std::string() : std::string(propVal);
+}
+
+std::string OS::getDeviceName(void) {
+  std::stringstream ss;
+  std::string manufacturer = getProperty("ro.product.manufacturer");
+  std::string model = getProperty("ro.product.model");
+  if (manufacturer.empty() || model.empty()) {
+    return std::string();
+  }
+  ss << manufacturer << "-" << model;
+  return ss.str();
+}
+#endif  // ELPP_OS_ANDROID
+
+const std::string OS::getBashOutput(const char* command) {
+#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+  if (command == nullptr) {
+    return std::string();
+  }
+  FILE* proc = nullptr;
+  if ((proc = popen(command, "r")) == nullptr) {
+    ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
+    return std::string();
+  }
+  char hBuff[4096];
+  if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
+    pclose(proc);
+    if (hBuff[strlen(hBuff) - 1] == '\n') {
+      hBuff[strlen(hBuff) - 1] = '\0';
+    }
+    return std::string(hBuff);
+  } else {
+    pclose(proc);
+  }
+  return std::string();
+#else
+  ELPP_UNUSED(command);
+  return std::string();
+#endif  // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+}
+
+std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal,
+                                       const char* alternativeBashCommand) {
+#if ELPP_OS_UNIX
+  const char* val = getenv(variableName);
+#elif ELPP_OS_WINDOWS
+  const char* val = getWindowsEnvironmentVariable(variableName);
+#endif  // ELPP_OS_UNIX
+  if ((val == nullptr) || ((strcmp(val, "") == 0))) {
+#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+    // Try harder on unix-based systems
+    std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
+    if (valBash.empty()) {
+      return std::string(defaultVal);
+    } else {
+      return valBash;
+    }
+#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
+    ELPP_UNUSED(alternativeBashCommand);
+    return std::string(defaultVal);
+#endif  // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+  }
+  return std::string(val);
+}
+
+std::string OS::currentUser(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+  return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
+#elif ELPP_OS_WINDOWS
+  return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
+#elif ELPP_OS_ANDROID
+  ELPP_UNUSED(base::consts::kUnknownUser);
+  return std::string("android");
+#else
+  return std::string();
+#endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+}
+
+std::string OS::currentHost(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+  return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
+#elif ELPP_OS_WINDOWS
+  return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
+#elif ELPP_OS_ANDROID
+  ELPP_UNUSED(base::consts::kUnknownHost);
+  return getDeviceName();
+#else
+  return std::string();
+#endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+}
+
+bool OS::termSupportsColor(void) {
+  std::string term = getEnvironmentVariable("TERM", "");
+  return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
+         || term == "screen" || term == "linux" || term == "cygwin"
+         || term == "screen-256color";
+}
+
+// DateTime
+
+void DateTime::gettimeofday(struct timeval* tv) {
+#if ELPP_OS_WINDOWS
+  if (tv != nullptr) {
+#  if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+    const unsigned __int64 delta_ = 11644473600000000Ui64;
+#  else
+    const unsigned __int64 delta_ = 11644473600000000ULL;
+#  endif  // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+    const double secOffSet = 0.000001;
+    const unsigned long usecOffSet = 1000000;
+    FILETIME fileTime;
+    GetSystemTimeAsFileTime(&fileTime);
+    unsigned __int64 present = 0;
+    present |= fileTime.dwHighDateTime;
+    present = present << 32;
+    present |= fileTime.dwLowDateTime;
+    present /= 10;  // mic-sec
+    // Subtract the difference
+    present -= delta_;
+    tv->tv_sec = static_cast<long>(present * secOffSet);
+    tv->tv_usec = static_cast<long>(present % usecOffSet);
+  }
+#else
+  ::gettimeofday(tv, nullptr);
+#endif  // ELPP_OS_WINDOWS
+}
+
+std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) {
+  struct timeval currTime;
+  gettimeofday(&currTime);
+  return timevalToString(currTime, format, ssPrec);
+}
+
+std::string DateTime::timevalToString(struct timeval tval, const char* format,
+                                      const el::base::SubsecondPrecision* ssPrec) {
+  struct ::tm timeInfo;
+  buildTimeInfo(&tval, &timeInfo);
+  const int kBuffSize = 30;
+  char buff_[kBuffSize] = "";
+  parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset),
+              ssPrec);
+  return std::string(buff_);
+}
+
+base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
+  base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
+  const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
+  for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
+    if (time <= base::consts::kTimeFormats[i].value) {
+      break;
+    }
+    if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) {
+      break;
+    }
+    time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value);
+    unit = base::consts::kTimeFormats[i + 1].unit;
+  }
+  base::type::stringstream_t ss;
+  ss << time << " " << unit;
+  return ss.str();
+}
+
+unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime,
+    base::TimestampUnit timestampUnit) {
+  if (timestampUnit == base::TimestampUnit::Microsecond) {
+    return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -
+                                           static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));
+  }
+  // milliseconds
+  auto conv = [](const struct timeval& tim) {
+    return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000));
+  };
+  return static_cast<unsigned long long>(conv(endTime) - conv(startTime));
+}
+
+struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
+#if ELPP_OS_UNIX
+  time_t rawTime = currTime->tv_sec;
+  ::elpptime_r(&rawTime, timeInfo);
+  return timeInfo;
+#else
+#  if ELPP_COMPILER_MSVC
+  ELPP_UNUSED(currTime);
+  time_t t;
+#    if defined(_USE_32BIT_TIME_T)
+  _time32(&t);
+#    else
+  _time64(&t);
+#    endif
+  elpptime_s(timeInfo, &t);
+  return timeInfo;
+#  else
+  // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
+  time_t rawTime = currTime->tv_sec;
+  struct tm* tmInf = elpptime(&rawTime);
+  *timeInfo = *tmInf;
+  return timeInfo;
+#  endif  // ELPP_COMPILER_MSVC
+#endif  // ELPP_OS_UNIX
+}
+
+char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
+                            std::size_t msec, const base::SubsecondPrecision* ssPrec) {
+  const char* bufLim = buf + bufSz;
+  for (; *format; ++format) {
+    if (*format == base::consts::kFormatSpecifierChar) {
+      switch (*++format) {
+      case base::consts::kFormatSpecifierChar:  // Escape
+        break;
+      case '\0':  // End
+        --format;
+        break;
+      case 'd':  // Day
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
+        continue;
+      case 'a':  // Day of week (short)
+        buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
+        continue;
+      case 'A':  // Day of week (long)
+        buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
+        continue;
+      case 'M':  // month
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
+        continue;
+      case 'b':  // month (short)
+        buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
+        continue;
+      case 'B':  // month (long)
+        buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
+        continue;
+      case 'y':  // year (two digits)
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
+        continue;
+      case 'Y':  // year (four digits)
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
+        continue;
+      case 'h':  // hour (12-hour)
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
+        continue;
+      case 'H':  // hour (24-hour)
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
+        continue;
+      case 'm':  // minute
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
+        continue;
+      case 's':  // second
+        buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
+        continue;
+      case 'z':  // subsecond part
+      case 'g':
+        buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim);
+        continue;
+      case 'F':  // AM/PM
+        buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
+        continue;
+      default:
+        continue;
+      }
+    }
+    if (buf == bufLim) break;
+    *buf++ = *format;
+  }
+  return buf;
+}
+
+// CommandLineArgs
+
+void CommandLineArgs::setArgs(int argc, char** argv) {
+  m_params.clear();
+  m_paramsWithValue.clear();
+  if (argc == 0 || argv == nullptr) {
+    return;
+  }
+  m_argc = argc;
+  m_argv = argv;
+  for (int i = 1; i < m_argc; ++i) {
+    const char* v = (strstr(m_argv[i], "="));
+    if (v != nullptr && strlen(v) > 0) {
+      std::string key = std::string(m_argv[i]);
+      key = key.substr(0, key.find_first_of('='));
+      if (hasParamWithValue(key.c_str())) {
+        ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
+                           << getParamValue(key.c_str()) << "]");
+      } else {
+        m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
+      }
+    }
+    if (v == nullptr) {
+      if (hasParam(m_argv[i])) {
+        ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
+      } else {
+        m_params.push_back(std::string(m_argv[i]));
+      }
+    }
+  }
+}
+
+bool CommandLineArgs::hasParamWithValue(const char* paramKey) const {
+  return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
+}
+
+const char* CommandLineArgs::getParamValue(const char* paramKey) const {
+  std::map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
+  return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
+}
+
+bool CommandLineArgs::hasParam(const char* paramKey) const {
+  return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
+}
+
+bool CommandLineArgs::empty(void) const {
+  return m_params.empty() && m_paramsWithValue.empty();
+}
+
+std::size_t CommandLineArgs::size(void) const {
+  return m_params.size() + m_paramsWithValue.size();
+}
+
+base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
+  for (int i = 1; i < c.m_argc; ++i) {
+    os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
+    if (i < c.m_argc - 1) {
+      os << ELPP_LITERAL(" ");
+    }
+  }
+  return os;
+}
+
+} // namespace utils
+
+// el::base::threading
+namespace threading {
+
+#if ELPP_THREADING_ENABLED
+#  if ELPP_USE_STD_THREADING
+#      if ELPP_ASYNC_LOGGING
+static void msleep(int ms) {
+  // Only when async logging enabled - this is because async is strict on compiler
+#         if defined(ELPP_NO_SLEEP_FOR)
+  usleep(ms * 1000);
+#         else
+  std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+#         endif  // defined(ELPP_NO_SLEEP_FOR)
+}
+#      endif  // ELPP_ASYNC_LOGGING
+#  endif  // !ELPP_USE_STD_THREADING
+#endif  // ELPP_THREADING_ENABLED
+
+} // namespace threading
+
+// el::base
+
+// SubsecondPrecision
+
+void SubsecondPrecision::init(int width) {
+  if (width < 1 || width > 6) {
+    width = base::consts::kDefaultSubsecondPrecision;
+  }
+  m_width = width;
+  switch (m_width) {
+  case 3:
+    m_offset = 1000;
+    break;
+  case 4:
+    m_offset = 100;
+    break;
+  case 5:
+    m_offset = 10;
+    break;
+  case 6:
+    m_offset = 1;
+    break;
+  default:
+    m_offset = 1000;
+    break;
+  }
+}
+
+// LogFormat
+
+LogFormat::LogFormat(void) :
+  m_level(Level::Unknown),
+  m_userFormat(base::type::string_t()),
+  m_format(base::type::string_t()),
+  m_dateTimeFormat(std::string()),
+  m_flags(0x0),
+  m_currentUser(base::utils::OS::currentUser()),
+  m_currentHost(base::utils::OS::currentHost()) {
+}
+
+LogFormat::LogFormat(Level level, const base::type::string_t& format)
+  : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()),
+    m_currentHost(base::utils::OS::currentHost()) {
+  parseFromFormat(m_userFormat);
+}
+
+LogFormat::LogFormat(const LogFormat& logFormat):
+  m_level(logFormat.m_level),
+  m_userFormat(logFormat.m_userFormat),
+  m_format(logFormat.m_format),
+  m_dateTimeFormat(logFormat.m_dateTimeFormat),
+  m_flags(logFormat.m_flags),
+  m_currentUser(logFormat.m_currentUser),
+  m_currentHost(logFormat.m_currentHost) {
+}
+
+LogFormat::LogFormat(LogFormat&& logFormat) {
+  m_level = std::move(logFormat.m_level);
+  m_userFormat = std::move(logFormat.m_userFormat);
+  m_format = std::move(logFormat.m_format);
+  m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
+  m_flags = std::move(logFormat.m_flags);
+  m_currentUser = std::move(logFormat.m_currentUser);
+  m_currentHost = std::move(logFormat.m_currentHost);
+}
+
+LogFormat& LogFormat::operator=(const LogFormat& logFormat) {
+  if (&logFormat != this) {
+    m_level = logFormat.m_level;
+    m_userFormat = logFormat.m_userFormat;
+    m_dateTimeFormat = logFormat.m_dateTimeFormat;
+    m_flags = logFormat.m_flags;
+    m_currentUser = logFormat.m_currentUser;
+    m_currentHost = logFormat.m_currentHost;
+  }
+  return *this;
+}
+
+bool LogFormat::operator==(const LogFormat& other) {
+  return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
+         m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
+}
+
+/// @brief Updates format to be used while logging.
+/// @param userFormat User provided format
+void LogFormat::parseFromFormat(const base::type::string_t& userFormat) {
+  // We make copy because we will be changing the format
+  // i.e, removing user provided date format from original format
+  // and then storing it.
+  base::type::string_t formatCopy = userFormat;
+  m_flags = 0x0;
+  auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
+    std::size_t foundAt = base::type::string_t::npos;
+    while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) {
+      if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+        if (hasFlag(flag)) {
+          // If we already have flag we remove the escape chars so that '%%' is turned to '%'
+          // even after specifier resolution - this is because we only replaceFirst specifier
+          formatCopy.erase(foundAt > 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 {
+    char baseFilename[base::consts::kSourceFilenameMaxLength] = "";
+    base::utils::File::buildBaseFilename(file, baseFilename);
+    std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
+    for (; it != m_modules.end(); ++it) {
+      if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) {
+        return vlevel <= it->second;
+      }
+    }
+    if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
+      return true;
+    }
+    return false;
+  }
+}
+
+void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
+  if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
+      commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
+    setLevel(base::consts::kMaxVerboseLevel);
+  } else if (commandLineArgs->hasParamWithValue("--v")) {
+    setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--v"))));
+  } else if (commandLineArgs->hasParamWithValue("--V")) {
+    setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--V"))));
+  } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
+    setModules(commandLineArgs->getParamValue("-vmodule"));
+  } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
+    setModules(commandLineArgs->getParamValue("-VMODULE"));
+  }
+}
+
+#if !defined(ELPP_DEFAULT_LOGGING_FLAGS)
+#   define ELPP_DEFAULT_LOGGING_FLAGS 0x0
+#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS)
+// Storage
+#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)
+  el::base::threading::ScopedLock lock_(ELPP->lock());
+  ELPP_UNUSED(lock_);
+  for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
+       it != ELPP->customFormatSpecifiers()->end(); ++it) {
+    std::string fs(it->formatSpecifier());
+    base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
+    base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage));
+  }
+#endif  // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
+  if (appendNewLine) logLine += ELPP_LITERAL("\n");
+  return logLine;
+}
+
+// LogDispatcher
+
+void LogDispatcher::dispatch(void) {
+  if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
+    m_proceed = false;
+  }
+  if (!m_proceed) {
+    return;
+  }
+  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) {
+    {
+      base::threading::ScopedLock scopedLock(ELPP->lock());
+      if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
+        // Somehow default logger has been unregistered. Not good! Register again
+        ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
+      }
+    }
+    Writer(Level::Debug, 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::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
+    base::utils::abort(1, reasonStream.str());
+  }
+  m_proceed = false;
+}
+
+// PErrorWriter
+
+PErrorWriter::~PErrorWriter(void) {
+  if (m_proceed) {
+#if ELPP_COMPILER_MSVC
+    char buff[256];
+    strerror_s(buff, 256, errno);
+    m_logger->stream() << ": " << buff << " [" << errno << "]";
+#else
+    m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
+#endif
+  }
+}
+
+// PerformanceTracker
+
+#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
+
+PerformanceTracker::PerformanceTracker(const std::string& blockName,
+                                       base::TimestampUnit timestampUnit,
+                                       const std::string& loggerId,
+                                       bool scopedLog, Level level) :
+  m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
+  m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+  // We store it locally so that if user happen to change configuration by the end of scope
+  // or before calling checkpoint, we still depend on state of configuraton at time of construction
+  el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
+  m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
+  if (m_enabled) {
+    base::utils::DateTime::gettimeofday(&m_startTime);
+  }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+}
+
+PerformanceTracker::~PerformanceTracker(void) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+  if (m_enabled) {
+    base::threading::ScopedLock scopedLock(lock());
+    if (m_scopedLog) {
+      base::utils::DateTime::gettimeofday(&m_endTime);
+      base::type::string_t formattedTime = getFormattedTimeTaken();
+      PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
+      data.init(this);
+      data.m_formattedTimeTaken = formattedTime;
+      PerformanceTrackingCallback* callback = nullptr;
+      for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+           : ELPP->m_performanceTrackingCallbacks) {
+        callback = h.second.get();
+        if (callback != nullptr && callback->enabled()) {
+          callback->handle(&data);
+        }
+      }
+    }
+  }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
+}
+
+void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line,
+                                    const char* func) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+  if (m_enabled) {
+    base::threading::ScopedLock scopedLock(lock());
+    base::utils::DateTime::gettimeofday(&m_endTime);
+    base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
+    PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
+    data.init(this);
+    data.m_checkpointId = id;
+    data.m_file = file;
+    data.m_line = line;
+    data.m_func = func;
+    data.m_formattedTimeTaken = formattedTime;
+    PerformanceTrackingCallback* callback = nullptr;
+    for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+         : ELPP->m_performanceTrackingCallbacks) {
+      callback = h.second.get();
+      if (callback != nullptr && callback->enabled()) {
+        callback->handle(&data);
+      }
+    }
+    base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
+    m_hasChecked = true;
+    m_lastCheckpointId = id;
+  }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+  ELPP_UNUSED(id);
+  ELPP_UNUSED(file);
+  ELPP_UNUSED(line);
+  ELPP_UNUSED(func);
+}
+
+const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const {
+  if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
+    base::type::stringstream_t ss;
+    ss << base::utils::DateTime::getTimeDifference(m_endTime,
+        startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>
+            (m_timestampUnit)].unit;
+    return ss.str();
+  }
+  return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
+         startTime, m_timestampUnit), m_timestampUnit);
+}
+
+#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
+
+namespace debug {
+#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
+
+// StackTrace
+
+StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const 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.95.3");
+}
+/// @brief Release date of current version
+const std::string VersionInfo::releaseDate(void) {
+  return std::string("13-10-2017 1134hrs");
+}
+
+} // namespace el

+ 4605 - 0
Third_party/EasyLogging++/easylogging++.h

@@ -0,0 +1,4605 @@
+//
+//  Bismillah ar-Rahmaan ar-Raheem
+//
+//  Easylogging++ v9.95.3
+//  Single-header only, 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
+//
+#ifndef EASYLOGGINGPP_H
+#define EASYLOGGINGPP_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
+#else
+#  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
+#  endif
+#endif
+// Visual C++
+#if defined(_MSC_VER)
+#  define ELPP_COMPILER_MSVC 1
+#else
+#  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
+#endif
+// Clang++
+#if (defined(__clang__) && (__clang__ == 1))
+#  define ELPP_COMPILER_CLANG 1
+#else
+#  define ELPP_COMPILER_CLANG 0
+#endif
+#if ELPP_COMPILER_CLANG
+#  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
+#else
+#  define ELPP_MINGW 0
+#endif
+#if (defined(__CYGWIN__) && (__CYGWIN__ == 1))
+#  define ELPP_CYGWIN 1
+#else
+#  define ELPP_CYGWIN 0
+#endif
+#if (defined(__INTEL_COMPILER))
+#  define ELPP_COMPILER_INTEL 1
+#else
+#  define ELPP_COMPILER_INTEL 0
+#endif
+// Operating System Evaluation
+// Windows
+#if (defined(_WIN32) || defined(_WIN64))
+#  define ELPP_OS_WINDOWS 1
+#else
+#  define ELPP_OS_WINDOWS 0
+#endif
+// Linux
+#if (defined(__linux) || defined(__linux__))
+#  define ELPP_OS_LINUX 1
+#else
+#  define ELPP_OS_LINUX 0
+#endif
+#if (defined(__APPLE__))
+#  define ELPP_OS_MAC 1
+#else
+#  define ELPP_OS_MAC 0
+#endif
+#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
+#  define ELPP_OS_FREEBSD 1
+#else
+#  define ELPP_OS_FREEBSD 0
+#endif
+#if (defined(__sun))
+#  define ELPP_OS_SOLARIS 1
+#else
+#  define ELPP_OS_SOLARIS 0
+#endif
+#if (defined(_AIX))
+#  define ELPP_OS_AIX 1
+#else
+#  define ELPP_OS_AIX 0
+#endif
+#if (defined(__NetBSD__))
+#  define ELPP_OS_NETBSD 1
+#else
+#  define ELPP_OS_NETBSD 0
+#endif
+// Unix
+#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX) && (!ELPP_OS_WINDOWS))
+#  define ELPP_OS_UNIX 1
+#else
+#  define ELPP_OS_UNIX 0
+#endif
+#if (defined(__ANDROID__))
+#  define ELPP_OS_ANDROID 1
+#else
+#  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
+#endif //  !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN
+#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO)
+#  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
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_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
+#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)) { \
+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)) { \
+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))
+#else
+#  define ELPP_ASSERT(x, y)
+#endif  //(!defined(ELPP_DISABLE_ASSERT)
+#if ELPP_COMPILER_MSVC
+#  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 \
+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) { \
+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
+#else
+#  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) { \
+std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \
+ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \
+<< ELPP_INTERNAL_DEBUGGING_ENDL; }}
+#  endif
+#else
+#  undef ELPP_INTERNAL_INFO
+#  define ELPP_INTERNAL_INFO(lvl, msg)
+#endif  // (defined(ELPP_DEBUG_INFO))
+#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG))
+#  if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID)
+#    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
+#    define ELPP_STACKTRACE 0
+#  endif  // ELPP_COMPILER_GCC
+#else
+#    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
+#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)
+#else
+#  define ELPP_EXPORT
+#endif  // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC
+// Some special functions that are VC++ specific
+#undef STRTOK
+#undef STRERROR
+#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)
+#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)
+#endif
+// Compiler specific support evaluations
+#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD))
+#  define ELPP_USE_STD_THREADING 0
+#else
+#  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
+#else
+#  define ELPP_FINAL final
+#endif  // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702)
+#if defined(ELPP_EXPERIMENTAL_ASYNC)
+#  define ELPP_ASYNC_LOGGING 1
+#else
+#  define ELPP_ASYNC_LOGGING 0
+#endif // defined(ELPP_EXPERIMENTAL_ASYNC)
+#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING
+#  define ELPP_THREADING_ENABLED 1
+#else
+#  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__
+#elif ELPP_COMPILER_GCC  // GCC
+#  define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_INTEL  // Intel C++
+#  define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_CLANG  // Clang++
+#  define ELPP_FUNC __PRETTY_FUNCTION__
+#else
+#  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
+#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \
+(ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800))
+// Logging Enable/Disable macros
+#if defined(ELPP_DISABLE_LOGS)
+#define ELPP_LOGGING_ENABLED 0
+#else
+#define ELPP_LOGGING_ENABLED 1
+#endif
+#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED))
+#  define ELPP_DEBUG_LOG 1
+#else
+#  define ELPP_DEBUG_LOG 0
+#endif  // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
+#  define ELPP_INFO_LOG 1
+#else
+#  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
+#else
+#  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
+#else
+#  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
+#else
+#  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
+#else
+#  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
+#else
+#  define ELPP_VERBOSE_LOG 0
+#endif  // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!(ELPP_CXX0X || ELPP_CXX11))
+#   error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)"
+#endif  // (!(ELPP_CXX0X || ELPP_CXX11))
+// Headers
+#if defined(ELPP_SYSLOG)
+#   include <syslog.h>
+#endif  // defined(ELPP_SYSLOG)
+#include <ctime>
+#include <cstring>
+#include <cstdlib>
+#include <cctype>
+#include <cwchar>
+#include <csignal>
+#include <cerrno>
+#include <cstdarg>
+#if defined(ELPP_UNICODE)
+#   include <locale>
+#  if ELPP_OS_WINDOWS
+#      include <codecvt>
+#  endif // ELPP_OS_WINDOWS
+#endif  // defined(ELPP_UNICODE)
+#if ELPP_STACKTRACE
+#   include <cxxabi.h>
+#   include <execinfo.h>
+#endif  // ELPP_STACKTRACE
+#if ELPP_OS_ANDROID
+#   include <sys/system_properties.h>
+#endif  // ELPP_OS_ANDROID
+#if ELPP_OS_UNIX
+#   include <sys/stat.h>
+#   include <sys/time.h>
+#elif ELPP_OS_WINDOWS
+#   include <direct.h>
+#   include <windows.h>
+#  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  // ELPP_OS_UNIX
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <functional>
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <type_traits>
+#if ELPP_THREADING_ENABLED
+#  if ELPP_USE_STD_THREADING
+#      include <mutex>
+#      include <thread>
+#  else
+#      if ELPP_OS_UNIX
+#         include <pthread.h>
+#      endif  // ELPP_OS_UNIX
+#  endif  // ELPP_USE_STD_THREADING
+#endif  // ELPP_THREADING_ENABLED
+#if ELPP_ASYNC_LOGGING
+#  if defined(ELPP_NO_SLEEP_FOR)
+#      include <unistd.h>
+#  endif  // defined(ELPP_NO_SLEEP_FOR)
+#   include <thread>
+#   include <queue>
+#   include <condition_variable>
+#endif  // ELPP_ASYNC_LOGGING
+#if defined(ELPP_STL_LOGGING)
+// For logging STL based templates
+#   include <list>
+#   include <queue>
+#   include <deque>
+#   include <set>
+#   include <bitset>
+#   include <stack>
+#  if defined(ELPP_LOG_STD_ARRAY)
+#      include <array>
+#  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)
+#      include <unordered_set>
+#  endif  // defined(ELPP_UNORDERED_SET)
+#endif  // defined(ELPP_STL_LOGGING)
+#if defined(ELPP_QT_LOGGING)
+// For logging Qt based classes & templates
+#   include <QString>
+#   include <QByteArray>
+#   include <QVector>
+#   include <QList>
+#   include <QPair>
+#   include <QMap>
+#   include <QQueue>
+#   include <QSet>
+#   include <QLinkedList>
+#   include <QHash>
+#   include <QMultiHash>
+#   include <QStack>
+#endif  // defined(ELPP_QT_LOGGING)
+#if defined(ELPP_BOOST_LOGGING)
+// For logging boost based classes & templates
+#   include <boost/container/vector.hpp>
+#   include <boost/container/stable_vector.hpp>
+#   include <boost/container/list.hpp>
+#   include <boost/container/deque.hpp>
+#   include <boost/container/map.hpp>
+#   include <boost/container/flat_map.hpp>
+#   include <boost/container/set.hpp>
+#   include <boost/container/flat_set.hpp>
+#endif  // defined(ELPP_BOOST_LOGGING)
+#if defined(ELPP_WXWIDGETS_LOGGING)
+// For logging wxWidgets based classes & templates
+#   include <wx/vector.h>
+#endif  // defined(ELPP_WXWIDGETS_LOGGING)
+#if defined(ELPP_UTC_DATETIME)
+#   define elpptime_r gmtime_r
+#   define elpptime_s gmtime_s
+#   define elpptime   gmtime
+#else
+#   define elpptime_r localtime_r
+#   define elpptime_s localtime_s
+#   define elpptime   localtime
+#endif  // defined(ELPP_UTC_DATETIME)
+// Forward declarations
+namespace el {
+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;
+#endif // ELPP_ASYNC_LOGGING
+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 {
+#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;
+#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;
+#endif  // defined(ELPP_UNICODE)
+#if defined(ELPP_CUSTOM_COUT_LINE)
+#  define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine)
+#else
+#  define ELPP_COUT_LINE(logLine) logLine << std::flush
+#endif // defined(ELPP_CUSTOM_COUT_LINE)
+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
+// Extra spaces after format specifiers are only for readability purposes in log files
+static const base::type::char_t* kInfoLevelLogValue     =   ELPP_LITERAL("INFO");
+static const base::type::char_t* kDebugLevelLogValue    =   ELPP_LITERAL("DEBUG");
+static const base::type::char_t* kWarningLevelLogValue  =   ELPP_LITERAL("WARNING");
+static const base::type::char_t* kErrorLevelLogValue    =   ELPP_LITERAL("ERROR");
+static const base::type::char_t* kFatalLevelLogValue    =   ELPP_LITERAL("FATAL");
+static const base::type::char_t* kVerboseLevelLogValue  =
+  ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
+static const base::type::char_t* kTraceLevelLogValue    =   ELPP_LITERAL("TRACE");
+static const base::type::char_t* kInfoLevelShortLogValue     =   ELPP_LITERAL("I");
+static const base::type::char_t* kDebugLevelShortLogValue    =   ELPP_LITERAL("D");
+static const base::type::char_t* kWarningLevelShortLogValue  =   ELPP_LITERAL("W");
+static const base::type::char_t* kErrorLevelShortLogValue    =   ELPP_LITERAL("E");
+static const base::type::char_t* kFatalLevelShortLogValue    =   ELPP_LITERAL("F");
+static const base::type::char_t* kVerboseLevelShortLogValue  =   ELPP_LITERAL("V");
+static const base::type::char_t* kTraceLevelShortLogValue    =   ELPP_LITERAL("T");
+// Format specifiers - These are used to define log format
+static const base::type::char_t* kAppNameFormatSpecifier          =      ELPP_LITERAL("%app");
+static const base::type::char_t* kLoggerIdFormatSpecifier         =      ELPP_LITERAL("%logger");
+static const base::type::char_t* kThreadIdFormatSpecifier         =      ELPP_LITERAL("%thread");
+static const base::type::char_t* kSeverityLevelFormatSpecifier    =      ELPP_LITERAL("%level");
+static const base::type::char_t* kSeverityLevelShortFormatSpecifier    =      ELPP_LITERAL("%levshort");
+static const base::type::char_t* kDateTimeFormatSpecifier         =      ELPP_LITERAL("%datetime");
+static const base::type::char_t* kLogFileFormatSpecifier          =      ELPP_LITERAL("%file");
+static const base::type::char_t* kLogFileBaseFormatSpecifier      =      ELPP_LITERAL("%fbase");
+static const base::type::char_t* kLogLineFormatSpecifier          =      ELPP_LITERAL("%line");
+static const base::type::char_t* kLogLocationFormatSpecifier      =      ELPP_LITERAL("%loc");
+static const base::type::char_t* kLogFunctionFormatSpecifier      =      ELPP_LITERAL("%func");
+static const base::type::char_t* kCurrentUserFormatSpecifier      =      ELPP_LITERAL("%user");
+static const base::type::char_t* kCurrentHostFormatSpecifier      =      ELPP_LITERAL("%host");
+static const base::type::char_t* kMessageFormatSpecifier          =      ELPP_LITERAL("%msg");
+static const base::type::char_t* kVerboseLevelFormatSpecifier     =      ELPP_LITERAL("%vlevel");
+static const char* kDateTimeFormatSpecifierForFilename            =      "%datetime";
+// Date/time
+static const char* kDays[7]                         =      { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+static const char* kDaysAbbrev[7]                   =      { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static const char* kMonths[12]                      =      { "January", "February", "March", "Apri", "May", "June", "July", "August",
+                                                             "September", "October", "November", "December"
+                                                           };
+static const char* kMonthsAbbrev[12]                =      { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+static const char* kDefaultDateTimeFormat           =      "%Y-%M-%d %H:%m:%s,%g";
+static const char* kDefaultDateTimeFormatInFilename =      "%Y-%M-%d_%H-%m";
+static const int kYearBase                          =      1900;
+static const char* kAm                              =      "AM";
+static const char* kPm                              =      "PM";
+// Miscellaneous constants
+#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";
+#endif  // defined(ELPP_SYSLOG)
+static const char* kNullPointer                            =      "nullptr";
+static const char  kFormatSpecifierChar                    =      '%';
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+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 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;
+#else
+#  if ELPP_OS_UNIX
+#      if ELPP_OS_ANDROID
+static const char* kDefaultLogFile                         =      "logs/myeasylog.log";
+#      else
+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
+#endif  // defined(ELPP_DEFAULT_LOG_FILE)
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+static const char* kDefaultLogFileParam                    =      "--default-log-file";
+#endif  // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+static const char* kLoggingFlagsParam                      =      "--logging-flags";
+#endif  // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+#if ELPP_OS_WINDOWS
+static const char* kFilePathSeperator                      =      "\\";
+#else
+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("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
+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
+/// @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();
+#      endif  // (ELPP_OS_WINDOWS)
+  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
+static inline std::string getCurrentThreadId(void) {
+  return std::string();
+}
+#endif  // ELPP_THREADING_ENABLED
+}  // 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,
+                                     const std::string& replaceWith);
+#endif  // defined(ELPP_UNICODE)
+  /// @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);
+#endif  // ELPP_OS_WINDOWS
+#if ELPP_OS_ANDROID
+  /// @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);
+
+  /// @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;
+    }
+    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 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)
+///
+/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions)
+///         of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be
+///         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;
+  }
+
+  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();
+    }
+  }
+
+/// @brief Registers new registry to repository.
+  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
+  void unregister(const T_Key& uniqKey) {
+    T_Ptr* existing = get(uniqKey);
+    if (existing != nullptr) {
+      this->list().erase(uniqKey);
+      base::utils::safeDelete(existing);
+    }
+  }
+
+/// @brief Gets pointer from repository. If none found, nullptr is returned.
+  T_Ptr* get(const T_Key& uniqKey) {
+    iterator it = this->list().find(uniqKey);
+    return it == this->list().end()
+           ? nullptr
+           : 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)
+///
+/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry<T_Ptr, Container>. Any implementation of this class
+/// 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);
+  }
+
+  /// @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;
+  }
+
+  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");
+    }
+    return os;
+  }
+
+ protected:
+  virtual void unregisterAll(void) ELPP_FINAL {
+    if (!this->empty()) {
+      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) {
+        if (ptr == *iter) {
+          break;
+        }
+      }
+      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);
+  }
+
+/// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate
+/// in order to validate pointer.
+  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 nullptr;
+  }
+
+ 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));
+    }
+  }
+};
+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;
+  }
+};
+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);
+  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<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) :
+    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;
+};
+/// @brief Represents single configuration that has representing level, configuration type and a string based value.
+///
+/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes
+/// and will be parsed later.
+///
+/// Consider some examples below:
+///   * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true");
+///   * 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);
+  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;
+  };
+
+ 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);
+
+  /// @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 '_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);
+
+    /// @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 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.
+    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();
+      }
+    }
+    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);
+      }
+    }
+    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);
+  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;
+    }
+    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);
+    }
+    ++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) {
+    }
+    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);
+  }
+
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+#  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
+#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);
+
+#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&);
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+
+  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;
+};
+#endif // ELPP_ASYNC_LOGGING
+/// @brief Easylogging++ management storage
+class Storage : base::NoCopy, public base::threading::ThreadSafe {
+ public:
+#if ELPP_ASYNC_LOGGING
+  Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker);
+#else
+  explicit Storage(const LogBuilderPtr& defaultLogBuilder);
+#endif  // ELPP_ASYNC_LOGGING
+
+  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;
+  }
+#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;
+  }
+
+  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) {
+    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;
+#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::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);
+ 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);
+};
+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;
+};
+/// @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
+#endif  // defined(ELPP_STL_LOGGING)
+// 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());
+  }
+  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) {                                \
+return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+}
+#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp)                                                    \
+template <typename T1, typename T2>                                                              \
+inline MessageBuilder& operator<<(const temp<T1, T2>& template_inst) {                           \
+return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+}
+#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp)                                                  \
+template <typename T1, typename T2, typename T3>                                                 \
+inline MessageBuilder& operator<<(const temp<T1, T2, T3>& template_inst) {                       \
+return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+}
+#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp)                                                   \
+template <typename T1, typename T2, typename T3, typename T4>                                    \
+inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4>& template_inst) {                   \
+return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+}
+#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp)                                                   \
+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>
+  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>
+  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)
+#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
+#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) ? \
+ELPP_LITERAL("\n    ") : ELPP_LITERAL(", ");\
+ContainerType::const_iterator elem = container.begin();\
+ContainerType::const_iterator endElem = container.end();\
+std::size_t size_ = container.SizeMethod; \
+ss << ELPP_LITERAL("[");\
+for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \
+ss << ElementInstance;\
+ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\
+}\
+if (elem != endElem) {\
+ss << ELPP_LITERAL("...");\
+}\
+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_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)
+#endif  // defined(ELPP_WXWIDGETS_LOGGING)
+  // 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>
+  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;
+  }
+
+  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;
+    }
+#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;
+  }
+
+  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;
+        }
+      }
+    }
+    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(""));
+      releaseLock();
+    }
+  } 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) {
+  acquireLock(); // released in Writer!
+  log_(level, 0, s, value, args...);
+}
+template <typename T>
+inline void Logger::log(Level level, const T& log) {
+  acquireLock(); // released in Writer!
+  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) {
+  acquireLock(); // released in Writer!
+  log_(el::Level::Verbose, vlevel, s, value, args...);
+}
+template <typename T>
+inline void Logger::verbose(int vlevel, const T& log) {
+  acquireLock(); // released in Writer!
+  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...);\
+}\
+template <typename T>\
+inline void Logger::FUNCTION_NAME(const T& value) {\
+log(LOG_LEVEL, value);\
+}
+#  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;\
+}\
+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
+#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__,\
+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
+#endif // ELPP_COMPILER_MSVC
+#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \
+writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#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, ...) \
+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, ...) \
+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, ...) \
+ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \
+writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
+class PerformanceTrackingData {
+ public:
+  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("'");
+        }
+        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);
+}
+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) {
+    }
+    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
+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) {
+#if defined(ELPP_SYSLOG)
+    openlog(processIdent, options, facility);
+#else
+    ELPP_UNUSED(processIdent);
+    ELPP_UNUSED(options);
+    ELPP_UNUSED(facility);
+#endif  // defined(ELPP_SYSLOG)
+  }
+  virtual ~SysLogInitializer(void) {
+#if defined(ELPP_SYSLOG)
+    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 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());
+#else
+    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 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
+#else
+#  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();
+/// @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_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();
+/// @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)
+#undef ELPP_COUNTER
+#undef ELPP_COUNTER_POS
+/// @brief Gets hit counter for file/line
+#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__))
+/// @brief Gets hit counter position for file/line, -1 if not registered yet
+#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts())
+// Undef levels to support LOG(LEVEL)
+#undef INFO
+#undef WARNING
+#undef DEBUG
+#undef ERROR
+#undef FATAL
+#undef TRACE
+#undef VERBOSE
+// Undef existing
+#undef CINFO
+#undef CWARNING
+#undef CDEBUG
+#undef CFATAL
+#undef CERROR
+#undef CTRACE
+#undef CVERBOSE
+#undef CINFO_IF
+#undef CWARNING_IF
+#undef CDEBUG_IF
+#undef CERROR_IF
+#undef CFATAL_IF
+#undef CTRACE_IF
+#undef CVERBOSE_IF
+#undef CINFO_EVERY_N
+#undef CWARNING_EVERY_N
+#undef CDEBUG_EVERY_N
+#undef CERROR_EVERY_N
+#undef CFATAL_EVERY_N
+#undef CTRACE_EVERY_N
+#undef CVERBOSE_EVERY_N
+#undef CINFO_AFTER_N
+#undef CWARNING_AFTER_N
+#undef CDEBUG_AFTER_N
+#undef CERROR_AFTER_N
+#undef CFATAL_AFTER_N
+#undef CTRACE_AFTER_N
+#undef CVERBOSE_AFTER_N
+#undef CINFO_N_TIMES
+#undef CWARNING_N_TIMES
+#undef CDEBUG_N_TIMES
+#undef CERROR_N_TIMES
+#undef CFATAL_N_TIMES
+#undef CTRACE_N_TIMES
+#undef CVERBOSE_N_TIMES
+// Normal logs
+#if ELPP_INFO_LOG
+#  define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#  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__)
+#else
+#  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__)
+#else
+#  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__)
+#else
+#  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__)
+#else
+#  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__)
+#else
+#  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(\
+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()
+#endif  // ELPP_VERBOSE_LOG
+// Conditional logs
+#if ELPP_INFO_LOG
+#  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()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#  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()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#  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()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#  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()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#  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()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#  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()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#  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()
+#endif  // ELPP_VERBOSE_LOG
+// Occasional logs
+#if ELPP_INFO_LOG
+#  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()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#  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()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#  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()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#  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()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#  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()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#  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()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#  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()
+#endif  // ELPP_VERBOSE_LOG
+// After N logs
+#if ELPP_INFO_LOG
+#  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()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#  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()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#  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()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#  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()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#  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()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#  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()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#  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()
+#endif  // ELPP_VERBOSE_LOG
+// N Times logs
+#if ELPP_INFO_LOG
+#  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()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#  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()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#  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()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#  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()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#  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()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#  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()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#  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()
+#endif  // ELPP_VERBOSE_LOG
+//
+// Custom Loggers - Requires (level, dispatchAction, loggerId/s)
+//
+// undef existing
+#undef CLOG
+#undef CLOG_VERBOSE
+#undef CVLOG
+#undef CLOG_IF
+#undef CLOG_VERBOSE_IF
+#undef CVLOG_IF
+#undef CLOG_EVERY_N
+#undef CVLOG_EVERY_N
+#undef CLOG_AFTER_N
+#undef CVLOG_AFTER_N
+#undef CLOG_N_TIMES
+#undef CVLOG_N_TIMES
+// Normal logs
+#define CLOG(LEVEL, ...)\
+C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Conditional logs
+#define CLOG_IF(condition, LEVEL, ...)\
+C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_IF(condition, vlevel, ...)\
+CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Hit counts based logs
+#define CLOG_EVERY_N(n, LEVEL, ...)\
+C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_EVERY_N(n, vlevel, ...)\
+CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_AFTER_N(n, LEVEL, ...)\
+C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_AFTER_N(n, vlevel, ...)\
+CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_N_TIMES(n, LEVEL, ...)\
+C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_N_TIMES(n, vlevel, ...)\
+CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+//
+// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+// undef existing
+#undef LOG
+#undef VLOG
+#undef LOG_IF
+#undef VLOG_IF
+#undef LOG_EVERY_N
+#undef VLOG_EVERY_N
+#undef LOG_AFTER_N
+#undef VLOG_AFTER_N
+#undef LOG_N_TIMES
+#undef VLOG_N_TIMES
+#undef ELPP_CURR_FILE_LOGGER_ID
+#if defined(ELPP_DEFAULT_LOGGER)
+#  define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER
+#else
+#  define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId
+#endif
+#undef ELPP_TRACE
+#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID)
+// Normal logs
+#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Generic PLOG()
+#undef CPLOG
+#undef CPLOG_IF
+#undef PLOG
+#undef PLOG_IF
+#undef DCPLOG
+#undef DCPLOG_IF
+#undef DPLOG
+#undef DPLOG_IF
+#define CPLOG(LEVEL, ...)\
+C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CPLOG_IF(condition, LEVEL, ...)\
+C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG(LEVEL, ...)\
+if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG_IF(condition, LEVEL, ...)\
+C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+// Generic SYSLOG()
+#undef CSYSLOG
+#undef CSYSLOG_IF
+#undef CSYSLOG_EVERY_N
+#undef CSYSLOG_AFTER_N
+#undef CSYSLOG_N_TIMES
+#undef SYSLOG
+#undef SYSLOG_IF
+#undef SYSLOG_EVERY_N
+#undef SYSLOG_AFTER_N
+#undef SYSLOG_N_TIMES
+#undef DCSYSLOG
+#undef DCSYSLOG_IF
+#undef DCSYSLOG_EVERY_N
+#undef DCSYSLOG_AFTER_N
+#undef DCSYSLOG_N_TIMES
+#undef DSYSLOG
+#undef DSYSLOG_IF
+#undef DSYSLOG_EVERY_N
+#undef DSYSLOG_AFTER_N
+#undef DSYSLOG_N_TIMES
+#if defined(ELPP_SYSLOG)
+#  define CSYSLOG(LEVEL, ...)\
+C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#  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, ...)\
+C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__)
+#  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, ...)\
+if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#  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)
+#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()
+#endif  // defined(ELPP_SYSLOG)
+//
+// Custom Debug Only Loggers - Requires (level, loggerId/s)
+//
+// undef existing
+#undef DCLOG
+#undef DCVLOG
+#undef DCLOG_IF
+#undef DCVLOG_IF
+#undef DCLOG_EVERY_N
+#undef DCVLOG_EVERY_N
+#undef DCLOG_AFTER_N
+#undef DCVLOG_AFTER_N
+#undef DCLOG_N_TIMES
+#undef DCVLOG_N_TIMES
+// Normal logs
+#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__)
+#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__)
+#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__)
+// Conditional logs
+#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__)
+#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__)
+// Hit counts based logs
+#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__)
+//
+// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+#if !defined(ELPP_NO_DEBUG_MACROS)
+// undef existing
+#undef DLOG
+#undef DVLOG
+#undef DLOG_IF
+#undef DVLOG_IF
+#undef DLOG_EVERY_N
+#undef DVLOG_EVERY_N
+#undef DLOG_AFTER_N
+#undef DVLOG_AFTER_N
+#undef DLOG_N_TIMES
+#undef DVLOG_N_TIMES
+// Normal logs
+#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#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
+#undef CCHECK
+#undef CPCHECK
+#undef CCHECK_EQ
+#undef CCHECK_NE
+#undef CCHECK_LT
+#undef CCHECK_GT
+#undef CCHECK_LE
+#undef CCHECK_GE
+#undef CCHECK_BOUNDS
+#undef CCHECK_NOTNULL
+#undef CCHECK_STRCASEEQ
+#undef CCHECK_STRCASENE
+#undef CHECK
+#undef PCHECK
+#undef CHECK_EQ
+#undef CHECK_NE
+#undef CHECK_LT
+#undef CHECK_GT
+#undef CHECK_LE
+#undef CHECK_GE
+#undef CHECK_BOUNDS
+#undef CHECK_NOTNULL
+#undef CHECK_STRCASEEQ
+#undef CHECK_STRCASENE
+#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__)
+#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__)
+#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__)
+#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__)
+#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__)
+#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__)
+#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__)
+#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__)
+#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+<< "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+<< "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+<< "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+<< "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#undef DCCHECK
+#undef DCCHECK_EQ
+#undef DCCHECK_NE
+#undef DCCHECK_LT
+#undef DCCHECK_GT
+#undef DCCHECK_LE
+#undef DCCHECK_GE
+#undef DCCHECK_BOUNDS
+#undef DCCHECK_NOTNULL
+#undef DCCHECK_STRCASEEQ
+#undef DCCHECK_STRCASENE
+#undef DCPCHECK
+#undef DCHECK
+#undef DCHECK_EQ
+#undef DCHECK_NE
+#undef DCHECK_LT
+#undef DCHECK_GT
+#undef DCHECK_LE
+#undef DCHECK_GE
+#undef DCHECK_BOUNDS_
+#undef DCHECK_NOTNULL
+#undef DCHECK_STRCASEEQ
+#undef DCHECK_STRCASENE
+#undef DPCHECK
+#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__)
+#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__)
+#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__)
+#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__)
+#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__)
+#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__)
+#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__)
+#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__)
+#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL((ptr), __VA_ARGS__)
+#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__)
+#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__)
+#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#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 // defined(ELPP_NO_CHECK_MACROS)
+#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+#  define ELPP_USE_DEF_CRASH_HANDLER false
+#else
+#  define ELPP_USE_DEF_CRASH_HANDLER true
+#endif  // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+#define ELPP_CRASH_HANDLER_INIT
+#define ELPP_INIT_EASYLOGGINGPP(val) \
+namespace el { \
+namespace base { \
+el::base::type::StoragePointer elStorage(val); \
+} \
+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()))
+#else
+#  define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())))
+#endif  // ELPP_ASYNC_LOGGING
+#define INITIALIZE_NULL_EASYLOGGINGPP \
+namespace el {\
+namespace base {\
+el::base::type::StoragePointer elStorage;\
+}\
+el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+}
+#define SHARE_EASYLOGGINGPP(initializedStorage)\
+namespace el {\
+namespace base {\
+el::base::type::StoragePointer elStorage(initializedStorage);\
+}\
+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(""))
+#else
+#  define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv)
+#endif  // defined(ELPP_UNICODE)
+#endif // EASYLOGGINGPP_H

BIN
bin/LotRO_dat_extractor.exe


BIN
bin/LotRO_dat_patcher.exe


+ 51 - 35
include/DatFile.h

@@ -52,10 +52,23 @@ namespace LOTRO_DAT {
     };
 
     enum DAT_RESULT : int {
-        UNKNOWN_ERROR = 0,
+        //----BASE----///
+        FAILED = 0,
         SUCCESS = 1,
-        NOFILE_ERROR = 2,
-
+        //----TRUE/FALSE----//
+        FALSE = 0,
+        TRUE = 1,
+
+        INCORRECT_STATE_ERROR = -1,
+        NO_FILE_ERROR = -2,
+        WRITE_TO_FILE_ERROR = -3,
+
+        INCORRECT_DAT_ID = -4,
+        INCORRECT_SUPERBLOCK_ERROR = -5,
+        INIT_ERROR = -6,
+        DAT_READ_ERROR = -7,
+        DUBLICATE_PATCH_FILES_ERROR = -8,
+        INCORRECT_PATCH_FILE = -9,
     };
 
     enum DAT_STATE {
@@ -79,103 +92,106 @@ namespace LOTRO_DAT {
     public:
         DatFile();
 
-        bool InitDatFile(const std::string &filename, int dat_id);
+        DAT_RESULT InitDatFile(const std::string &filename, int dat_id);
 
         DAT_STATE DatFileState() const;
 
         ~DatFile();
 
-        bool ExtractFile(long long file_id, const std::string &path = "");
+        DAT_RESULT ExtractFile(long long file_id, const std::string &path = "");
 
-        bool ExtractFile(long long file_id, Database *db);
+        DAT_RESULT ExtractFile(long long file_id, Database *db);
 
         int ExtractAllFilesByType(FILE_TYPE type, std::string path = "");
 
         int ExtractAllFilesByType(FILE_TYPE type, Database *db);
 
-        bool PatchFile(const char *filename, YAML::Node options);
+        DAT_RESULT PatchFile(const char *filename, YAML::Node options);
 
-        bool PatchFile(const SubfileData &data, bool rewrite_original = false);
+        DAT_RESULT PatchFile(const SubfileData &data, bool rewrite_original = false);
 
-        bool PatchAllDatabase(Database *db);
+        DAT_RESULT PatchAllDatabase(Database *db);
 
-        void WriteUnorderedDictionary(std::string path) const;
+        DAT_RESULT WriteUnorderedDictionary(std::string path) const;
 
         long long files_number() const;
 
+        const std::string& filename() const;
+
         BinaryData GetFileData(const Subfile *file, long long offset = 0);
 
-        bool CommitChanges();
+        DAT_RESULT CommitChanges();
 
-        bool CloseDatFile();
+        DAT_RESULT CloseDatFile();
 
     private:
         // INIT SECTION
-        void OpenDatFile(const char *dat_name);
+        DAT_RESULT OpenDatFile(const char *dat_name);
 
-        void ReadSuperBlock();
+        DAT_RESULT ReadSuperBlock();
 
-        void MakeDirectories();
+        DAT_RESULT MakeDirectories();
 
-        void MakeDictionary();
+        DAT_RESULT MakeDictionary();
 
         // READ-WRITE SECTION
 
-        void ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
+        DAT_RESULT ReadData(BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
 
-        void WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
+        DAT_RESULT WriteData(const BinaryData &data, long long size, long long offset = 0, long long data_offset = 0);
 
         // PATCH SECTION
 
-        void ApplyFilePatch(Subfile *file, const BinaryData &data, bool rewrite_original = false);
+        DAT_RESULT ApplyFilePatch(Subfile *file, const BinaryData &data, bool rewrite_original = false);
 
         std::vector<std::pair<long long, long long> > GetFragmentationJournal();
 
-        void UpdateHeader();
+        DAT_RESULT UpdateHeader();
 
-        void UpdateSubdirectories();
+        DAT_RESULT UpdateSubdirectories();
 
-        void UpdateFragmentationJournal(const std::vector<std::pair<long long, long long> > &journal);
+        DAT_RESULT UpdateFragmentationJournal(const std::vector<std::pair<long long, long long> > &journal);
 
         // LOCALE MANAGING SECTION
     private:
-        void InitLocales();
+        DAT_RESULT InitLocales();
 
-        void CommitLocales();
+        DAT_RESULT CommitLocales();
 
     public:
-        void SetLocale(LOCALE locale);
+        DAT_RESULT SetLocale(LOCALE locale);
 
         bool CheckIfUpdatedByGame();
 
-        void RepairPatches(Database *db);
+        DAT_RESULT RepairPatches(Database *db);
 
-        void FinishRepairingPatches();
+        DAT_RESULT FinishRepairingPatches();
 
         LOCALE current_locale();
 
-        void EnableCategory(int category);
+        DAT_RESULT EnableCategory(int category);
 
-        void DisableCategory(int category);
+        DAT_RESULT DisableCategory(int category);
 
-        const std::unordered_set<long long>& GetInactiveCategoriesList();
+        const std::set<long long>& GetInactiveCategoriesList();
 
     public:
         bool CheckIfNotPatched();
+
         bool CheckIfPatchedByOldLauncher();
 
     private:
-        std::unordered_map<long long, Subfile*>* GetLocaleDictReference(LOCALE locale);
+        std::map<long long, Subfile*>* GetLocaleDictReference(LOCALE locale);
 
     private:
         LOCALE current_locale_;
 
         std::string filename_;
 
-        std::unordered_map<long long, Subfile*> orig_dict_;
-        std::unordered_map<long long, Subfile*> patch_dict_;
-        std::unordered_set<long long> pending_patch_;
-        std::unordered_set<long long> inactive_categories;
+        std::map<long long, Subfile*> orig_dict_;
+        std::map<long long, Subfile*> patch_dict_;
+        std::set<long long> pending_patch_;
+        std::set<long long> inactive_categories;
 
     private:
         FILE *file_handler_;

BIN
lib/libLotroDat.dll.a


BIN
lib/libLotroDat_static.a


Plik diff jest za duży
+ 350 - 305
src/DatFile.cpp


+ 242 - 94
src/Examples/extractor_example.cpp

@@ -13,129 +13,277 @@
 
 #include "LotroDat.h"
 using namespace LOTRO_DAT;
+using namespace std;
 
+// Change these variables to true if you want export category to files.
+bool exportImagesToFiles = false;
+bool exportFontsToFiles = false;
+bool exportSoundsToFiles = false;
+bool exportTexturesToFiles = true;
+bool exportUnknownToFiles = false;
 
-// Change these 2 variables to your path and name of .dat file
-const std::string path = "";//"E:\\SteamLibrary\\steamapps\\common\\";//Lord Of The Rings Online\\";
-const std::string filename = "client_local_English.dat";
-
-// Change these variables to true if you want export catecory to files.
-const bool exportImagesToFiles = false;
-const bool exportFontsToFiles = false;
-const bool exportSoundsToFiles = false;
-const bool exportTexturesToFiles = false;
-const bool exportUnknownToFiles = true;
-
-// Change these variables to true if you want export catecory to databases.
-const bool exportTextsToDb = false;
-const bool exportImagesToDb = false;
-const bool exportFontsToDb = false;
-const bool exportSoundsToDb = false;
-const bool exportTexturesToDb = false;
-const bool exportUnknownToDb = false;
+// Change these variables to true if you want export category to databases.
+bool exportTextsToDb = false;
+bool exportImagesToDb = false;
+bool exportFontsToDb = false;
+bool exportSoundsToDb = false;
+bool exportTexturesToDb = false;
+bool exportUnknownToDb = false;
 // There is no need to change anything else
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat extractor ver. 3.3.0" << std::endl;
-    const clock_t begin_time = clock();
+    std::cout << "Gi1dor's LotRO .dat extractor ver. 4.0.0" << std::endl;
+    freopen("extractor_errors.log", "w", stderr);
 
-    mkdir("Extracted data", 744);
-
-    std::time_t result = std::time(nullptr);
-    char *ttime = std::asctime(std::localtime(&result));
-    auto *out_time = new char[25];
-    memcpy(out_time, ttime, 24);
-    out_time[24] = '\0';
-
-    std::string output_dir = std::string("Extracted data\\") + filename + " " + std::string(out_time) + "\\";
-    std::replace(output_dir.begin(), output_dir.end(), ':', '-');
-
-    mkdir(output_dir.c_str(), 744);
-    freopen((output_dir + std::string("errors.log")).c_str(), "w", stderr);
     setbuf(stdout, NULL);
     setbuf(stderr, NULL);
+
     setvbuf (stdout, NULL, _IONBF, BUFSIZ);
     setvbuf (stderr, NULL, _IONBF, BUFSIZ);
 
-    std::cout << "Starting search...\n" << std::flush;
+    std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
+            "if you write path to it (with name of file) in file \"dat_file_path.txt\"\n";
+    DatFile file;
+
+    ifstream in("dat_file_path.txt");
+    if (!in.fail()) {
+        std::string filename;
+        getline(in, filename);
 
-    DatFile a;
+        std::cout << "Using .dat file from dat_file_path.txt...\n";
+        std::cout << "Opening file " << filename << std::endl;
 
-    if (!a.InitDatFile(path + filename, 0)) {
-        std::cout << "Unable to initialize .dat file in " << path + filename << std::endl;
-        system("pause");
-        return 0;
+        if (file.InitDatFile(filename, 0) == false) {
+            std::cout << "Dat file path from dat_file_path.txt - " << filename << " may be incorrect (cannot open "
+                    "DatFile due to error. See it in errors.log)\n";
+            file.CloseDatFile();
+        }
     }
 
-    std::cout << "Total files found: " << a.files_number() << std::endl << std::flush;
-    a.WriteUnorderedDictionary(output_dir);
-    std::cout << "Beginning unpacking... Please, wait for some minutes."
-    "\nMaybe it's a good idea to have a cup of tea, while unpacker is working...\n" << std::flush;
+    while (file.DatFileState() == CLOSED) {
+        std::cout << "Please, tell, where the .dat file is\n";
+        std::cout << "Enter path to file (including filename): ";
+        std::string filename;
+        std::getline(std::cin, filename);
 
-    Database output_db;
+        std::cout << "Opening file " << filename << std::endl;
 
-    if (exportImagesToDb) {
-        output_db.InitDatabase(output_dir + std::string("Images.db"));
-        std::cout << "Extracted " << a.ExtractAllFilesByType(JPG, &output_db) << " .jpg files to Images.db" << std::endl << std::flush;
-        output_db.CloseDatabase();
+        if (file.InitDatFile(filename, 0) == false) {
+            std::cout << "Some error caused while opening the file... "
+                    "Could you enter .dat filename once more?" << std::endl;
+            file.CloseDatFile();
+        }
     }
 
-    if (exportSoundsToDb) {
-        output_db.InitDatabase(output_dir + std::string("Sounds.db"));
-        std::cout << "Extracted " << a.ExtractAllFilesByType(WAV, &output_db) << " .wav files to Sounds.db" << std::endl << std::flush;
-        std::cout << "Extracted " << a.ExtractAllFilesByType(OGG, &output_db) << " .ogg files to Sounds.db" << std::endl << std::flush;
-        output_db.CloseDatabase();
-    }
+    std::cout << "Great! File initialised successfully!\n";
+    if (file.CheckIfNotPatched())
+        std::cout << "MESSAGE: Dat file is new and haven't been patched yet\n";
 
-    if (exportTextsToDb) {
-        output_db.InitDatabase(output_dir + std::string("Texts.db"));
-        std::cout << "Extracted " << a.ExtractAllFilesByType(TEXT, &output_db) << " text files to Texts.db" << std::endl << std::flush;
-        output_db.CloseDatabase();
-    }
+    if (file.CheckIfPatchedByOldLauncher())
+        std::cout << "MESSAGE: Dat file was patched by old launcher. Capability isn't guaranteed! Some functions may not work properly!!!\n";
 
-    if (exportFontsToDb) {
-        output_db.InitDatabase(output_dir + std::string("Fonts.db"));
-        std::cout << "Extracted " << a.ExtractAllFilesByType(FONT, &output_db) << " font files to Fonts.db" << std::endl << std::flush;
-        output_db.CloseDatabase();
-    }
+    if (file.CheckIfUpdatedByGame())
+        std::cout << "MESSAGE: .dat file was updated by game! Need to repair patches with functions RepairPatches() and FinishRepairingPatches()\n";
 
-    if (exportTexturesToDb) {
-        output_db.InitDatabase(output_dir + std::string("Textures.db"));
-        std::cout << "Extracted " << a.ExtractAllFilesByType(DDS, &output_db) << " .dds files to Textures.db" << std::endl << std::flush;
-        output_db.CloseDatabase();
-    }
+    std::cout << "Files number: " << file.files_number() << std::endl;
 
-    if (exportImagesToFiles) {
-        mkdir((output_dir + "jpg").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(JPG, output_dir + "jpg\\") << " .jpg files to directory" << std::endl << std::flush;
-    }
+    int cmd = 0;
+    while (true) {
+        std::cout << "Please, choose, what should I do. I can extract .dat file to files (press 1), "
+                    "open another .dat file (press 2), choose, what to extract or exit (press -1)\n";
 
-    if (exportTexturesToFiles) {
-        mkdir((output_dir + "dds").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(DDS, output_dir + "dds\\") << " .dds files to directory" << std::endl << std::flush;
-    }
+        if (cmd != 3) {
+            std::cout << "Enter number of command (1-2): ";
+            std::cin >> cmd;
+        }
 
-    if (exportSoundsToFiles) {
-        mkdir((output_dir + "wav").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(WAV, output_dir + "wav\\") << " .wav files to directory" << std::endl << std::flush;
-        mkdir((output_dir + "ogg").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(OGG, output_dir + "ogg\\") << " .ogg files to directory" << std::endl << std::flush;
-    }
+        std::string tmp;
+        std::getline(std::cin, tmp);
 
-    if (exportFontsToFiles) {
-        mkdir((output_dir + "fonts").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(FONT, output_dir + "fonts\\") << " font files to directory" << std::endl << std::flush;
-    }
+        if (cmd == -1) {
+            std::cout << "Exiting. Thanks for using me!\n";
+            break;
+        }
 
-    if (exportUnknownToFiles) {
-        mkdir((output_dir + "unknown").c_str(), 744);
-        std::cout << "Extracted " << a.ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\") << " unknown files to directory" << std::endl << std::flush;
-    }
+        if (cmd == 1) {
+            const clock_t begin_time = clock();
+
+            mkdir("Extracted data", 744);
+
+            std::time_t result = std::time(nullptr);
+            char *ttime = std::asctime(std::localtime(&result));
+            auto *out_time = new char[25];
+            memcpy(out_time, ttime, 24);
+            out_time[24] = '\0';
+
+            std::string output_dir = std::string("Extracted data\\") + file.filename().substr(file.filename().length() - 16, 16) + " " + std::string(out_time) + "\\";
+            std::replace(output_dir.begin(), output_dir.end(), ':', '-');
+
+            mkdir(output_dir.c_str(), 744);
+            
+            std::cout << "Total files found: " << file.files_number() << std::endl << std::flush;
+            file.WriteUnorderedDictionary(output_dir);
+            std::cout << "Beginning unpacking... Please, wait for some minutes."
+                    "\nMaybe it's a good idea to have a cup of tea, while unpacker is working...\n" << std::flush;
+
+            Database output_db;
+
+            if (exportImagesToDb) {
+                output_db.InitDatabase(output_dir + std::string("Images.db"));
+                std::cout << "Extracted " << file.ExtractAllFilesByType(JPG, &output_db) << " .jpg files to Images.db" << std::endl << std::flush;
+                output_db.CloseDatabase();
+            }
+
+            if (exportSoundsToDb) {
+                output_db.InitDatabase(output_dir + std::string("Sounds.db"));
+                std::cout << "Extracted " << file.ExtractAllFilesByType(WAV, &output_db) << " .wav files to Sounds.db" << std::endl << std::flush;
+                std::cout << "Extracted " << file.ExtractAllFilesByType(OGG, &output_db) << " .ogg files to Sounds.db" << std::endl << std::flush;
+                output_db.CloseDatabase();
+            }
+
+            if (exportTextsToDb) {
+                output_db.InitDatabase(output_dir + std::string("Texts.db"));
+                std::cout << "Extracted " << file.ExtractAllFilesByType(TEXT, &output_db) << " text files to Texts.db" << std::endl << std::flush;
+                output_db.CloseDatabase();
+            }
 
-    a.CloseDatFile();
+            if (exportFontsToDb) {
+                output_db.InitDatabase(output_dir + std::string("Fonts.db"));
+                std::cout << "Extracted " << file.ExtractAllFilesByType(FONT, &output_db) << " font files to Fonts.db" << std::endl << std::flush;
+                output_db.CloseDatabase();
+            }
 
-    fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
-            float(clock() - begin_time) / CLOCKS_PER_SEC);
+            if (exportTexturesToDb) {
+                output_db.InitDatabase(output_dir + std::string("Textures.db"));
+                std::cout << "Extracted " << file.ExtractAllFilesByType(DDS, &output_db) << " .dds files to Textures.db" << std::endl << std::flush;
+                output_db.CloseDatabase();
+            }
+
+            if (exportImagesToFiles) {
+                mkdir((output_dir + "jpg").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(JPG, output_dir + "jpg\\") << " .jpg files to directory" << std::endl << std::flush;
+            }
+
+            if (exportTexturesToFiles) {
+                mkdir((output_dir + "dds").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(DDS, output_dir + "dds\\") << " .dds files to directory" << std::endl << std::flush;
+            }
+
+            if (exportSoundsToFiles) {
+                mkdir((output_dir + "wav").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(WAV, output_dir + "wav\\") << " .wav files to directory" << std::endl << std::flush;
+                mkdir((output_dir + "ogg").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(OGG, output_dir + "ogg\\") << " .ogg files to directory" << std::endl << std::flush;
+            }
+
+            if (exportFontsToFiles) {
+                mkdir((output_dir + "fonts").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(FONT, output_dir + "fonts\\") << " font files to directory" << std::endl << std::flush;
+            }
+
+            if (exportUnknownToFiles) {
+                mkdir((output_dir + "unknown").c_str(), 744);
+                std::cout << "Extracted " << file.ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\") << " unknown files to directory" << std::endl << std::flush;
+            }
+
+            file.CloseDatFile();
+
+            fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
+                    float(clock() - begin_time) / CLOCKS_PER_SEC);
+        }
+
+        if (cmd == 2) {
+            std::cout << "Closing file...\n";
+            if (!file.CloseDatFile())
+                std::cout << "An error occured while closing the file!!!\n";
+
+            while (file.DatFileState() == CLOSED) {
+                std::cout << "Please, tell, where the .dat file is\n";
+                std::cout << "Enter path to file (including filename): ";
+                std::string filename;
+                std::getline(std::cin, filename);
+
+                std::cout << "Opening file " << filename << std::endl;
+
+                if (file.InitDatFile(filename, 0) == false) {
+                    std::cout << "Some error caused while opening the file... "
+                            "Could you enter .dat filename once more?" << std::endl;
+                    file.CloseDatFile();
+                }
+            }
+        }
+
+        if (cmd == 3) {
+            std::cout << "Current parameters:\n";
+            std::cout << "(01) Export texts to database - " << (exportTextsToDb ? "true\n" : "false\n");
+
+            std::cout << "(11) Export images to files - " << (exportImagesToFiles ? "true\n" : "false\n");
+            std::cout << "(12) Export images to database - " << (exportImagesToDb ? "true\n" : "false\n");
+
+            std::cout << "(21) Export fonts to files - " << (exportFontsToFiles ? "true\n" : "false\n");
+            std::cout << "(22) Export fonts to database - " << (exportFontsToDb ? "true\n" : "false\n");
+
+            std::cout << "(31) Export sounds to files - " << (exportSoundsToFiles ? "true\n" : "false\n");
+            std::cout << "(32) Export sounds to database - " << (exportSoundsToDb ? "true\n" : "false\n");
+
+            std::cout << "(41) Export textures to files - " << (exportTexturesToFiles ? "true\n" : "false\n");
+            std::cout << "(42) Export textures to database - " << (exportTexturesToDb ? "true\n" : "false\n");
+
+            std::cout << "(51) Export unknown files to files - " << (exportUnknownToFiles ? "true\n" : "false\n");
+            std::cout << "(52) Export unknown files to database - " << (exportUnknownToDb ? "true\n" : "false\n");
+
+            std::cout << "Enter number of parameter to change (or -1 to exit this menu): ";
+
+            int number = 0;
+
+            std::cin >> number;
+            std::string tmp;
+            std::getline(std::cin, tmp);
+
+            if (number == -1) {
+                std::cout << "Returning to main menu..\n";
+            } else {
+                switch (number) {
+                    case 01:
+                        exportTextsToDb = !exportTextsToDb;
+                        break;
+                    case 11:
+                        exportImagesToFiles = !exportImagesToFiles;
+                        break;
+                    case 12:
+                        exportImagesToDb = !exportImagesToDb;
+                        break;
+                    case 21:
+                        exportFontsToFiles = !exportFontsToFiles;
+                        break;
+                    case 22:
+                        exportFontsToDb = !exportFontsToDb;
+                        break;
+                    case 31:
+                        exportSoundsToFiles = !exportSoundsToFiles;
+                        break;
+                    case 32:
+                        exportSoundsToDb = !exportSoundsToDb;
+                        break;
+                    case 41:
+                        exportTexturesToFiles = !exportTexturesToFiles;
+                        break;
+                    case 42:
+                        exportTexturesToDb = !exportTexturesToDb;
+                        break;
+                    case 51:
+                        exportUnknownToFiles = !exportTexturesToFiles;
+                        break;
+                    case 52:
+                        exportUnknownToDb = !exportTexturesToDb;
+                        break;
+                    default:
+                        std::cout << "Incorrect number. Returning to main menu..\n";
+                }
+            }
+        }
+    }
+    file.CloseDatFile();
     system("pause");
     return 0;
 }

+ 2 - 2
src/Examples/patcher_example.cpp

@@ -16,8 +16,8 @@ using namespace LOTRO_DAT;
 using namespace std;
 
 int main() {
-    std::cout << "Gi1dor's LotRO .dat patcher ver. 3.3.0" << std::endl;
-    freopen("errors.log", "w", stderr);
+    std::cout << "Gi1dor's LotRO .dat patcher ver. 4.0.0" << std::endl;
+    freopen("patcher_errors.log", "w", stderr);
 
     setbuf(stdout, NULL);
     setbuf(stderr, NULL);

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików