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 {
parsestd::formatter16 constexpr auto parse(std::format_parse_context& ctx)
17 {
18 return ctx.begin();
19 }
formatstd::formatter20 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
getLogLevelFromName(std::string_view name)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
getBmcwebCurrentLoggingLevel()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)
FormatStringcrow::FormatString68 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>
logPtr(T p)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>
vlog(std::format_string<Args...> && format,Args &&...args,const std::source_location & loc)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)
BMCWEB_LOG_CRITICALBMCWEB_LOG_CRITICAL129 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)
BMCWEB_LOG_ERRORBMCWEB_LOG_ERROR142 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)
BMCWEB_LOG_WARNINGBMCWEB_LOG_WARNING155 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)
BMCWEB_LOG_INFOBMCWEB_LOG_INFO168 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)
BMCWEB_LOG_DEBUGBMCWEB_LOG_DEBUG181 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...>,
192 Args&&...) -> BMCWEB_LOG_CRITICAL<Args...>;
193
194 template <typename... Args>
195 BMCWEB_LOG_ERROR(std::format_string<Args...>,
196 Args&&...) -> BMCWEB_LOG_ERROR<Args...>;
197
198 template <typename... Args>
199 BMCWEB_LOG_WARNING(std::format_string<Args...>,
200 Args&&...) -> BMCWEB_LOG_WARNING<Args...>;
201
202 template <typename... Args>
203 BMCWEB_LOG_INFO(std::format_string<Args...>,
204 Args&&...) -> BMCWEB_LOG_INFO<Args...>;
205
206 template <typename... Args>
207 BMCWEB_LOG_DEBUG(std::format_string<Args...>,
208 Args&&...) -> BMCWEB_LOG_DEBUG<Args...>;
209