| 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 | |