xref: /openbmc/bmcweb/http/logging.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
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 
18 // NOLINTBEGIN(readability-convert-member-functions-to-static, cert-dcl58-cpp)
19 template <>
20 struct std::formatter<void*>
21 {
parsestd::formatter22     constexpr auto parse(std::format_parse_context& ctx)
23     {
24         return ctx.begin();
25     }
formatstd::formatter26     auto format(const void*& ptr, auto& ctx) const
27     {
28         return std::format_to(ctx.out(), "{}",
29                               std::to_string(std::bit_cast<size_t>(ptr)));
30     }
31 };
32 // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp)
33 
34 namespace crow
35 {
36 enum class LogLevel
37 {
38     Disabled = 0,
39     Critical,
40     Error,
41     Warning,
42     Info,
43     Debug,
44     Enabled,
45 };
46 
47 // Mapping of the external loglvl name to internal loglvl
48 constexpr std::array<std::string_view, 7> mapLogLevelFromName{
49     "DISABLED", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "ENABLED"};
50 
getLogLevelFromName(std::string_view name)51 constexpr crow::LogLevel getLogLevelFromName(std::string_view name)
52 {
53     const auto* iter = std::ranges::find(mapLogLevelFromName, name);
54     if (iter != mapLogLevelFromName.end())
55     {
56         return static_cast<LogLevel>(iter - mapLogLevelFromName.begin());
57     }
58     return crow::LogLevel::Disabled;
59 }
60 
61 // configured bmcweb LogLevel
getBmcwebCurrentLoggingLevel()62 inline crow::LogLevel& getBmcwebCurrentLoggingLevel()
63 {
64     static crow::LogLevel level = getLogLevelFromName(BMCWEB_LOGGING_LEVEL);
65     return level;
66 }
67 
68 struct FormatString
69 {
70     std::string_view str;
71     std::source_location loc;
72 
73     // NOLINTNEXTLINE(google-explicit-constructor)
FormatStringcrow::FormatString74     FormatString(const char* stringIn, const std::source_location& locIn =
75                                            std::source_location::current()) :
76         str(stringIn), loc(locIn)
77     {}
78 };
79 
80 template <typename T>
logPtr(T p)81 const void* logPtr(T p)
82 {
83     static_assert(std::is_pointer<T>::value,
84                   "Can't use logPtr without pointer");
85     return std::bit_cast<const void*>(p);
86 }
87 
88 template <LogLevel level, typename... Args>
vlog(std::format_string<Args...> && format,Args &&...args,const std::source_location & loc)89 inline void vlog(std::format_string<Args...>&& format, Args&&... args,
90                  const std::source_location& loc) noexcept
91 {
92     if (getBmcwebCurrentLoggingLevel() < level)
93     {
94         return;
95     }
96     constexpr size_t stringIndex = static_cast<size_t>(level);
97     static_assert(stringIndex < mapLogLevelFromName.size(),
98                   "Missing string for level");
99     constexpr std::string_view levelString = mapLogLevelFromName[stringIndex];
100     std::string_view filename = loc.file_name();
101     filename = filename.substr(filename.rfind('/'));
102     if (!filename.empty())
103     {
104         filename.remove_prefix(1);
105     }
106     std::string logLocation;
107     try
108     {
109         // TODO, multiple static analysis tools flag that this could potentially
110         // throw Based on the documentation, it shouldn't throw, so long as none
111         // of the formatters throw, so unclear at this point why this try/catch
112         // is required, but add it to silence the static analysis tools.
113         logLocation =
114             std::format("[{} {}:{}] ", levelString, filename, loc.line());
115         logLocation +=
116             std::format(std::move(format), std::forward<Args>(args)...);
117     }
118     catch (const std::format_error& /*error*/)
119     {
120         logLocation += "Failed to format";
121         // Nothing more we can do here if logging is broken.
122     }
123     logLocation += '\n';
124     // Intentionally ignore error return.
125     fwrite(logLocation.data(), sizeof(std::string::value_type),
126            logLocation.size(), stdout);
127     fflush(stdout);
128 }
129 } // namespace crow
130 
131 template <typename... Args>
132 struct BMCWEB_LOG_CRITICAL
133 {
134     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_CRITICALBMCWEB_LOG_CRITICAL135     BMCWEB_LOG_CRITICAL(std::format_string<Args...> format, Args&&... args,
136                         const std::source_location& loc =
137                             std::source_location::current()) noexcept
138     {
139         crow::vlog<crow::LogLevel::Critical, Args...>(
140             std::move(format), std::forward<Args>(args)..., loc);
141     }
142 };
143 
144 template <typename... Args>
145 struct BMCWEB_LOG_ERROR
146 {
147     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_ERRORBMCWEB_LOG_ERROR148     BMCWEB_LOG_ERROR(std::format_string<Args...> format, Args&&... args,
149                      const std::source_location& loc =
150                          std::source_location::current()) noexcept
151     {
152         crow::vlog<crow::LogLevel::Error, Args...>(
153             std::move(format), std::forward<Args>(args)..., loc);
154     }
155 };
156 
157 template <typename... Args>
158 struct BMCWEB_LOG_WARNING
159 {
160     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_WARNINGBMCWEB_LOG_WARNING161     BMCWEB_LOG_WARNING(std::format_string<Args...> format, Args&&... args,
162                        const std::source_location& loc =
163                            std::source_location::current()) noexcept
164     {
165         crow::vlog<crow::LogLevel::Warning, Args...>(
166             std::move(format), std::forward<Args>(args)..., loc);
167     }
168 };
169 
170 template <typename... Args>
171 struct BMCWEB_LOG_INFO
172 {
173     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_INFOBMCWEB_LOG_INFO174     BMCWEB_LOG_INFO(std::format_string<Args...> format, Args&&... args,
175                     const std::source_location& loc =
176                         std::source_location::current()) noexcept
177     {
178         crow::vlog<crow::LogLevel::Info, Args...>(
179             std::move(format), std::forward<Args>(args)..., loc);
180     }
181 };
182 
183 template <typename... Args>
184 struct BMCWEB_LOG_DEBUG
185 {
186     // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_DEBUGBMCWEB_LOG_DEBUG187     BMCWEB_LOG_DEBUG(std::format_string<Args...> format, Args&&... args,
188                      const std::source_location& loc =
189                          std::source_location::current()) noexcept
190     {
191         crow::vlog<crow::LogLevel::Debug, Args...>(
192             std::move(format), std::forward<Args>(args)..., loc);
193     }
194 };
195 
196 template <typename... Args>
197 BMCWEB_LOG_CRITICAL(std::format_string<Args...>, Args&&...)
198     -> BMCWEB_LOG_CRITICAL<Args...>;
199 
200 template <typename... Args>
201 BMCWEB_LOG_ERROR(std::format_string<Args...>, Args&&...)
202     -> BMCWEB_LOG_ERROR<Args...>;
203 
204 template <typename... Args>
205 BMCWEB_LOG_WARNING(std::format_string<Args...>, Args&&...)
206     -> BMCWEB_LOG_WARNING<Args...>;
207 
208 template <typename... Args>
209 BMCWEB_LOG_INFO(std::format_string<Args...>, Args&&...)
210     -> BMCWEB_LOG_INFO<Args...>;
211 
212 template <typename... Args>
213 BMCWEB_LOG_DEBUG(std::format_string<Args...>, Args&&...)
214     -> BMCWEB_LOG_DEBUG<Args...>;
215