xref: /openbmc/bmcweb/http/logging.hpp (revision bd1299b7)
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