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