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