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