xref: /openbmc/bmcweb/http/logging.hpp (revision 62598e31d0988d589506d5091bd38f72d61faf5e)
1 #pragma once
2 
3 #include "bmcweb_config.h"
4 
5 #include <boost/system/error_code.hpp>
6 #include <boost/url/pct_string_view.hpp>
7 #include <boost/url/string_view.hpp>
8 #include <boost/url/url.hpp>
9 #include <nlohmann/json.hpp>
10 
11 #include <bit>
12 #include <format>
13 #include <iostream>
14 #include <source_location>
15 #include <string_view>
16 #include <system_error>
17 
18 // Clang-tidy would rather these be static, but using static causes the template
19 // specialization to not function.  Ignore the warning.
20 // NOLINTBEGIN(readability-convert-member-functions-to-static, cert-dcl58-cpp)
21 template <>
22 struct std::formatter<boost::system::error_code>
23 {
24     constexpr auto parse(std::format_parse_context& ctx)
25     {
26         return ctx.begin();
27     }
28 
29     auto format(const boost::system::error_code& ec, auto& ctx) const
30     {
31         return std::format_to(ctx.out(), "{}", ec.what());
32     }
33 };
34 
35 template <>
36 struct std::formatter<boost::urls::pct_string_view>
37 {
38     constexpr auto parse(std::format_parse_context& ctx)
39     {
40         return ctx.begin();
41     }
42     auto format(const boost::urls::pct_string_view& msg, auto& ctx) const
43     {
44         return std::format_to(ctx.out(), "{}",
45                               std::string_view(msg.data(), msg.size()));
46     }
47 };
48 
49 template <>
50 struct std::formatter<boost::urls::url>
51 {
52     constexpr auto parse(std::format_parse_context& ctx)
53     {
54         return ctx.begin();
55     }
56     auto format(const boost::urls::url& msg, auto& ctx) const
57     {
58         return std::format_to(ctx.out(), "{}", std::string_view(msg.buffer()));
59     }
60 };
61 
62 template <>
63 struct std::formatter<boost::core::string_view>
64 {
65     constexpr auto parse(std::format_parse_context& ctx)
66     {
67         return ctx.begin();
68     }
69     auto format(const boost::core::string_view& msg, auto& ctx) const
70     {
71         return std::format_to(ctx.out(), "{}", std::string_view(msg));
72     }
73 };
74 
75 template <>
76 struct std::formatter<void*>
77 {
78     constexpr auto parse(std::format_parse_context& ctx)
79     {
80         return ctx.begin();
81     }
82     auto format(const void*& ptr, auto& ctx) const
83     {
84         return std::format_to(ctx.out(), "{}",
85                               std::to_string(std::bit_cast<size_t>(ptr)));
86     }
87 };
88 
89 template <>
90 struct std::formatter<nlohmann::json::json_pointer>
91 {
92     constexpr auto parse(std::format_parse_context& ctx)
93     {
94         return ctx.begin();
95     }
96     auto format(const nlohmann::json::json_pointer& ptr, auto& ctx) const
97     {
98         return std::format_to(ctx.out(), "{}", ptr.to_string());
99     }
100 };
101 
102 template <>
103 struct std::formatter<nlohmann::json>
104 {
105     static constexpr auto parse(std::format_parse_context& ctx)
106     {
107         return ctx.begin();
108     }
109     auto format(const nlohmann::json& json, auto& ctx) const
110     {
111         return std::format_to(
112             ctx.out(), "{}",
113             json.dump(-1, ' ', false,
114                       nlohmann::json::error_handler_t::replace));
115     }
116 };
117 // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp)
118 
119 namespace crow
120 {
121 enum class LogLevel
122 {
123     Disabled = 0,
124     Debug,
125     Info,
126     Warning,
127     Error,
128     Critical,
129 };
130 
131 // Mapping of the external loglvl name to internal loglvl
132 constexpr std::array<std::pair<std::string_view, crow::LogLevel>, 7>
133     mapLogLevelFromName{{{"disabled", crow::LogLevel::Disabled},
134                          {"enabled", crow::LogLevel::Debug},
135                          {"debug", crow::LogLevel::Debug},
136                          {"info", crow::LogLevel::Info},
137                          {"warning", crow::LogLevel::Warning},
138                          {"error", crow::LogLevel::Error},
139                          {"critical", crow::LogLevel::Critical}}};
140 
141 constexpr crow::LogLevel getLogLevelFromName(std::string_view name)
142 {
143     const auto* iter =
144         std::find_if(begin(mapLogLevelFromName), end(mapLogLevelFromName),
145                      [&name](const auto& v) { return v.first == name; });
146     if (iter != end(mapLogLevelFromName))
147     {
148         return iter->second;
149     }
150     return crow::LogLevel::Disabled;
151 }
152 
153 // configured bmcweb LogLevel
154 constexpr crow::LogLevel bmcwebCurrentLoggingLevel =
155     getLogLevelFromName(bmcwebLoggingLevel);
156 
157 struct FormatString
158 {
159     std::string_view str;
160     std::source_location loc;
161 
162     // NOLINTNEXTLINE(google-explicit-constructor)
163     FormatString(const char* strIn, const std::source_location& locIn =
164                                         std::source_location::current()) :
165         str(strIn),
166         loc(locIn)
167     {}
168 };
169 
170 template <typename T>
171 const void* logPtr(T p)
172 {
173     static_assert(std::is_pointer<T>::value,
174                   "Can't use logPtr without pointer");
175     return std::bit_cast<const void*>(p);
176 }
177 
178 template <LogLevel level>
179 inline void vlog(const FormatString& format, std::format_args&& args)
180 {
181     if constexpr (bmcwebCurrentLoggingLevel > level)
182     {
183         return;
184     }
185     constexpr size_t stringIndex = static_cast<size_t>(level);
186     static_assert(stringIndex < mapLogLevelFromName.size(),
187                   "Missing string for level");
188     constexpr std::string_view levelString =
189         mapLogLevelFromName[stringIndex].first;
190     std::string_view filename = format.loc.file_name();
191     if (filename.starts_with("../"))
192     {
193         filename = filename.substr(3);
194     }
195     std::cout << std::format("[{} {}:{}] ", levelString, filename,
196                              format.loc.line());
197     std::cout << std::vformat(format.str, args);
198     std::putc('\n', stdout);
199 }
200 } // namespace crow
201 
202 template <typename... Args>
203 inline void BMCWEB_LOG_CRITICAL(const crow::FormatString& format,
204                                 Args&&... args)
205 {
206     crow::vlog<crow::LogLevel::Critical>(
207         format, std::make_format_args(std::forward<Args>(args)...));
208 }
209 
210 template <typename... Args>
211 inline void BMCWEB_LOG_ERROR(const crow::FormatString& format, Args&&... args)
212 {
213     crow::vlog<crow::LogLevel::Error>(
214         format, std::make_format_args(std::forward<Args>(args)...));
215 }
216 
217 template <typename... Args>
218 inline void BMCWEB_LOG_WARNING(const crow::FormatString& format, Args&&... args)
219 {
220     crow::vlog<crow::LogLevel::Warning>(
221         format, std::make_format_args(std::forward<Args>(args)...));
222 }
223 
224 template <typename... Args>
225 inline void BMCWEB_LOG_INFO(const crow::FormatString& format, Args&&... args)
226 {
227     crow::vlog<crow::LogLevel::Info>(
228         format, std::make_format_args(std::forward<Args>(args)...));
229 }
230 
231 template <typename... Args>
232 inline void BMCWEB_LOG_DEBUG(const crow::FormatString& format, Args&&... args)
233 {
234     crow::vlog<crow::LogLevel::Debug>(
235         format, std::make_format_args(std::forward<Args>(args)...));
236 }
237