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