1 //
2 //  Bismillah ar-Rahmaan ar-Raheem
3 //
4 //  Easylogging++ v9.96.4
5 //  Cross-platform logging library for C++ applications
6 //
7 //  Copyright (c) 2012-2018 Muflihun Labs
8 //  Copyright (c) 2012-2018 @abumusamq
9 //
10 //  This library is released under the MIT Licence.
11 //  https://github.com/muflihun/easyloggingpp/blob/master/LICENSE
12 //
13 //  https://github.com/muflihun/easyloggingpp
14 //  https://muflihun.github.io/easyloggingpp
15 //  http://muflihun.com
16 //
17 
18 #include "easylogging++.h"
19 
20 #if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
21 INITIALIZE_EASYLOGGINGPP
22 #endif
23 
24 namespace el {
25 
26 // el::base
27 namespace base {
28 // el::base::consts
29 namespace consts {
30 
31 // Level log values - These are values that are replaced in place of %level format specifier
32 // Extra spaces after format specifiers are only for readability purposes in log files
33 static const base::type::char_t* kInfoLevelLogValue     =   ELPP_LITERAL("INFO");
34 static const base::type::char_t* kDebugLevelLogValue    =   ELPP_LITERAL("DEBUG");
35 static const base::type::char_t* kWarningLevelLogValue  =   ELPP_LITERAL("WARNING");
36 static const base::type::char_t* kErrorLevelLogValue    =   ELPP_LITERAL("ERROR");
37 static const base::type::char_t* kFatalLevelLogValue    =   ELPP_LITERAL("FATAL");
38 static const base::type::char_t* kVerboseLevelLogValue  =
39   ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
40 static const base::type::char_t* kTraceLevelLogValue    =   ELPP_LITERAL("TRACE");
41 static const base::type::char_t* kInfoLevelShortLogValue     =   ELPP_LITERAL("I");
42 static const base::type::char_t* kDebugLevelShortLogValue    =   ELPP_LITERAL("D");
43 static const base::type::char_t* kWarningLevelShortLogValue  =   ELPP_LITERAL("W");
44 static const base::type::char_t* kErrorLevelShortLogValue    =   ELPP_LITERAL("E");
45 static const base::type::char_t* kFatalLevelShortLogValue    =   ELPP_LITERAL("F");
46 static const base::type::char_t* kVerboseLevelShortLogValue  =   ELPP_LITERAL("V");
47 static const base::type::char_t* kTraceLevelShortLogValue    =   ELPP_LITERAL("T");
48 // Format specifiers - These are used to define log format
49 static const base::type::char_t* kAppNameFormatSpecifier          =      ELPP_LITERAL("%app");
50 static const base::type::char_t* kLoggerIdFormatSpecifier         =      ELPP_LITERAL("%logger");
51 static const base::type::char_t* kThreadIdFormatSpecifier         =      ELPP_LITERAL("%thread");
52 static const base::type::char_t* kSeverityLevelFormatSpecifier    =      ELPP_LITERAL("%level");
53 static const base::type::char_t* kSeverityLevelShortFormatSpecifier    =      ELPP_LITERAL("%levshort");
54 static const base::type::char_t* kDateTimeFormatSpecifier         =      ELPP_LITERAL("%datetime");
55 static const base::type::char_t* kLogFileFormatSpecifier          =      ELPP_LITERAL("%file");
56 static const base::type::char_t* kLogFileBaseFormatSpecifier      =      ELPP_LITERAL("%fbase");
57 static const base::type::char_t* kLogLineFormatSpecifier          =      ELPP_LITERAL("%line");
58 static const base::type::char_t* kLogLocationFormatSpecifier      =      ELPP_LITERAL("%loc");
59 static const base::type::char_t* kLogFunctionFormatSpecifier      =      ELPP_LITERAL("%func");
60 static const base::type::char_t* kCurrentUserFormatSpecifier      =      ELPP_LITERAL("%user");
61 static const base::type::char_t* kCurrentHostFormatSpecifier      =      ELPP_LITERAL("%host");
62 static const base::type::char_t* kMessageFormatSpecifier          =      ELPP_LITERAL("%msg");
63 static const base::type::char_t* kVerboseLevelFormatSpecifier     =      ELPP_LITERAL("%vlevel");
64 static const char* kDateTimeFormatSpecifierForFilename            =      "%datetime";
65 // Date/time
66 static const char* kDays[7]                         =      { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
67 static const char* kDaysAbbrev[7]                   =      { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
68 static const char* kMonths[12]                      =      { "January", "February", "March", "Apri", "May", "June", "July", "August",
69                                                              "September", "October", "November", "December"
70                                                            };
71 static const char* kMonthsAbbrev[12]                =      { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
72 static const char* kDefaultDateTimeFormat           =      "%Y-%M-%d %H:%m:%s,%g";
73 static const char* kDefaultDateTimeFormatInFilename =      "%Y-%M-%d_%H-%m";
74 static const int kYearBase                          =      1900;
75 static const char* kAm                              =      "AM";
76 static const char* kPm                              =      "PM";
77 // Miscellaneous constants
78 
79 static const char* kNullPointer                            =      "nullptr";
80 #if ELPP_VARIADIC_TEMPLATES_SUPPORTED
81 #endif  // ELPP_VARIADIC_TEMPLATES_SUPPORTED
82 static const base::type::VerboseLevel kMaxVerboseLevel     =      9;
83 static const char* kUnknownUser                            =      "user";
84 static const char* kUnknownHost                            =      "unknown-host";
85 
86 #ifndef ELPP_DEFAULT_PERFORMANCE_LOGGER
87 static const char* kPerformanceLoggerId                    =      "performance";
88 #endif
89 
90 
91 //---------------- DEFAULT LOG FILE -----------------------
92 
93 #if defined(ELPP_NO_DEFAULT_LOG_FILE)
94 #  if ELPP_OS_UNIX
95 static const char* kDefaultLogFile                         =      "/dev/null";
96 #  elif ELPP_OS_WINDOWS
97 static const char* kDefaultLogFile                         =      "nul";
98 #  endif  // ELPP_OS_UNIX
99 #elif defined(ELPP_DEFAULT_LOG_FILE)
100 static const char* kDefaultLogFile                         =      ELPP_DEFAULT_LOG_FILE;
101 #else
102 static const char* kDefaultLogFile                         =      "myeasylog.log";
103 #endif // defined(ELPP_NO_DEFAULT_LOG_FILE)
104 
105 
106 #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
107 static const char* kDefaultLogFileParam                    =      "--default-log-file";
108 #endif  // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
109 #if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
110 static const char* kLoggingFlagsParam                      =      "--logging-flags";
111 #endif  // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
112 static const char* kValidLoggerIdSymbols                   =
113   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
114 static const char* kConfigurationComment                   =      "##";
115 static const char* kConfigurationLevel                     =      "*";
116 static const char* kConfigurationLoggerId                  =      "--";
117 }
118 // el::base::utils
119 namespace utils {
120 
121 /// @brief Aborts application due with user-defined status
abort(int status,const std::string & reason)122 static void abort(int status, const std::string& reason) {
123   // Both status and reason params are there for debugging with tools like gdb etc
124   ELPP_UNUSED(status);
125   ELPP_UNUSED(reason);
126 #if defined(ELPP_HANDLE_SIGABRT)
127   // Disable the handler when easylogging causes the abort to avoid an infinite loop
128   signal(base::consts::kCrashSignals[0].numb, SIG_DFL);
129 #endif
130 #if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
131   // Ignore msvc critical error dialog - break instead (on debug mode)
132   _asm int 3
133 #else
134   ::abort();
135 #endif  // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
136 }
137 
138 } // namespace utils
139 } // namespace base
140 
141 // el
142 
143 // LevelHelper
144 
convertToString(Level level)145 const char* LevelHelper::convertToString(Level level) {
146   // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
147   if (level == Level::Global) return "GLOBAL";
148   if (level == Level::Debug) return "DEBUG";
149   if (level == Level::Info) return "INFO";
150   if (level == Level::Warning) return "WARNING";
151   if (level == Level::Error) return "ERROR";
152   if (level == Level::Fatal) return "FATAL";
153   if (level == Level::Verbose) return "VERBOSE";
154   if (level == Level::Trace) return "TRACE";
155   return "UNKNOWN";
156 }
157 
158 struct StringToLevelItem {
159   const char* levelString;
160   Level level;
161 };
162 
163 static struct StringToLevelItem stringToLevelMap[] = {
164   { "global", Level::Global },
165   { "debug", Level::Debug },
166   { "info", Level::Info },
167   { "warning", Level::Warning },
168   { "error", Level::Error },
169   { "fatal", Level::Fatal },
170   { "verbose", Level::Verbose },
171   { "trace", Level::Trace }
172 };
173 
convertFromString(const char * levelStr)174 Level LevelHelper::convertFromString(const char* levelStr) {
175   for (auto& item : stringToLevelMap) {
176     if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) {
177       return item.level;
178     }
179   }
180   return Level::Unknown;
181 }
182 
forEachLevel(base::type::EnumType * startIndex,const std::function<bool (void)> & fn)183 void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
184   base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
185   do {
186     if (fn()) {
187       break;
188     }
189     *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
190   } while (*startIndex <= lIndexMax);
191 }
192 
193 // ConfigurationTypeHelper
194 
convertToString(ConfigurationType configurationType)195 const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) {
196   // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
197   if (configurationType == ConfigurationType::Enabled) return "ENABLED";
198   if (configurationType == ConfigurationType::Filename) return "FILENAME";
199   if (configurationType == ConfigurationType::Format) return "FORMAT";
200   if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
201   if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
202   if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION";
203   if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
204   if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
205   if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
206   return "UNKNOWN";
207 }
208 
209 struct ConfigurationStringToTypeItem {
210   const char* configString;
211   ConfigurationType configType;
212 };
213 
214 static struct ConfigurationStringToTypeItem configStringToTypeMap[] = {
215   { "enabled", ConfigurationType::Enabled },
216   { "to_file", ConfigurationType::ToFile },
217   { "to_standard_output", ConfigurationType::ToStandardOutput },
218   { "format", ConfigurationType::Format },
219   { "filename", ConfigurationType::Filename },
220   { "subsecond_precision", ConfigurationType::SubsecondPrecision },
221   { "milliseconds_width", ConfigurationType::MillisecondsWidth },
222   { "performance_tracking", ConfigurationType::PerformanceTracking },
223   { "max_log_file_size", ConfigurationType::MaxLogFileSize },
224   { "log_flush_threshold", ConfigurationType::LogFlushThreshold },
225 };
226 
convertFromString(const char * configStr)227 ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) {
228   for (auto& item : configStringToTypeMap) {
229     if (base::utils::Str::cStringCaseEq(configStr, item.configString)) {
230       return item.configType;
231     }
232   }
233   return ConfigurationType::Unknown;
234 }
235 
forEachConfigType(base::type::EnumType * startIndex,const std::function<bool (void)> & fn)236 void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
237   base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
238   do {
239     if (fn()) {
240       break;
241     }
242     *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
243   } while (*startIndex <= cIndexMax);
244 }
245 
246 // Configuration
247 
Configuration(const Configuration & c)248 Configuration::Configuration(const Configuration& c) :
249   m_level(c.m_level),
250   m_configurationType(c.m_configurationType),
251   m_value(c.m_value) {
252 }
253 
operator =(const Configuration & c)254 Configuration& Configuration::operator=(const Configuration& c) {
255   if (&c != this) {
256     m_level = c.m_level;
257     m_configurationType = c.m_configurationType;
258     m_value = c.m_value;
259   }
260   return *this;
261 }
262 
263 /// @brief Full constructor used to sets value of configuration
Configuration(Level level,ConfigurationType configurationType,const std::string & value)264 Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
265   m_level(level),
266   m_configurationType(configurationType),
267   m_value(value) {
268 }
269 
log(el::base::type::ostream_t & os) const270 void Configuration::log(el::base::type::ostream_t& os) const {
271   os << LevelHelper::convertToString(m_level)
272      << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
273      << ELPP_LITERAL(" = ") << m_value.c_str();
274 }
275 
276 /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
Predicate(Level level,ConfigurationType configurationType)277 Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) :
278   m_level(level),
279   m_configurationType(configurationType) {
280 }
281 
operator ()(const Configuration * conf) const282 bool Configuration::Predicate::operator()(const Configuration* conf) const {
283   return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
284 }
285 
286 // Configurations
287 
Configurations(void)288 Configurations::Configurations(void) :
289   m_configurationFile(std::string()),
290   m_isFromFile(false) {
291 }
292 
Configurations(const std::string & configurationFile,bool useDefaultsForRemaining,Configurations * base)293 Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining,
294                                Configurations* base) :
295   m_configurationFile(configurationFile),
296   m_isFromFile(false) {
297   parseFromFile(configurationFile, base);
298   if (useDefaultsForRemaining) {
299     setRemainingToDefault();
300   }
301 }
302 
parseFromFile(const std::string & configurationFile,Configurations * base)303 bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) {
304   // We initial assertion with true because if we have assertion diabled, we want to pass this
305   // check and if assertion is enabled we will have values re-assigned any way.
306   bool assertionPassed = true;
307   ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true,
308               "Configuration file [" << configurationFile << "] does not exist!");
309   if (!assertionPassed) {
310     return false;
311   }
312   bool success = Parser::parseFromFile(configurationFile, this, base);
313   m_isFromFile = success;
314   return success;
315 }
316 
parseFromText(const std::string & configurationsString,Configurations * base)317 bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) {
318   bool success = Parser::parseFromText(configurationsString, this, base);
319   if (success) {
320     m_isFromFile = false;
321   }
322   return success;
323 }
324 
setFromBase(Configurations * base)325 void Configurations::setFromBase(Configurations* base) {
326   if (base == nullptr || base == this) {
327     return;
328   }
329   base::threading::ScopedLock scopedLock(base->lock());
330   for (Configuration*& conf : base->list()) {
331     set(conf);
332   }
333 }
334 
hasConfiguration(ConfigurationType configurationType)335 bool Configurations::hasConfiguration(ConfigurationType configurationType) {
336   base::type::EnumType lIndex = LevelHelper::kMinValid;
337   bool result = false;
338   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
339     if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
340       result = true;
341     }
342     return result;
343   });
344   return result;
345 }
346 
hasConfiguration(Level level,ConfigurationType configurationType)347 bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) {
348   base::threading::ScopedLock scopedLock(lock());
349 #if ELPP_COMPILER_INTEL
350   // We cant specify template types here, Intel C++ throws compilation error
351   // "error: type name is not allowed"
352   return RegistryWithPred::get(level, configurationType) != nullptr;
353 #else
354   return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr;
355 #endif  // ELPP_COMPILER_INTEL
356 }
357 
set(Level level,ConfigurationType configurationType,const std::string & value)358 void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) {
359   base::threading::ScopedLock scopedLock(lock());
360   unsafeSet(level, configurationType, value);  // This is not unsafe anymore as we have locked mutex
361   if (level == Level::Global) {
362     unsafeSetGlobally(configurationType, value, false);  // Again this is not unsafe either
363   }
364 }
365 
set(Configuration * conf)366 void Configurations::set(Configuration* conf) {
367   if (conf == nullptr) {
368     return;
369   }
370   set(conf->level(), conf->configurationType(), conf->value());
371 }
372 
setToDefault(void)373 void Configurations::setToDefault(void) {
374   setGlobally(ConfigurationType::Enabled, std::string("true"), true);
375   setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
376 #if defined(ELPP_NO_LOG_TO_FILE)
377   setGlobally(ConfigurationType::ToFile, std::string("false"), true);
378 #else
379   setGlobally(ConfigurationType::ToFile, std::string("true"), true);
380 #endif // defined(ELPP_NO_LOG_TO_FILE)
381   setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
382   setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true);
383   setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
384   setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
385   setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
386 
387   setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
388   set(Level::Debug, ConfigurationType::Format,
389       std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
390   // INFO and WARNING are set to default by Level::Global
391   set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
392   set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
393   set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
394   set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
395 }
396 
setRemainingToDefault(void)397 void Configurations::setRemainingToDefault(void) {
398   base::threading::ScopedLock scopedLock(lock());
399 #if defined(ELPP_NO_LOG_TO_FILE)
400   unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false"));
401 #else
402   unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
403 #endif // defined(ELPP_NO_LOG_TO_FILE)
404   unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
405   unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
406   unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3"));
407   unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
408   unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
409   unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
410   unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format,
411                       std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
412   // INFO and WARNING are set to default by Level::Global
413   unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
414   unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
415   unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
416   unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format,
417                       std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
418 }
419 
parseFromFile(const std::string & configurationFile,Configurations * sender,Configurations * base)420 bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender,
421     Configurations* base) {
422   sender->setFromBase(base);
423   std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
424   ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
425   bool parsedSuccessfully = false;
426   std::string line = std::string();
427   Level currLevel = Level::Unknown;
428   std::string currConfigStr = std::string();
429   std::string currLevelStr = std::string();
430   while (fileStream_.good()) {
431     std::getline(fileStream_, line);
432     parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
433     ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
434   }
435   return parsedSuccessfully;
436 }
437 
parseFromText(const std::string & configurationsString,Configurations * sender,Configurations * base)438 bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender,
439     Configurations* base) {
440   sender->setFromBase(base);
441   bool parsedSuccessfully = false;
442   std::stringstream ss(configurationsString);
443   std::string line = std::string();
444   Level currLevel = Level::Unknown;
445   std::string currConfigStr = std::string();
446   std::string currLevelStr = std::string();
447   while (std::getline(ss, line)) {
448     parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
449     ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
450   }
451   return parsedSuccessfully;
452 }
453 
ignoreComments(std::string * line)454 void Configurations::Parser::ignoreComments(std::string* line) {
455   std::size_t foundAt = 0;
456   std::size_t quotesStart = line->find("\"");
457   std::size_t quotesEnd = std::string::npos;
458   if (quotesStart != std::string::npos) {
459     quotesEnd = line->find("\"", quotesStart + 1);
460     while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
461       // Do not erase slash yet - we will erase it in parseLine(..) while loop
462       quotesEnd = line->find("\"", quotesEnd + 2);
463     }
464   }
465   if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
466     if (foundAt < quotesEnd) {
467       foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
468     }
469     *line = line->substr(0, foundAt);
470   }
471 }
472 
isLevel(const std::string & line)473 bool Configurations::Parser::isLevel(const std::string& line) {
474   return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
475 }
476 
isComment(const std::string & line)477 bool Configurations::Parser::isComment(const std::string& line) {
478   return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
479 }
480 
isConfig(const std::string & line)481 bool Configurations::Parser::isConfig(const std::string& line) {
482   std::size_t assignment = line.find('=');
483   return line != "" &&
484          ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) &&
485          (assignment != std::string::npos) &&
486          (line.size() > assignment);
487 }
488 
parseLine(std::string * line,std::string * currConfigStr,std::string * currLevelStr,Level * currLevel,Configurations * conf)489 bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr,
490                                        Level* currLevel,
491                                        Configurations* conf) {
492   ConfigurationType currConfig = ConfigurationType::Unknown;
493   std::string currValue = std::string();
494   *line = base::utils::Str::trim(*line);
495   if (isComment(*line)) return true;
496   ignoreComments(line);
497   *line = base::utils::Str::trim(*line);
498   if (line->empty()) {
499     // Comment ignored
500     return true;
501   }
502   if (isLevel(*line)) {
503     if (line->size() <= 2) {
504       return true;
505     }
506     *currLevelStr = line->substr(1, line->size() - 2);
507     *currLevelStr = base::utils::Str::toUpper(*currLevelStr);
508     *currLevelStr = base::utils::Str::trim(*currLevelStr);
509     *currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
510     return true;
511   }
512   if (isConfig(*line)) {
513     std::size_t assignment = line->find('=');
514     *currConfigStr = line->substr(0, assignment);
515     *currConfigStr = base::utils::Str::toUpper(*currConfigStr);
516     *currConfigStr = base::utils::Str::trim(*currConfigStr);
517     currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
518     currValue = line->substr(assignment + 1);
519     currValue = base::utils::Str::trim(currValue);
520     std::size_t quotesStart = currValue.find("\"", 0);
521     std::size_t quotesEnd = std::string::npos;
522     if (quotesStart != std::string::npos) {
523       quotesEnd = currValue.find("\"", quotesStart + 1);
524       while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
525         currValue = currValue.erase(quotesEnd - 1, 1);
526         quotesEnd = currValue.find("\"", quotesEnd + 2);
527       }
528     }
529     if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
530       // Quote provided - check and strip if valid
531       ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in ["
532                   << currConfigStr << "]");
533       ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
534       if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
535         // Explicit check in case if assertion is disabled
536         currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
537       }
538     }
539   }
540   ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
541   ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
542   if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
543     return false;  // unrecognizable level or config
544   }
545   conf->set(*currLevel, currConfig, currValue);
546   return true;
547 }
548 
unsafeSetIfNotExist(Level level,ConfigurationType configurationType,const std::string & value)549 void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
550   Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
551   if (conf == nullptr) {
552     unsafeSet(level, configurationType, value);
553   }
554 }
555 
unsafeSet(Level level,ConfigurationType configurationType,const std::string & value)556 void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
557   Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
558   if (conf == nullptr) {
559     registerNew(new Configuration(level, configurationType, value));
560   } else {
561     conf->setValue(value);
562   }
563   if (level == Level::Global) {
564     unsafeSetGlobally(configurationType, value, false);
565   }
566 }
567 
setGlobally(ConfigurationType configurationType,const std::string & value,bool includeGlobalLevel)568 void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value,
569                                  bool includeGlobalLevel) {
570   if (includeGlobalLevel) {
571     set(Level::Global, configurationType, value);
572   }
573   base::type::EnumType lIndex = LevelHelper::kMinValid;
574   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
575     set(LevelHelper::castFromInt(lIndex), configurationType, value);
576     return false;  // Do not break lambda function yet as we need to set all levels regardless
577   });
578 }
579 
unsafeSetGlobally(ConfigurationType configurationType,const std::string & value,bool includeGlobalLevel)580 void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value,
581                                        bool includeGlobalLevel) {
582   if (includeGlobalLevel) {
583     unsafeSet(Level::Global, configurationType, value);
584   }
585   base::type::EnumType lIndex = LevelHelper::kMinValid;
586   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool  {
587     unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
588     return false;  // Do not break lambda function yet as we need to set all levels regardless
589   });
590 }
591 
592 // LogBuilder
593 
convertToColoredOutput(base::type::string_t * logLine,Level level)594 void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) {
595   if (!m_termSupportsColor) return;
596   const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
597   if (level == Level::Error || level == Level::Fatal)
598     *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
599   else if (level == Level::Warning)
600     *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
601   else if (level == Level::Debug)
602     *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor;
603   else if (level == Level::Info)
604     *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor;
605   else if (level == Level::Trace)
606     *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor;
607 }
608 
609 // Logger
610 
Logger(const std::string & id,base::LogStreamsReferenceMap * logStreamsReference)611 Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
612   m_id(id),
613   m_typedConfigurations(nullptr),
614   m_parentApplicationName(std::string()),
615   m_isConfigured(false),
616   m_logStreamsReference(logStreamsReference) {
617   initUnflushedCount();
618 }
619 
Logger(const std::string & id,const Configurations & configurations,base::LogStreamsReferenceMap * logStreamsReference)620 Logger::Logger(const std::string& id, const Configurations& configurations,
621                base::LogStreamsReferenceMap* logStreamsReference) :
622   m_id(id),
623   m_typedConfigurations(nullptr),
624   m_parentApplicationName(std::string()),
625   m_isConfigured(false),
626   m_logStreamsReference(logStreamsReference) {
627   initUnflushedCount();
628   configure(configurations);
629 }
630 
Logger(const Logger & logger)631 Logger::Logger(const Logger& logger) {
632   base::utils::safeDelete(m_typedConfigurations);
633   m_id = logger.m_id;
634   m_typedConfigurations = logger.m_typedConfigurations;
635   m_parentApplicationName = logger.m_parentApplicationName;
636   m_isConfigured = logger.m_isConfigured;
637   m_configurations = logger.m_configurations;
638   m_unflushedCount = logger.m_unflushedCount;
639   m_logStreamsReference = logger.m_logStreamsReference;
640 }
641 
operator =(const Logger & logger)642 Logger& Logger::operator=(const Logger& logger) {
643   if (&logger != this) {
644     base::utils::safeDelete(m_typedConfigurations);
645     m_id = logger.m_id;
646     m_typedConfigurations = logger.m_typedConfigurations;
647     m_parentApplicationName = logger.m_parentApplicationName;
648     m_isConfigured = logger.m_isConfigured;
649     m_configurations = logger.m_configurations;
650     m_unflushedCount = logger.m_unflushedCount;
651     m_logStreamsReference = logger.m_logStreamsReference;
652   }
653   return *this;
654 }
655 
configure(const Configurations & configurations)656 void Logger::configure(const Configurations& configurations) {
657   m_isConfigured = false;  // we set it to false in case if we fail
658   initUnflushedCount();
659   if (m_typedConfigurations != nullptr) {
660     Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations());
661     if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
662       flush();
663     }
664   }
665   base::threading::ScopedLock scopedLock(lock());
666   if (m_configurations != configurations) {
667     m_configurations.setFromBase(const_cast<Configurations*>(&configurations));
668   }
669   base::utils::safeDelete(m_typedConfigurations);
670   m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
671   resolveLoggerFormatSpec();
672   m_isConfigured = true;
673 }
674 
reconfigure(void)675 void Logger::reconfigure(void) {
676   ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
677   configure(m_configurations);
678 }
679 
isValidId(const std::string & id)680 bool Logger::isValidId(const std::string& id) {
681   for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
682     if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
683       return false;
684     }
685   }
686   return true;
687 }
688 
flush(void)689 void Logger::flush(void) {
690   ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
691   base::threading::ScopedLock scopedLock(lock());
692   base::type::EnumType lIndex = LevelHelper::kMinValid;
693   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
694     flush(LevelHelper::castFromInt(lIndex), nullptr);
695     return false;
696   });
697 }
698 
flush(Level level,base::type::fstream_t * fs)699 void Logger::flush(Level level, base::type::fstream_t* fs) {
700   if (fs == nullptr && m_typedConfigurations->toFile(level)) {
701     fs = m_typedConfigurations->fileStream(level);
702   }
703   if (fs != nullptr) {
704     fs->flush();
705     std::unordered_map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level);
706     if (iter != m_unflushedCount.end()) {
707       iter->second = 0;
708     }
709     Helpers::validateFileRolling(this, level);
710   }
711 }
712 
initUnflushedCount(void)713 void Logger::initUnflushedCount(void) {
714   m_unflushedCount.clear();
715   base::type::EnumType lIndex = LevelHelper::kMinValid;
716   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
717     m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
718     return false;
719   });
720 }
721 
resolveLoggerFormatSpec(void) const722 void Logger::resolveLoggerFormatSpec(void) const {
723   base::type::EnumType lIndex = LevelHelper::kMinValid;
724   LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
725     base::LogFormat* logFormat =
726     const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
727     base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
728     return false;
729   });
730 }
731 
732 // el::base
733 namespace base {
734 
735 // el::base::utils
736 namespace utils {
737 
738 // File
739 
newFileStream(const std::string & filename)740 base::type::fstream_t* File::newFileStream(const std::string& filename) {
741   base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(),
742       base::type::fstream_t::out
743 #if !defined(ELPP_FRESH_LOG_FILE)
744       | base::type::fstream_t::app
745 #endif
746                                                        );
747 #if defined(ELPP_UNICODE)
748   std::locale elppUnicodeLocale("");
749 #  if ELPP_OS_WINDOWS
750   std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>);
751   elppUnicodeLocale = elppUnicodeLocaleWindows;
752 #  endif // ELPP_OS_WINDOWS
753   fs->imbue(elppUnicodeLocale);
754 #endif  // defined(ELPP_UNICODE)
755   if (fs->is_open()) {
756     fs->flush();
757   } else {
758     base::utils::safeDelete(fs);
759     ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
760   }
761   return fs;
762 }
763 
getSizeOfFile(base::type::fstream_t * fs)764 std::size_t File::getSizeOfFile(base::type::fstream_t* fs) {
765   if (fs == nullptr) {
766     return 0;
767   }
768   // Since the file stream is appended to or truncated, the current
769   // offset is the file size.
770   std::size_t size = static_cast<std::size_t>(fs->tellg());
771   return size;
772 }
773 
pathExists(const char * path,bool considerFile)774 bool File::pathExists(const char* path, bool considerFile) {
775   if (path == nullptr) {
776     return false;
777   }
778 #if ELPP_OS_UNIX
779   ELPP_UNUSED(considerFile);
780   struct stat st;
781   return (stat(path, &st) == 0);
782 #elif ELPP_OS_WINDOWS
783   DWORD fileType = GetFileAttributesA(path);
784   if (fileType == INVALID_FILE_ATTRIBUTES) {
785     return false;
786   }
787   return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
788 #endif  // ELPP_OS_UNIX
789 }
790 
createPath(const std::string & path)791 bool File::createPath(const std::string& path) {
792   if (path.empty()) {
793     return false;
794   }
795   if (base::utils::File::pathExists(path.c_str())) {
796     return true;
797   }
798   int status = -1;
799 
800   char* currPath = const_cast<char*>(path.c_str());
801   std::string builtPath = std::string();
802 #if ELPP_OS_UNIX
803   if (path[0] == '/') {
804     builtPath = "/";
805   }
806   currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
807 #elif ELPP_OS_WINDOWS
808   // Use secure functions API
809   char* nextTok_ = nullptr;
810   currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
811   ELPP_UNUSED(nextTok_);
812 #endif  // ELPP_OS_UNIX
813   while (currPath != nullptr) {
814     builtPath.append(currPath);
815     builtPath.append(base::consts::kFilePathSeperator);
816 #if ELPP_OS_UNIX
817     status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
818     currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
819 #elif ELPP_OS_WINDOWS
820     status = _mkdir(builtPath.c_str());
821     currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
822 #endif  // ELPP_OS_UNIX
823   }
824   if (status == -1) {
825     ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
826     return false;
827   }
828   return true;
829 }
830 
extractPathFromFilename(const std::string & fullPath,const char * separator)831 std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) {
832   if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) {
833     return fullPath;
834   }
835   std::size_t lastSlashAt = fullPath.find_last_of(separator);
836   if (lastSlashAt == 0) {
837     return std::string(separator);
838   }
839   return fullPath.substr(0, lastSlashAt + 1);
840 }
841 
buildStrippedFilename(const char * filename,char buff[],std::size_t limit)842 void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) {
843   std::size_t sizeOfFilename = strlen(filename);
844   if (sizeOfFilename >= limit) {
845     filename += (sizeOfFilename - limit);
846     if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
847       filename += 3;  // 3 = '..'
848       STRCAT(buff, "..", limit);
849     }
850   }
851   STRCAT(buff, filename, limit);
852 }
853 
buildBaseFilename(const std::string & fullPath,char buff[],std::size_t limit,const char * separator)854 void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) {
855   const char *filename = fullPath.c_str();
856   std::size_t lastSlashAt = fullPath.find_last_of(separator);
857   filename += lastSlashAt ? lastSlashAt+1 : 0;
858   std::size_t sizeOfFilename = strlen(filename);
859   if (sizeOfFilename >= limit) {
860     filename += (sizeOfFilename - limit);
861     if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
862       filename += 3;  // 3 = '..'
863       STRCAT(buff, "..", limit);
864     }
865   }
866   STRCAT(buff, filename, limit);
867 }
868 
869 // Str
870 
wildCardMatch(const char * str,const char * pattern)871 bool Str::wildCardMatch(const char* str, const char* pattern) {
872   while (*pattern) {
873     switch (*pattern) {
874     case '?':
875       if (!*str)
876         return false;
877       ++str;
878       ++pattern;
879       break;
880     case '*':
881       if (wildCardMatch(str, pattern + 1))
882         return true;
883       if (*str && wildCardMatch(str + 1, pattern))
884         return true;
885       return false;
886     default:
887       if (*str++ != *pattern++)
888         return false;
889       break;
890     }
891   }
892   return !*str && !*pattern;
893 }
894 
ltrim(std::string & str)895 std::string& Str::ltrim(std::string& str) {
896   str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) {
897     return !std::isspace(c);
898   } ));
899   return str;
900 }
901 
rtrim(std::string & str)902 std::string& Str::rtrim(std::string& str) {
903   str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) {
904     return !std::isspace(c);
905   }).base(), str.end());
906   return str;
907 }
908 
trim(std::string & str)909 std::string& Str::trim(std::string& str) {
910   return ltrim(rtrim(str));
911 }
912 
startsWith(const std::string & str,const std::string & start)913 bool Str::startsWith(const std::string& str, const std::string& start) {
914   return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
915 }
916 
endsWith(const std::string & str,const std::string & end)917 bool Str::endsWith(const std::string& str, const std::string& end) {
918   return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
919 }
920 
replaceAll(std::string & str,char replaceWhat,char replaceWith)921 std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) {
922   std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
923   return str;
924 }
925 
replaceAll(std::string & str,const std::string & replaceWhat,const std::string & replaceWith)926 std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat,
927                              const std::string& replaceWith) {
928   if (replaceWhat == replaceWith)
929     return str;
930   std::size_t foundAt = std::string::npos;
931   while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
932     str.replace(foundAt, replaceWhat.length(), replaceWith);
933   }
934   return str;
935 }
936 
replaceFirstWithEscape(base::type::string_t & str,const base::type::string_t & replaceWhat,const base::type::string_t & replaceWith)937 void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
938                                  const base::type::string_t& replaceWith) {
939   std::size_t foundAt = base::type::string_t::npos;
940   while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
941     if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
942       str.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
943       ++foundAt;
944     } else {
945       str.replace(foundAt, replaceWhat.length(), replaceWith);
946       return;
947     }
948   }
949 }
950 #if defined(ELPP_UNICODE)
replaceFirstWithEscape(base::type::string_t & str,const base::type::string_t & replaceWhat,const std::string & replaceWith)951 void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat,
952                                  const std::string& replaceWith) {
953   replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
954 }
955 #endif  // defined(ELPP_UNICODE)
956 
toUpper(std::string & str)957 std::string& Str::toUpper(std::string& str) {
958   std::transform(str.begin(), str.end(), str.begin(),
959   [](char c) {
960     return static_cast<char>(::toupper(c));
961   });
962   return str;
963 }
964 
cStringEq(const char * s1,const char * s2)965 bool Str::cStringEq(const char* s1, const char* s2) {
966   if (s1 == nullptr && s2 == nullptr) return true;
967   if (s1 == nullptr || s2 == nullptr) return false;
968   return strcmp(s1, s2) == 0;
969 }
970 
cStringCaseEq(const char * s1,const char * s2)971 bool Str::cStringCaseEq(const char* s1, const char* s2) {
972   if (s1 == nullptr && s2 == nullptr) return true;
973   if (s1 == nullptr || s2 == nullptr) return false;
974 
975   // With thanks to cygwin for this code
976   int d = 0;
977 
978   while (true) {
979     const int c1 = toupper(*s1++);
980     const int c2 = toupper(*s2++);
981 
982     if (((d = c1 - c2) != 0) || (c2 == '\0')) {
983       break;
984     }
985   }
986 
987   return d == 0;
988 }
989 
contains(const char * str,char c)990 bool Str::contains(const char* str, char c) {
991   for (; *str; ++str) {
992     if (*str == c)
993       return true;
994   }
995   return false;
996 }
997 
convertAndAddToBuff(std::size_t n,int len,char * buf,const char * bufLim,bool zeroPadded)998 char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) {
999   char localBuff[10] = "";
1000   char* p = localBuff + sizeof(localBuff) - 2;
1001   if (n > 0) {
1002     for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
1003       *--p = static_cast<char>(n % 10 + '0');
1004   } else {
1005     *--p = '0';
1006     --len;
1007   }
1008   if (zeroPadded)
1009     while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
1010   return addToBuff(p, buf, bufLim);
1011 }
1012 
addToBuff(const char * str,char * buf,const char * bufLim)1013 char* Str::addToBuff(const char* str, char* buf, const char* bufLim) {
1014   while ((buf < bufLim) && ((*buf = *str++) != '\0'))
1015     ++buf;
1016   return buf;
1017 }
1018 
clearBuff(char buff[],std::size_t lim)1019 char* Str::clearBuff(char buff[], std::size_t lim) {
1020   STRCPY(buff, "", lim);
1021   ELPP_UNUSED(lim);  // For *nix we dont have anything using lim in above STRCPY macro
1022   return buff;
1023 }
1024 
1025 /// @brief Converst wchar* to char*
1026 ///        NOTE: Need to free return value after use!
wcharPtrToCharPtr(const wchar_t * line)1027 char* Str::wcharPtrToCharPtr(const wchar_t* line) {
1028   std::size_t len_ = wcslen(line) + 1;
1029   char* buff_ = static_cast<char*>(malloc(len_ + 1));
1030 #      if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
1031   std::wcstombs(buff_, line, len_);
1032 #      elif ELPP_OS_WINDOWS
1033   std::size_t convCount_ = 0;
1034   mbstate_t mbState_;
1035   ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
1036   wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
1037 #      endif  // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
1038   return buff_;
1039 }
1040 
1041 // OS
1042 
1043 #if ELPP_OS_WINDOWS
1044 /// @brief Gets environment variables for Windows based OS.
1045 ///        We are not using <code>getenv(const char*)</code> because of CRT deprecation
1046 /// @param varname Variable name to get environment variable value for
1047 /// @return If variable exist the value of it otherwise nullptr
getWindowsEnvironmentVariable(const char * varname)1048 const char* OS::getWindowsEnvironmentVariable(const char* varname) {
1049   const DWORD bufferLen = 50;
1050   static char buffer[bufferLen];
1051   if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
1052     return buffer;
1053   }
1054   return nullptr;
1055 }
1056 #endif  // ELPP_OS_WINDOWS
1057 #if ELPP_OS_ANDROID
getProperty(const char * prop)1058 std::string OS::getProperty(const char* prop) {
1059   char propVal[PROP_VALUE_MAX + 1];
1060   int ret = __system_property_get(prop, propVal);
1061   return ret == 0 ? std::string() : std::string(propVal);
1062 }
1063 
getDeviceName(void)1064 std::string OS::getDeviceName(void) {
1065   std::stringstream ss;
1066   std::string manufacturer = getProperty("ro.product.manufacturer");
1067   std::string model = getProperty("ro.product.model");
1068   if (manufacturer.empty() || model.empty()) {
1069     return std::string();
1070   }
1071   ss << manufacturer << "-" << model;
1072   return ss.str();
1073 }
1074 #endif  // ELPP_OS_ANDROID
1075 
getBashOutput(const char * command)1076 const std::string OS::getBashOutput(const char* command) {
1077 #if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
1078   if (command == nullptr) {
1079     return std::string();
1080   }
1081   FILE* proc = nullptr;
1082   if ((proc = popen(command, "r")) == nullptr) {
1083     ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
1084     return std::string();
1085   }
1086   char hBuff[4096];
1087   if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
1088     pclose(proc);
1089     const std::size_t buffLen = strlen(hBuff);
1090     if (buffLen > 0 && hBuff[buffLen - 1] == '\n') {
1091       hBuff[buffLen - 1] = '\0';
1092     }
1093     return std::string(hBuff);
1094   } else {
1095     pclose(proc);
1096   }
1097   return std::string();
1098 #else
1099   ELPP_UNUSED(command);
1100   return std::string();
1101 #endif  // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
1102 }
1103 
getEnvironmentVariable(const char * variableName,const char * defaultVal,const char * alternativeBashCommand)1104 std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal,
1105                                        const char* alternativeBashCommand) {
1106 #if ELPP_OS_UNIX
1107   const char* val = getenv(variableName);
1108 #elif ELPP_OS_WINDOWS
1109   const char* val = getWindowsEnvironmentVariable(variableName);
1110 #endif  // ELPP_OS_UNIX
1111   if ((val == nullptr) || ((strcmp(val, "") == 0))) {
1112 #if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
1113     // Try harder on unix-based systems
1114     std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
1115     if (valBash.empty()) {
1116       return std::string(defaultVal);
1117     } else {
1118       return valBash;
1119     }
1120 #elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
1121     ELPP_UNUSED(alternativeBashCommand);
1122     return std::string(defaultVal);
1123 #endif  // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
1124   }
1125   return std::string(val);
1126 }
1127 
currentUser(void)1128 std::string OS::currentUser(void) {
1129 #if ELPP_OS_UNIX && !ELPP_OS_ANDROID
1130   return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
1131 #elif ELPP_OS_WINDOWS
1132   return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
1133 #elif ELPP_OS_ANDROID
1134   ELPP_UNUSED(base::consts::kUnknownUser);
1135   return std::string("android");
1136 #else
1137   return std::string();
1138 #endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
1139 }
1140 
currentHost(void)1141 std::string OS::currentHost(void) {
1142 #if ELPP_OS_UNIX && !ELPP_OS_ANDROID
1143   return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
1144 #elif ELPP_OS_WINDOWS
1145   return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
1146 #elif ELPP_OS_ANDROID
1147   ELPP_UNUSED(base::consts::kUnknownHost);
1148   return getDeviceName();
1149 #else
1150   return std::string();
1151 #endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
1152 }
1153 
termSupportsColor(void)1154 bool OS::termSupportsColor(void) {
1155   std::string term = getEnvironmentVariable("TERM", "");
1156   return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
1157          || term == "screen" || term == "linux" || term == "cygwin"
1158          || term == "screen-256color";
1159 }
1160 
1161 // DateTime
1162 
gettimeofday(struct timeval * tv)1163 void DateTime::gettimeofday(struct timeval* tv) {
1164 #if ELPP_OS_WINDOWS
1165   if (tv != nullptr) {
1166 #  if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
1167     const unsigned __int64 delta_ = 11644473600000000Ui64;
1168 #  else
1169     const unsigned __int64 delta_ = 11644473600000000ULL;
1170 #  endif  // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
1171     const double secOffSet = 0.000001;
1172     const unsigned long usecOffSet = 1000000;
1173     FILETIME fileTime;
1174     GetSystemTimeAsFileTime(&fileTime);
1175     unsigned __int64 present = 0;
1176     present |= fileTime.dwHighDateTime;
1177     present = present << 32;
1178     present |= fileTime.dwLowDateTime;
1179     present /= 10;  // mic-sec
1180     // Subtract the difference
1181     present -= delta_;
1182     tv->tv_sec = static_cast<long>(present * secOffSet);
1183     tv->tv_usec = static_cast<long>(present % usecOffSet);
1184   }
1185 #else
1186   ::gettimeofday(tv, nullptr);
1187 #endif  // ELPP_OS_WINDOWS
1188 }
1189 
getDateTime(const char * format,const base::SubsecondPrecision * ssPrec)1190 std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) {
1191   struct timeval currTime;
1192   gettimeofday(&currTime);
1193   return timevalToString(currTime, format, ssPrec);
1194 }
1195 
timevalToString(struct timeval tval,const char * format,const el::base::SubsecondPrecision * ssPrec)1196 std::string DateTime::timevalToString(struct timeval tval, const char* format,
1197                                       const el::base::SubsecondPrecision* ssPrec) {
1198   struct ::tm timeInfo;
1199   buildTimeInfo(&tval, &timeInfo);
1200   const int kBuffSize = 30;
1201   char buff_[kBuffSize] = "";
1202   parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset),
1203               ssPrec);
1204   return std::string(buff_);
1205 }
1206 
formatTime(unsigned long long time,base::TimestampUnit timestampUnit)1207 base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
1208   base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
1209   const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
1210   for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
1211     if (time <= base::consts::kTimeFormats[i].value) {
1212       break;
1213     }
1214     if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) {
1215       break;
1216     }
1217     time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value);
1218     unit = base::consts::kTimeFormats[i + 1].unit;
1219   }
1220   base::type::stringstream_t ss;
1221   ss << time << " " << unit;
1222   return ss.str();
1223 }
1224 
getTimeDifference(const struct timeval & endTime,const struct timeval & startTime,base::TimestampUnit timestampUnit)1225 unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime,
1226     base::TimestampUnit timestampUnit) {
1227   if (timestampUnit == base::TimestampUnit::Microsecond) {
1228     return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -
1229                                            static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));
1230   }
1231   // milliseconds
1232   auto conv = [](const struct timeval& tim) {
1233     return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000));
1234   };
1235   return static_cast<unsigned long long>(conv(endTime) - conv(startTime));
1236 }
1237 
buildTimeInfo(struct timeval * currTime,struct::tm * timeInfo)1238 struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
1239 #if ELPP_OS_UNIX
1240   time_t rawTime = currTime->tv_sec;
1241   ::elpptime_r(&rawTime, timeInfo);
1242   return timeInfo;
1243 #else
1244 #  if ELPP_COMPILER_MSVC
1245   ELPP_UNUSED(currTime);
1246   time_t t;
1247 #    if defined(_USE_32BIT_TIME_T)
1248   _time32(&t);
1249 #    else
1250   _time64(&t);
1251 #    endif
1252   elpptime_s(timeInfo, &t);
1253   return timeInfo;
1254 #  else
1255   // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
1256   time_t rawTime = currTime->tv_sec;
1257   struct tm* tmInf = elpptime(&rawTime);
1258   *timeInfo = *tmInf;
1259   return timeInfo;
1260 #  endif  // ELPP_COMPILER_MSVC
1261 #endif  // ELPP_OS_UNIX
1262 }
1263 
parseFormat(char * buf,std::size_t bufSz,const char * format,const struct tm * tInfo,std::size_t msec,const base::SubsecondPrecision * ssPrec)1264 char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
1265                             std::size_t msec, const base::SubsecondPrecision* ssPrec) {
1266   const char* bufLim = buf + bufSz;
1267   for (; *format; ++format) {
1268     if (*format == base::consts::kFormatSpecifierChar) {
1269       switch (*++format) {
1270       case base::consts::kFormatSpecifierChar:  // Escape
1271         break;
1272       case '\0':  // End
1273         --format;
1274         break;
1275       case 'd':  // Day
1276         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
1277         continue;
1278       case 'a':  // Day of week (short)
1279         buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
1280         continue;
1281       case 'A':  // Day of week (long)
1282         buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
1283         continue;
1284       case 'M':  // month
1285         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
1286         continue;
1287       case 'b':  // month (short)
1288         buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
1289         continue;
1290       case 'B':  // month (long)
1291         buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
1292         continue;
1293       case 'y':  // year (two digits)
1294         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
1295         continue;
1296       case 'Y':  // year (four digits)
1297         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
1298         continue;
1299       case 'h':  // hour (12-hour)
1300         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
1301         continue;
1302       case 'H':  // hour (24-hour)
1303         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
1304         continue;
1305       case 'm':  // minute
1306         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
1307         continue;
1308       case 's':  // second
1309         buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
1310         continue;
1311       case 'z':  // subsecond part
1312       case 'g':
1313         buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim);
1314         continue;
1315       case 'F':  // AM/PM
1316         buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
1317         continue;
1318       default:
1319         continue;
1320       }
1321     }
1322     if (buf == bufLim) break;
1323     *buf++ = *format;
1324   }
1325   return buf;
1326 }
1327 
1328 // CommandLineArgs
1329 
setArgs(int argc,char ** argv)1330 void CommandLineArgs::setArgs(int argc, char** argv) {
1331   m_params.clear();
1332   m_paramsWithValue.clear();
1333   if (argc == 0 || argv == nullptr) {
1334     return;
1335   }
1336   m_argc = argc;
1337   m_argv = argv;
1338   for (int i = 1; i < m_argc; ++i) {
1339     const char* v = (strstr(m_argv[i], "="));
1340     if (v != nullptr && strlen(v) > 0) {
1341       std::string key = std::string(m_argv[i]);
1342       key = key.substr(0, key.find_first_of('='));
1343       if (hasParamWithValue(key.c_str())) {
1344         ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value ["
1345                            << getParamValue(key.c_str()) << "]");
1346       } else {
1347         m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
1348       }
1349     }
1350     if (v == nullptr) {
1351       if (hasParam(m_argv[i])) {
1352         ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
1353       } else {
1354         m_params.push_back(std::string(m_argv[i]));
1355       }
1356     }
1357   }
1358 }
1359 
hasParamWithValue(const char * paramKey) const1360 bool CommandLineArgs::hasParamWithValue(const char* paramKey) const {
1361   return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
1362 }
1363 
getParamValue(const char * paramKey) const1364 const char* CommandLineArgs::getParamValue(const char* paramKey) const {
1365   std::unordered_map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
1366   return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
1367 }
1368 
hasParam(const char * paramKey) const1369 bool CommandLineArgs::hasParam(const char* paramKey) const {
1370   return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
1371 }
1372 
empty(void) const1373 bool CommandLineArgs::empty(void) const {
1374   return m_params.empty() && m_paramsWithValue.empty();
1375 }
1376 
size(void) const1377 std::size_t CommandLineArgs::size(void) const {
1378   return m_params.size() + m_paramsWithValue.size();
1379 }
1380 
operator <<(base::type::ostream_t & os,const CommandLineArgs & c)1381 base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
1382   for (int i = 1; i < c.m_argc; ++i) {
1383     os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
1384     if (i < c.m_argc - 1) {
1385       os << ELPP_LITERAL(" ");
1386     }
1387   }
1388   return os;
1389 }
1390 
1391 } // namespace utils
1392 
1393 // el::base::threading
1394 namespace threading {
1395 
1396 #if ELPP_THREADING_ENABLED
1397 #  if ELPP_USE_STD_THREADING
1398 #      if ELPP_ASYNC_LOGGING
msleep(int ms)1399 static void msleep(int ms) {
1400   // Only when async logging enabled - this is because async is strict on compiler
1401 #         if defined(ELPP_NO_SLEEP_FOR)
1402   usleep(ms * 1000);
1403 #         else
1404   std::this_thread::sleep_for(std::chrono::milliseconds(ms));
1405 #         endif  // defined(ELPP_NO_SLEEP_FOR)
1406 }
1407 #      endif  // ELPP_ASYNC_LOGGING
1408 #  endif  // !ELPP_USE_STD_THREADING
1409 #endif  // ELPP_THREADING_ENABLED
1410 
1411 } // namespace threading
1412 
1413 // el::base
1414 
1415 // SubsecondPrecision
1416 
init(int width)1417 void SubsecondPrecision::init(int width) {
1418   if (width < 1 || width > 6) {
1419     width = base::consts::kDefaultSubsecondPrecision;
1420   }
1421   m_width = width;
1422   switch (m_width) {
1423   case 3:
1424     m_offset = 1000;
1425     break;
1426   case 4:
1427     m_offset = 100;
1428     break;
1429   case 5:
1430     m_offset = 10;
1431     break;
1432   case 6:
1433     m_offset = 1;
1434     break;
1435   default:
1436     m_offset = 1000;
1437     break;
1438   }
1439 }
1440 
1441 // LogFormat
1442 
LogFormat(void)1443 LogFormat::LogFormat(void) :
1444   m_level(Level::Unknown),
1445   m_userFormat(base::type::string_t()),
1446   m_format(base::type::string_t()),
1447   m_dateTimeFormat(std::string()),
1448   m_flags(0x0),
1449   m_currentUser(base::utils::OS::currentUser()),
1450   m_currentHost(base::utils::OS::currentHost()) {
1451 }
1452 
LogFormat(Level level,const base::type::string_t & format)1453 LogFormat::LogFormat(Level level, const base::type::string_t& format)
1454   : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()),
1455     m_currentHost(base::utils::OS::currentHost()) {
1456   parseFromFormat(m_userFormat);
1457 }
1458 
LogFormat(const LogFormat & logFormat)1459 LogFormat::LogFormat(const LogFormat& logFormat):
1460   m_level(logFormat.m_level),
1461   m_userFormat(logFormat.m_userFormat),
1462   m_format(logFormat.m_format),
1463   m_dateTimeFormat(logFormat.m_dateTimeFormat),
1464   m_flags(logFormat.m_flags),
1465   m_currentUser(logFormat.m_currentUser),
1466   m_currentHost(logFormat.m_currentHost) {
1467 }
1468 
LogFormat(LogFormat && logFormat)1469 LogFormat::LogFormat(LogFormat&& logFormat) {
1470   m_level = std::move(logFormat.m_level);
1471   m_userFormat = std::move(logFormat.m_userFormat);
1472   m_format = std::move(logFormat.m_format);
1473   m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
1474   m_flags = std::move(logFormat.m_flags);
1475   m_currentUser = std::move(logFormat.m_currentUser);
1476   m_currentHost = std::move(logFormat.m_currentHost);
1477 }
1478 
operator =(const LogFormat & logFormat)1479 LogFormat& LogFormat::operator=(const LogFormat& logFormat) {
1480   if (&logFormat != this) {
1481     m_level = logFormat.m_level;
1482     m_userFormat = logFormat.m_userFormat;
1483     m_dateTimeFormat = logFormat.m_dateTimeFormat;
1484     m_flags = logFormat.m_flags;
1485     m_currentUser = logFormat.m_currentUser;
1486     m_currentHost = logFormat.m_currentHost;
1487   }
1488   return *this;
1489 }
1490 
operator ==(const LogFormat & other)1491 bool LogFormat::operator==(const LogFormat& other) {
1492   return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
1493          m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
1494 }
1495 
1496 /// @brief Updates format to be used while logging.
1497 /// @param userFormat User provided format
parseFromFormat(const base::type::string_t & userFormat)1498 void LogFormat::parseFromFormat(const base::type::string_t& userFormat) {
1499   // We make copy because we will be changing the format
1500   // i.e, removing user provided date format from original format
1501   // and then storing it.
1502   base::type::string_t formatCopy = userFormat;
1503   m_flags = 0x0;
1504   auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
1505     std::size_t foundAt = base::type::string_t::npos;
1506     while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) {
1507       if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
1508         if (hasFlag(flag)) {
1509           // If we already have flag we remove the escape chars so that '%%' is turned to '%'
1510           // even after specifier resolution - this is because we only replaceFirst specifier
1511           formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
1512           ++foundAt;
1513         }
1514       } else {
1515         if (!hasFlag(flag)) addFlag(flag);
1516       }
1517     }
1518   };
1519   conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
1520   conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
1521   conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
1522   conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
1523   conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
1524   conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
1525   conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
1526   conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
1527   conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
1528   conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
1529   conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
1530   conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
1531   conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
1532   conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
1533   // For date/time we need to extract user's date format first
1534   std::size_t dateIndex = std::string::npos;
1535   if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
1536     while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
1537       dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
1538     }
1539     if (dateIndex != std::string::npos) {
1540       addFlag(base::FormatFlags::DateTime);
1541       updateDateFormat(dateIndex, formatCopy);
1542     }
1543   }
1544   m_format = formatCopy;
1545   updateFormatSpec();
1546 }
1547 
updateDateFormat(std::size_t index,base::type::string_t & currFormat)1548 void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) {
1549   if (hasFlag(base::FormatFlags::DateTime)) {
1550     index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
1551   }
1552   const base::type::char_t* ptr = currFormat.c_str() + index;
1553   if ((currFormat.size() > index) && (ptr[0] == '{')) {
1554     // User has provided format for date/time
1555     ++ptr;
1556     int count = 1;  // Start by 1 in order to remove starting brace
1557     std::stringstream ss;
1558     for (; *ptr; ++ptr, ++count) {
1559       if (*ptr == '}') {
1560         ++count;  // In order to remove ending brace
1561         break;
1562       }
1563       ss << static_cast<char>(*ptr);
1564     }
1565     currFormat.erase(index, count);
1566     m_dateTimeFormat = ss.str();
1567   } else {
1568     // No format provided, use default
1569     if (hasFlag(base::FormatFlags::DateTime)) {
1570       m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
1571     }
1572   }
1573 }
1574 
updateFormatSpec(void)1575 void LogFormat::updateFormatSpec(void) {
1576   // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
1577   if (m_level == Level::Debug) {
1578     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1579         base::consts::kDebugLevelLogValue);
1580     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1581         base::consts::kDebugLevelShortLogValue);
1582   } else if (m_level == Level::Info) {
1583     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1584         base::consts::kInfoLevelLogValue);
1585     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1586         base::consts::kInfoLevelShortLogValue);
1587   } else if (m_level == Level::Warning) {
1588     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1589         base::consts::kWarningLevelLogValue);
1590     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1591         base::consts::kWarningLevelShortLogValue);
1592   } else if (m_level == Level::Error) {
1593     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1594         base::consts::kErrorLevelLogValue);
1595     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1596         base::consts::kErrorLevelShortLogValue);
1597   } else if (m_level == Level::Fatal) {
1598     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1599         base::consts::kFatalLevelLogValue);
1600     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1601         base::consts::kFatalLevelShortLogValue);
1602   } else if (m_level == Level::Verbose) {
1603     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1604         base::consts::kVerboseLevelLogValue);
1605     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1606         base::consts::kVerboseLevelShortLogValue);
1607   } else if (m_level == Level::Trace) {
1608     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
1609         base::consts::kTraceLevelLogValue);
1610     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
1611         base::consts::kTraceLevelShortLogValue);
1612   }
1613   if (hasFlag(base::FormatFlags::User)) {
1614     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
1615         m_currentUser);
1616   }
1617   if (hasFlag(base::FormatFlags::Host)) {
1618     base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
1619         m_currentHost);
1620   }
1621   // Ignore Level::Global and Level::Unknown
1622 }
1623 
1624 // TypedConfigurations
1625 
TypedConfigurations(Configurations * configurations,base::LogStreamsReferenceMap * logStreamsReference)1626 TypedConfigurations::TypedConfigurations(Configurations* configurations,
1627     base::LogStreamsReferenceMap* logStreamsReference) {
1628   m_configurations = configurations;
1629   m_logStreamsReference = logStreamsReference;
1630   build(m_configurations);
1631 }
1632 
TypedConfigurations(const TypedConfigurations & other)1633 TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) {
1634   this->m_configurations = other.m_configurations;
1635   this->m_logStreamsReference = other.m_logStreamsReference;
1636   build(m_configurations);
1637 }
1638 
enabled(Level level)1639 bool TypedConfigurations::enabled(Level level) {
1640   return getConfigByVal<bool>(level, &m_enabledMap, "enabled");
1641 }
1642 
toFile(Level level)1643 bool TypedConfigurations::toFile(Level level) {
1644   return getConfigByVal<bool>(level, &m_toFileMap, "toFile");
1645 }
1646 
filename(Level level)1647 const std::string& TypedConfigurations::filename(Level level) {
1648   return getConfigByRef<std::string>(level, &m_filenameMap, "filename");
1649 }
1650 
toStandardOutput(Level level)1651 bool TypedConfigurations::toStandardOutput(Level level) {
1652   return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput");
1653 }
1654 
logFormat(Level level)1655 const base::LogFormat& TypedConfigurations::logFormat(Level level) {
1656   return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat");
1657 }
1658 
subsecondPrecision(Level level)1659 const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) {
1660   return getConfigByRef<base::SubsecondPrecision>(level, &m_subsecondPrecisionMap, "subsecondPrecision");
1661 }
1662 
millisecondsWidth(Level level)1663 const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) {
1664   return getConfigByRef<base::MillisecondsWidth>(level, &m_subsecondPrecisionMap, "millisecondsWidth");
1665 }
1666 
performanceTracking(Level level)1667 bool TypedConfigurations::performanceTracking(Level level) {
1668   return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking");
1669 }
1670 
fileStream(Level level)1671 base::type::fstream_t* TypedConfigurations::fileStream(Level level) {
1672   return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get();
1673 }
1674 
maxLogFileSize(Level level)1675 std::size_t TypedConfigurations::maxLogFileSize(Level level) {
1676   return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize");
1677 }
1678 
logFlushThreshold(Level level)1679 std::size_t TypedConfigurations::logFlushThreshold(Level level) {
1680   return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold");
1681 }
1682 
build(Configurations * configurations)1683 void TypedConfigurations::build(Configurations* configurations) {
1684   base::threading::ScopedLock scopedLock(lock());
1685   auto getBool = [] (std::string boolStr) -> bool {  // Pass by value for trimming
1686     base::utils::Str::trim(boolStr);
1687     return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
1688   };
1689   std::vector<Configuration*> withFileSizeLimit;
1690   for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
1691     Configuration* conf = *it;
1692     // We cannot use switch on strong enums because Intel C++ dont support them yet
1693     if (conf->configurationType() == ConfigurationType::Enabled) {
1694       setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
1695     } else if (conf->configurationType() == ConfigurationType::ToFile) {
1696       setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
1697     } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
1698       setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
1699     } else if (conf->configurationType() == ConfigurationType::Filename) {
1700       // We do not yet configure filename but we will configure in another
1701       // loop. This is because if file cannot be created, we will force ToFile
1702       // to be false. Because configuring logger is not necessarily performance
1703       // sensative operation, we can live with another loop; (by the way this loop
1704       // is not very heavy either)
1705     } else if (conf->configurationType() == ConfigurationType::Format) {
1706       setValue(conf->level(), base::LogFormat(conf->level(),
1707                                               base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
1708     } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) {
1709       setValue(Level::Global,
1710                base::SubsecondPrecision(static_cast<int>(getULong(conf->value()))), &m_subsecondPrecisionMap);
1711     } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
1712       setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
1713     } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
1714       auto v = getULong(conf->value());
1715       setValue(conf->level(), static_cast<std::size_t>(v), &m_maxLogFileSizeMap);
1716       if (v != 0) {
1717         withFileSizeLimit.push_back(conf);
1718       }
1719     } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
1720       setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap);
1721     }
1722   }
1723   // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files
1724   for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
1725     Configuration* conf = *it;
1726     if (conf->configurationType() == ConfigurationType::Filename) {
1727       insertFile(conf->level(), conf->value());
1728     }
1729   }
1730   for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin();
1731        conf != withFileSizeLimit.end(); ++conf) {
1732     // This is not unsafe as mutex is locked in currect scope
1733     unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
1734   }
1735 }
1736 
getULong(std::string confVal)1737 unsigned long TypedConfigurations::getULong(std::string confVal) {
1738   bool valid = true;
1739   base::utils::Str::trim(confVal);
1740   valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
1741   [](char c) {
1742     return !base::utils::Str::isDigit(c);
1743   }) == confVal.end();
1744   if (!valid) {
1745     valid = false;
1746     ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
1747     return 0;
1748   }
1749   return atol(confVal.c_str());
1750 }
1751 
resolveFilename(const std::string & filename)1752 std::string TypedConfigurations::resolveFilename(const std::string& filename) {
1753   std::string resultingFilename = filename;
1754   std::size_t dateIndex = std::string::npos;
1755   std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
1756   if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
1757     while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
1758       dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
1759     }
1760     if (dateIndex != std::string::npos) {
1761       const char* ptr = resultingFilename.c_str() + dateIndex;
1762       // Goto end of specifier
1763       ptr += dateTimeFormatSpecifierStr.size();
1764       std::string fmt;
1765       if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
1766         // User has provided format for date/time
1767         ++ptr;
1768         int count = 1;  // Start by 1 in order to remove starting brace
1769         std::stringstream ss;
1770         for (; *ptr; ++ptr, ++count) {
1771           if (*ptr == '}') {
1772             ++count;  // In order to remove ending brace
1773             break;
1774           }
1775           ss << *ptr;
1776         }
1777         resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
1778         fmt = ss.str();
1779       } else {
1780         fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
1781       }
1782       base::SubsecondPrecision ssPrec(3);
1783       std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec);
1784       base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
1785       base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
1786     }
1787   }
1788   return resultingFilename;
1789 }
1790 
insertFile(Level level,const std::string & fullFilename)1791 void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) {
1792   std::string resolvedFilename = resolveFilename(fullFilename);
1793   if (resolvedFilename.empty()) {
1794     std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
1795               << LevelHelper::convertToString(level) << "]";
1796   }
1797   std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
1798   if (filePath.size() < resolvedFilename.size()) {
1799     base::utils::File::createPath(filePath);
1800   }
1801   auto create = [&](Level level) {
1802     base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
1803     base::type::fstream_t* fs = nullptr;
1804     if (filestreamIter == m_logStreamsReference->end()) {
1805       // We need a completely new stream, nothing to share with
1806       fs = base::utils::File::newFileStream(resolvedFilename);
1807       m_filenameMap.insert(std::make_pair(level, resolvedFilename));
1808       m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
1809       m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
1810     } else {
1811       // Woops! we have an existing one, share it!
1812       m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
1813       m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
1814       fs = filestreamIter->second.get();
1815     }
1816     if (fs == nullptr) {
1817       // We display bad file error from newFileStream()
1818       ELPP_INTERNAL_ERROR("Setting [TO_FILE] of ["
1819                           << LevelHelper::convertToString(level) << "] to FALSE", false);
1820       setValue(level, false, &m_toFileMap);
1821     }
1822   };
1823   // If we dont have file conf for any level, create it for Level::Global first
1824   // otherwise create for specified level
1825   create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
1826 }
1827 
unsafeValidateFileRolling(Level level,const PreRollOutCallback & preRollOutCallback)1828 bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) {
1829   base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
1830   if (fs == nullptr) {
1831     return true;
1832   }
1833   std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
1834   std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
1835   if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
1836     std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
1837     ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
1838                        << LevelHelper::convertToString(level) << "]");
1839     fs->close();
1840     preRollOutCallback(fname.c_str(), currFileSize);
1841     fs->open(fname, std::fstream::out | std::fstream::trunc);
1842     return true;
1843   }
1844   return false;
1845 }
1846 
1847 // RegisteredHitCounters
1848 
validateEveryN(const char * filename,base::type::LineNumber lineNumber,std::size_t n)1849 bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1850   base::threading::ScopedLock scopedLock(lock());
1851   base::HitCounter* counter = get(filename, lineNumber);
1852   if (counter == nullptr) {
1853     registerNew(counter = new base::HitCounter(filename, lineNumber));
1854   }
1855   counter->validateHitCounts(n);
1856   bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
1857   return result;
1858 }
1859 
1860 /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
1861 /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
validateAfterN(const char * filename,base::type::LineNumber lineNumber,std::size_t n)1862 bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1863   base::threading::ScopedLock scopedLock(lock());
1864   base::HitCounter* counter = get(filename, lineNumber);
1865   if (counter == nullptr) {
1866     registerNew(counter = new base::HitCounter(filename, lineNumber));
1867   }
1868   // Do not use validateHitCounts here since we do not want to reset counter here
1869   // Note the >= instead of > because we are incrementing
1870   // after this check
1871   if (counter->hitCounts() >= n)
1872     return true;
1873   counter->increment();
1874   return false;
1875 }
1876 
1877 /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
1878 /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
validateNTimes(const char * filename,base::type::LineNumber lineNumber,std::size_t n)1879 bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) {
1880   base::threading::ScopedLock scopedLock(lock());
1881   base::HitCounter* counter = get(filename, lineNumber);
1882   if (counter == nullptr) {
1883     registerNew(counter = new base::HitCounter(filename, lineNumber));
1884   }
1885   counter->increment();
1886   // Do not use validateHitCounts here since we do not want to reset counter here
1887   if (counter->hitCounts() <= n)
1888     return true;
1889   return false;
1890 }
1891 
1892 // RegisteredLoggers
1893 
RegisteredLoggers(const LogBuilderPtr & defaultLogBuilder)1894 RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
1895   m_defaultLogBuilder(defaultLogBuilder) {
1896   m_defaultConfigurations.setToDefault();
1897 }
1898 
get(const std::string & id,bool forceCreation)1899 Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) {
1900   base::threading::ScopedLock scopedLock(lock());
1901   Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id);
1902   if (logger_ == nullptr && forceCreation) {
1903     bool validId = Logger::isValidId(id);
1904     if (!validId) {
1905       ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
1906       return nullptr;
1907     }
1908     logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
1909     logger_->m_logBuilder = m_defaultLogBuilder;
1910     registerNew(id, logger_);
1911     LoggerRegistrationCallback* callback = nullptr;
1912     for (const std::pair<std::string, base::type::LoggerRegistrationCallbackPtr>& h
1913          : m_loggerRegistrationCallbacks) {
1914       callback = h.second.get();
1915       if (callback != nullptr && callback->enabled()) {
1916         callback->handle(logger_);
1917       }
1918     }
1919   }
1920   return logger_;
1921 }
1922 
remove(const std::string & id)1923 bool RegisteredLoggers::remove(const std::string& id) {
1924   if (id == base::consts::kDefaultLoggerId) {
1925     return false;
1926   }
1927   // get has internal lock
1928   Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
1929   if (logger != nullptr) {
1930     // unregister has internal lock
1931     unregister(logger);
1932   }
1933   return true;
1934 }
1935 
unsafeFlushAll(void)1936 void RegisteredLoggers::unsafeFlushAll(void) {
1937   ELPP_INTERNAL_INFO(1, "Flushing all log files");
1938   for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
1939        it != m_logStreamsReference.end(); ++it) {
1940     if (it->second.get() == nullptr) continue;
1941     it->second->flush();
1942   }
1943 }
1944 
1945 // VRegistry
1946 
VRegistry(base::type::VerboseLevel level,base::type::EnumType * pFlags)1947 VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
1948 }
1949 
1950 /// @brief Sets verbose level. Accepted range is 0-9
setLevel(base::type::VerboseLevel level)1951 void VRegistry::setLevel(base::type::VerboseLevel level) {
1952   base::threading::ScopedLock scopedLock(lock());
1953   if (level > 9)
1954     m_level = base::consts::kMaxVerboseLevel;
1955   else
1956     m_level = level;
1957 }
1958 
setModules(const char * modules)1959 void VRegistry::setModules(const char* modules) {
1960   base::threading::ScopedLock scopedLock(lock());
1961   auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
1962     if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
1963       std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
1964       ss.str(std::string(""));
1965       ss << chr;
1966     }
1967     if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
1968       std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
1969       ss.str(std::string(""));
1970       ss << chr;
1971     }
1972     ss << sfx;
1973   };
1974   auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
1975     if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
1976       addSuffix(ss, ".h", nullptr);
1977       m_modules.insert(std::make_pair(ss.str(), level));
1978       addSuffix(ss, ".c", ".h");
1979       m_modules.insert(std::make_pair(ss.str(), level));
1980       addSuffix(ss, ".cpp", ".c");
1981       m_modules.insert(std::make_pair(ss.str(), level));
1982       addSuffix(ss, ".cc", ".cpp");
1983       m_modules.insert(std::make_pair(ss.str(), level));
1984       addSuffix(ss, ".cxx", ".cc");
1985       m_modules.insert(std::make_pair(ss.str(), level));
1986       addSuffix(ss, ".-inl.h", ".cxx");
1987       m_modules.insert(std::make_pair(ss.str(), level));
1988       addSuffix(ss, ".hxx", ".-inl.h");
1989       m_modules.insert(std::make_pair(ss.str(), level));
1990       addSuffix(ss, ".hpp", ".hxx");
1991       m_modules.insert(std::make_pair(ss.str(), level));
1992       addSuffix(ss, ".hh", ".hpp");
1993     }
1994     m_modules.insert(std::make_pair(ss.str(), level));
1995   };
1996   bool isMod = true;
1997   bool isLevel = false;
1998   std::stringstream ss;
1999   int level = -1;
2000   for (; *modules; ++modules) {
2001     switch (*modules) {
2002     case '=':
2003       isLevel = true;
2004       isMod = false;
2005       break;
2006     case ',':
2007       isLevel = false;
2008       isMod = true;
2009       if (!ss.str().empty() && level != -1) {
2010         insert(ss, static_cast<base::type::VerboseLevel>(level));
2011         ss.str(std::string(""));
2012         level = -1;
2013       }
2014       break;
2015     default:
2016       if (isMod) {
2017         ss << *modules;
2018       } else if (isLevel) {
2019         if (isdigit(*modules)) {
2020           level = static_cast<base::type::VerboseLevel>(*modules) - 48;
2021         }
2022       }
2023       break;
2024     }
2025   }
2026   if (!ss.str().empty() && level != -1) {
2027     insert(ss, static_cast<base::type::VerboseLevel>(level));
2028   }
2029 }
2030 
allowed(base::type::VerboseLevel vlevel,const char * file)2031 bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) {
2032   base::threading::ScopedLock scopedLock(lock());
2033   if (m_modules.empty() || file == nullptr) {
2034     return vlevel <= m_level;
2035   } else {
2036     char baseFilename[base::consts::kSourceFilenameMaxLength] = "";
2037     base::utils::File::buildBaseFilename(file, baseFilename);
2038     std::unordered_map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
2039     for (; it != m_modules.end(); ++it) {
2040       if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) {
2041         return vlevel <= it->second;
2042       }
2043     }
2044     if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
2045       return true;
2046     }
2047     return false;
2048   }
2049 }
2050 
setFromArgs(const base::utils::CommandLineArgs * commandLineArgs)2051 void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
2052   if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
2053       commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
2054     setLevel(base::consts::kMaxVerboseLevel);
2055   } else if (commandLineArgs->hasParamWithValue("--v")) {
2056     setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--v"))));
2057   } else if (commandLineArgs->hasParamWithValue("--V")) {
2058     setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--V"))));
2059   } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
2060     setModules(commandLineArgs->getParamValue("-vmodule"));
2061   } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
2062     setModules(commandLineArgs->getParamValue("-VMODULE"));
2063   }
2064 }
2065 
2066 #if !defined(ELPP_DEFAULT_LOGGING_FLAGS)
2067 #   define ELPP_DEFAULT_LOGGING_FLAGS 0x0
2068 #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS)
2069 // Storage
2070 #if ELPP_ASYNC_LOGGING
Storage(const LogBuilderPtr & defaultLogBuilder,base::IWorker * asyncDispatchWorker)2071 Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
2072 #else
2073 Storage::Storage(const LogBuilderPtr& defaultLogBuilder) :
2074 #endif  // ELPP_ASYNC_LOGGING
2075   m_registeredHitCounters(new base::RegisteredHitCounters()),
2076   m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
2077   m_flags(ELPP_DEFAULT_LOGGING_FLAGS),
2078   m_vRegistry(new base::VRegistry(0, &m_flags)),
2079 #if ELPP_ASYNC_LOGGING
2080   m_asyncLogQueue(new base::AsyncLogQueue()),
2081   m_asyncDispatchWorker(asyncDispatchWorker),
2082 #endif  // ELPP_ASYNC_LOGGING
2083   m_preRollOutCallback(base::defaultPreRollOutCallback) {
2084   // Register default logger
2085   m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
2086   // We register default logger anyway (worse case it's not going to register) just in case
2087   m_registeredLoggers->get("default");
2088   // Register performance logger and reconfigure format
2089   Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
2090   m_registeredLoggers->get("performance");
2091   performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
2092   performanceLogger->reconfigure();
2093 #if defined(ELPP_SYSLOG)
2094   // Register syslog logger and reconfigure format
2095   Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
2096   sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
2097   sysLogLogger->reconfigure();
2098 #endif //  defined(ELPP_SYSLOG)
2099   addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
2100 #if ELPP_ASYNC_LOGGING
2101   installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
2102 #else
2103   installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
2104 #endif  // ELPP_ASYNC_LOGGING
2105 #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2106   installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>
2107   (std::string("DefaultPerformanceTrackingCallback"));
2108 #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2109   ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
2110 #if ELPP_ASYNC_LOGGING
2111   m_asyncDispatchWorker->start();
2112 #endif  // ELPP_ASYNC_LOGGING
2113 }
2114 
~Storage(void)2115 Storage::~Storage(void) {
2116   ELPP_INTERNAL_INFO(4, "Destroying storage");
2117 #if ELPP_ASYNC_LOGGING
2118   ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
2119   uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
2120   installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
2121   ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
2122   base::utils::safeDelete(m_asyncDispatchWorker);
2123   ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
2124   base::utils::safeDelete(m_asyncLogQueue);
2125 #endif  // ELPP_ASYNC_LOGGING
2126   ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
2127   base::utils::safeDelete(m_registeredHitCounters);
2128   ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
2129   base::utils::safeDelete(m_registeredLoggers);
2130   ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
2131   base::utils::safeDelete(m_vRegistry);
2132 }
2133 
hasCustomFormatSpecifier(const char * formatSpecifier)2134 bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) {
2135   base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2136   return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
2137                    formatSpecifier) != m_customFormatSpecifiers.end();
2138 }
2139 
installCustomFormatSpecifier(const CustomFormatSpecifier & customFormatSpecifier)2140 void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
2141   if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
2142     return;
2143   }
2144   base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2145   m_customFormatSpecifiers.push_back(customFormatSpecifier);
2146 }
2147 
uninstallCustomFormatSpecifier(const char * formatSpecifier)2148 bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) {
2149   base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
2150   std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(),
2151       m_customFormatSpecifiers.end(), formatSpecifier);
2152   if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
2153     m_customFormatSpecifiers.erase(it);
2154     return true;
2155   }
2156   return false;
2157 }
2158 
setApplicationArguments(int argc,char ** argv)2159 void Storage::setApplicationArguments(int argc, char** argv) {
2160   m_commandLineArgs.setArgs(argc, argv);
2161   m_vRegistry->setFromArgs(commandLineArgs());
2162   // default log file
2163 #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
2164   if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
2165     Configurations c;
2166     c.setGlobally(ConfigurationType::Filename,
2167                   std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
2168     registeredLoggers()->setDefaultConfigurations(c);
2169     for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
2170          it != registeredLoggers()->end(); ++it) {
2171       it->second->configure(c);
2172     }
2173   }
2174 #endif  // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
2175 #if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
2176   if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
2177     int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
2178     if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) {
2179       m_flags = userInput;
2180     } else {
2181       base::utils::addFlag<base::type::EnumType>(userInput, &m_flags);
2182     }
2183   }
2184 #endif  // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
2185 }
2186 
2187 } // namespace base
2188 
2189 // LogDispatchCallback
handle(const LogDispatchData * data)2190 void LogDispatchCallback::handle(const LogDispatchData* data) {
2191 #if defined(ELPP_THREAD_SAFE)
2192   base::threading::ScopedLock scopedLock(m_fileLocksMapLock);
2193   std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level());
2194   auto lock = m_fileLocks.find(filename);
2195   if (lock == m_fileLocks.end()) {
2196     m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr<base::threading::Mutex>(new base::threading::Mutex)));
2197   }
2198 #endif
2199 }
2200 
fileHandle(const LogDispatchData * data)2201 base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) {
2202   auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()));
2203   return *(it->second.get());
2204 }
2205 
2206 namespace base {
2207 // DefaultLogDispatchCallback
2208 
handle(const LogDispatchData * data)2209 void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
2210 #if defined(ELPP_THREAD_SAFE)
2211   LogDispatchCallback::handle(data);
2212   base::threading::ScopedLock scopedLock(fileHandle(data));
2213 #endif
2214   m_data = data;
2215   dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
2216            m_data->dispatchAction() == base::DispatchAction::NormalLog));
2217 }
2218 
dispatch(base::type::string_t && logLine)2219 void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) {
2220   if (m_data->dispatchAction() == base::DispatchAction::NormalLog) {
2221     if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
2222       base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(
2223                                     m_data->logMessage()->level());
2224       if (fs != nullptr) {
2225         fs->write(logLine.c_str(), logLine.size());
2226         if (fs->fail()) {
2227           ELPP_INTERNAL_ERROR("Unable to write log to file ["
2228                               << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n"
2229                               << "Few possible reasons (could be something else):\n" << "      * Permission denied\n"
2230                               << "      * Disk full\n" << "      * Disk is not writable", true);
2231         } else {
2232           if (ELPP->hasFlag(LoggingFlag::ImmediateFlush)
2233               || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) {
2234             m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs);
2235           }
2236         }
2237       } else {
2238         ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] "
2239                             << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: "
2240                             << m_data->logMessage()->logger()->id() << "]", false);
2241       }
2242     }
2243     if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
2244       if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
2245         m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level());
2246       ELPP_COUT << ELPP_COUT_LINE(logLine);
2247     }
2248   }
2249 #if defined(ELPP_SYSLOG)
2250   else if (m_data->dispatchAction() == base::DispatchAction::SysLog) {
2251     // Determine syslog priority
2252     int sysLogPriority = 0;
2253     if (m_data->logMessage()->level() == Level::Fatal)
2254       sysLogPriority = LOG_EMERG;
2255     else if (m_data->logMessage()->level() == Level::Error)
2256       sysLogPriority = LOG_ERR;
2257     else if (m_data->logMessage()->level() == Level::Warning)
2258       sysLogPriority = LOG_WARNING;
2259     else if (m_data->logMessage()->level() == Level::Info)
2260       sysLogPriority = LOG_INFO;
2261     else if (m_data->logMessage()->level() == Level::Debug)
2262       sysLogPriority = LOG_DEBUG;
2263     else
2264       sysLogPriority = LOG_NOTICE;
2265 #  if defined(ELPP_UNICODE)
2266     char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
2267     syslog(sysLogPriority, "%s", line);
2268     free(line);
2269 #  else
2270     syslog(sysLogPriority, "%s", logLine.c_str());
2271 #  endif
2272   }
2273 #endif  // defined(ELPP_SYSLOG)
2274 }
2275 
2276 #if ELPP_ASYNC_LOGGING
2277 
2278 // AsyncLogDispatchCallback
2279 
handle(const LogDispatchData * data)2280 void AsyncLogDispatchCallback::handle(const LogDispatchData* data) {
2281   base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(),
2282                                  data->dispatchAction() == base::DispatchAction::NormalLog);
2283   if (data->dispatchAction() == base::DispatchAction::NormalLog
2284       && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
2285     if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
2286       data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
2287     ELPP_COUT << ELPP_COUT_LINE(logLine);
2288   }
2289   // Save resources and only queue if we want to write to file otherwise just ignore handler
2290   if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) {
2291     ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine));
2292   }
2293 }
2294 
2295 // AsyncDispatchWorker
AsyncDispatchWorker()2296 AsyncDispatchWorker::AsyncDispatchWorker() {
2297   setContinueRunning(false);
2298 }
2299 
~AsyncDispatchWorker()2300 AsyncDispatchWorker::~AsyncDispatchWorker() {
2301   setContinueRunning(false);
2302   ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue");
2303   clean();
2304   ELPP_INTERNAL_INFO(6, "Log queue cleaned");
2305 }
2306 
clean(void)2307 bool AsyncDispatchWorker::clean(void) {
2308   std::mutex m;
2309   std::unique_lock<std::mutex> lk(m);
2310   cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); });
2311   emptyQueue();
2312   lk.unlock();
2313   cv.notify_one();
2314   return ELPP->asyncLogQueue()->empty();
2315 }
2316 
emptyQueue(void)2317 void AsyncDispatchWorker::emptyQueue(void) {
2318   while (!ELPP->asyncLogQueue()->empty()) {
2319     AsyncLogItem data = ELPP->asyncLogQueue()->next();
2320     handle(&data);
2321     base::threading::msleep(100);
2322   }
2323 }
2324 
start(void)2325 void AsyncDispatchWorker::start(void) {
2326   base::threading::msleep(5000); // 5s (why?)
2327   setContinueRunning(true);
2328   std::thread t1(&AsyncDispatchWorker::run, this);
2329   t1.join();
2330 }
2331 
handle(AsyncLogItem * logItem)2332 void AsyncDispatchWorker::handle(AsyncLogItem* logItem) {
2333   LogDispatchData* data = logItem->data();
2334   LogMessage* logMessage = logItem->logMessage();
2335   Logger* logger = logMessage->logger();
2336   base::TypedConfigurations* conf = logger->typedConfigurations();
2337   base::type::string_t logLine = logItem->logLine();
2338   if (data->dispatchAction() == base::DispatchAction::NormalLog) {
2339     if (conf->toFile(logMessage->level())) {
2340       base::type::fstream_t* fs = conf->fileStream(logMessage->level());
2341       if (fs != nullptr) {
2342         fs->write(logLine.c_str(), logLine.size());
2343         if (fs->fail()) {
2344           ELPP_INTERNAL_ERROR("Unable to write log to file ["
2345                               << conf->filename(logMessage->level()) << "].\n"
2346                               << "Few possible reasons (could be something else):\n" << "      * Permission denied\n"
2347                               << "      * Disk full\n" << "      * Disk is not writable", true);
2348         } else {
2349           if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) {
2350             logger->flush(logMessage->level(), fs);
2351           }
2352         }
2353       } else {
2354         ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] "
2355                             << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false);
2356       }
2357     }
2358   }
2359 #  if defined(ELPP_SYSLOG)
2360   else if (data->dispatchAction() == base::DispatchAction::SysLog) {
2361     // Determine syslog priority
2362     int sysLogPriority = 0;
2363     if (logMessage->level() == Level::Fatal)
2364       sysLogPriority = LOG_EMERG;
2365     else if (logMessage->level() == Level::Error)
2366       sysLogPriority = LOG_ERR;
2367     else if (logMessage->level() == Level::Warning)
2368       sysLogPriority = LOG_WARNING;
2369     else if (logMessage->level() == Level::Info)
2370       sysLogPriority = LOG_INFO;
2371     else if (logMessage->level() == Level::Debug)
2372       sysLogPriority = LOG_DEBUG;
2373     else
2374       sysLogPriority = LOG_NOTICE;
2375 #      if defined(ELPP_UNICODE)
2376     char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
2377     syslog(sysLogPriority, "%s", line);
2378     free(line);
2379 #      else
2380     syslog(sysLogPriority, "%s", logLine.c_str());
2381 #      endif
2382   }
2383 #  endif  // defined(ELPP_SYSLOG)
2384 }
2385 
run(void)2386 void AsyncDispatchWorker::run(void) {
2387   while (continueRunning()) {
2388     emptyQueue();
2389     base::threading::msleep(10); // 10ms
2390   }
2391 }
2392 #endif  // ELPP_ASYNC_LOGGING
2393 
2394 // DefaultLogBuilder
2395 
build(const LogMessage * logMessage,bool appendNewLine) const2396 base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const {
2397   base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations();
2398   const base::LogFormat* logFormat = &tc->logFormat(logMessage->level());
2399   base::type::string_t logLine = logFormat->format();
2400   char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = "";
2401   const char* bufLim = buff + sizeof(buff);
2402   if (logFormat->hasFlag(base::FormatFlags::AppName)) {
2403     // App name
2404     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier,
2405         logMessage->logger()->parentApplicationName());
2406   }
2407   if (logFormat->hasFlag(base::FormatFlags::ThreadId)) {
2408     // Thread ID
2409     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier,
2410         ELPP->getThreadName(base::threading::getCurrentThreadId()));
2411   }
2412   if (logFormat->hasFlag(base::FormatFlags::DateTime)) {
2413     // DateTime
2414     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier,
2415         base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(),
2416                                            &tc->subsecondPrecision(logMessage->level())));
2417   }
2418   if (logFormat->hasFlag(base::FormatFlags::Function)) {
2419     // Function
2420     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func());
2421   }
2422   if (logFormat->hasFlag(base::FormatFlags::File)) {
2423     // File
2424     base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
2425     base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
2426     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff));
2427   }
2428   if (logFormat->hasFlag(base::FormatFlags::FileBase)) {
2429     // FileBase
2430     base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
2431     base::utils::File::buildBaseFilename(logMessage->file(), buff);
2432     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff));
2433   }
2434   if (logFormat->hasFlag(base::FormatFlags::Line)) {
2435     // Line
2436     char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength);
2437     buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false);
2438     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff));
2439   }
2440   if (logFormat->hasFlag(base::FormatFlags::Location)) {
2441     // Location
2442     char* buf = base::utils::Str::clearBuff(buff,
2443                                             base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength);
2444     base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
2445     buf = base::utils::Str::addToBuff(buff, buf, bufLim);
2446     buf = base::utils::Str::addToBuff(":", buf, bufLim);
2447     buf = base::utils::Str::convertAndAddToBuff(logMessage->line(),  base::consts::kSourceLineMaxLength, buf, bufLim,
2448           false);
2449     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff));
2450   }
2451   if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) {
2452     // Verbose level
2453     char* buf = base::utils::Str::clearBuff(buff, 1);
2454     buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false);
2455     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff));
2456   }
2457   if (logFormat->hasFlag(base::FormatFlags::LogMessage)) {
2458     // Log message
2459     base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
2460   }
2461 #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
2462   el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock());
2463   ELPP_UNUSED(lock_);
2464   for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
2465        it != ELPP->customFormatSpecifiers()->end(); ++it) {
2466     std::string fs(it->formatSpecifier());
2467     base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
2468     base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage));
2469   }
2470 #endif  // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
2471   if (appendNewLine) logLine += ELPP_LITERAL("\n");
2472   return logLine;
2473 }
2474 
2475 // LogDispatcher
2476 
dispatch(void)2477 void LogDispatcher::dispatch(void) {
2478   if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
2479     m_proceed = false;
2480   }
2481   if (!m_proceed) {
2482     return;
2483   }
2484 #ifndef ELPP_NO_GLOBAL_LOCK
2485   // see https://github.com/muflihun/easyloggingpp/issues/580
2486   // global lock is turned off by default unless
2487   // ELPP_NO_GLOBAL_LOCK is defined
2488   base::threading::ScopedLock scopedLock(ELPP->lock());
2489 #endif
2490   base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations;
2491   if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
2492     tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback());
2493   }
2494   LogDispatchCallback* callback = nullptr;
2495   LogDispatchData data;
2496   for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h
2497        : ELPP->m_logDispatchCallbacks) {
2498     callback = h.second.get();
2499     if (callback != nullptr && callback->enabled()) {
2500       data.setLogMessage(m_logMessage);
2501       data.setDispatchAction(m_dispatchAction);
2502       callback->handle(&data);
2503     }
2504   }
2505 }
2506 
2507 // MessageBuilder
2508 
initialize(Logger * logger)2509 void MessageBuilder::initialize(Logger* logger) {
2510   m_logger = logger;
2511   m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ?
2512                             ELPP_LITERAL("\n    ") : ELPP_LITERAL(", ");
2513 }
2514 
operator <<(const wchar_t * msg)2515 MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) {
2516   if (msg == nullptr) {
2517     m_logger->stream() << base::consts::kNullPointer;
2518     return *this;
2519   }
2520 #  if defined(ELPP_UNICODE)
2521   m_logger->stream() << msg;
2522 #  else
2523   char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
2524   m_logger->stream() << buff_;
2525   free(buff_);
2526 #  endif
2527   if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
2528     m_logger->stream() << " ";
2529   }
2530   return *this;
2531 }
2532 
2533 // Writer
2534 
construct(Logger * logger,bool needLock)2535 Writer& Writer::construct(Logger* logger, bool needLock) {
2536   m_logger = logger;
2537   initializeLogger(logger->id(), false, needLock);
2538   m_messageBuilder.initialize(m_logger);
2539   return *this;
2540 }
2541 
construct(int count,const char * loggerIds,...)2542 Writer& Writer::construct(int count, const char* loggerIds, ...) {
2543   if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
2544     va_list loggersList;
2545     va_start(loggersList, loggerIds);
2546     const char* id = loggerIds;
2547     m_loggerIds.reserve(count);
2548     for (int i = 0; i < count; ++i) {
2549       m_loggerIds.push_back(std::string(id));
2550       id = va_arg(loggersList, const char*);
2551     }
2552     va_end(loggersList);
2553     initializeLogger(m_loggerIds.at(0));
2554   } else {
2555     initializeLogger(std::string(loggerIds));
2556   }
2557   m_messageBuilder.initialize(m_logger);
2558   return *this;
2559 }
2560 
initializeLogger(const std::string & loggerId,bool lookup,bool needLock)2561 void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) {
2562   if (lookup) {
2563     m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
2564   }
2565   if (m_logger == nullptr) {
2566     {
2567       if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
2568         // Somehow default logger has been unregistered. Not good! Register again
2569         ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
2570       }
2571     }
2572     Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
2573         << "Logger [" << loggerId << "] is not registered yet!";
2574     m_proceed = false;
2575   } else {
2576     if (needLock) {
2577       m_logger->acquireLock();  // This should not be unlocked by checking m_proceed because
2578       // m_proceed can be changed by lines below
2579     }
2580     if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) {
2581       m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) :
2582                   LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel);
2583     } else {
2584       m_proceed = m_logger->enabled(m_level);
2585     }
2586   }
2587 }
2588 
processDispatch()2589 void Writer::processDispatch() {
2590 #if ELPP_LOGGING_ENABLED
2591   if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
2592     bool firstDispatched = false;
2593     base::type::string_t logMessage;
2594     std::size_t i = 0;
2595     do {
2596       if (m_proceed) {
2597         if (firstDispatched) {
2598           m_logger->stream() << logMessage;
2599         } else {
2600           firstDispatched = true;
2601           if (m_loggerIds.size() > 1) {
2602             logMessage = m_logger->stream().str();
2603           }
2604         }
2605         triggerDispatch();
2606       } else if (m_logger != nullptr) {
2607         m_logger->stream().str(ELPP_LITERAL(""));
2608         m_logger->releaseLock();
2609       }
2610       if (i + 1 < m_loggerIds.size()) {
2611         initializeLogger(m_loggerIds.at(i + 1));
2612       }
2613     } while (++i < m_loggerIds.size());
2614   } else {
2615     if (m_proceed) {
2616       triggerDispatch();
2617     } else if (m_logger != nullptr) {
2618       m_logger->stream().str(ELPP_LITERAL(""));
2619       m_logger->releaseLock();
2620     }
2621   }
2622 #else
2623   if (m_logger != nullptr) {
2624     m_logger->stream().str(ELPP_LITERAL(""));
2625     m_logger->releaseLock();
2626   }
2627 #endif // ELPP_LOGGING_ENABLED
2628 }
2629 
triggerDispatch(void)2630 void Writer::triggerDispatch(void) {
2631   if (m_proceed) {
2632     if (m_msg == nullptr) {
2633       LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel,
2634                      m_logger);
2635       base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch();
2636     } else {
2637       base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch();
2638     }
2639   }
2640   if (m_logger != nullptr) {
2641     m_logger->stream().str(ELPP_LITERAL(""));
2642     m_logger->releaseLock();
2643   }
2644   if (m_proceed && m_level == Level::Fatal
2645       && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
2646     base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
2647         << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
2648     std::stringstream reasonStream;
2649     reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
2650                  << " If you wish to disable 'abort on fatal log' please use "
2651                  << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
2652     base::utils::abort(1, reasonStream.str());
2653   }
2654   m_proceed = false;
2655 }
2656 
2657 // PErrorWriter
2658 
~PErrorWriter(void)2659 PErrorWriter::~PErrorWriter(void) {
2660   if (m_proceed) {
2661 #if ELPP_COMPILER_MSVC
2662     char buff[256];
2663     strerror_s(buff, 256, errno);
2664     m_logger->stream() << ": " << buff << " [" << errno << "]";
2665 #else
2666     m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
2667 #endif
2668   }
2669 }
2670 
2671 // PerformanceTracker
2672 
2673 #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2674 
PerformanceTracker(const std::string & blockName,base::TimestampUnit timestampUnit,const std::string & loggerId,bool scopedLog,Level level)2675 PerformanceTracker::PerformanceTracker(const std::string& blockName,
2676                                        base::TimestampUnit timestampUnit,
2677                                        const std::string& loggerId,
2678                                        bool scopedLog, Level level) :
2679   m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
2680   m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
2681 #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2682   // We store it locally so that if user happen to change configuration by the end of scope
2683   // or before calling checkpoint, we still depend on state of configuraton at time of construction
2684   el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
2685   m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
2686   if (m_enabled) {
2687     base::utils::DateTime::gettimeofday(&m_startTime);
2688   }
2689 #endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2690 }
2691 
~PerformanceTracker(void)2692 PerformanceTracker::~PerformanceTracker(void) {
2693 #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2694   if (m_enabled) {
2695     base::threading::ScopedLock scopedLock(lock());
2696     if (m_scopedLog) {
2697       base::utils::DateTime::gettimeofday(&m_endTime);
2698       base::type::string_t formattedTime = getFormattedTimeTaken();
2699       PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
2700       data.init(this);
2701       data.m_formattedTimeTaken = formattedTime;
2702       PerformanceTrackingCallback* callback = nullptr;
2703       for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
2704            : ELPP->m_performanceTrackingCallbacks) {
2705         callback = h.second.get();
2706         if (callback != nullptr && callback->enabled()) {
2707           callback->handle(&data);
2708         }
2709       }
2710     }
2711   }
2712 #endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
2713 }
2714 
checkpoint(const std::string & id,const char * file,base::type::LineNumber line,const char * func)2715 void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line,
2716                                     const char* func) {
2717 #if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2718   if (m_enabled) {
2719     base::threading::ScopedLock scopedLock(lock());
2720     base::utils::DateTime::gettimeofday(&m_endTime);
2721     base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
2722     PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
2723     data.init(this);
2724     data.m_checkpointId = id;
2725     data.m_file = file;
2726     data.m_line = line;
2727     data.m_func = func;
2728     data.m_formattedTimeTaken = formattedTime;
2729     PerformanceTrackingCallback* callback = nullptr;
2730     for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
2731          : ELPP->m_performanceTrackingCallbacks) {
2732       callback = h.second.get();
2733       if (callback != nullptr && callback->enabled()) {
2734         callback->handle(&data);
2735       }
2736     }
2737     base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
2738     m_hasChecked = true;
2739     m_lastCheckpointId = id;
2740   }
2741 #endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
2742   ELPP_UNUSED(id);
2743   ELPP_UNUSED(file);
2744   ELPP_UNUSED(line);
2745   ELPP_UNUSED(func);
2746 }
2747 
getFormattedTimeTaken(struct timeval startTime) const2748 const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const {
2749   if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
2750     base::type::stringstream_t ss;
2751     ss << base::utils::DateTime::getTimeDifference(m_endTime,
2752         startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>
2753             (m_timestampUnit)].unit;
2754     return ss.str();
2755   }
2756   return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
2757          startTime, m_timestampUnit), m_timestampUnit);
2758 }
2759 
2760 #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
2761 
2762 namespace debug {
2763 #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2764 
2765 // StackTrace
2766 
StackTraceEntry(std::size_t index,const std::string & loc,const std::string & demang,const std::string & hex,const std::string & addr)2767 StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang,
2768     const std::string& hex,
2769     const std::string& addr) :
2770   m_index(index),
2771   m_location(loc),
2772   m_demangled(demang),
2773   m_hex(hex),
2774   m_addr(addr) {
2775 }
2776 
operator <<(std::ostream & ss,const StackTrace::StackTraceEntry & si)2777 std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) {
2778   ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr <<
2779      (si.m_demangled.empty() ? "" : ":") << si.m_demangled;
2780   return ss;
2781 }
2782 
operator <<(std::ostream & os,const StackTrace & st)2783 std::ostream& operator<<(std::ostream& os, const StackTrace& st) {
2784   std::vector<StackTrace::StackTraceEntry>::const_iterator it = st.m_stack.begin();
2785   while (it != st.m_stack.end()) {
2786     os << "    " << *it++ << "\n";
2787   }
2788   return os;
2789 }
2790 
generateNew(void)2791 void StackTrace::generateNew(void) {
2792 #if ELPP_STACKTRACE
2793   m_stack.clear();
2794   void* stack[kMaxStack];
2795   unsigned int size = backtrace(stack, kMaxStack);
2796   char** strings = backtrace_symbols(stack, size);
2797   if (size > kStackStart) {  // Skip StackTrace c'tor and generateNew
2798     for (std::size_t i = kStackStart; i < size; ++i) {
2799       std::string mangName;
2800       std::string location;
2801       std::string hex;
2802       std::string addr;
2803 
2804       // entry: 2   crash.cpp.bin                       0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21
2805       const std::string line(strings[i]);
2806       auto p = line.find("_");
2807       if (p != std::string::npos) {
2808         mangName = line.substr(p);
2809         mangName = mangName.substr(0, mangName.find(" +"));
2810       }
2811       p = line.find("0x");
2812       if (p != std::string::npos) {
2813         addr = line.substr(p);
2814         addr = addr.substr(0, addr.find("_"));
2815       }
2816       // Perform demangling if parsed properly
2817       if (!mangName.empty()) {
2818         int status = 0;
2819         char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status);
2820         // if demangling is successful, output the demangled function name
2821         if (status == 0) {
2822           // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
2823           StackTraceEntry entry(i - 1, location, demangName, hex, addr);
2824           m_stack.push_back(entry);
2825         } else {
2826           // Not successful - we will use mangled name
2827           StackTraceEntry entry(i - 1, location, mangName, hex, addr);
2828           m_stack.push_back(entry);
2829         }
2830         free(demangName);
2831       } else {
2832         StackTraceEntry entry(i - 1, line);
2833         m_stack.push_back(entry);
2834       }
2835     }
2836   }
2837   free(strings);
2838 #else
2839   ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler");
2840 #endif  // ELPP_STACKTRACE
2841 }
2842 
2843 // Static helper functions
2844 
crashReason(int sig)2845 static std::string crashReason(int sig) {
2846   std::stringstream ss;
2847   bool foundReason = false;
2848   for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) {
2849     if (base::consts::kCrashSignals[i].numb == sig) {
2850       ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal";
2851       if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) {
2852         ss << std::endl <<
2853            "    " << base::consts::kCrashSignals[i].brief << std::endl <<
2854            "    " << base::consts::kCrashSignals[i].detail;
2855       }
2856       foundReason = true;
2857     }
2858   }
2859   if (!foundReason) {
2860     ss << "Application has crashed due to unknown signal [" << sig << "]";
2861   }
2862   return ss.str();
2863 }
2864 /// @brief Logs reason of crash from sig
logCrashReason(int sig,bool stackTraceIfAvailable,Level level,const char * logger)2865 static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
2866   std::stringstream ss;
2867   ss << "CRASH HANDLED; ";
2868   ss << crashReason(sig);
2869 #if ELPP_STACKTRACE
2870   if (stackTraceIfAvailable) {
2871     ss << std::endl << "    ======= Backtrace: =========" << std::endl << base::debug::StackTrace();
2872   }
2873 #else
2874   ELPP_UNUSED(stackTraceIfAvailable);
2875 #endif  // ELPP_STACKTRACE
2876   ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str();
2877 }
2878 
crashAbort(int sig)2879 static inline void crashAbort(int sig) {
2880   base::utils::abort(sig, std::string());
2881 }
2882 
2883 /// @brief Default application crash handler
2884 ///
2885 /// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program.
defaultCrashHandler(int sig)2886 static inline void defaultCrashHandler(int sig) {
2887   base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId);
2888   base::debug::crashAbort(sig);
2889 }
2890 
2891 // CrashHandler
2892 
CrashHandler(bool useDefault)2893 CrashHandler::CrashHandler(bool useDefault) {
2894   if (useDefault) {
2895     setHandler(defaultCrashHandler);
2896   }
2897 }
2898 
setHandler(const Handler & cHandler)2899 void CrashHandler::setHandler(const Handler& cHandler) {
2900   m_handler = cHandler;
2901 #if defined(ELPP_HANDLE_SIGABRT)
2902   int i = 0;  // SIGABRT is at base::consts::kCrashSignals[0]
2903 #else
2904   int i = 1;
2905 #endif  // defined(ELPP_HANDLE_SIGABRT)
2906   for (; i < base::consts::kCrashSignalsCount; ++i) {
2907     m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler);
2908   }
2909 }
2910 
2911 #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2912 }  // namespace debug
2913 } // namespace base
2914 
2915 // el
2916 
2917 // Helpers
2918 
2919 #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2920 
crashAbort(int sig,const char * sourceFile,unsigned int long line)2921 void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) {
2922   std::stringstream ss;
2923   ss << base::debug::crashReason(sig).c_str();
2924   ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
2925   if (sourceFile != nullptr && strlen(sourceFile) > 0) {
2926     ss << " - Source: " << sourceFile;
2927     if (line > 0)
2928       ss << ":" << line;
2929     else
2930       ss << " (line number not specified)";
2931   }
2932   base::utils::abort(sig, ss.str());
2933 }
2934 
logCrashReason(int sig,bool stackTraceIfAvailable,Level level,const char * logger)2935 void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
2936   el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
2937 }
2938 
2939 #endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
2940 
2941 // Loggers
2942 
getLogger(const std::string & identity,bool registerIfNotAvailable)2943 Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) {
2944   return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
2945 }
2946 
setDefaultLogBuilder(el::LogBuilderPtr & logBuilderPtr)2947 void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) {
2948   ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr);
2949 }
2950 
unregisterLogger(const std::string & identity)2951 bool Loggers::unregisterLogger(const std::string& identity) {
2952   return ELPP->registeredLoggers()->remove(identity);
2953 }
2954 
hasLogger(const std::string & identity)2955 bool Loggers::hasLogger(const std::string& identity) {
2956   return ELPP->registeredLoggers()->has(identity);
2957 }
2958 
reconfigureLogger(Logger * logger,const Configurations & configurations)2959 Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) {
2960   if (!logger) return nullptr;
2961   logger->configure(configurations);
2962   return logger;
2963 }
2964 
reconfigureLogger(const std::string & identity,const Configurations & configurations)2965 Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) {
2966   return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
2967 }
2968 
reconfigureLogger(const std::string & identity,ConfigurationType configurationType,const std::string & value)2969 Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType,
2970                                    const std::string& value) {
2971   Logger* logger = Loggers::getLogger(identity);
2972   if (logger == nullptr) {
2973     return nullptr;
2974   }
2975   logger->configurations()->set(Level::Global, configurationType, value);
2976   logger->reconfigure();
2977   return logger;
2978 }
2979 
reconfigureAllLoggers(const Configurations & configurations)2980 void Loggers::reconfigureAllLoggers(const Configurations& configurations) {
2981   for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
2982        it != ELPP->registeredLoggers()->end(); ++it) {
2983     Loggers::reconfigureLogger(it->second, configurations);
2984   }
2985 }
2986 
reconfigureAllLoggers(Level level,ConfigurationType configurationType,const std::string & value)2987 void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType,
2988                                     const std::string& value) {
2989   for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
2990        it != ELPP->registeredLoggers()->end(); ++it) {
2991     Logger* logger = it->second;
2992     logger->configurations()->set(level, configurationType, value);
2993     logger->reconfigure();
2994   }
2995 }
2996 
setDefaultConfigurations(const Configurations & configurations,bool reconfigureExistingLoggers)2997 void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) {
2998   ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
2999   if (reconfigureExistingLoggers) {
3000     Loggers::reconfigureAllLoggers(configurations);
3001   }
3002 }
3003 
defaultConfigurations(void)3004 const Configurations* Loggers::defaultConfigurations(void) {
3005   return ELPP->registeredLoggers()->defaultConfigurations();
3006 }
3007 
logStreamsReference(void)3008 const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) {
3009   return ELPP->registeredLoggers()->logStreamsReference();
3010 }
3011 
defaultTypedConfigurations(void)3012 base::TypedConfigurations Loggers::defaultTypedConfigurations(void) {
3013   return base::TypedConfigurations(
3014            ELPP->registeredLoggers()->defaultConfigurations(),
3015            ELPP->registeredLoggers()->logStreamsReference());
3016 }
3017 
populateAllLoggerIds(std::vector<std::string> * targetList)3018 std::vector<std::string>* Loggers::populateAllLoggerIds(std::vector<std::string>* targetList) {
3019   targetList->clear();
3020   for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
3021        it != ELPP->registeredLoggers()->list().end(); ++it) {
3022     targetList->push_back(it->first);
3023   }
3024   return targetList;
3025 }
3026 
configureFromGlobal(const char * globalConfigurationFilePath)3027 void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) {
3028   std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
3029   ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath
3030               << "] for parsing.");
3031   std::string line = std::string();
3032   std::stringstream ss;
3033   Logger* logger = nullptr;
3034   auto configure = [&](void) {
3035     ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str()
3036                        << "\n--------------");
3037     Configurations c;
3038     c.parseFromText(ss.str());
3039     logger->configure(c);
3040   };
3041   while (gcfStream.good()) {
3042     std::getline(gcfStream, line);
3043     ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
3044     base::utils::Str::trim(line);
3045     if (Configurations::Parser::isComment(line)) continue;
3046     Configurations::Parser::ignoreComments(&line);
3047     base::utils::Str::trim(line);
3048     if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) {
3049       if (!ss.str().empty() && logger != nullptr) {
3050         configure();
3051       }
3052       ss.str(std::string(""));
3053       line = line.substr(2);
3054       base::utils::Str::trim(line);
3055       if (line.size() > 1) {
3056         ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
3057         logger = getLogger(line);
3058       }
3059     } else {
3060       ss << line << "\n";
3061     }
3062   }
3063   if (!ss.str().empty() && logger != nullptr) {
3064     configure();
3065   }
3066 }
3067 
configureFromArg(const char * argKey)3068 bool Loggers::configureFromArg(const char* argKey) {
3069 #if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
3070   ELPP_UNUSED(argKey);
3071 #else
3072   if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) {
3073     return false;
3074   }
3075   configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
3076 #endif  // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
3077   return true;
3078 }
3079 
flushAll(void)3080 void Loggers::flushAll(void) {
3081   ELPP->registeredLoggers()->flushAll();
3082 }
3083 
setVerboseLevel(base::type::VerboseLevel level)3084 void Loggers::setVerboseLevel(base::type::VerboseLevel level) {
3085   ELPP->vRegistry()->setLevel(level);
3086 }
3087 
verboseLevel(void)3088 base::type::VerboseLevel Loggers::verboseLevel(void) {
3089   return ELPP->vRegistry()->level();
3090 }
3091 
setVModules(const char * modules)3092 void Loggers::setVModules(const char* modules) {
3093   if (ELPP->vRegistry()->vModulesEnabled()) {
3094     ELPP->vRegistry()->setModules(modules);
3095   }
3096 }
3097 
clearVModules(void)3098 void Loggers::clearVModules(void) {
3099   ELPP->vRegistry()->clearModules();
3100 }
3101 
3102 // VersionInfo
3103 
version(void)3104 const std::string VersionInfo::version(void) {
3105   return std::string("9.96.4");
3106 }
3107 /// @brief Release date of current version
releaseDate(void)3108 const std::string VersionInfo::releaseDate(void) {
3109   return std::string("03-04-2018 1019hrs");
3110 }
3111 
3112 } // namespace el