1 #pragma once
2
3 #include "bmcweb_config.h"
4
5 #include <bit>
6 #include <format>
7 #include <iostream>
8 #include <source_location>
9 #include <string_view>
10 #include <system_error>
11
12 // NOLINTBEGIN(readability-convert-member-functions-to-static, cert-dcl58-cpp)
13 template <>
14 struct std::formatter<void*>
15 {
parsestd::formatter16 constexpr auto parse(std::format_parse_context& ctx)
17 {
18 return ctx.begin();
19 }
formatstd::formatter20 auto format(const void*& ptr, auto& ctx) const
21 {
22 return std::format_to(ctx.out(), "{}",
23 std::to_string(std::bit_cast<size_t>(ptr)));
24 }
25 };
26 // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp)
27
28 namespace crow
29 {
30 enum class LogLevel
31 {
32 Disabled = 0,
33 Critical,
34 Error,
35 Warning,
36 Info,
37 Debug,
38 Enabled,
39 };
40
41 // Mapping of the external loglvl name to internal loglvl
42 constexpr std::array<std::string_view, 7> mapLogLevelFromName{
43 "DISABLED", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "ENABLED"};
44
getLogLevelFromName(std::string_view name)45 constexpr crow::LogLevel getLogLevelFromName(std::string_view name)
46 {
47 const auto* iter = std::ranges::find(mapLogLevelFromName, name);
48 if (iter != mapLogLevelFromName.end())
49 {
50 return static_cast<LogLevel>(iter - mapLogLevelFromName.begin());
51 }
52 return crow::LogLevel::Disabled;
53 }
54
55 // configured bmcweb LogLevel
56 constexpr crow::LogLevel bmcwebCurrentLoggingLevel =
57 getLogLevelFromName(BMCWEB_LOGGING_LEVEL);
58
59 template <typename T>
logPtr(T p)60 const void* logPtr(T p)
61 {
62 static_assert(std::is_pointer<T>::value,
63 "Can't use logPtr without pointer");
64 return std::bit_cast<const void*>(p);
65 }
66
67 template <LogLevel level, typename... Args>
vlog(std::format_string<Args...> && format,Args &&...args,const std::source_location & loc)68 inline void vlog(std::format_string<Args...>&& format, Args&&... args,
69 const std::source_location& loc) noexcept
70 {
71 if constexpr (bmcwebCurrentLoggingLevel < level)
72 {
73 return;
74 }
75 constexpr size_t stringIndex = static_cast<size_t>(level);
76 static_assert(stringIndex < mapLogLevelFromName.size(),
77 "Missing string for level");
78 constexpr std::string_view levelString = mapLogLevelFromName[stringIndex];
79 std::string_view filename = loc.file_name();
80 filename = filename.substr(filename.rfind('/') + 1);
81 std::string logLocation;
82 try
83 {
84 // TODO, multiple static analysis tools flag that this could potentially
85 // throw Based on the documentation, it shouldn't throw, so long as none
86 // of the formatters throw, so unclear at this point why this try/catch
87 // is required, but add it to silence the static analysis tools.
88 logLocation = std::format("[{} {}:{}] ", levelString, filename,
89 loc.line());
90 logLocation += std::format(std::move(format),
91 std::forward<Args>(args)...);
92 }
93 catch (const std::format_error& /*error*/)
94 {
95 logLocation += "Failed to format";
96 // Nothing more we can do here if logging is broken.
97 }
98 logLocation += '\n';
99 // Intentionally ignore error return.
100 fwrite(logLocation.data(), sizeof(std::string::value_type),
101 logLocation.size(), stdout);
102 fflush(stdout);
103 }
104 } // namespace crow
105
106 template <typename... Args>
107 struct BMCWEB_LOG_CRITICAL
108 {
109 // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_CRITICALBMCWEB_LOG_CRITICAL110 BMCWEB_LOG_CRITICAL(std::format_string<Args...> format, Args&&... args,
111 const std::source_location& loc =
112 std::source_location::current()) noexcept
113 {
114 crow::vlog<crow::LogLevel::Critical, Args...>(
115 std::move(format), std::forward<Args>(args)..., loc);
116 }
117 };
118
119 template <typename... Args>
120 struct BMCWEB_LOG_ERROR
121 {
122 // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_ERRORBMCWEB_LOG_ERROR123 BMCWEB_LOG_ERROR(std::format_string<Args...> format, Args&&... args,
124 const std::source_location& loc =
125 std::source_location::current()) noexcept
126 {
127 crow::vlog<crow::LogLevel::Error, Args...>(
128 std::move(format), std::forward<Args>(args)..., loc);
129 }
130 };
131
132 template <typename... Args>
133 struct BMCWEB_LOG_WARNING
134 {
135 // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_WARNINGBMCWEB_LOG_WARNING136 BMCWEB_LOG_WARNING(std::format_string<Args...> format, Args&&... args,
137 const std::source_location& loc =
138 std::source_location::current()) noexcept
139 {
140 crow::vlog<crow::LogLevel::Warning, Args...>(
141 std::move(format), std::forward<Args>(args)..., loc);
142 }
143 };
144
145 template <typename... Args>
146 struct BMCWEB_LOG_INFO
147 {
148 // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_INFOBMCWEB_LOG_INFO149 BMCWEB_LOG_INFO(std::format_string<Args...> format, Args&&... args,
150 const std::source_location& loc =
151 std::source_location::current()) noexcept
152 {
153 crow::vlog<crow::LogLevel::Info, Args...>(
154 std::move(format), std::forward<Args>(args)..., loc);
155 }
156 };
157
158 template <typename... Args>
159 struct BMCWEB_LOG_DEBUG
160 {
161 // NOLINTNEXTLINE(google-explicit-constructor)
BMCWEB_LOG_DEBUGBMCWEB_LOG_DEBUG162 BMCWEB_LOG_DEBUG(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::Debug, Args...>(
167 std::move(format), std::forward<Args>(args)..., loc);
168 }
169 };
170
171 template <typename... Args>
172 BMCWEB_LOG_CRITICAL(std::format_string<Args...>, Args&&...)
173 -> BMCWEB_LOG_CRITICAL<Args...>;
174
175 template <typename... Args>
176 BMCWEB_LOG_ERROR(std::format_string<Args...>, Args&&...)
177 -> BMCWEB_LOG_ERROR<Args...>;
178
179 template <typename... Args>
180 BMCWEB_LOG_WARNING(std::format_string<Args...>, Args&&...)
181 -> BMCWEB_LOG_WARNING<Args...>;
182
183 template <typename... Args>
184 BMCWEB_LOG_INFO(std::format_string<Args...>, Args&&...)
185 -> BMCWEB_LOG_INFO<Args...>;
186
187 template <typename... Args>
188 BMCWEB_LOG_DEBUG(std::format_string<Args...>, Args&&...)
189 -> BMCWEB_LOG_DEBUG<Args...>;
190