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), 71 loc(locIn) 72 {} 73 }; 74 75 template <typename T> 76 const void* logPtr(T p) 77 { 78 static_assert(std::is_pointer<T>::value, 79 "Can't use logPtr without pointer"); 80 return std::bit_cast<const void*>(p); 81 } 82 83 template <LogLevel level, typename... Args> 84 inline void vlog(std::format_string<Args...>&& format, Args&&... args, 85 const std::source_location& loc) noexcept 86 { 87 if (getBmcwebCurrentLoggingLevel() < level) 88 { 89 return; 90 } 91 constexpr size_t stringIndex = static_cast<size_t>(level); 92 static_assert(stringIndex < mapLogLevelFromName.size(), 93 "Missing string for level"); 94 constexpr std::string_view levelString = mapLogLevelFromName[stringIndex]; 95 std::string_view filename = loc.file_name(); 96 filename = filename.substr(filename.rfind('/')); 97 if (!filename.empty()) 98 { 99 filename.remove_prefix(1); 100 } 101 std::string logLocation; 102 try 103 { 104 // TODO, multiple static analysis tools flag that this could potentially 105 // throw Based on the documentation, it shouldn't throw, so long as none 106 // of the formatters throw, so unclear at this point why this try/catch 107 // is required, but add it to silence the static analysis tools. 108 logLocation = 109 std::format("[{} {}:{}] ", levelString, filename, loc.line()); 110 logLocation += 111 std::format(std::move(format), std::forward<Args>(args)...); 112 } 113 catch (const std::format_error& /*error*/) 114 { 115 logLocation += "Failed to format"; 116 // Nothing more we can do here if logging is broken. 117 } 118 logLocation += '\n'; 119 // Intentionally ignore error return. 120 fwrite(logLocation.data(), sizeof(std::string::value_type), 121 logLocation.size(), stdout); 122 fflush(stdout); 123 } 124 } // namespace crow 125 126 template <typename... Args> 127 struct BMCWEB_LOG_CRITICAL 128 { 129 // NOLINTNEXTLINE(google-explicit-constructor) 130 BMCWEB_LOG_CRITICAL(std::format_string<Args...> format, Args&&... args, 131 const std::source_location& loc = 132 std::source_location::current()) noexcept 133 { 134 crow::vlog<crow::LogLevel::Critical, Args...>( 135 std::move(format), std::forward<Args>(args)..., loc); 136 } 137 }; 138 139 template <typename... Args> 140 struct BMCWEB_LOG_ERROR 141 { 142 // NOLINTNEXTLINE(google-explicit-constructor) 143 BMCWEB_LOG_ERROR(std::format_string<Args...> format, Args&&... args, 144 const std::source_location& loc = 145 std::source_location::current()) noexcept 146 { 147 crow::vlog<crow::LogLevel::Error, Args...>( 148 std::move(format), std::forward<Args>(args)..., loc); 149 } 150 }; 151 152 template <typename... Args> 153 struct BMCWEB_LOG_WARNING 154 { 155 // NOLINTNEXTLINE(google-explicit-constructor) 156 BMCWEB_LOG_WARNING(std::format_string<Args...> format, Args&&... args, 157 const std::source_location& loc = 158 std::source_location::current()) noexcept 159 { 160 crow::vlog<crow::LogLevel::Warning, Args...>( 161 std::move(format), std::forward<Args>(args)..., loc); 162 } 163 }; 164 165 template <typename... Args> 166 struct BMCWEB_LOG_INFO 167 { 168 // NOLINTNEXTLINE(google-explicit-constructor) 169 BMCWEB_LOG_INFO(std::format_string<Args...> format, Args&&... args, 170 const std::source_location& loc = 171 std::source_location::current()) noexcept 172 { 173 crow::vlog<crow::LogLevel::Info, Args...>( 174 std::move(format), std::forward<Args>(args)..., loc); 175 } 176 }; 177 178 template <typename... Args> 179 struct BMCWEB_LOG_DEBUG 180 { 181 // NOLINTNEXTLINE(google-explicit-constructor) 182 BMCWEB_LOG_DEBUG(std::format_string<Args...> format, Args&&... args, 183 const std::source_location& loc = 184 std::source_location::current()) noexcept 185 { 186 crow::vlog<crow::LogLevel::Debug, Args...>( 187 std::move(format), std::forward<Args>(args)..., loc); 188 } 189 }; 190 191 template <typename... Args> 192 BMCWEB_LOG_CRITICAL(std::format_string<Args...>, 193 Args&&...) -> BMCWEB_LOG_CRITICAL<Args...>; 194 195 template <typename... Args> 196 BMCWEB_LOG_ERROR(std::format_string<Args...>, 197 Args&&...) -> BMCWEB_LOG_ERROR<Args...>; 198 199 template <typename... Args> 200 BMCWEB_LOG_WARNING(std::format_string<Args...>, 201 Args&&...) -> BMCWEB_LOG_WARNING<Args...>; 202 203 template <typename... Args> 204 BMCWEB_LOG_INFO(std::format_string<Args...>, 205 Args&&...) -> BMCWEB_LOG_INFO<Args...>; 206 207 template <typename... Args> 208 BMCWEB_LOG_DEBUG(std::format_string<Args...>, 209 Args&&...) -> BMCWEB_LOG_DEBUG<Args...>; 210