1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <atomic>
20 #include <format>
21 #include <sstream>
22 #include <string_view>
23
24 #ifndef LOG_TAG
25 #define LOG_TAG "bluetooth"
26 #endif // LOG_TAG
27
28 namespace bluetooth::log_internal {
29
30 /// Android framework log priority levels.
31 /// They are defined in system/logging/liblog/include/android/log.h by
32 /// the Android Framework code.
33 enum Level {
34 kVerbose = 2,
35 kDebug = 3,
36 kInfo = 4,
37 kWarn = 5,
38 kError = 6,
39 kFatal = 7,
40 };
41
42 /// Information about the location a log is printed from.
43 /// Passing this parameter by default value will fill in
44 /// the correct information.
45 struct source_location {
46 source_location(char const* file_name = __builtin_FILE(), int line = __builtin_LINE(),
47 char const* function_name = __builtin_FUNCTION())
linesource_location48 : line(line), file_name(file_name), function_name(function_name) {}
49
50 int line;
51 char const* file_name;
52 char const* function_name;
53 };
54
55 /// Write a single log line.
56 /// The implementation of this function is dependent on the backend.
57 void vlog(Level level, char const* tag, source_location location, std::string_view fmt,
58 std::format_args vargs);
59
60 /// Capture invalid parameter values that would cause runtime
61 /// formatting errors.
62 template <class T>
format_replace(T & arg)63 [[maybe_unused]] static inline T& format_replace(T& arg) {
64 return arg;
65 }
66
67 /// Specialization of format_replace for nullptr string parameters.
68 template <>
format_replace(char const * & arg)69 char const*& format_replace(char const*& arg) {
70 static char const* nullptr_str = "(nullptr)";
71 if (arg) {
72 return arg;
73 }
74 return nullptr_str;
75 }
76
77 /// Specialization of format_replace for nullptr string parameters.
78 template <>
format_replace(char * & arg)79 char*& format_replace(char*& arg) {
80 static char* nullptr_str = (char*)"(nullptr)";
81 if (arg) {
82 return arg;
83 }
84 return nullptr_str;
85 }
86
87 template <Level level, typename... T>
88 struct log {
89 log(std::format_string<T...> fmt, T&&... args, source_location location = source_location()) {
90 vlog(level, LOG_TAG, location, fmt.get(), std::make_format_args(format_replace(args)...));
91 }
92 };
93
94 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
95
96 template <int level, typename... T>
97 log(std::format_string<T...>, T&&...) -> log<level, T...>;
98
99 #endif
100
101 } // namespace bluetooth::log_internal
102
103 namespace bluetooth::log {
104
105 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
106
107 template <typename... T>
108 using error = log_internal::log<log_internal::kError, T...>;
109 template <typename... T>
110 using warning = log_internal::log<log_internal::kWarning, T...>;
111 template <typename... T>
112 using info = log_internal::log<log_internal::kInfo, T...>;
113 template <typename... T>
114 using debug = log_internal::log<log_internal::kDebug, T...>;
115 template <typename... T>
116 using verbose = log_internal::log<log_internal::kVerbose, T...>;
117
118 #else
119
120 template <typename... T>
121 struct error : log_internal::log<log_internal::kError, T...> {
122 using log_internal::log<log_internal::kError, T...>::log;
123 };
124 template <typename... T>
125 struct warn : log_internal::log<log_internal::kWarn, T...> {
126 using log_internal::log<log_internal::kWarn, T...>::log;
127 };
128 template <typename... T>
129 struct info : log_internal::log<log_internal::kInfo, T...> {
130 using log_internal::log<log_internal::kInfo, T...>::log;
131 };
132 template <typename... T>
133 struct debug : log_internal::log<log_internal::kDebug, T...> {
134 using log_internal::log<log_internal::kDebug, T...>::log;
135 };
136 template <typename... T>
137 struct verbose : log_internal::log<log_internal::kVerbose, T...> {
138 using log_internal::log<log_internal::kVerbose, T...>::log;
139 };
140
141 template <typename... T>
142 error(std::format_string<T...>, T&&...) -> error<T...>;
143 template <typename... T>
144 warn(std::format_string<T...>, T&&...) -> warn<T...>;
145 template <typename... T>
146 info(std::format_string<T...>, T&&...) -> info<T...>;
147 template <typename... T>
148 debug(std::format_string<T...>, T&&...) -> debug<T...>;
149 template <typename... T>
150 verbose(std::format_string<T...>, T&&...) -> verbose<T...>;
151
152 #endif // GCC / C++20
153
154 [[noreturn]] [[maybe_unused]] static void fatal(
155 std::format_string<> fmt,
156 log_internal::source_location location = log_internal::source_location()) {
157 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args());
158 std::abort(); // Enforce [[noreturn]]
159 }
160
161 template <typename T0>
162 [[noreturn]] [[maybe_unused]] static void fatal(
163 std::format_string<T0> fmt, T0&& arg0,
164 log_internal::source_location location = log_internal::source_location()) {
165 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
166 std::make_format_args(log_internal::format_replace(arg0)));
167 std::abort(); // Enforce [[noreturn]]
168 }
169
170 template <typename T0, typename T1>
171 [[noreturn]] [[maybe_unused]] static void fatal(
172 std::format_string<T0, T1> fmt, T0&& arg0, T1&& arg1,
173 log_internal::source_location location = log_internal::source_location()) {
174 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
175 std::make_format_args(log_internal::format_replace(arg0),
176 log_internal::format_replace(arg1)));
177 std::abort(); // Enforce [[noreturn]]
178 }
179
180 template <typename T0, typename T1, typename T2>
181 [[noreturn]] [[maybe_unused]] static void fatal(
182 std::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
183 log_internal::source_location location = log_internal::source_location()) {
184 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
185 std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
186 log_internal::format_replace(arg2)));
187 std::abort(); // Enforce [[noreturn]]
188 }
189
190 template <typename T0, typename T1, typename T2, typename T3>
191 [[noreturn]] [[maybe_unused]] static void fatal(
192 std::format_string<T0, T1, T2, T3> fmt, T0&& arg0, T1&& arg1, T2&& arg2, T3&& arg3,
193 log_internal::source_location location = log_internal::source_location()) {
194 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
195 std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
196 log_internal::format_replace(arg2),
197 log_internal::format_replace(arg3)));
198 std::abort(); // Enforce [[noreturn]]
199 }
200
201 template <typename... T>
202 struct assert_that {
203 assert_that(bool cond, std::format_string<T...> fmt, T&&... args,
204 log_internal::source_location location = log_internal::source_location()) {
205 if (!cond) {
206 vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
207 std::make_format_args(log_internal::format_replace(args)...));
208 }
209 }
210 };
211
212 template <typename... T>
213 assert_that(bool, std::format_string<T...>, T&&...) -> assert_that<T...>;
214
215 } // namespace bluetooth::log
216
217 namespace std {
218
219 /// Helper to format a pointer value as the memory address.
220 /// Use this helper as `std::format("{}", std::format_ptr(value));`.
221 template <typename T>
format_ptr(T * ptr)222 const void* format_ptr(T* ptr) {
223 return reinterpret_cast<const void*>(ptr);
224 }
225
226 /// Derive formatter for std::atomic<T> types where T is formattable.
227 /// The formatter uses the default memory order `std::memory_order_seq_cst`
228 /// for reading the value.
229 template <typename T, typename CharT>
230 struct formatter<std::atomic<T>, CharT> : formatter<T, CharT> {
231 template <typename Context>
232 auto format(const std::atomic<T>& v, Context& ctx) const -> decltype(ctx.out()) {
233 return formatter<T, CharT>::format(v.load(), ctx);
234 }
235 };
236
237 /// Default formatter implementation for formatting
238 /// types overloading the ostream `operator<<`.
239 ///
240 /// Enable this formatter in the code by declaring:
241 /// ```
242 /// template<>
243 /// struct std::formatter<T> : ostream_formatter {};
244 /// ```
245 template <typename CharT>
246 struct basic_ostream_formatter : formatter<basic_string_view<CharT>, CharT> {
247 void set_debug_format() = delete;
248
249 template <typename T, typename Context>
250 auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
251 auto&& output = std::basic_stringstream<CharT>();
252 output.imbue(std::locale::classic()); // The default is always unlocalized.
253 output << value;
254 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
255 return formatter<basic_string_view<CharT>, CharT>::format(output.str(), ctx);
256 }
257 };
258
259 using ostream_formatter = basic_ostream_formatter<char>;
260
261 /// Default formatter implementation for formatting
262 /// enum class values to the underlying type.
263 ///
264 /// Enable this formatter in the code by declaring:
265 /// ```
266 /// template<>
267 /// struct std::formatter<EnumT> : enum_formatter<EnumT> {};
268 /// ```
269 template <typename EnumT, class CharT = char>
270 struct enum_formatter : std::formatter<std::underlying_type_t<EnumT>, CharT> {
271 template <class Context>
272 typename Context::iterator format(EnumT value, Context& ctx) const {
273 return std::formatter<std::underlying_type_t<EnumT>, CharT>::format(
274 static_cast<std::underlying_type_t<EnumT>>(value), ctx);
275 }
276 };
277
278 /// Default formatter implementation for formatting
279 /// values of type T for which a string conversion function
280 /// T_to_str is implemented.
281 ///
282 /// Enable this formatter in the code by declaring:
283 /// ```
284 /// template<>
285 /// struct std::formatter<T> : string_formatter<T, &T_to_str> {};
286 /// ```
287 template <typename T, std::string (*F)(const T&), class CharT = char>
288 struct string_formatter : std::formatter<std::string> {
289 template <class Context>
290 typename Context::iterator format(const T& value, Context& ctx) const {
291 return std::formatter<std::string>::format(F(value), ctx);
292 }
293 };
294
295 } // namespace std
296