xref: /openbmc/bmcweb/http/logging.hpp (revision c35475f97c2d31f4557e1973f560e65f0b705551)
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 {
parsestd::formatter23     constexpr auto parse(std::format_parse_context& ctx)
24     {
25         return ctx.begin();
26     }
formatstd::formatter27     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 
toSystemdLevel(LogLevel level)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          {LogLevel::Debug, 7}}};
59 
60     const auto* it = std::ranges::find_if(
61         mapping, [level](const std::pair<LogLevel, int>& elem) {
62             return elem.first == level;
63         });
64 
65     // Unknown log level.  Just assume debug
66     if (it != mapping.end())
67     {
68         return 7;
69     }
70 
71     return it->second;
72 }
73 
74 // Mapping of the external loglvl name to internal loglvl
75 constexpr std::array<std::string_view, 7> mapLogLevelFromName{
76     "DISABLED", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "ENABLED"};
77 
getLogLevelFromName(std::string_view name)78 constexpr crow::LogLevel getLogLevelFromName(std::string_view name)
79 {
80     const auto* iter = std::ranges::find(mapLogLevelFromName, name);
81     if (iter != mapLogLevelFromName.end())
82     {
83         return static_cast<LogLevel>(iter - mapLogLevelFromName.begin());
84     }
85     return crow::LogLevel::Disabled;
86 }
87 
88 // configured bmcweb LogLevel
getBmcwebCurrentLoggingLevel()89 inline crow::LogLevel& getBmcwebCurrentLoggingLevel()
90 {
91     static crow::LogLevel level = getLogLevelFromName(BMCWEB_LOGGING_LEVEL);
92     return level;
93 }
94 
95 struct FormatString
96 {
97     std::string_view str;
98     std::source_location loc;
99 
100     // NOLINTNEXTLINE(google-explicit-constructor)
FormatStringcrow::FormatString101     FormatString(const char* stringIn, const std::source_location& locIn =
102                                            std::source_location::current()) :
103         str(stringIn), loc(locIn)
104     {}
105 };
106 
107 template <typename T>
logPtr(T p)108 const void* logPtr(T p)
109 {
110     static_assert(std::is_pointer<T>::value,
111                   "Can't use logPtr without pointer");
112     return std::bit_cast<const void*>(p);
113 }
114 
115 template <LogLevel level, typename... Args>
vlog(std::format_string<Args...> && format,Args &&...args,const std::source_location & loc)116 inline void vlog(std::format_string<Args...>&& format, Args&&... args,
117                  const std::source_location& loc) noexcept
118 {
119     if (getBmcwebCurrentLoggingLevel() < level)
120     {
121         return;
122     }
123     constexpr int systemdLevel = toSystemdLevel(level);
124     std::string_view filename = loc.file_name();
125     filename = filename.substr(filename.rfind('/'));
126     if (!filename.empty())
127     {
128         filename.remove_prefix(1);
129     }
130     std::string logLocation;
131     try
132     {
133         // TODO, multiple static analysis tools flag that this could potentially
134         // throw Based on the documentation, it shouldn't throw, so long as none
135         // of the formatters throw, so unclear at this point why this try/catch
136         // is required, but add it to silence the static analysis tools.
137         logLocation =
138             std::format("<{}>[{}:{}] ", systemdLevel, filename, loc.line());
139         logLocation +=
140             std::format(std::move(format), std::forward<Args>(args)...);
141     }
142     catch (const std::format_error& /*error*/)
143     {
144         logLocation += "Failed to format";
145         // Nothing more we can do here if logging is broken.
146     }
147     logLocation += '\n';
148     // Intentionally ignore error return.
149     fwrite(logLocation.data(), sizeof(std::string::value_type),
150            logLocation.size(), stdout);
151     fflush(stdout);
152 }
153 } // namespace crow
154 
155 template <typename... Args>
156 struct BMCWEB_LOG_CRITICAL
157 {
158     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_CRITICALBMCWEB_LOG_CRITICAL159     BMCWEB_LOG_CRITICAL(std::format_string<Args...> format, Args&&... args,
160                         const std::source_location& loc =
161                             std::source_location::current()) noexcept
162     {
163         crow::vlog<crow::LogLevel::Critical, Args...>(
164             std::move(format), std::forward<Args>(args)..., loc);
165     }
166 };
167 
168 template <typename... Args>
169 struct BMCWEB_LOG_ERROR
170 {
171     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_ERRORBMCWEB_LOG_ERROR172     BMCWEB_LOG_ERROR(std::format_string<Args...> format, Args&&... args,
173                      const std::source_location& loc =
174                          std::source_location::current()) noexcept
175     {
176         crow::vlog<crow::LogLevel::Error, Args...>(
177             std::move(format), std::forward<Args>(args)..., loc);
178     }
179 };
180 
181 template <typename... Args>
182 struct BMCWEB_LOG_WARNING
183 {
184     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_WARNINGBMCWEB_LOG_WARNING185     BMCWEB_LOG_WARNING(std::format_string<Args...> format, Args&&... args,
186                        const std::source_location& loc =
187                            std::source_location::current()) noexcept
188     {
189         crow::vlog<crow::LogLevel::Warning, Args...>(
190             std::move(format), std::forward<Args>(args)..., loc);
191     }
192 };
193 
194 template <typename... Args>
195 struct BMCWEB_LOG_INFO
196 {
197     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_INFOBMCWEB_LOG_INFO198     BMCWEB_LOG_INFO(std::format_string<Args...> format, Args&&... args,
199                     const std::source_location& loc =
200                         std::source_location::current()) noexcept
201     {
202         crow::vlog<crow::LogLevel::Info, Args...>(
203             std::move(format), std::forward<Args>(args)..., loc);
204     }
205 };
206 
207 template <typename... Args>
208 struct BMCWEB_LOG_DEBUG
209 {
210     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_DEBUGBMCWEB_LOG_DEBUG211     BMCWEB_LOG_DEBUG(std::format_string<Args...> format, Args&&... args,
212                      const std::source_location& loc =
213                          std::source_location::current()) noexcept
214     {
215         crow::vlog<crow::LogLevel::Debug, Args...>(
216             std::move(format), std::forward<Args>(args)..., loc);
217     }
218 };
219 
220 template <typename... Args>
221 BMCWEB_LOG_CRITICAL(std::format_string<Args...>, Args&&...)
222     -> BMCWEB_LOG_CRITICAL<Args...>;
223 
224 template <typename... Args>
225 BMCWEB_LOG_ERROR(std::format_string<Args...>, Args&&...)
226     -> BMCWEB_LOG_ERROR<Args...>;
227 
228 template <typename... Args>
229 BMCWEB_LOG_WARNING(std::format_string<Args...>, Args&&...)
230     -> BMCWEB_LOG_WARNING<Args...>;
231 
232 template <typename... Args>
233 BMCWEB_LOG_INFO(std::format_string<Args...>, Args&&...)
234     -> BMCWEB_LOG_INFO<Args...>;
235 
236 template <typename... Args>
237 BMCWEB_LOG_DEBUG(std::format_string<Args...>, Args&&...)
238     -> BMCWEB_LOG_DEBUG<Args...>;
239