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) |
20 | INITIALIZE_EASYLOGGINGPP |
21 | #endif |
22 | |
23 | namespace el { |
24 | |
25 | // el::base |
26 | namespace base { |
27 | // el::base::consts |
28 | namespace 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 |
32 | static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO" ); |
33 | static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG" ); |
34 | static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING" ); |
35 | static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR" ); |
36 | static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL" ); |
37 | static const base::type::char_t* kVerboseLevelLogValue = |
38 | ELPP_LITERAL("VERBOSE" ); // will become VERBOSE-x where x = verbose level |
39 | static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE" ); |
40 | static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I" ); |
41 | static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D" ); |
42 | static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W" ); |
43 | static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E" ); |
44 | static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F" ); |
45 | static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V" ); |
46 | static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T" ); |
47 | // Format specifiers - These are used to define log format |
48 | static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app" ); |
49 | static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger" ); |
50 | static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread" ); |
51 | static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level" ); |
52 | static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort" ); |
53 | static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime" ); |
54 | static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file" ); |
55 | static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase" ); |
56 | static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line" ); |
57 | static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc" ); |
58 | static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func" ); |
59 | static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user" ); |
60 | static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host" ); |
61 | static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg" ); |
62 | static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel" ); |
63 | static const char* kDateTimeFormatSpecifierForFilename = "%datetime" ; |
64 | // Date/time |
65 | static const char* kDays[7] = { "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" }; |
66 | static const char* kDaysAbbrev[7] = { "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" }; |
67 | static const char* kMonths[12] = { "January" , "February" , "March" , "Apri" , "May" , "June" , "July" , "August" , |
68 | "September" , "October" , "November" , "December" |
69 | }; |
70 | static const char* kMonthsAbbrev[12] = { "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec" }; |
71 | static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g" ; |
72 | static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m" ; |
73 | static const int kYearBase = 1900; |
74 | static const char* kAm = "AM" ; |
75 | static const char* kPm = "PM" ; |
76 | // Miscellaneous constants |
77 | |
78 | static const char* kNullPointer = "nullptr" ; |
79 | #if ELPP_VARIADIC_TEMPLATES_SUPPORTED |
80 | #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED |
81 | static const base::type::VerboseLevel kMaxVerboseLevel = 9; |
82 | static const char* kUnknownUser = "user" ; |
83 | static 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 |
90 | static const char* kDefaultLogFile = "/dev/null" ; |
91 | # elif ELPP_OS_WINDOWS |
92 | static const char* kDefaultLogFile = "nul" ; |
93 | # endif // ELPP_OS_UNIX |
94 | #elif defined(ELPP_DEFAULT_LOG_FILE) |
95 | static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; |
96 | #else |
97 | static 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) |
102 | static const char* kDefaultLogFileParam = "--default-log-file" ; |
103 | #endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) |
104 | #if defined(ELPP_LOGGING_FLAGS_FROM_ARG) |
105 | static const char* kLoggingFlagsParam = "--logging-flags" ; |
106 | #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) |
107 | static const char* kValidLoggerIdSymbols = |
108 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._" ; |
109 | static const char* = "##" ; |
110 | static const char* kConfigurationLevel = "*" ; |
111 | static const char* kConfigurationLoggerId = "--" ; |
112 | } |
113 | // el::base::utils |
114 | namespace utils { |
115 | |
116 | /// @brief Aborts application due with user-defined status |
117 | static 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 | |
136 | const 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 | |
149 | struct StringToLevelItem { |
150 | const char* levelString; |
151 | Level level; |
152 | }; |
153 | |
154 | static 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 | |
165 | Level 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 | |
174 | void 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 | |
186 | const 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 | |
200 | struct ConfigurationStringToTypeItem { |
201 | const char* configString; |
202 | ConfigurationType configType; |
203 | }; |
204 | |
205 | static 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 | |
218 | ConfigurationType 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 | |
227 | void 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 | |
239 | Configuration::Configuration(const Configuration& c) : |
240 | m_level(c.m_level), |
241 | m_configurationType(c.m_configurationType), |
242 | m_value(c.m_value) { |
243 | } |
244 | |
245 | Configuration& 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 |
255 | Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : |
256 | m_level(level), |
257 | m_configurationType(configurationType), |
258 | m_value(value) { |
259 | } |
260 | |
261 | void 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. |
268 | Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : |
269 | m_level(level), |
270 | m_configurationType(configurationType) { |
271 | } |
272 | |
273 | bool Configuration::Predicate::operator()(const Configuration* conf) const { |
274 | return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); |
275 | } |
276 | |
277 | // Configurations |
278 | |
279 | Configurations::Configurations(void) : |
280 | m_configurationFile(std::string()), |
281 | m_isFromFile(false) { |
282 | } |
283 | |
284 | Configurations::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 | |
294 | bool 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 | |
308 | bool 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 | |
316 | void 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 | |
326 | bool 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 | |
338 | bool 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 | |
349 | void 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 | |
357 | void Configurations::set(Configuration* conf) { |
358 | if (conf == nullptr) { |
359 | return; |
360 | } |
361 | set(conf->level(), conf->configurationType(), conf->value()); |
362 | } |
363 | |
364 | void 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 | |
388 | void 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 | |
411 | bool 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 | |
429 | bool 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 | |
445 | void Configurations::Parser::(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 | |
464 | bool Configurations::Parser::isLevel(const std::string& line) { |
465 | return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); |
466 | } |
467 | |
468 | bool Configurations::Parser::(const std::string& line) { |
469 | return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); |
470 | } |
471 | |
472 | bool 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 | |
480 | bool 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 | |
540 | void 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 | |
547 | void 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 | |
559 | void 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 | |
571 | void 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 | |
585 | void 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 | |
602 | Logger::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 | |
611 | Logger::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 | |
622 | Logger::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 | |
633 | Logger& 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 | |
647 | void 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 | |
666 | void Logger::reconfigure(void) { |
667 | ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]" ); |
668 | configure(m_configurations); |
669 | } |
670 | |
671 | bool 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 | |
680 | void 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 | |
690 | void 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 | |
704 | void 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 | |
713 | void 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 |
724 | namespace base { |
725 | |
726 | // el::base::utils |
727 | namespace utils { |
728 | |
729 | // File |
730 | |
731 | base::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 | |
755 | std::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 | |
765 | bool 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 | |
782 | bool 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 | |
822 | std::string File::(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 | |
833 | void 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 | |
845 | void 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 | |
862 | bool 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 | |
886 | std::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 | |
893 | std::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 | |
900 | std::string& Str::trim(std::string& str) { |
901 | return ltrim(rtrim(str)); |
902 | } |
903 | |
904 | bool 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 | |
908 | bool 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 | |
912 | std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { |
913 | std::replace(str.begin(), str.end(), replaceWhat, replaceWith); |
914 | return str; |
915 | } |
916 | |
917 | std::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 | |
928 | void 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) |
942 | void 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 | |
948 | std::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 | |
956 | bool 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 | |
962 | bool 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 | |
981 | bool Str::contains(const char* str, char c) { |
982 | for (; *str; ++str) { |
983 | if (*str == c) |
984 | return true; |
985 | } |
986 | return false; |
987 | } |
988 | |
989 | char* 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 | |
1004 | char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { |
1005 | while ((buf < bufLim) && ((*buf = *str++) != '\0')) |
1006 | ++buf; |
1007 | return buf; |
1008 | } |
1009 | |
1010 | char* 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! |
1018 | char* 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 |
1039 | const 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 |
1049 | std::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 | |
1055 | std::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 | |
1067 | const 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 | |
1095 | std::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 | |
1119 | std::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 | |
1132 | std::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 | |
1145 | bool 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 | |
1154 | void 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 | |
1181 | std::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 | |
1187 | std::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 | |
1198 | base::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 | |
1216 | unsigned 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 | |
1229 | struct ::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 | |
1255 | char* 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 | |
1321 | void 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 | |
1351 | bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { |
1352 | return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); |
1353 | } |
1354 | |
1355 | const 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 | |
1360 | bool CommandLineArgs::hasParam(const char* paramKey) const { |
1361 | return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); |
1362 | } |
1363 | |
1364 | bool CommandLineArgs::empty(void) const { |
1365 | return m_params.empty() && m_paramsWithValue.empty(); |
1366 | } |
1367 | |
1368 | std::size_t CommandLineArgs::size(void) const { |
1369 | return m_params.size() + m_paramsWithValue.size(); |
1370 | } |
1371 | |
1372 | base::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 |
1385 | namespace threading { |
1386 | |
1387 | #if ELPP_THREADING_ENABLED |
1388 | # if ELPP_USE_STD_THREADING |
1389 | # if ELPP_ASYNC_LOGGING |
1390 | static 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 | |
1408 | void 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 | |
1434 | LogFormat::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 | |
1444 | LogFormat::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 | |
1450 | LogFormat::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 | |
1460 | LogFormat::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 | |
1470 | LogFormat& 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 | |
1482 | bool 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 |
1489 | void 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 | |
1539 | void 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 | |
1566 | void 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 | |
1617 | TypedConfigurations::TypedConfigurations(Configurations* configurations, |
1618 | base::LogStreamsReferenceMap* logStreamsReference) { |
1619 | m_configurations = configurations; |
1620 | m_logStreamsReference = logStreamsReference; |
1621 | build(m_configurations); |
1622 | } |
1623 | |
1624 | TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { |
1625 | this->m_configurations = other.m_configurations; |
1626 | this->m_logStreamsReference = other.m_logStreamsReference; |
1627 | build(m_configurations); |
1628 | } |
1629 | |
1630 | bool TypedConfigurations::enabled(Level level) { |
1631 | return getConfigByVal<bool>(level, &m_enabledMap, "enabled" ); |
1632 | } |
1633 | |
1634 | bool TypedConfigurations::toFile(Level level) { |
1635 | return getConfigByVal<bool>(level, &m_toFileMap, "toFile" ); |
1636 | } |
1637 | |
1638 | const std::string& TypedConfigurations::filename(Level level) { |
1639 | return getConfigByRef<std::string>(level, &m_filenameMap, "filename" ); |
1640 | } |
1641 | |
1642 | bool TypedConfigurations::toStandardOutput(Level level) { |
1643 | return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput" ); |
1644 | } |
1645 | |
1646 | const base::LogFormat& TypedConfigurations::logFormat(Level level) { |
1647 | return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat" ); |
1648 | } |
1649 | |
1650 | const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { |
1651 | return getConfigByRef<base::SubsecondPrecision>(level, &m_subsecondPrecisionMap, "subsecondPrecision" ); |
1652 | } |
1653 | |
1654 | const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { |
1655 | return getConfigByRef<base::MillisecondsWidth>(level, &m_subsecondPrecisionMap, "millisecondsWidth" ); |
1656 | } |
1657 | |
1658 | bool TypedConfigurations::performanceTracking(Level level) { |
1659 | return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking" ); |
1660 | } |
1661 | |
1662 | base::type::fstream_t* TypedConfigurations::fileStream(Level level) { |
1663 | return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream" ).get(); |
1664 | } |
1665 | |
1666 | std::size_t TypedConfigurations::maxLogFileSize(Level level) { |
1667 | return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize" ); |
1668 | } |
1669 | |
1670 | std::size_t TypedConfigurations::logFlushThreshold(Level level) { |
1671 | return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold" ); |
1672 | } |
1673 | |
1674 | void 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 | |
1728 | unsigned 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 | |
1743 | std::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 | |
1782 | void 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 | |
1819 | bool 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 | |
1840 | bool 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 |
1853 | bool 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 |
1870 | bool 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 | |
1885 | RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : |
1886 | m_defaultLogBuilder(defaultLogBuilder) { |
1887 | m_defaultConfigurations.setToDefault(); |
1888 | } |
1889 | |
1890 | Logger* 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 | |
1914 | bool 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 | |
1927 | void 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 | |
1938 | VRegistry::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 |
1942 | void 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 | |
1950 | void 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 | |
2022 | bool 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 | |
2042 | void 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 |
2062 | Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : |
2063 | #else |
2064 | Storage::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 | |
2112 | Storage::~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 | |
2131 | bool 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 | |
2137 | void 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 | |
2145 | bool 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 | |
2156 | void 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 |
2187 | void 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 | |
2200 | base::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 | |
2205 | namespace base { |
2206 | // DefaultLogDispatchCallback |
2207 | |
2208 | void 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 | |
2218 | void 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 | |
2279 | void 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 |
2295 | AsyncDispatchWorker::AsyncDispatchWorker() { |
2296 | setContinueRunning(false); |
2297 | } |
2298 | |
2299 | AsyncDispatchWorker::~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 | |
2306 | bool 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 | |
2316 | void 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 | |
2324 | void 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 | |
2331 | void 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 | |
2385 | void 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 | |
2395 | base::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 | |
2476 | void 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 | |
2508 | void MessageBuilder::initialize(Logger* logger) { |
2509 | m_logger = logger; |
2510 | m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? |
2511 | ELPP_LITERAL("\n " ) : ELPP_LITERAL(", " ); |
2512 | } |
2513 | |
2514 | MessageBuilder& 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 | |
2534 | Writer& 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 | |
2541 | Writer& 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 | |
2560 | void 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 | |
2588 | void 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 | |
2629 | void 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 | |
2658 | PErrorWriter::~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 | |
2674 | PerformanceTracker::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 | |
2691 | PerformanceTracker::~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 | |
2714 | void 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 | |
2747 | const 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 | |
2761 | namespace debug { |
2762 | #if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) |
2763 | |
2764 | // StackTrace |
2765 | |
2766 | StackTrace::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 | |
2776 | std::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 | |
2782 | std::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 | |
2790 | void 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 | |
2844 | static 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 |
2864 | static 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 | |
2881 | static 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. |
2888 | static 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 | |
2895 | CrashHandler::CrashHandler(bool useDefault) { |
2896 | if (useDefault) { |
2897 | setHandler(defaultCrashHandler); |
2898 | } |
2899 | } |
2900 | |
2901 | void 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 | |
2923 | void 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 | |
2937 | void 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 | |
2945 | Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { |
2946 | return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); |
2947 | } |
2948 | |
2949 | void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { |
2950 | ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); |
2951 | } |
2952 | |
2953 | bool Loggers::unregisterLogger(const std::string& identity) { |
2954 | return ELPP->registeredLoggers()->remove(identity); |
2955 | } |
2956 | |
2957 | bool Loggers::hasLogger(const std::string& identity) { |
2958 | return ELPP->registeredLoggers()->has(identity); |
2959 | } |
2960 | |
2961 | Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { |
2962 | if (!logger) return nullptr; |
2963 | logger->configure(configurations); |
2964 | return logger; |
2965 | } |
2966 | |
2967 | Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { |
2968 | return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); |
2969 | } |
2970 | |
2971 | Logger* 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 | |
2982 | void 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 | |
2989 | void 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 | |
2999 | void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { |
3000 | ELPP->registeredLoggers()->setDefaultConfigurations(configurations); |
3001 | if (reconfigureExistingLoggers) { |
3002 | Loggers::reconfigureAllLoggers(configurations); |
3003 | } |
3004 | } |
3005 | |
3006 | const Configurations* Loggers::defaultConfigurations(void) { |
3007 | return ELPP->registeredLoggers()->defaultConfigurations(); |
3008 | } |
3009 | |
3010 | const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) { |
3011 | return ELPP->registeredLoggers()->logStreamsReference(); |
3012 | } |
3013 | |
3014 | base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { |
3015 | return base::TypedConfigurations( |
3016 | ELPP->registeredLoggers()->defaultConfigurations(), |
3017 | ELPP->registeredLoggers()->logStreamsReference()); |
3018 | } |
3019 | |
3020 | std::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 | |
3029 | void 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 | |
3070 | bool 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 | |
3082 | void Loggers::flushAll(void) { |
3083 | ELPP->registeredLoggers()->flushAll(); |
3084 | } |
3085 | |
3086 | void Loggers::setVerboseLevel(base::type::VerboseLevel level) { |
3087 | ELPP->vRegistry()->setLevel(level); |
3088 | } |
3089 | |
3090 | base::type::VerboseLevel Loggers::verboseLevel(void) { |
3091 | return ELPP->vRegistry()->level(); |
3092 | } |
3093 | |
3094 | void Loggers::setVModules(const char* modules) { |
3095 | if (ELPP->vRegistry()->vModulesEnabled()) { |
3096 | ELPP->vRegistry()->setModules(modules); |
3097 | } |
3098 | } |
3099 | |
3100 | void Loggers::clearVModules(void) { |
3101 | ELPP->vRegistry()->clearModules(); |
3102 | } |
3103 | |
3104 | // VersionInfo |
3105 | |
3106 | const std::string VersionInfo::version(void) { |
3107 | return std::string("9.96.7" ); |
3108 | } |
3109 | /// @brief Release date of current version |
3110 | const std::string VersionInfo::releaseDate(void) { |
3111 | return std::string("24-11-2018 0728hrs" ); |
3112 | } |
3113 | |
3114 | } // namespace el |
3115 | |