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