1 #pragma once 2 3 #include <cstdio> 4 #include <cstdlib> 5 #include <ctime> 6 #include <filesystem> 7 #include <iostream> 8 #include <sstream> 9 #include <string> 10 11 namespace crow 12 { 13 enum class LogLevel 14 { 15 Debug = 0, 16 Info, 17 Warning, 18 Error, 19 Critical, 20 }; 21 22 class Logger 23 { 24 private: 25 // 26 static std::string timestamp() 27 { 28 std::string date; 29 date.resize(32, '\0'); 30 time_t t = time(nullptr); 31 32 tm myTm{}; 33 34 gmtime_r(&t, &myTm); 35 36 size_t sz = 37 strftime(date.data(), date.size(), "%Y-%m-%d %H:%M:%S", &myTm); 38 date.resize(sz); 39 return date; 40 } 41 42 public: 43 Logger([[maybe_unused]] const std::string& prefix, 44 [[maybe_unused]] const std::string& filename, 45 [[maybe_unused]] const size_t line, LogLevel levelIn) : 46 level(levelIn) 47 { 48 #ifdef BMCWEB_ENABLE_LOGGING 49 stringstream << "(" << timestamp() << ") [" << prefix << " " 50 << std::filesystem::path(filename).filename() << ":" 51 << line << "] "; 52 #endif 53 } 54 ~Logger() 55 { 56 if (level >= getCurrentLogLevel()) 57 { 58 #ifdef BMCWEB_ENABLE_LOGGING 59 stringstream << std::endl; 60 std::cerr << stringstream.str(); 61 #endif 62 } 63 } 64 65 Logger(const Logger&) = delete; 66 Logger(Logger&&) = delete; 67 Logger& operator=(const Logger&) = delete; 68 Logger& operator=(const Logger&&) = delete; 69 70 // 71 template <typename T> 72 Logger& operator<<([[maybe_unused]] T const& value) 73 { 74 if (level >= getCurrentLogLevel()) 75 { 76 #ifdef BMCWEB_ENABLE_LOGGING 77 // Somewhere in the code we're implicitly casting an array to a 78 // pointer in logging code. It's non-trivial to find, so disable 79 // the check here for now 80 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 81 stringstream << value; 82 #endif 83 } 84 return *this; 85 } 86 87 // 88 static void setLogLevel(LogLevel level) 89 { 90 getLogLevelRef() = level; 91 } 92 93 static LogLevel getCurrentLogLevel() 94 { 95 return getLogLevelRef(); 96 } 97 98 private: 99 // 100 static LogLevel& getLogLevelRef() 101 { 102 static auto currentLevel = static_cast<LogLevel>(1); 103 return currentLevel; 104 } 105 106 // 107 std::ostringstream stringstream; 108 LogLevel level; 109 }; 110 } // namespace crow 111 112 // The logging functions currently use macros. Now that we have c++20, ideally 113 // they'd use source_location with fixed functions, but for the moment, disable 114 // the check. 115 116 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 117 #define BMCWEB_LOG_CRITICAL \ 118 if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Critical) \ 119 crow::Logger("CRITICAL", __FILE__, __LINE__, crow::LogLevel::Critical) 120 121 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 122 #define BMCWEB_LOG_ERROR \ 123 if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Error) \ 124 crow::Logger("ERROR", __FILE__, __LINE__, crow::LogLevel::Error) 125 126 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 127 #define BMCWEB_LOG_WARNING \ 128 if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Warning) \ 129 crow::Logger("WARNING", __FILE__, __LINE__, crow::LogLevel::Warning) 130 131 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 132 #define BMCWEB_LOG_INFO \ 133 if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Info) \ 134 crow::Logger("INFO", __FILE__, __LINE__, crow::LogLevel::Info) 135 136 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 137 #define BMCWEB_LOG_DEBUG \ 138 if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Debug) \ 139 crow::Logger("DEBUG", __FILE__, __LINE__, crow::LogLevel::Debug) 140