::value, void>::type
+ safeDelete(T*& pointer) {
+ if (pointer == nullptr)
+ return;
+ delete pointer;
+ pointer = nullptr;
+ }
+ /// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned
+ static inline const char* charPtrVal(const char* pointer) {
+ return pointer == nullptr ? base::consts::kNullPointer : pointer;
+ }
+ /// @brief Aborts application due with user-defined status
+ static inline void abort(int status, const std::string& reason = std::string()) {
+ // Both status and reason params are there for debugging with tools like gdb etc
+ ELPP_UNUSED(status);
+ ELPP_UNUSED(reason);
+#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+ // Ignore msvc critical error dialog - break instead (on debug mode)
+ _asm int 3
+#else
+ ::abort();
+#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+ }
+ /// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation
+ /// Use these function as flag = bitwise::Or(MyEnum::val1, flag);
+ namespace bitwise {
+ template
+ static inline base::type::EnumType And(Enum e, base::type::EnumType flag) {
+ return static_cast(flag) & static_cast(e);
+ }
+ template
+ static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) {
+ return static_cast(flag) & ~(static_cast(e));
+ }
+ template
+ static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) {
+ return static_cast(flag) | static_cast(e);
+ }
+ } // namespace bitwise
+ template
+ static inline void addFlag(Enum e, base::type::EnumType* flag) {
+ *flag = base::utils::bitwise::Or(e, *flag);
+ }
+ template
+ static inline void removeFlag(Enum e, base::type::EnumType* flag) {
+ *flag = base::utils::bitwise::Not(e, *flag);
+ }
+ template
+ static inline bool hasFlag(Enum e, base::type::EnumType flag) {
+ return base::utils::bitwise::And(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::mutex
+ class Mutex : base::NoCopy {
+ public:
+ Mutex(void) {
+# if ELPP_OS_UNIX
+ pthread_mutex_init(&m_underlyingMutex, nullptr);
+# 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
+ class ScopedLock : base::NoCopy {
+ public:
+ explicit ScopedLock(M& mutex) {
+ m_mutex = &mutex;
+ m_mutex->lock();
+ }
+
+ virtual ~ScopedLock(void) {
+ m_mutex->unlock();
+ }
+ private:
+ M* m_mutex;
+ ScopedLock(void);
+ };
+ } // namespace internal
+ /// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned.
+ static inline std::string getCurrentThreadId(void) {
+ std::stringstream ss;
+# if (ELPP_OS_WINDOWS)
+ ss << GetCurrentThreadId();
+# endif // (ELPP_OS_WINDOWS)
+ return ss.str();
+ }
+ static inline void msleep(int) {
+ // No implementation for non std::thread version
+ }
+ typedef base::threading::internal::Mutex Mutex;
+ typedef base::threading::internal::ScopedLock ScopedLock;
+# else
+ /// @brief Gets ID of currently running threading using std::this_thread::get_id()
+ static inline std::string getCurrentThreadId(void) {
+ std::stringstream ss;
+ ss << std::this_thread::get_id();
+ return ss.str();
+ }
+ static inline void msleep(int ms) {
+ // Only when async logging enabled - this is because async is strict on compiler
+# if ELPP_ASYNC_LOGGING
+# if defined(ELPP_NO_SLEEP_FOR)
+ usleep(ms * 1000);
+# else
+ std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+# endif // defined(ELPP_NO_SLEEP_FOR)
+# else
+ ELPP_UNUSED(ms);
+# endif // ELPP_ASYNC_LOGGING
+ }
+ typedef std::mutex Mutex;
+ typedef std::lock_guard 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
+ class NoScopedLock : base::NoCopy {
+ public:
+ explicit NoScopedLock(Mutex&) {
+ }
+ virtual ~NoScopedLock(void) {
+ }
+ private:
+ NoScopedLock(void);
+ };
+ } // namespace internal
+ static inline std::string getCurrentThreadId(void) {
+ return std::string();
+ }
+ static inline void msleep(int) {
+ // No custom implementation
+ }
+ typedef base::threading::internal::NoMutex Mutex;
+ typedef base::threading::internal::NoScopedLock 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;
+ };
+ } // namespace threading
+ namespace utils {
+ class File : base::StaticClass {
+ public:
+ /// @brief Creates new out file stream for specified filename.
+ /// @return Pointer to newly created fstream or nullptr
+ static base::type::fstream_t* newFileStream(const std::string& filename) {
+ base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(),
+ base::type::fstream_t::out
+#if !defined(ELPP_FRESH_LOG_FILE)
+ | base::type::fstream_t::app
+#endif
+ );
+#if defined(ELPP_UNICODE)
+ std::locale elppUnicodeLocale("");
+# if ELPP_OS_WINDOWS
+ std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16);
+ elppUnicodeLocale = elppUnicodeLocaleWindows;
+# endif // ELPP_OS_WINDOWS
+ fs->imbue(elppUnicodeLocale);
+#endif // defined(ELPP_UNICODE)
+ if (fs->is_open()) {
+ fs->flush();
+ } else {
+ base::utils::safeDelete(fs);
+ ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
+ }
+ return fs;
+ }
+
+ /// @brief Gets size of file provided in stream
+ static std::size_t getSizeOfFile(base::type::fstream_t* fs) {
+ if (fs == nullptr) {
+ return 0;
+ }
+ std::streampos currPos = fs->tellg();
+ fs->seekg(0, fs->end);
+ std::size_t size = static_cast(fs->tellg());
+ fs->seekg(currPos);
+ return size;
+ }
+
+ /// @brief Determines whether or not provided path exist in current file system
+ static inline bool pathExists(const char* path, bool considerFile = false) {
+ if (path == nullptr) {
+ return false;
+ }
+#if ELPP_OS_UNIX
+ ELPP_UNUSED(considerFile);
+ struct stat st;
+ return (stat(path, &st) == 0);
+#elif ELPP_OS_WINDOWS
+ DWORD fileType = GetFileAttributesA(path);
+ if (fileType == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
+#endif // ELPP_OS_UNIX
+ }
+
+ /// @brief Creates specified path on file system
+ /// @param path Path to create.
+ static bool createPath(const std::string& path) {
+ if (path.empty()) {
+ return false;
+ }
+ if (base::utils::File::pathExists(path.c_str())) {
+ return true;
+ }
+ int status = -1;
+
+ char* currPath = const_cast(path.c_str());
+ std::string builtPath = std::string();
+#if ELPP_OS_UNIX
+ if (path[0] == '/') {
+ builtPath = "/";
+ }
+ currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+ // Use secure functions API
+ char* nextTok_ = nullptr;
+ currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
+ ELPP_UNUSED(nextTok_);
+#endif // ELPP_OS_UNIX
+ while (currPath != nullptr) {
+ builtPath.append(currPath);
+ builtPath.append(base::consts::kFilePathSeperator);
+#if ELPP_OS_UNIX
+ status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
+ currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+ status = _mkdir(builtPath.c_str());
+ currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
+#endif // ELPP_OS_UNIX
+ }
+ if (status == -1) {
+ ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
+ return false;
+ }
+ return true;
+ }
+ /// @brief Extracts path of filename with leading slash
+ static std::string extractPathFromFilename(const std::string& fullPath,
+ const char* seperator = base::consts::kFilePathSeperator) {
+ if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) {
+ return fullPath;
+ }
+ std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+ if (lastSlashAt == 0) {
+ return std::string(seperator);
+ }
+ return fullPath.substr(0, lastSlashAt + 1);
+ }
+ /// @brief builds stripped filename and puts it in buff
+ static void buildStrippedFilename(const char* filename, char buff[],
+ std::size_t limit = base::consts::kSourceFilenameMaxLength) {
+ std::size_t sizeOfFilename = strlen(filename);
+ if (sizeOfFilename >= limit) {
+ filename += (sizeOfFilename - limit);
+ if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
+ filename += 3; // 3 = '..'
+ STRCAT(buff, "..", limit);
+ }
+ }
+ STRCAT(buff, filename, limit);
+ }
+ /// @brief builds base filename and puts it in buff
+ static void buildBaseFilename(const std::string& fullPath, char buff[],
+ std::size_t limit = base::consts::kSourceFilenameMaxLength,
+ const char* seperator = base::consts::kFilePathSeperator) {
+ const char *filename = fullPath.c_str();
+ std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+ filename += lastSlashAt ? lastSlashAt+1 : 0;
+ std::size_t sizeOfFilename = strlen(filename);
+ if (sizeOfFilename >= limit) {
+ filename += (sizeOfFilename - limit);
+ if (filename[0] != '.' && filename[1] != '.') { // prepend if not already
+ filename += 3; // 3 = '..'
+ STRCAT(buff, "..", limit);
+ }
+ }
+ STRCAT(buff, filename, limit);
+ }
+ };
+ /// @brief String utilities helper class used internally. You should not use it.
+ class Str : base::StaticClass {
+ public:
+ /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues.
+ static inline bool isDigit(char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ /// @brief Matches wildcards, '*' and '?' only supported.
+ static bool wildCardMatch(const char* str, const char* pattern) {
+ while (*pattern) {
+ switch (*pattern) {
+ case '?':
+ if (!*str)
+ return false;
+ ++str;
+ ++pattern;
+ break;
+ case '*':
+ if (wildCardMatch(str, pattern + 1))
+ return true;
+ if (*str && wildCardMatch(str + 1, pattern))
+ return true;
+ return false;
+ default:
+ if (*str++ != *pattern++)
+ return false;
+ break;
+ }
+ }
+ return !*str && !*pattern;
+ }
+
+ /// @brief Trims string from start
+ /// @param [in,out] str String to trim
+ static inline std::string& ltrim(std::string& str) {
+ str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun(&std::isspace))));
+ return str;
+ }
+
+ /// @brief Trim string from end
+ /// @param [in,out] str String to trim
+ static inline std::string& rtrim(std::string& str) {
+ str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun(&std::isspace))).base(), str.end());
+ return str;
+ }
+
+ /// @brief Trims string from left and right
+ /// @param [in,out] str String to trim
+ static inline std::string& trim(std::string& str) {
+ return ltrim(rtrim(str));
+ }
+
+ /// @brief Determines whether or not str starts with specified string
+ /// @param str String to check
+ /// @param start String to check against
+ /// @return Returns true if starts with specified string, false otherwise
+ static inline bool startsWith(const std::string& str, const std::string& start) {
+ return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
+ }
+
+ /// @brief Determines whether or not str ends with specified string
+ /// @param str String to check
+ /// @param end String to check against
+ /// @return Returns true if ends with specified string, false otherwise
+ static inline bool endsWith(const std::string& str, const std::string& end) {
+ return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
+ }
+
+ /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance.
+ /// @param [in,out] str String to replace from
+ /// @param replaceWhat Character to replace
+ /// @param replaceWith Character to replace with
+ /// @return Modified version of str
+ static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) {
+ std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
+ return str;
+ }
+
+ /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place
+ /// @param str String to replace from
+ /// @param replaceWhat Character to replace
+ /// @param replaceWith Character to replace with
+ /// @return Modified (original) str
+ static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT
+ const std::string& replaceWith) {
+ if (replaceWhat == replaceWith)
+ return str;
+ std::size_t foundAt = std::string::npos;
+ while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
+ str.replace(foundAt, replaceWhat.length(), replaceWith);
+ }
+ return str;
+ }
+
+ static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+ const base::type::string_t& replaceWith) {
+ std::size_t foundAt = base::type::string_t::npos;
+ while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
+ if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+ str.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+ ++foundAt;
+ } else {
+ str.replace(foundAt, replaceWhat.length(), replaceWith);
+ return;
+ }
+ }
+ }
+#if defined(ELPP_UNICODE)
+ static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+ const std::string& replaceWith) {
+ replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
+ }
+#endif // defined(ELPP_UNICODE)
+ /// @brief Converts string to uppercase
+ /// @param str String to convert
+ /// @return Uppercase string
+ static inline std::string& toUpper(std::string& str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+ return str;
+ }
+
+ /// @brief Compares cstring equality - uses strcmp
+ static inline bool cStringEq(const char* s1, const char* s2) {
+ if (s1 == nullptr && s2 == nullptr) return true;
+ if (s1 == nullptr || s2 == nullptr) return false;
+ return strcmp(s1, s2) == 0;
+ }
+
+ /// @brief Compares cstring equality (case-insensitive) - uses toupper(char)
+ /// Dont use strcasecmp because of CRT (VC++)
+ static bool cStringCaseEq(const char* s1, const char* s2) {
+ if (s1 == nullptr && s2 == nullptr) return true;
+ if (s1 == nullptr || s2 == nullptr) return false;
+ if (strlen(s1) != strlen(s2)) return false;
+ while (*s1 != '\0' && *s2 != '\0') {
+ if (::toupper(*s1) != ::toupper(*s2)) return false;
+ ++s1;
+ ++s2;
+ }
+ return true;
+ }
+
+ /// @brief Returns true if c exist in str
+ static inline bool contains(const char* str, char c) {
+ for (; *str; ++str) {
+ if (*str == c)
+ return true;
+ }
+ return false;
+ }
+
+ static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) {
+ char localBuff[10] = "";
+ char* p = localBuff + sizeof(localBuff) - 2;
+ if (n > 0) {
+ for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
+ *--p = static_cast(n % 10 + '0');
+ } else {
+ *--p = '0';
+ --len;
+ }
+ if (zeroPadded)
+ while (p > localBuff && len-- > 0) *--p = static_cast('0');
+ return addToBuff(p, buf, bufLim);
+ }
+
+ static inline char* addToBuff(const char* str, char* buf, const char* bufLim) {
+ while ((buf < bufLim) && ((*buf = *str++) != '\0'))
+ ++buf;
+ return buf;
+ }
+
+ static inline char* clearBuff(char buff[], std::size_t lim) {
+ STRCPY(buff, "", lim);
+ ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro
+ return buff;
+ }
+
+ /// @brief Converst wchar* to char*
+ /// NOTE: Need to free return value after use!
+ static char* wcharPtrToCharPtr(const wchar_t* line) {
+ std::size_t len_ = wcslen(line) + 1;
+ char* buff_ = static_cast(malloc(len_ + 1));
+# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+ std::wcstombs(buff_, line, len_);
+# elif ELPP_OS_WINDOWS
+ std::size_t convCount_ = 0;
+ mbstate_t mbState_;
+ ::memset(static_cast(&mbState_), 0, sizeof(mbState_));
+ wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
+# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+ return buff_;
+ }
+ };
+ /// @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 getenv(const char*) because of CRT deprecation
+ /// @param varname Variable name to get environment variable value for
+ /// @return If variable exist the value of it otherwise nullptr
+ static const char* 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
+ /// @brief Reads android property value
+ static inline std::string getProperty(const char* prop) {
+ char propVal[PROP_VALUE_MAX + 1];
+ int ret = __system_property_get(prop, propVal);
+ return ret == 0 ? std::string() : std::string(propVal);
+ }
+
+ /// @brief Reads android device name
+ static std::string getDeviceName(void) {
+ std::stringstream ss;
+ std::string manufacturer = getProperty("ro.product.manufacturer");
+ std::string model = getProperty("ro.product.model");
+ if (manufacturer.empty() || model.empty()) {
+ return std::string();
+ }
+ ss << manufacturer << "-" << model;
+ return ss.str();
+ }
+#endif // ELPP_OS_ANDROID
+
+ /// @brief Runs command on terminal and returns the output.
+ ///
+ /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned.
+ /// @param command Bash command
+ /// @return Result of bash output or empty string if no result found.
+ static const std::string getBashOutput(const char* command) {
+#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+ if (command == nullptr) {
+ return std::string();
+ }
+ FILE* proc = nullptr;
+ if ((proc = popen(command, "r")) == nullptr) {
+ ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
+ return std::string();
+ }
+ char hBuff[4096];
+ if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
+ pclose(proc);
+ if (hBuff[strlen(hBuff) - 1] == '\n') {
+ hBuff[strlen(hBuff) - 1] = '\0';
+ }
+ return std::string(hBuff);
+ }
+ return std::string();
+#else
+ ELPP_UNUSED(command);
+ return std::string();
+#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+ }
+
+ /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++)
+ /// @param variableName Environment variable name
+ /// @param defaultVal If no environment variable or value found the value to return by default
+ /// @param alternativeBashCommand If environment variable not found what would be alternative bash command
+ /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami'
+ static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) {
+#if ELPP_OS_UNIX
+ const char* val = getenv(variableName);
+#elif ELPP_OS_WINDOWS
+ const char* val = getWindowsEnvironmentVariable(variableName);
+#endif // ELPP_OS_UNIX
+ if ((val == nullptr) || ((strcmp(val, "") == 0))) {
+#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+ // Try harder on unix-based systems
+ std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
+ if (valBash.empty()) {
+ return std::string(defaultVal);
+ } else {
+ return valBash;
+ }
+#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
+ ELPP_UNUSED(alternativeBashCommand);
+ return std::string(defaultVal);
+#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+ }
+ return std::string(val);
+ }
+ /// @brief Gets current username.
+ static inline std::string currentUser(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
+#elif ELPP_OS_WINDOWS
+ return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
+#elif ELPP_OS_ANDROID
+ ELPP_UNUSED(base::consts::kUnknownUser);
+ return std::string("android");
+#else
+ return std::string();
+#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ }
+
+ /// @brief Gets current host name or computer name.
+ ///
+ /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen
+ static inline std::string currentHost(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
+#elif ELPP_OS_WINDOWS
+ return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
+#elif ELPP_OS_ANDROID
+ ELPP_UNUSED(base::consts::kUnknownHost);
+ return getDeviceName();
+#else
+ return std::string();
+#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+ }
+ /// @brief Whether or not terminal supports colors
+ static inline bool termSupportsColor(void) {
+ std::string term = getEnvironmentVariable("TERM", "");
+ return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
+ || term == "screen" || term == "linux" || term == "cygwin"
+ || term == "screen-256color";
+ }
+ };
+ extern std::string s_currentUser;
+ extern std::string s_currentHost;
+ extern bool s_termSupportsColor;
+#define ELPP_INITI_BASIC_DECLR \
+namespace el {\
+namespace base {\
+namespace utils {\
+std::string s_currentUser = el::base::utils::OS::currentUser(); \
+std::string s_currentHost = el::base::utils::OS::currentHost(); \
+bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \
+}\
+}\
+}
+ /// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str
+ class DateTime : base::StaticClass {
+ public:
+ /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond.
+ ///
+ /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided
+ /// @param [in,out] tv Pointer that gets updated
+ static void gettimeofday(struct timeval* tv) {
+#if ELPP_OS_WINDOWS
+ if (tv != nullptr) {
+# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+ const unsigned __int64 delta_ = 11644473600000000Ui64;
+# else
+ const unsigned __int64 delta_ = 11644473600000000ULL;
+# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+ const double secOffSet = 0.000001;
+ const unsigned long usecOffSet = 1000000;
+ FILETIME fileTime;
+ GetSystemTimeAsFileTime(&fileTime);
+ unsigned __int64 present = 0;
+ present |= fileTime.dwHighDateTime;
+ present = present << 32;
+ present |= fileTime.dwLowDateTime;
+ present /= 10; // mic-sec
+ // Subtract the difference
+ present -= delta_;
+ tv->tv_sec = static_cast(present * secOffSet);
+ tv->tv_usec = static_cast(present % usecOffSet);
+ }
+#else
+ ::gettimeofday(tv, nullptr);
+#endif // ELPP_OS_WINDOWS
+ }
+
+ /// @brief Gets current date and time with milliseconds.
+ /// @param format User provided date/time format
+ /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null)
+ /// @returns string based date time in specified format.
+ static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) {
+ struct timeval currTime;
+ gettimeofday(&currTime);
+ struct ::tm timeInfo;
+ buildTimeInfo(&currTime, &timeInfo);
+ const int kBuffSize = 30;
+ char buff_[kBuffSize] = "";
+ parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(currTime.tv_usec / msWidth->m_offset), msWidth);
+ return std::string(buff_);
+ }
+
+ /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc
+ static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
+ double result = static_cast(time);
+ base::type::EnumType start = static_cast(timestampUnit);
+ const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
+ for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
+ if (result <= base::consts::kTimeFormats[i].value) {
+ break;
+ }
+ result /= base::consts::kTimeFormats[i].value;
+ unit = base::consts::kTimeFormats[i + 1].unit;
+ }
+ base::type::stringstream_t ss;
+ ss << result << " " << unit;
+ return ss.str();
+ }
+
+ /// @brief Gets time difference in milli/micro second depending on timestampUnit
+ static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) {
+ if (timestampUnit == base::TimestampUnit::Microsecond) {
+ return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) -
+ static_cast(1000000 * startTime.tv_sec + startTime.tv_usec));
+ } else {
+ return static_cast((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000);
+ }
+ }
+
+ private:
+ static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
+#if ELPP_OS_UNIX
+ time_t rawTime = currTime->tv_sec;
+ ::localtime_r(&rawTime, timeInfo);
+ return timeInfo;
+#else
+# if ELPP_COMPILER_MSVC
+ ELPP_UNUSED(currTime);
+ time_t t;
+ _time64(&t);
+ localtime_s(timeInfo, &t);
+ return timeInfo;
+# else
+ // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
+ time_t rawTime = currTime->tv_sec;
+ struct tm* tmInf = localtime(&rawTime);
+ *timeInfo = *tmInf;
+ return timeInfo;
+# endif // ELPP_COMPILER_MSVC
+#endif // ELPP_OS_UNIX
+ }
+ static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
+ std::size_t msec, const base::MillisecondsWidth* msWidth) {
+ const char* bufLim = buf + bufSz;
+ for (; *format; ++format) {
+ if (*format == base::consts::kFormatSpecifierChar) {
+ switch (*++format) {
+ case base::consts::kFormatSpecifierChar: // Escape
+ break;
+ case '\0': // End
+ --format;
+ break;
+ case 'd': // Day
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
+ continue;
+ case 'a': // Day of week (short)
+ buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
+ continue;
+ case 'A': // Day of week (long)
+ buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
+ continue;
+ case 'M': // month
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
+ continue;
+ case 'b': // month (short)
+ buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
+ continue;
+ case 'B': // month (long)
+ buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
+ continue;
+ case 'y': // year (two digits)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
+ continue;
+ case 'Y': // year (four digits)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
+ continue;
+ case 'h': // hour (12-hour)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
+ continue;
+ case 'H': // hour (24-hour)
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
+ continue;
+ case 'm': // minute
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
+ continue;
+ case 's': // second
+ buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
+ continue;
+ case 'z': // milliseconds
+ case 'g':
+ buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim);
+ continue;
+ case 'F': // AM/PM
+ buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (buf == bufLim) break;
+ *buf++ = *format;
+ }
+ return buf;
+ }
+ };
+ /// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..)
+ class CommandLineArgs {
+ public:
+ CommandLineArgs(void) {
+ setArgs(0, static_cast(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(argv));
+ }
+ /// @brief Sets arguments and parses them
+ inline void setArgs(int argc, char** argv) {
+ m_params.clear();
+ m_paramsWithValue.clear();
+ if (argc == 0 || argv == nullptr) {
+ return;
+ }
+ m_argc = argc;
+ m_argv = argv;
+ for (int i = 1; i < m_argc; ++i) {
+ const char* v = (strstr(m_argv[i], "="));
+ if (v != nullptr && strlen(v) > 0) {
+ std::string key = std::string(m_argv[i]);
+ key = key.substr(0, key.find_first_of('='));
+ if (hasParamWithValue(key.c_str())) {
+ ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
+ << getParamValue(key.c_str()) << "]");
+ } else {
+ m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
+ }
+ }
+ if (v == nullptr) {
+ if (hasParam(m_argv[i])) {
+ ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
+ } else {
+ m_params.push_back(std::string(m_argv[i]));
+ }
+ }
+ }
+ }
+ /// @brief Returns true if arguments contain paramKey with a value (seperated by '=')
+ inline bool hasParamWithValue(const char* paramKey) const {
+ return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
+ }
+ /// @brief Returns value of arguments
+ /// @see hasParamWithValue(const char*)
+ inline const char* getParamValue(const char* paramKey) const {
+ return m_paramsWithValue.find(std::string(paramKey))->second.c_str();
+ }
+ /// @brief Return true if arguments has a param (not having a value) i,e without '='
+ inline bool hasParam(const char* paramKey) const {
+ return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
+ }
+ /// @brief Returns true if no params available. This exclude argv[0]
+ inline bool empty(void) const {
+ return m_params.empty() && m_paramsWithValue.empty();
+ }
+ /// @brief Returns total number of arguments. This exclude argv[0]
+ inline std::size_t size(void) const {
+ return m_params.size() + m_paramsWithValue.size();
+ }
+ inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
+ for (int i = 1; i < c.m_argc; ++i) {
+ os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
+ if (i < c.m_argc - 1) {
+ os << ELPP_LITERAL(" ");
+ }
+ }
+ return os;
+ }
+
+ private:
+ int m_argc;
+ char** m_argv;
+ std::map m_paramsWithValue;
+ std::vector 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&) 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
+ 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& 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& 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&) = 0;
+ void reinitDeepCopy(const AbstractRegistry& 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. Any implementation of this class should be
+/// explicitly (by using lock functions)
+template
+class Registry : public AbstractRegistry> {
+public:
+ typedef typename Registry::iterator iterator;
+ typedef typename Registry::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>() {
+ 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 inline 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 inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL {
+unregister(uniqKey);
+this->list().insert(std::make_pair(uniqKey, ptr));
+}
+
+/// @brief Unregisters single entry mapped to specified unique key
+inline void unregister(const T_Key& uniqKey) {
+ T_Ptr* existing = get(uniqKey);
+ if (existing != nullptr) {
+ base::utils::safeDelete(existing);
+ this->list().erase(uniqKey);
+ }
+}
+
+/// @brief Gets pointer from repository. If none found, nullptr is returned.
+inline T_Ptr* get(const T_Key& uniqKey) {
+ iterator it = this->list().find(uniqKey);
+ return it == this->list().end()
+ ? nullptr
+ : it->second;
+}
+
+private:
+virtual inline void deepCopy(const AbstractRegistry>& 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. Any implementation of this class
+/// should be made thread-safe explicitly
+template
+class RegistryWithPred : public AbstractRegistry> {
+public:
+ typedef typename RegistryWithPred::iterator iterator;
+ typedef typename RegistryWithPred::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>() {
+ 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 inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) {
+ for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+ os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n");
+ }
+ return os;
+ }
+
+protected:
+ virtual inline 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
+inline 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 inline void deepCopy(const AbstractRegistry>& sr) {
+ for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+ registerNew(new T_Ptr(**it));
+ }
+}
+};
+
+} // 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) :
+ m_level(Level::Unknown),
+ m_userFormat(base::type::string_t()),
+ m_format(base::type::string_t()),
+ m_dateTimeFormat(std::string()),
+ m_flags(0x0) {
+ }
+
+ LogFormat(Level level, const base::type::string_t& format)
+ : m_level(level), m_userFormat(format) {
+ parseFromFormat(m_userFormat);
+ }
+
+ LogFormat(const LogFormat& logFormat) {
+ m_level = logFormat.m_level;
+ m_userFormat = logFormat.m_userFormat;
+ m_format = logFormat.m_format;
+ m_dateTimeFormat = logFormat.m_dateTimeFormat;
+ m_flags = logFormat.m_flags;
+ }
+
+ LogFormat(LogFormat&& logFormat) {
+ m_level = std::move(logFormat.m_level);
+ m_userFormat = std::move(logFormat.m_userFormat);
+ m_format = std::move(logFormat.m_format);
+ m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
+ m_flags = std::move(logFormat.m_flags);
+ }
+
+ LogFormat& operator=(const LogFormat& logFormat) {
+ m_level = logFormat.m_level;
+ m_userFormat = logFormat.m_userFormat;
+ m_dateTimeFormat = logFormat.m_dateTimeFormat;
+ m_flags = logFormat.m_flags;
+ return *this;
+ }
+
+ virtual ~LogFormat(void) {
+ }
+
+ inline bool operator==(const LogFormat& other) {
+ return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
+ m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
+ }
+
+ /// @brief Updates format to be used while logging.
+ /// @param userFormat User provided format
+ void parseFromFormat(const base::type::string_t& userFormat) {
+ // We make copy because we will be changing the format
+ // i.e, removing user provided date format from original format
+ // and then storing it.
+ base::type::string_t formatCopy = userFormat;
+ m_flags = 0x0;
+ auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
+ std::size_t foundAt = base::type::string_t::npos;
+ while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){
+ if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+ if (hasFlag(flag)) {
+ // If we already have flag we remove the escape chars so that '%%' is turned to '%'
+ // even after specifier resolution - this is because we only replaceFirst specifier
+ formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+ ++foundAt;
+ }
+ } else {
+ if (!hasFlag(flag)) addFlag(flag);
+ }
+ }
+ };
+ conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
+ conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
+ conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
+ conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
+ conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
+ conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
+ conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
+ conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
+ conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
+ conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
+ conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
+ conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
+ conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
+ conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
+ // For date/time we need to extract user's date format first
+ std::size_t dateIndex = std::string::npos;
+ if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
+ while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+ dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
+ }
+ if (dateIndex != std::string::npos) {
+ addFlag(base::FormatFlags::DateTime);
+ updateDateFormat(dateIndex, formatCopy);
+ }
+ }
+ m_format = formatCopy;
+ updateFormatSpec();
+ }
+
+ inline Level level(void) const {
+ return m_level;
+ }
+
+ inline const base::type::string_t& userFormat(void) const {
+ return m_userFormat;
+ }
+
+ inline const base::type::string_t& format(void) const {
+ return m_format;
+ }
+
+ inline const std::string& dateTimeFormat(void) const {
+ return m_dateTimeFormat;
+ }
+
+ inline base::type::EnumType flags(void) const {
+ return m_flags;
+ }
+
+ inline bool hasFlag(base::FormatFlags flag) const {
+ return base::utils::hasFlag(flag, m_flags);
+ }
+
+ virtual void log(el::base::type::ostream_t& os) const {
+ os << m_format;
+ }
+
+ protected:
+ /// @brief Updates date time format if available in currFormat.
+ /// @param index Index where %datetime, %date or %time was found
+ /// @param [in,out] currFormat current format that is being used to format
+ virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL {
+ if (hasFlag(base::FormatFlags::DateTime)) {
+ index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
+ }
+ const base::type::char_t* ptr = currFormat.c_str() + index;
+ if ((currFormat.size() > index) && (ptr[0] == '{')) {
+ // User has provided format for date/time
+ ++ptr;
+ int count = 1; // Start by 1 in order to remove starting brace
+ std::stringstream ss;
+ for (; *ptr; ++ptr, ++count) {
+ if (*ptr == '}') {
+ ++count; // In order to remove ending brace
+ break;
+ }
+ ss << *ptr;
+ }
+ currFormat.erase(index, count);
+ m_dateTimeFormat = ss.str();
+ } else {
+ // No format provided, use default
+ if (hasFlag(base::FormatFlags::DateTime)) {
+ m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
+ }
+ }
+ }
+
+ /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level
+ virtual void updateFormatSpec(void) ELPP_FINAL {
+ // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+ if (m_level == Level::Debug) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kDebugLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kDebugLevelShortLogValue);
+ } else if (m_level == Level::Info) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kInfoLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kInfoLevelShortLogValue);
+ } else if (m_level == Level::Warning) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kWarningLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kWarningLevelShortLogValue);
+ } else if (m_level == Level::Error) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kErrorLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kErrorLevelShortLogValue);
+ } else if (m_level == Level::Fatal) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kFatalLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kFatalLevelShortLogValue);
+ } else if (m_level == Level::Verbose) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kVerboseLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kVerboseLevelShortLogValue);
+ } else if (m_level == Level::Trace) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+ base::consts::kTraceLevelLogValue);
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+ base::consts::kTraceLevelShortLogValue);
+ }
+ if (hasFlag(base::FormatFlags::User)) {
+ std::string s = base::utils::s_currentUser;
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
+ base::utils::s_currentUser);
+ }
+ if (hasFlag(base::FormatFlags::Host)) {
+ base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
+ base::utils::s_currentHost);
+ }
+ // Ignore Level::Global and Level::Unknown
+ }
+
+ inline void addFlag(base::FormatFlags flag) {
+ base::utils::addFlag(flag, &m_flags);
+ }
+
+ private:
+ Level m_level;
+ base::type::string_t m_userFormat;
+ base::type::string_t m_format;
+ std::string m_dateTimeFormat;
+ base::type::EnumType m_flags;
+ friend class el::Logger; // To resolve loggerId format specifier easily
+ };
+} // namespace base
+/// @brief Resolving function for format specifier
+typedef std::function 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) :
+ m_level(c.m_level),
+ m_configurationType(c.m_configurationType),
+ m_value(c.m_value) {
+ }
+
+ Configuration& operator=(const Configuration& c) {
+ m_level = c.m_level;
+ m_configurationType = c.m_configurationType;
+ m_value = c.m_value;
+ return *this;
+ }
+
+ virtual ~Configuration(void) {
+ }
+
+ /// @brief Full constructor used to sets value of configuration
+ Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
+ m_level(level),
+ m_configurationType(configurationType),
+ m_value(value) {
+ }
+
+ /// @brief Gets level of current configuration
+ inline Level level(void) const {
+ return m_level;
+ }
+
+ /// @brief Gets configuration type of current configuration
+ inline ConfigurationType configurationType(void) const {
+ return m_configurationType;
+ }
+
+ /// @brief Gets string based configuration value
+ inline const std::string& value(void) const {
+ return m_value;
+ }
+
+ /// @brief Set string based configuration value
+ /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values
+ /// use them in quotes. They will be parsed when configuring
+ inline void setValue(const std::string& value) {
+ m_value = value;
+ }
+
+ virtual inline void log(el::base::type::ostream_t& os) const {
+ os << LevelHelper::convertToString(m_level)
+ << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
+ << ELPP_LITERAL(" = ") << m_value.c_str();
+ }
+
+ /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
+ class Predicate {
+ public:
+ Predicate(Level level, ConfigurationType configurationType) :
+ m_level(level),
+ m_configurationType(configurationType) {
+ }
+
+ inline bool operator()(const Configuration* conf) const {
+ return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
+ }
+
+ private:
+ Level m_level;
+ ConfigurationType m_configurationType;
+ };
+
+private:
+ 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 {
+public:
+ /// @brief Default constructor with empty repository
+ Configurations(void) :
+ m_configurationFile(std::string()),
+ m_isFromFile(false) {
+ }
+
+ /// @brief Constructor used to set configurations using configuration file.
+ /// @param configurationFile Full path to configuration file
+ /// @param useDefaultsForRemaining Lets you set the remaining configurations to default.
+ /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to.
+ /// @see parseFromFile(const std::string&, Configurations* base)
+ /// @see setRemainingToDefault()
+ Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) :
+ m_configurationFile(configurationFile),
+ m_isFromFile(false) {
+ parseFromFile(configurationFile, base);
+ if (useDefaultsForRemaining) {
+ setRemainingToDefault();
+ }
+ }
+
+ virtual ~Configurations(void) {
+ }
+
+ /// @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.
+ inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) {
+ // We initial assertion with true because if we have assertion diabled, we want to pass this
+ // check and if assertion is enabled we will have values re-assigned any way.
+ bool assertionPassed = true;
+ ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)),
+ "Configuration file [" << configurationFile << "] does not exist!");
+ if (!assertionPassed) {
+ return false;
+ }
+ bool success = Parser::parseFromFile(configurationFile, this, base);
+ m_isFromFile = success;
+ return success;
+ }
+
+ /// @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.
+ inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) {
+ bool success = Parser::parseFromText(configurationsString, this, base);
+ if (success) {
+ m_isFromFile = false;
+ }
+ return success;
+ }
+
+ /// @brief Sets configuration based-off an existing configurations.
+ /// @param base Pointer to existing configurations.
+ inline void setFromBase(Configurations* base) {
+ if (base == nullptr || base == this) {
+ return;
+ }
+ base::threading::ScopedLock scopedLock(base->lock());
+ for (Configuration*& conf : base->list()) {
+ set(conf);
+ }
+ }
+
+ /// @brief Determines whether or not specified configuration type exists in the repository.
+ ///
+ /// @detail Returns as soon as first level is found.
+ /// @param configurationType Type of configuration to check existence for.
+ bool hasConfiguration(ConfigurationType configurationType) {
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ bool result = false;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
+ result = true;
+ }
+ return result;
+ });
+ return result;
+ }
+
+ /// @brief Determines whether or not specified configuration type exists for specified level
+ /// @param level Level to check
+ /// @param configurationType Type of configuration to check existence for.
+ inline bool hasConfiguration(Level level, ConfigurationType configurationType) {
+ base::threading::ScopedLock scopedLock(lock());
+#if ELPP_COMPILER_INTEL
+ // We cant specify template types here, Intel C++ throws compilation error
+ // "error: type name is not allowed"
+ return RegistryWithPred::get(level, configurationType) != nullptr;
+#else
+ return RegistryWithPred::get(level, configurationType) != nullptr;
+#endif // ELPP_COMPILER_INTEL
+ }
+
+ /// @brief Sets value of configuration for specified level.
+ ///
+ /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types
+ /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for
+ /// Level::Global because these configurations are not dependant on level.
+ /// @param level Level to set configuration for (el::Level).
+ /// @param configurationType Type of configuration (el::ConfigurationType)
+ /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string
+ /// from users' point of view. This is then parsed later to be used internally.
+ /// @see Configuration::setValue(const std::string& value)
+ /// @see el::Level
+ /// @see el::ConfigurationType
+ inline void set(Level level, ConfigurationType configurationType, const std::string& value) {
+ base::threading::ScopedLock scopedLock(lock());
+ unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex
+ if (level == Level::Global) {
+ unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either
+ }
+ }
+
+ /// @brief Sets single configuration based on other single configuration.
+ /// @see set(Level level, ConfigurationType configurationType, const std::string& value)
+ inline void set(Configuration* conf) {
+ if (conf == nullptr) {
+ return;
+ }
+ set(conf->level(), conf->configurationType(), conf->value());
+ }
+
+ inline Configuration* get(Level level, ConfigurationType configurationType) {
+ base::threading::ScopedLock scopedLock(lock());
+ return RegistryWithPred::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) {
+ setGlobally(ConfigurationType::Enabled, std::string("true"), true);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
+#else
+ ELPP_UNUSED(base::consts::kDefaultLogFile);
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ setGlobally(ConfigurationType::ToFile, std::string("true"), true);
+ setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
+ setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true);
+ setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
+ setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
+ setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
+
+ setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
+ set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+ // INFO and WARNING are set to default by Level::Global
+ set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+ set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+ }
+
+ /// @brief Lets you set the remaining configurations to default.
+ ///
+ /// @detail By remaining, it means that the level/type a configuration does not exist for.
+ /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets
+ /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e,
+ /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor
+ /// and try to access a value, an error is thrown
+ void setRemainingToDefault(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
+#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
+ unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format,
+ std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+ // INFO and WARNING are set to default by Level::Global
+ unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+ unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+ }
+
+ /// @brief Parser used internally to parse configurations from file or text.
+ ///
+ /// @detail This class makes use of base::utils::Str.
+ /// You should not need this unless you are working on some tool for Easylogging++
+ class Parser : base::StaticClass {
+ public:
+ /// @brief Parses configuration from file.
+ /// @param configurationFile Full path to configuration file
+ /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration file.
+ /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+ /// do not proceed without successful parse.
+ static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) {
+ sender->setFromBase(base);
+ std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
+ ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
+ bool parsedSuccessfully = false;
+ std::string line = std::string();
+ Level currLevel = Level::Unknown;
+ std::string currConfigStr = std::string();
+ std::string currLevelStr = std::string();
+ while (fileStream_.good()) {
+ std::getline(fileStream_, line);
+ parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+ ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+ }
+ return parsedSuccessfully;
+ }
+
+ /// @brief Parse configurations from configuration string.
+ ///
+ /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
+ /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+ /// do not proceed without successful parse (This is recommended)
+ /// @param configurationsString
+ /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+ /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+ /// existing Configurations to base all the values and then set rest of configuration via configuration text.
+ /// @return True if successfully parsed, false otherwise.
+ static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) {
+ sender->setFromBase(base);
+ bool parsedSuccessfully = false;
+ std::stringstream ss(configurationsString);
+ std::string line = std::string();
+ Level currLevel = Level::Unknown;
+ std::string currConfigStr = std::string();
+ std::string currLevelStr = std::string();
+ while (std::getline(ss, line)) {
+ parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+ ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+ }
+ return parsedSuccessfully;
+ }
+
+ private:
+ friend class el::Loggers;
+ static void ignoreComments(std::string* line) {
+ std::size_t foundAt = 0;
+ std::size_t quotesStart = line->find("\"");
+ std::size_t quotesEnd = std::string::npos;
+ if (quotesStart != std::string::npos) {
+ quotesEnd = line->find("\"", quotesStart + 1);
+ while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
+ // Do not erase slash yet - we will erase it in parseLine(..) while loop
+ quotesEnd = line->find("\"", quotesEnd + 2);
+ }
+ }
+ if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
+ if (foundAt < quotesEnd) {
+ foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
+ }
+ *line = line->substr(0, foundAt);
+ }
+ }
+ static inline bool isLevel(const std::string& line) {
+ return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
+ }
+
+ static inline bool isComment(const std::string& line) {
+ return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
+ }
+
+ static inline bool isConfig(const std::string& line) {
+ std::size_t assignment = line.find('=');
+ return line != "" &&
+ ((line[0] >= 65 && line[0] <= 90) || (line[0] >= 97 && line[0] <= 122)) &&
+ (assignment != std::string::npos) &&
+ (line.size() > assignment);
+ }
+
+ static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) {
+ ConfigurationType currConfig = ConfigurationType::Unknown;
+ std::string currValue = std::string();
+ *line = base::utils::Str::trim(*line);
+ if (isComment(*line)) return true;
+ ignoreComments(line);
+ *line = base::utils::Str::trim(*line);
+ if (line->empty()) {
+ // Comment ignored
+ return true;
+ }
+ if (isLevel(*line)) {
+ if (line->size() <= 2) {
+ return true;
+ }
+ *currLevelStr = line->substr(1, line->size() - 2);
+ *currLevelStr = base::utils::Str::toUpper(*currLevelStr);
+ *currLevelStr = base::utils::Str::trim(*currLevelStr);
+ *currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
+ return true;
+ }
+ if (isConfig(*line)) {
+ std::size_t assignment = line->find('=');
+ *currConfigStr = line->substr(0, assignment);
+ *currConfigStr = base::utils::Str::toUpper(*currConfigStr);
+ *currConfigStr = base::utils::Str::trim(*currConfigStr);
+ currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
+ currValue = line->substr(assignment + 1);
+ currValue = base::utils::Str::trim(currValue);
+ std::size_t quotesStart = currValue.find("\"", 0);
+ std::size_t quotesEnd = std::string::npos;
+ if (quotesStart != std::string::npos) {
+ quotesEnd = currValue.find("\"", quotesStart + 1);
+ while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
+ currValue = currValue.erase(quotesEnd - 1, 1);
+ quotesEnd = currValue.find("\"", quotesEnd + 2);
+ }
+ }
+ if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
+ // Quote provided - check and strip if valid
+ ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in ["
+ << currConfigStr << "]");
+ ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
+ if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
+ // Explicit check in case if assertion is disabled
+ currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
+ }
+ }
+ }
+ ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
+ ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
+ if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
+ return false; // unrecognizable level or config
+ }
+ conf->set(*currLevel, currConfig, currValue);
+ return true;
+ }
+ };
+
+private:
+ std::string m_configurationFile;
+ bool m_isFromFile;
+ friend class el::Loggers;
+
+ /// @brief Unsafely sets configuration if does not already exist
+ void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
+ Configuration* conf = RegistryWithPred::get(level, configurationType);
+ if (conf == nullptr) {
+ unsafeSet(level, configurationType, value);
+ }
+ }
+
+ /// @brief Thread unsafe set
+ void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
+ Configuration* conf = RegistryWithPred::get(level, configurationType);
+ if (conf == nullptr) {
+ registerNew(new Configuration(level, configurationType, value));
+ } else {
+ conf->setValue(value);
+ }
+ if (level == Level::Global) {
+ unsafeSetGlobally(configurationType, value, false);
+ }
+ }
+
+ /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true
+ /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+ void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+ if (includeGlobalLevel) {
+ set(Level::Global, configurationType, value);
+ }
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ set(LevelHelper::castFromInt(lIndex), configurationType, value);
+ return false; // Do not break lambda function yet as we need to set all levels regardless
+ });
+ }
+
+ /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true
+ /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+ void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+ if (includeGlobalLevel) {
+ unsafeSet(Level::Global, configurationType, value);
+ }
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
+ return false; // Do not break lambda function yet as we need to set all levels regardless
+ });
+ }
+};
+
+namespace base {
+ typedef std::shared_ptr FileStreamPtr;
+ typedef std::map LogStreamsReferenceMap;
+ /// @brief Configurations with data types.
+ ///
+ /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations.
+ /// This is to perform faster while writing logs using correct configurations.
+ ///
+ /// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class)
+ class TypedConfigurations : public base::threading::ThreadSafe {
+ public:
+ /// @brief Constructor to initialize (construct) the object off el::Configurations
+ /// @param configurations Configurations pointer/reference to base this typed configurations off.
+ /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference()
+ TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) {
+ m_configurations = configurations;
+ m_logStreamsReference = logStreamsReference;
+ build(m_configurations);
+ }
+
+ TypedConfigurations(const TypedConfigurations& other) {
+ this->m_configurations = other.m_configurations;
+ this->m_logStreamsReference = other.m_logStreamsReference;
+ build(m_configurations);
+ }
+
+ virtual ~TypedConfigurations(void) {
+ }
+
+ const Configurations* configurations(void) const {
+ return m_configurations;
+ }
+
+ inline bool enabled(Level level) {
+ return getConfigByVal(level, &m_enabledMap, "enabled");
+ }
+
+ inline bool toFile(Level level) {
+ return getConfigByVal(level, &m_toFileMap, "toFile");
+ }
+
+ inline const std::string& filename(Level level) {
+ return getConfigByRef(level, &m_filenameMap, "filename");
+ }
+
+ inline bool toStandardOutput(Level level) {
+ return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput");
+ }
+
+ inline const base::LogFormat& logFormat(Level level) {
+ return getConfigByRef(level, &m_logFormatMap, "logFormat");
+ }
+
+ inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) {
+ return getConfigByRef(level, &m_millisecondsWidthMap, "millisecondsWidth");
+ }
+
+ inline bool performanceTracking(Level level = Level::Global) {
+ return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking");
+ }
+
+ inline base::type::fstream_t* fileStream(Level level) {
+ return getConfigByRef(level, &m_fileStreamMap, "fileStream").get();
+ }
+
+ inline std::size_t maxLogFileSize(Level level) {
+ return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+ }
+
+ inline std::size_t logFlushThreshold(Level level) {
+ return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold");
+ }
+
+ private:
+ Configurations* m_configurations;
+ std::map m_enabledMap;
+ std::map m_toFileMap;
+ std::map m_filenameMap;
+ std::map m_toStandardOutputMap;
+ std::map m_logFormatMap;
+ std::map m_millisecondsWidthMap;
+ std::map m_performanceTrackingMap;
+ std::map m_fileStreamMap;
+ std::map m_maxLogFileSizeMap;
+ std::map 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
+ inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
+ }
+
+ template
+ inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
+ }
+
+ template
+ inline Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) {
+ ELPP_UNUSED(confName);
+ typename std::map::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
+ inline Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) {
+ ELPP_UNUSED(confName);
+ typename std::map::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
+ void setValue(Level level, const Conf_T& value, std::map* 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::iterator it = confMap->find(Level::Global);
+ if (it != confMap->end() && it->second == value) {
+ return;
+ }
+ // Now make sure we dont double up values if we really need to add it to explicit level
+ it = confMap->find(level);
+ if (it == confMap->end()) {
+ // Value not found for level, add new
+ confMap->insert(std::make_pair(level, value));
+ } else {
+ // Value found, just update value
+ confMap->at(level) = value;
+ }
+ }
+
+ void build(Configurations* configurations) {
+ base::threading::ScopedLock scopedLock(lock());
+ auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming
+ base::utils::Str::trim(boolStr);
+ return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
+ };
+ std::vector withFileSizeLimit;
+ for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+ Configuration* conf = *it;
+ // We cannot use switch on strong enums because Intel C++ dont support them yet
+ if (conf->configurationType() == ConfigurationType::Enabled) {
+ setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
+ } else if (conf->configurationType() == ConfigurationType::ToFile) {
+ setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
+ } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
+ setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
+ } else if (conf->configurationType() == ConfigurationType::Filename) {
+ // We do not yet configure filename but we will configure in another
+ // loop. This is because if file cannot be created, we will force ToFile
+ // to be false. Because configuring logger is not necessarily performance
+ // sensative operation, we can live with another loop; (by the way this loop
+ // is not very heavy either)
+ } else if (conf->configurationType() == ConfigurationType::Format) {
+ setValue(conf->level(), base::LogFormat(conf->level(),
+ base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
+ } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) {
+ setValue(Level::Global,
+ base::MillisecondsWidth(static_cast(getULong(conf->value()))), &m_millisecondsWidthMap);
+ } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
+ setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
+ } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
+ setValue(conf->level(), static_cast(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(getULong(conf->value())), &m_logFlushThresholdMap);
+ }
+ }
+ // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files
+ for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+ Configuration* conf = *it;
+ if (conf->configurationType() == ConfigurationType::Filename) {
+ insertFile(conf->level(), conf->value());
+ }
+ }
+ for (std::vector::iterator conf = withFileSizeLimit.begin();
+ conf != withFileSizeLimit.end(); ++conf) {
+ // This is not unsafe as mutex is locked in currect scope
+ unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
+ }
+ }
+
+ unsigned long getULong(std::string confVal) {
+ bool valid = true;
+ base::utils::Str::trim(confVal);
+ valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
+ [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end();
+ if (!valid) {
+ valid = false;
+ ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
+ return 0;
+ }
+ return atol(confVal.c_str());
+ }
+
+ std::string resolveFilename(const std::string& filename) {
+ std::string resultingFilename = filename;
+ std::size_t dateIndex = std::string::npos;
+ std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
+ if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
+ while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+ dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
+ }
+ if (dateIndex != std::string::npos) {
+ const char* ptr = resultingFilename.c_str() + dateIndex;
+ // Goto end of specifier
+ ptr += dateTimeFormatSpecifierStr.size();
+ std::string fmt;
+ if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
+ // User has provided format for date/time
+ ++ptr;
+ int count = 1; // Start by 1 in order to remove starting brace
+ std::stringstream ss;
+ for (; *ptr; ++ptr, ++count) {
+ if (*ptr == '}') {
+ ++count; // In order to remove ending brace
+ break;
+ }
+ ss << *ptr;
+ }
+ resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
+ fmt = ss.str();
+ } else {
+ fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
+ }
+ base::MillisecondsWidth msWidth(3);
+ std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth);
+ base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
+ base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
+ }
+ }
+ return resultingFilename;
+ }
+
+ void insertFile(Level level, const std::string& fullFilename) {
+ std::string resolvedFilename = resolveFilename(fullFilename);
+ if (resolvedFilename.empty()) {
+ std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
+ << LevelHelper::convertToString(level) << "]";
+ }
+ std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
+ if (filePath.size() < resolvedFilename.size()) {
+ base::utils::File::createPath(filePath);
+ }
+ auto create = [&](Level level) {
+ base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
+ base::type::fstream_t* fs = nullptr;
+ if (filestreamIter == m_logStreamsReference->end()) {
+ // We need a completely new stream, nothing to share with
+ fs = base::utils::File::newFileStream(resolvedFilename);
+ m_filenameMap.insert(std::make_pair(level, resolvedFilename));
+ m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
+ m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
+ } else {
+ // Woops! we have an existing one, share it!
+ m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
+ m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
+ fs = filestreamIter->second.get();
+ }
+ if (fs == nullptr) {
+ // We display bad file error from newFileStream()
+ ELPP_INTERNAL_ERROR("Setting [TO_FILE] of ["
+ << LevelHelper::convertToString(level) << "] to FALSE", false);
+ setValue(level, false, &m_toFileMap);
+ }
+ };
+ // If we dont have file conf for any level, create it for Level::Global first
+ // otherwise create for specified level
+ create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
+ }
+
+ bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+ base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
+ if (fs == nullptr) {
+ return true;
+ }
+ std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+ std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
+ if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
+ std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
+ ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
+ << LevelHelper::convertToString(level) << "]");
+ fs->close();
+ PreRollOutCallback(fname.c_str(), currFileSize);
+ fs->open(fname, std::fstream::out | std::fstream::trunc);
+ return true;
+ }
+ return false;
+ }
+
+ bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+ base::threading::ScopedLock scopedLock(lock());
+ return unsafeValidateFileRolling(level, PreRollOutCallback);
+ }
+ };
+ /// @brief Class that keeps record of current line hit for occasional logging
+ class HitCounter {
+ public:
+ HitCounter(void) :
+ m_filename(""),
+ m_lineNumber(0),
+ m_hitCounts(0) {
+ }
+
+ HitCounter(const char* filename, unsigned long int lineNumber) :
+ m_filename(filename),
+ m_lineNumber(lineNumber),
+ m_hitCounts(0) {
+ }
+
+ HitCounter(const HitCounter& hitCounter) :
+ m_filename(hitCounter.m_filename),
+ m_lineNumber(hitCounter.m_lineNumber),
+ m_hitCounts(hitCounter.m_hitCounts) {
+ }
+
+ HitCounter& operator=(const HitCounter& hitCounter) {
+ m_filename = hitCounter.m_filename;
+ m_lineNumber = hitCounter.m_lineNumber;
+ m_hitCounts = hitCounter.m_hitCounts;
+ return *this;
+ }
+
+ virtual ~HitCounter(void) {
+ }
+
+ /// @brief Resets location of current hit counter
+ inline void resetLocation(const char* filename, unsigned long int lineNumber) {
+ m_filename = filename;
+ m_lineNumber = lineNumber;
+ }
+
+ /// @brief Validates hit counts and resets it if necessary
+ inline void validateHitCounts(std::size_t n) {
+ if (m_hitCounts >= base::consts::kMaxLogPerCounter) {
+ m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0);
+ }
+ ++m_hitCounts;
+ }
+
+ inline const char* filename(void) const {
+ return m_filename;
+ }
+
+ inline unsigned long int lineNumber(void) const {
+ return m_lineNumber;
+ }
+
+ inline std::size_t hitCounts(void) const {
+ return m_hitCounts;
+ }
+
+ inline void increment(void) {
+ ++m_hitCounts;
+ }
+
+ class Predicate {
+ public:
+ Predicate(const char* filename, unsigned long int lineNumber)
+ : m_filename(filename),
+ m_lineNumber(lineNumber) {
+ }
+ inline bool operator()(const HitCounter* counter) {
+ return ((counter != nullptr) &&
+ (strcmp(counter->m_filename, m_filename) == 0) &&
+ (counter->m_lineNumber == m_lineNumber));
+ }
+
+ private:
+ const char* m_filename;
+ unsigned long int m_lineNumber;
+ };
+
+ private:
+ const char* m_filename;
+ unsigned long int m_lineNumber;
+ std::size_t m_hitCounts;
+ };
+ /// @brief Repository for hit counters used across the application
+ class RegisteredHitCounters : public base::utils::RegistryWithPred {
+ public:
+ /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ counter->validateHitCounts(n);
+ bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
+ return result;
+ }
+
+ /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ // Do not use validateHitCounts here since we do not want to reset counter here
+ // Note the >= instead of > because we are incrementing
+ // after this check
+ if (counter->hitCounts() >= n)
+ return true;
+ counter->increment();
+ return false;
+ }
+
+ /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
+ /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+ bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::HitCounter* counter = get(filename, lineNumber);
+ if (counter == nullptr) {
+ registerNew(counter = new base::HitCounter(filename, lineNumber));
+ }
+ counter->increment();
+ // Do not use validateHitCounts here since we do not want to reset counter here
+ if (counter->hitCounts() <= n)
+ return true;
+ return false;
+ }
+
+ /// @brief Gets hit counter registered at specified position
+ inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) {
+ base::threading::ScopedLock scopedLock(lock());
+ return get(filename, lineNumber);
+ }
+ };
+ /// @brief Action to be taken for dispatching
+ enum class DispatchAction : base::type::EnumType {
+ None = 1, NormalLog = 2, SysLog = 4
+ };
+ } // namespace base
+ template
+ 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 {
+ private:
+ friend class base::LogDispatcher;
+ };
+ class PerformanceTrackingCallback : public Callback {
+ private:
+ friend class base::PerformanceTracker;
+ };
+ class LogBuilder : base::NoCopy {
+ public:
+ virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")}
+ virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0;
+ void convertToColoredOutput(base::type::string_t* logLine, Level level) {
+ if (!base::utils::s_termSupportsColor) return;
+ const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
+ if (level == Level::Error || level == Level::Fatal)
+ *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
+ else if (level == Level::Warning)
+ *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
+ else if (level == Level::Debug)
+ *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
+ else if (level == Level::Info)
+ *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
+ else if (level == Level::Trace)
+ *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
+ }
+ private:
+ friend class el::base::DefaultLogDispatchCallback;
+ };
+ typedef std::shared_ptr LogBuilderPtr;
+ /// @brief Represents a logger holding ID and configurations we need to write logs
+ ///
+ /// @detail This class does not write logs itself instead its used by writer to read configuations from.
+ class Logger : public base::threading::ThreadSafe, public Loggable {
+ public:
+ Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
+ m_id(id),
+ m_typedConfigurations(nullptr),
+ m_parentApplicationName(std::string()),
+ m_isConfigured(false),
+ m_logStreamsReference(logStreamsReference) {
+ initUnflushedCount();
+ }
+
+ Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) :
+ m_id(id),
+ m_typedConfigurations(nullptr),
+ m_parentApplicationName(std::string()),
+ m_isConfigured(false),
+ m_logStreamsReference(logStreamsReference) {
+ initUnflushedCount();
+ configure(configurations);
+ }
+
+ Logger(const Logger& logger) {
+ base::utils::safeDelete(m_typedConfigurations);
+ m_id = logger.m_id;
+ m_typedConfigurations = logger.m_typedConfigurations;
+ m_parentApplicationName = logger.m_parentApplicationName;
+ m_isConfigured = logger.m_isConfigured;
+ m_configurations = logger.m_configurations;
+ m_unflushedCount = logger.m_unflushedCount;
+ m_logStreamsReference = logger.m_logStreamsReference;
+ }
+
+ Logger& operator=(const Logger& logger) {
+ base::utils::safeDelete(m_typedConfigurations);
+ m_id = logger.m_id;
+ m_typedConfigurations = logger.m_typedConfigurations;
+ m_parentApplicationName = logger.m_parentApplicationName;
+ m_isConfigured = logger.m_isConfigured;
+ m_configurations = logger.m_configurations;
+ m_unflushedCount = logger.m_unflushedCount;
+ m_logStreamsReference = logger.m_logStreamsReference;
+ return *this;
+ }
+
+ virtual ~Logger(void) {
+ base::utils::safeDelete(m_typedConfigurations);
+ }
+
+ virtual inline void log(el::base::type::ostream_t& os) const {
+ os << m_id.c_str();
+ }
+
+ /// @brief Configures the logger using specified configurations.
+ void configure(const Configurations& configurations) {
+ m_isConfigured = false; // we set it to false in case if we fail
+ initUnflushedCount();
+ if (m_typedConfigurations != nullptr) {
+ Configurations* c = const_cast(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));
+ }
+ base::utils::safeDelete(m_typedConfigurations);
+ m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
+ resolveLoggerFormatSpec();
+ m_isConfigured = true;
+ }
+
+ /// @brief Reconfigures logger using existing configurations
+ inline void reconfigure(void) {
+ ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
+ configure(m_configurations);
+ }
+
+ inline const std::string& id(void) const {
+ return m_id;
+ }
+
+ inline const std::string& parentApplicationName(void) const {
+ return m_parentApplicationName;
+ }
+
+ inline void setParentApplicationName(const std::string& parentApplicationName) {
+ m_parentApplicationName = parentApplicationName;
+ }
+
+ inline Configurations* configurations(void) {
+ return &m_configurations;
+ }
+
+ inline base::TypedConfigurations* typedConfigurations(void) {
+ return m_typedConfigurations;
+ }
+
+ static inline bool isValidId(const std::string& id) {
+ for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
+ if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /// @brief Flushes logger to sync all log files for all levels
+ inline void flush(void) {
+ ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
+ base::threading::ScopedLock scopedLock(lock());
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ flush(LevelHelper::castFromInt(lIndex), nullptr);
+ return false;
+ });
+ }
+
+ inline void flush(Level level, base::type::fstream_t* fs) {
+ if (fs == nullptr && m_typedConfigurations->toFile(level)) {
+ fs = m_typedConfigurations->fileStream(level);
+ }
+ if (fs != nullptr) {
+ fs->flush();
+ m_unflushedCount.find(level)->second = 0;
+ }
+ }
+
+ inline bool isFlushNeeded(Level level) {
+ return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level);
+ }
+
+ inline LogBuilder* logBuilder(void) const {
+ return m_logBuilder.get();
+ }
+
+ inline void setLogBuilder(const LogBuilderPtr& logBuilder) {
+ m_logBuilder = logBuilder;
+ }
+
+ inline bool enabled(Level level) const {
+ return m_typedConfigurations->enabled(level);
+ }
+
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\
+template \
+inline void FUNCTION_NAME(const char*, const T&, const Args&...);\
+template \
+inline void FUNCTION_NAME(const T&);
+
+ template
+ inline void verbose(int, const char*, const T&, const Args&...);
+
+ template
+ 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 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
+ void log_(Level, int, const char*, const T&, const Args&...);
+
+ template
+ inline void log_(Level, int, const T&);
+
+ template
+ void log(Level, const char*, const T&, const Args&...);
+
+ template
+ inline void log(Level, const T&);
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+
+ void initUnflushedCount(void) {
+ m_unflushedCount.clear();
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
+ return false;
+ });
+ }
+
+ inline base::type::stringstream_t& stream(void) {
+ return m_stream;
+ }
+
+ void resolveLoggerFormatSpec(void) const {
+ base::type::EnumType lIndex = LevelHelper::kMinValid;
+ LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+ base::LogFormat* logFormat =
+ const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
+ base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
+ return false;
+ });
+ }
+ };
+ namespace base {
+ /// @brief Loggers repository
+ class RegisteredLoggers : public base::utils::Registry {
+ public:
+ explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
+ m_defaultLogBuilder(defaultLogBuilder) {
+ m_defaultConfigurations.setToDefault();
+ }
+
+ virtual ~RegisteredLoggers(void) {
+ unsafeFlushAll();
+ }
+
+ inline void setDefaultConfigurations(const Configurations& configurations) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_defaultConfigurations.setFromBase(const_cast(&configurations));
+ }
+
+ inline Configurations* defaultConfigurations(void) {
+ return &m_defaultConfigurations;
+ }
+
+ Logger* get(const std::string& id, bool forceCreation = true) {
+ base::threading::ScopedLock scopedLock(lock());
+ Logger* logger_ = base::utils::Registry::get(id);
+ if (logger_ == nullptr && forceCreation) {
+ bool validId = Logger::isValidId(id);
+ if (!validId) {
+ ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
+ return nullptr;
+ }
+ logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
+ logger_->m_logBuilder = m_defaultLogBuilder;
+ registerNew(id, logger_);
+ }
+ return logger_;
+ }
+
+ bool remove(const std::string& id) {
+ if (id == "default") {
+ return false;
+ }
+ Logger* logger = base::utils::Registry::get(id);
+ if (logger != nullptr) {
+ unregister(logger);
+ }
+ return true;
+ }
+
+ inline bool has(const std::string& id) {
+ return get(id, false) != nullptr;
+ }
+
+ inline void unregister(Logger*& logger) {
+ base::threading::ScopedLock scopedLock(lock());
+ base::utils::Registry::unregister(logger->id());
+ }
+
+ inline base::LogStreamsReferenceMap* logStreamsReference(void) {
+ return &m_logStreamsReference;
+ }
+
+ inline void flushAll(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ unsafeFlushAll();
+ }
+
+ private:
+ LogBuilderPtr m_defaultLogBuilder;
+ Configurations m_defaultConfigurations;
+ base::LogStreamsReferenceMap m_logStreamsReference;
+ friend class el::base::Storage;
+
+ inline void unsafeFlushAll(void) {
+ ELPP_INTERNAL_INFO(1, "Flushing all log files");
+ for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
+ it != m_logStreamsReference.end(); ++it) {
+ if (it->second.get() == nullptr) continue;
+ it->second->flush();
+ }
+ }
+ };
+ /// @brief Represents registries for verbose logging
+ class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
+ public:
+ explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
+ }
+
+ /// @brief Sets verbose level. Accepted range is 0-9
+ inline void setLevel(base::type::VerboseLevel level) {
+ base::threading::ScopedLock scopedLock(lock());
+ if (level < 0)
+ m_level = 0;
+ else if (level > 9)
+ m_level = base::consts::kMaxVerboseLevel;
+ else
+ m_level = level;
+ }
+
+ inline base::type::VerboseLevel level(void) const {
+ return m_level;
+ }
+
+ inline void clearModules(void) {
+ base::threading::ScopedLock scopedLock(lock());
+ m_modules.clear();
+ }
+
+ void setModules(const char* modules) {
+ base::threading::ScopedLock scopedLock(lock());
+ auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
+ if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
+ std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
+ ss.str(std::string(""));
+ ss << chr;
+ }
+ if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
+ std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
+ ss.str(std::string(""));
+ ss << chr;
+ }
+ ss << sfx;
+ };
+ auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
+ if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
+ addSuffix(ss, ".h", nullptr);
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".c", ".h");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cpp", ".c");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cc", ".cpp");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".cxx", ".cc");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".-inl.h", ".cxx");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hxx", ".-inl.h");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hpp", ".hxx");
+ m_modules.insert(std::make_pair(ss.str(), level));
+ addSuffix(ss, ".hh", ".hpp");
+ }
+ m_modules.insert(std::make_pair(ss.str(), level));
+ };
+ bool isMod = true;
+ bool isLevel = false;
+ std::stringstream ss;
+ int level = -1;
+ for (; *modules; ++modules) {
+ switch (*modules) {
+ case '=':
+ isLevel = true;
+ isMod = false;
+ break;
+ case ',':
+ isLevel = false;
+ isMod = true;
+ if (!ss.str().empty() && level != -1) {
+ insert(ss, level);
+ ss.str(std::string(""));
+ level = -1;
+ }
+ break;
+ default:
+ if (isMod) {
+ ss << *modules;
+ } else if (isLevel) {
+ if (isdigit(*modules)) {
+ level = static_cast(*modules) - 48;
+ }
+ }
+ break;
+ }
+ }
+ if (!ss.str().empty() && level != -1) {
+ insert(ss, level);
+ }
+ }
+
+ bool allowed(base::type::VerboseLevel vlevel, const char* file) {
+ base::threading::ScopedLock scopedLock(lock());
+ if (m_modules.empty() || file == nullptr) {
+ return vlevel <= m_level;
+ } else {
+ std::map::iterator it = m_modules.begin();
+ for (; it != m_modules.end(); ++it) {
+ if (base::utils::Str::wildCardMatch(file, it->first.c_str())) {
+ return vlevel <= it->second;
+ }
+ }
+ if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ inline const std::map& modules(void) const {
+ return m_modules;
+ }
+
+ void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
+ if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
+ commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
+ setLevel(base::consts::kMaxVerboseLevel);
+ } else if (commandLineArgs->hasParamWithValue("--v")) {
+ setLevel(atoi(commandLineArgs->getParamValue("--v")));
+ } else if (commandLineArgs->hasParamWithValue("--V")) {
+ setLevel(atoi(commandLineArgs->getParamValue("--V")));
+ } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
+ setModules(commandLineArgs->getParamValue("-vmodule"));
+ } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
+ setModules(commandLineArgs->getParamValue("-VMODULE"));
+ }
+ }
+
+ /// @brief Whether or not vModules enabled
+ inline bool vModulesEnabled(void) {
+ return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags);
+ }
+
+ private:
+ base::type::VerboseLevel m_level;
+ base::type::EnumType* m_pFlags;
+ std::map m_modules;
+ };
+ } // namespace base
+ class LogMessage {
+ public:
+ LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func,
+ base::type::VerboseLevel verboseLevel, Logger* logger) :
+ m_level(level), m_file(file), m_line(line), m_func(func),
+ m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) {
+ }
+ inline Level level(void) const { return m_level; }
+ inline const std::string& file(void) const { return m_file; }
+ inline unsigned long int line(void) const { return m_line; } // NOLINT
+ inline const std::string& func(void) const { return m_func; }
+ inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; }
+ inline Logger* logger(void) const { return m_logger; }
+ inline const base::type::string_t& message(void) const { return m_message; }
+ private:
+ Level m_level;
+ std::string m_file;
+ unsigned long int m_line;
+ std::string m_func;
+ base::type::VerboseLevel m_verboseLevel;
+ Logger* m_logger;
+ base::type::string_t m_message;
+ };
+ namespace base {
+#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 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
+ m_registeredHitCounters(new base::RegisteredHitCounters()),
+ m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
+ m_flags(0x0),
+ m_vRegistry(new base::VRegistry(0, &m_flags)),
+#if ELPP_ASYNC_LOGGING
+ m_asyncLogQueue(new base::AsyncLogQueue()),
+ m_asyncDispatchWorker(asyncDispatchWorker),
+#endif // ELPP_ASYNC_LOGGING
+ m_preRollOutCallback(base::defaultPreRollOutCallback) {
+ // Register default logger
+ m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
+ // Register performance logger and reconfigure format
+ Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
+ performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
+ performanceLogger->reconfigure();
+#if defined(ELPP_SYSLOG)
+ // Register syslog logger and reconfigure format
+ Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
+ sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
+ sysLogLogger->reconfigure();
+#endif // defined(ELPP_SYSLOG)
+ addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
+#if ELPP_ASYNC_LOGGING
+ installLogDispatchCallback(std::string("AsyncLogDispatchCallback"));
+#else
+ installLogDispatchCallback(std::string("DefaultLogDispatchCallback"));
+#endif // ELPP_ASYNC_LOGGING
+ installPerformanceTrackingCallback(std::string("DefaultPerformanceTrackingCallback"));
+ ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
+#if ELPP_ASYNC_LOGGING
+ m_asyncDispatchWorker->start();
+#endif // ELPP_ASYNC_LOGGING
+ }
+
+ virtual ~Storage(void) {
+ ELPP_INTERNAL_INFO(4, "Destroying storage");
+#if ELPP_ASYNC_LOGGING
+ ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
+ uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback"));
+ installLogDispatchCallback(std::string("DefaultLogDispatchCallback"));
+ ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
+ base::utils::safeDelete(m_asyncDispatchWorker);
+ ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
+ base::utils::safeDelete(m_asyncLogQueue);
+#endif // ELPP_ASYNC_LOGGING
+ ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
+ base::utils::safeDelete(m_registeredHitCounters);
+ ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
+ base::utils::safeDelete(m_registeredLoggers);
+ ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
+ base::utils::safeDelete(m_vRegistry);
+ }
+
+ inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) {
+ return hitCounters()->validateEveryN(filename, lineNumber, occasion);
+ }
+
+ inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+ return hitCounters()->validateAfterN(filename, lineNumber, n);
+ }
+
+ inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+ return hitCounters()->validateNTimes(filename, lineNumber, n);
+ }
+
+ inline base::RegisteredHitCounters* hitCounters(void) const {
+ return m_registeredHitCounters;
+ }
+
+ inline base::RegisteredLoggers* registeredLoggers(void) const {
+ return m_registeredLoggers;
+ }
+
+ inline base::VRegistry* vRegistry(void) const {
+ return m_vRegistry;
+ }
+
+#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;
+ }
+
+ inline bool hasCustomFormatSpecifier(const char* formatSpecifier) {
+ base::threading::ScopedLock scopedLock(lock());
+ return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
+ formatSpecifier) != m_customFormatSpecifiers.end();
+ }
+
+ inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
+ if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
+ return;
+ }
+ base::threading::ScopedLock scopedLock(lock());
+ m_customFormatSpecifiers.push_back(customFormatSpecifier);
+ }
+
+ inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+ base::threading::ScopedLock scopedLock(lock());
+ std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(),
+ m_customFormatSpecifiers.end(), formatSpecifier);
+ if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
+ m_customFormatSpecifiers.erase(it);
+ return true;
+ }
+ return false;
+ }
+
+ const std::vector* customFormatSpecifiers(void) const {
+ return &m_customFormatSpecifiers;
+ }
+
+ inline void setLoggingLevel(Level level) {
+ m_loggingLevel = level;
+ }
+
+ template
+ inline bool installLogDispatchCallback(const std::string& id) {
+ return installCallback(id, &m_logDispatchCallbacks);
+ }
+
+ template
+ inline void uninstallLogDispatchCallback(const std::string& id) {
+ uninstallCallback(id, &m_logDispatchCallbacks);
+ }
+ template
+ inline T* logDispatchCallback(const std::string& id) {
+ return callback(id, &m_logDispatchCallbacks);
+ }
+
+ template
+ inline bool installPerformanceTrackingCallback(const std::string& id) {
+ return installCallback(id, &m_performanceTrackingCallbacks);
+ }
+
+ template
+ inline void uninstallPerformanceTrackingCallback(const std::string& id) {
+ uninstallCallback(id, &m_performanceTrackingCallbacks);
+ }
+
+ template
+ inline T* performanceTrackingCallback(const std::string& id) {
+ return callback(id, &m_performanceTrackingCallbacks);
+ }
+ 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 m_logDispatchCallbacks;
+ std::map m_performanceTrackingCallbacks;
+ std::vector m_customFormatSpecifiers;
+ Level m_loggingLevel;
+
+ friend class el::Helpers;
+ friend class el::base::DefaultLogDispatchCallback;
+ friend class el::LogBuilder;
+ friend class el::base::MessageBuilder;
+ friend class el::base::Writer;
+ friend class el::base::PerformanceTracker;
+ friend class el::base::LogDispatcher;
+
+ void setApplicationArguments(int argc, char** argv) {
+ m_commandLineArgs.setArgs(argc, argv);
+ m_vRegistry->setFromArgs(commandLineArgs());
+ // default log file
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+ if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
+ Configurations c;
+ c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
+ registeredLoggers()->setDefaultConfigurations(c);
+ for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
+ it != registeredLoggers()->end(); ++it) {
+ it->second->configure(c);
+ }
+ }
+#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+ if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
+ m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
+ }
+#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+ }
+
+ inline void setApplicationArguments(int argc, const char** argv) {
+ setApplicationArguments(argc, const_cast(argv));
+ }
+
+ template
+ inline bool installCallback(const std::string& id, std::map* mapT) {
+ if (mapT->find(id) == mapT->end()) {
+ mapT->insert(std::make_pair(id, TPtr(new T())));
+ return true;
+ }
+ return false;
+ }
+
+ template
+ inline void uninstallCallback(const std::string& id, std::map* mapT) {
+ if (mapT->find(id) != mapT->end()) {
+ mapT->erase(id);
+ }
+ }
+
+ template
+ inline T* callback(const std::string& id, std::map* mapT) {
+ typename std::map