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