| #pragma once | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <ctime> | |
| #include <filesystem> // NOLINT | |
| #include <iostream> // NOLINT | |
| #include <sstream> | |
| #include <string> | |
| namespace crow { | |
| enum class LogLevel : std::uint8_t { | |
| Debug = 0, | |
| Info, | |
| Warning, | |
| Error, | |
| Critical, | |
| Always, | |
| }; | |
| class Logger { | |
| private: | |
| // | |
| static std::string timestamp() { | |
| std::string date; | |
| date.resize(32, '\0'); | |
| time_t t = time(nullptr); | |
| tm myTm{}; | |
| gmtime_r(&t, &myTm); | |
| size_t sz = strftime(date.data(), date.size(), "%Y-%m-%d %H:%M:%S", &myTm); | |
| date.resize(sz); | |
| return date; | |
| } | |
| public: | |
| Logger([[maybe_unused]] const std::string& prefix, | |
| [[maybe_unused]] const std::string& filename, | |
| [[maybe_unused]] const size_t line, LogLevel levelIn) | |
| : level(levelIn) { | |
| #ifdef BMCWEB_ENABLE_LOGGING | |
| stringstream << "(" << timestamp() << ") [" << prefix << " " | |
| << std::filesystem::path(filename).filename() << ":" << line | |
| << "] "; | |
| #endif | |
| } | |
| ~Logger() { | |
| if (level >= getCurrentLogLevel()) { | |
| #ifdef BMCWEB_ENABLE_LOGGING | |
| stringstream << '\n'; | |
| std::cerr << stringstream.str(); | |
| #endif | |
| } | |
| } | |
| Logger(const Logger&) = delete; | |
| Logger(Logger&&) = delete; | |
| Logger& operator=(const Logger&) = delete; | |
| Logger& operator=(const Logger&&) = delete; | |
| // | |
| template <typename T> | |
| Logger& operator<<([[maybe_unused]] T const& value) { | |
| if (level >= getCurrentLogLevel()) { | |
| #ifdef BMCWEB_ENABLE_LOGGING | |
| // Somewhere in the code we're implicitly casting an array to a | |
| // pointer in logging code. It's non-trivial to find, so disable | |
| // the check here for now | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) | |
| stringstream << value; | |
| #endif | |
| } | |
| return *this; | |
| } | |
| // | |
| static void setLogLevel(LogLevel level) { getLogLevelRef() = level; } | |
| static LogLevel getCurrentLogLevel() { return getLogLevelRef(); } | |
| static void enableStatefulLogs() { getStatefulLogsEnableRef() = true; } | |
| static void disableStatefulLogs() { getStatefulLogsEnableRef() = false; } | |
| static bool isStatefulLogsEnabled() { return getStatefulLogsEnableRef(); } | |
| private: | |
| // | |
| static LogLevel& getLogLevelRef() { | |
| static auto currentLevel = static_cast<LogLevel>(1); | |
| return currentLevel; | |
| } | |
| static bool& getStatefulLogsEnableRef() { | |
| static auto isStatefulLogsEnabled = static_cast<bool>(false); | |
| return isStatefulLogsEnabled; | |
| } | |
| // | |
| std::ostringstream stringstream; | |
| LogLevel level; | |
| }; | |
| } // namespace crow | |
| // The logging functions currently use macros. Now that we have c++20, ideally | |
| // they'd use source_location with fixed functions, but for the moment, disable | |
| // the check. | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_CRITICAL \ | |
| if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Critical) \ | |
| crow::Logger("CRITICAL", __FILE__, __LINE__, crow::LogLevel::Critical) | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_ERROR \ | |
| if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Error) \ | |
| crow::Logger("ERROR", __FILE__, __LINE__, crow::LogLevel::Error) | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_WARNING \ | |
| if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Warning) \ | |
| crow::Logger("WARNING", __FILE__, __LINE__, crow::LogLevel::Warning) | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_INFO \ | |
| if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Info) \ | |
| crow::Logger("INFO", __FILE__, __LINE__, crow::LogLevel::Info) | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_DEBUG \ | |
| if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Debug) \ | |
| crow::Logger("DEBUG", __FILE__, __LINE__, crow::LogLevel::Debug) | |
| // Prints critical to triage and debug BMCWeb (always enabled - for now) | |
| // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) | |
| #define BMCWEB_LOG_ALWAYS \ | |
| crow::Logger("ALWAYS", __FILE__, __LINE__, crow::LogLevel::Always) | |
| // Stateful specific prints: | |
| #define BMCWEB_LOG_STATEFUL_DEBUG \ | |
| if (crow::Logger::isStatefulLogsEnabled()) BMCWEB_LOG_DEBUG << "Stateful: " | |
| #define BMCWEB_LOG_STATEFUL_INFO \ | |
| if (crow::Logger::isStatefulLogsEnabled()) BMCWEB_LOG_INFO << "Stateful: " | |
| #define BMCWEB_LOG_STATEFUL_WARNING \ | |
| if (crow::Logger::isStatefulLogsEnabled()) \ | |
| BMCWEB_LOG_WARNING << "Stateful:" \ | |
| " " | |
| #define BMCWEB_LOG_STATEFUL_ERROR \ | |
| if (crow::Logger::isStatefulLogsEnabled()) BMCWEB_LOG_ERROR << "Stateful: " | |
| #define BMCWEB_LOG_STATEFUL_CRITICAL \ | |
| if (crow::Logger::isStatefulLogsEnabled()) \ | |
| BMCWEB_LOG_CRITICAL << "Stateful" \ | |
| ": " | |
| #define BMCWEB_LOG_STATEFUL_ALWAYS BMCWEB_LOG_ALWAYS << "Stateful: " |