xref: /openbmc/bmcweb/http/logging.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
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 {
parsestd::formatter18     constexpr auto parse(std::format_parse_context& ctx)
19     {
20         return ctx.begin();
21     }
formatstd::formatter22     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 
getLogLevelFromName(std::string_view name)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
getBmcwebCurrentLoggingLevel()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)
FormatStringcrow::FormatString70     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>
logPtr(T p)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>
vlog(std::format_string<Args...> && format,Args &&...args,const std::source_location & loc)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)
BMCWEB_LOG_CRITICALBMCWEB_LOG_CRITICAL131     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)
BMCWEB_LOG_ERRORBMCWEB_LOG_ERROR144     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)
BMCWEB_LOG_WARNINGBMCWEB_LOG_WARNING157     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)
BMCWEB_LOG_INFOBMCWEB_LOG_INFO170     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)
BMCWEB_LOG_DEBUGBMCWEB_LOG_DEBUG183     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