1 #pragma once 2 3 #include "bmcweb_config.h" 4 5 #include <bit> 6 #include <format> 7 #include <iostream> 8 #include <source_location> 9 #include <string_view> 10 #include <system_error> 11 12 // NOLINTBEGIN(readability-convert-member-functions-to-static, cert-dcl58-cpp) 13 template <> 14 struct std::formatter<void*> 15 { 16 constexpr auto parse(std::format_parse_context& ctx) 17 { 18 return ctx.begin(); 19 } 20 auto format(const void*& ptr, auto& ctx) const 21 { 22 return std::format_to(ctx.out(), "{}", 23 std::to_string(std::bit_cast<size_t>(ptr))); 24 } 25 }; 26 // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp) 27 28 namespace crow 29 { 30 enum class LogLevel 31 { 32 Disabled = 0, 33 Critical, 34 Error, 35 Warning, 36 Info, 37 Debug, 38 Enabled, 39 }; 40 41 // Mapping of the external loglvl name to internal loglvl 42 constexpr std::array<std::string_view, 7> mapLogLevelFromName{ 43 "DISABLED", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "ENABLED"}; 44 45 constexpr crow::LogLevel getLogLevelFromName(std::string_view name) 46 { 47 const auto* iter = std::ranges::find(mapLogLevelFromName, name); 48 if (iter != mapLogLevelFromName.end()) 49 { 50 return static_cast<LogLevel>(iter - mapLogLevelFromName.begin()); 51 } 52 return crow::LogLevel::Disabled; 53 } 54 55 // configured bmcweb LogLevel 56 inline crow::LogLevel& getBmcwebCurrentLoggingLevel() 57 { 58 static crow::LogLevel level = getLogLevelFromName(BMCWEB_LOGGING_LEVEL); 59 return level; 60 } 61 62 struct FormatString 63 { 64 std::string_view str; 65 std::source_location loc; 66 67 // NOLINTNEXTLINE(google-explicit-constructor) 68 FormatString(const char* stringIn, const std::source_location& locIn = 69 std::source_location::current()) : 70 str(stringIn), loc(locIn) 71 {} 72 }; 73 74 template <typename T> 75 const void* logPtr(T p) 76 { 77 static_assert(std::is_pointer<T>::value, 78 "Can't use logPtr without pointer"); 79 return std::bit_cast<const void*>(p); 80 } 81 82 template <LogLevel level, typename... Args> 83 inline void vlog(std::format_string<Args...>&& format, Args&&... args, 84 const std::source_location& loc) noexcept 85 { 86 if (getBmcwebCurrentLoggingLevel() < level) 87 { 88 return; 89 } 90 constexpr size_t stringIndex = static_cast<size_t>(level); 91 static_assert(stringIndex < mapLogLevelFromName.size(), 92 "Missing string for level"); 93 constexpr std::string_view levelString = mapLogLevelFromName[stringIndex]; 94 std::string_view filename = loc.file_name(); 95 filename = filename.substr(filename.rfind('/')); 96 if (!filename.empty()) 97 { 98 filename.remove_prefix(1); 99 } 100 std::string logLocation; 101 try 102 { 103 // TODO, multiple static analysis tools flag that this could potentially 104 // throw Based on the documentation, it shouldn't throw, so long as none 105 // of the formatters throw, so unclear at this point why this try/catch 106 // is required, but add it to silence the static analysis tools. 107 logLocation = 108 std::format("[{} {}:{}] ", levelString, filename, loc.line()); 109 logLocation += 110 std::format(std::move(format), std::forward<Args>(args)...); 111 } 112 catch (const std::format_error& /*error*/) 113 { 114 logLocation += "Failed to format"; 115 // Nothing more we can do here if logging is broken. 116 } 117 logLocation += '\n'; 118 // Intentionally ignore error return. 119 fwrite(logLocation.data(), sizeof(std::string::value_type), 120 logLocation.size(), stdout); 121 fflush(stdout); 122 } 123 } // namespace crow 124 125 template <typename... Args> 126 struct BMCWEB_LOG_CRITICAL 127 { 128 // NOLINTNEXTLINE(google-explicit-constructor) 129 BMCWEB_LOG_CRITICAL(std::format_string<Args...> format, Args&&... args, 130 const std::source_location& loc = 131 std::source_location::current()) noexcept 132 { 133 crow::vlog<crow::LogLevel::Critical, Args...>( 134 std::move(format), std::forward<Args>(args)..., loc); 135 } 136 }; 137 138 template <typename... Args> 139 struct BMCWEB_LOG_ERROR 140 { 141 // NOLINTNEXTLINE(google-explicit-constructor) 142 BMCWEB_LOG_ERROR(std::format_string<Args...> format, Args&&... args, 143 const std::source_location& loc = 144 std::source_location::current()) noexcept 145 { 146 crow::vlog<crow::LogLevel::Error, Args...>( 147 std::move(format), std::forward<Args>(args)..., loc); 148 } 149 }; 150 151 template <typename... Args> 152 struct BMCWEB_LOG_WARNING 153 { 154 // NOLINTNEXTLINE(google-explicit-constructor) 155 BMCWEB_LOG_WARNING(std::format_string<Args...> format, Args&&... args, 156 const std::source_location& loc = 157 std::source_location::current()) noexcept 158 { 159 crow::vlog<crow::LogLevel::Warning, Args...>( 160 std::move(format), std::forward<Args>(args)..., loc); 161 } 162 }; 163 164 template <typename... Args> 165 struct BMCWEB_LOG_INFO 166 { 167 // NOLINTNEXTLINE(google-explicit-constructor) 168 BMCWEB_LOG_INFO(std::format_string<Args...> format, Args&&... args, 169 const std::source_location& loc = 170 std::source_location::current()) noexcept 171 { 172 crow::vlog<crow::LogLevel::Info, Args...>( 173 std::move(format), std::forward<Args>(args)..., loc); 174 } 175 }; 176 177 template <typename... Args> 178 struct BMCWEB_LOG_DEBUG 179 { 180 // NOLINTNEXTLINE(google-explicit-constructor) 181 BMCWEB_LOG_DEBUG(std::format_string<Args...> format, Args&&... args, 182 const std::source_location& loc = 183 std::source_location::current()) noexcept 184 { 185 crow::vlog<crow::LogLevel::Debug, Args...>( 186 std::move(format), std::forward<Args>(args)..., loc); 187 } 188 }; 189 190 template <typename... Args> 191 BMCWEB_LOG_CRITICAL(std::format_string<Args...>, Args&&...) 192 -> BMCWEB_LOG_CRITICAL<Args...>; 193 194 template <typename... Args> 195 BMCWEB_LOG_ERROR(std::format_string<Args...>, Args&&...) 196 -> BMCWEB_LOG_ERROR<Args...>; 197 198 template <typename... Args> 199 BMCWEB_LOG_WARNING(std::format_string<Args...>, Args&&...) 200 -> BMCWEB_LOG_WARNING<Args...>; 201 202 template <typename... Args> 203 BMCWEB_LOG_INFO(std::format_string<Args...>, Args&&...) 204 -> BMCWEB_LOG_INFO<Args...>; 205 206 template <typename... Args> 207 BMCWEB_LOG_DEBUG(std::format_string<Args...>, Args&&...) 208 -> BMCWEB_LOG_DEBUG<Args...>; 209