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 <fmt/core.h>
20 #include <fmt/format.h>
21 #include <fmt/std.h>
22
23 #ifndef LOG_TAG
24 #define LOG_TAG "bluetooth"
25 #endif // LOG_TAG
26
27 namespace bluetooth::log_internal {
28
29 /// Android framework log priority levels.
30 /// They are defined in system/logging/liblog/include/android/log.h by
31 /// the Android Framework code.
32 enum Level {
33 kVerbose = 2,
34 kDebug = 3,
35 kInfo = 4,
36 kWarn = 5,
37 kError = 6,
38 kFatal = 7,
39 };
40
41 /// Information about the location a log is printed from.
42 /// Passing this parameter by default value will fill in
43 /// the correct information.
44 struct source_location {
45 source_location(char const* file_name = __builtin_FILE(),
46 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,
58 fmt::string_view fmt, fmt::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) return arg;
72 return nullptr_str;
73 }
74
75 /// Specialization of format_replace for nullptr string parameters.
76 template <>
format_replace(char * & arg)77 char*& format_replace(char*& arg) {
78 static char* nullptr_str = (char*)"(nullptr)";
79 if (arg) return arg;
80 return nullptr_str;
81 }
82
83 template <Level level, typename... T>
84 struct log {
85 log(fmt::format_string<T...> fmt, T&&... args,
86 source_location location = source_location()) {
87 vlog(level, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
88 fmt::make_format_args(format_replace(args)...));
89 }
90 };
91
92 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
93
94 template <int level, typename... T>
95 log(fmt::format_string<T...>, T&&...) -> log<level, T...>;
96
97 #endif
98
99 } // namespace bluetooth::log_internal
100
101 namespace bluetooth::log {
102
103 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
104
105 template <typename... T>
106 using error = log_internal::log<log_internal::kError, T...>;
107 template <typename... T>
108 using warning = log_internal::log<log_internal::kWarning, T...>;
109 template <typename... T>
110 using info = log_internal::log<log_internal::kInfo, T...>;
111 template <typename... T>
112 using debug = log_internal::log<log_internal::kDebug, T...>;
113 template <typename... T>
114 using verbose = log_internal::log<log_internal::kVerbose, T...>;
115
116 #else
117
118 template <typename... T>
119 struct error : log_internal::log<log_internal::kError, T...> {
120 using log_internal::log<log_internal::kError, T...>::log;
121 };
122 template <typename... T>
123 struct warn : log_internal::log<log_internal::kWarn, T...> {
124 using log_internal::log<log_internal::kWarn, T...>::log;
125 };
126 template <typename... T>
127 struct info : log_internal::log<log_internal::kInfo, T...> {
128 using log_internal::log<log_internal::kInfo, T...>::log;
129 };
130 template <typename... T>
131 struct debug : log_internal::log<log_internal::kDebug, T...> {
132 using log_internal::log<log_internal::kDebug, T...>::log;
133 };
134 template <typename... T>
135 struct verbose : log_internal::log<log_internal::kVerbose, T...> {
136 using log_internal::log<log_internal::kVerbose, T...>::log;
137 };
138
139 template <typename... T>
140 error(fmt::format_string<T...>, T&&...) -> error<T...>;
141 template <typename... T>
142 warn(fmt::format_string<T...>, T&&...) -> warn<T...>;
143 template <typename... T>
144 info(fmt::format_string<T...>, T&&...) -> info<T...>;
145 template <typename... T>
146 debug(fmt::format_string<T...>, T&&...) -> debug<T...>;
147 template <typename... T>
148 verbose(fmt::format_string<T...>, T&&...) -> verbose<T...>;
149
150 #endif // GCC / C++20
151
152 [[noreturn]] [[maybe_unused]] static void fatal(
153 fmt::format_string<> fmt,
154 log_internal::source_location location = log_internal::source_location()) {
155 vlog(log_internal::kFatal, LOG_TAG, location,
156 static_cast<fmt::string_view>(fmt), fmt::make_format_args());
157 std::abort(); // Enforce [[noreturn]]
158 }
159
160 template <typename T0>
161 [[noreturn]] [[maybe_unused]] static void fatal(
162 fmt::format_string<T0> fmt, T0&& arg0,
163 log_internal::source_location location = log_internal::source_location()) {
164 vlog(log_internal::kFatal, LOG_TAG, location,
165 static_cast<fmt::string_view>(fmt),
166 fmt::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 fmt::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,
175 static_cast<fmt::string_view>(fmt),
176 fmt::make_format_args(log_internal::format_replace(arg0),
177 log_internal::format_replace(arg1)));
178 std::abort(); // Enforce [[noreturn]]
179 }
180
181 template <typename T0, typename T1, typename T2>
182 [[noreturn]] [[maybe_unused]] static void fatal(
183 fmt::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
184 log_internal::source_location location = log_internal::source_location()) {
185 vlog(log_internal::kFatal, LOG_TAG, location,
186 static_cast<fmt::string_view>(fmt),
187 fmt::make_format_args(log_internal::format_replace(arg0),
188 log_internal::format_replace(arg1),
189 log_internal::format_replace(arg2)));
190 std::abort(); // Enforce [[noreturn]]
191 }
192
193 template <typename T0, typename T1, typename T2, typename T3>
194 [[noreturn]] [[maybe_unused]] static void fatal(
195 fmt::format_string<T0, T1, T2, T3> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
196 T3&& arg3,
197 log_internal::source_location location = log_internal::source_location()) {
198 vlog(log_internal::kFatal, LOG_TAG, location,
199 static_cast<fmt::string_view>(fmt),
200 fmt::make_format_args(log_internal::format_replace(arg0),
201 log_internal::format_replace(arg1),
202 log_internal::format_replace(arg2),
203 log_internal::format_replace(arg3)));
204 std::abort(); // Enforce [[noreturn]]
205 }
206
207 template <typename... T>
208 struct assert_that {
209 assert_that(bool cond, fmt::format_string<T...> fmt, T&&... args,
210 log_internal::source_location location =
211 log_internal::source_location()) {
212 if (!cond) {
213 vlog(log_internal::kFatal, LOG_TAG, location,
214 static_cast<fmt::string_view>(fmt),
215 fmt::make_format_args(log_internal::format_replace(args)...));
216 }
217 }
218 };
219
220 template <typename... T>
221 assert_that(bool, fmt::format_string<T...>, T&&...) -> assert_that<T...>;
222
223 } // namespace bluetooth::log
224
225 namespace fmt {
226
227 /// Default formatter implementation for formatting
228 /// enum class values to the underlying type.
229 ///
230 /// Enable this formatter in the code by declaring:
231 /// ```
232 /// template<>
233 /// struct fmt::formatter<EnumT> : enum_formatter<EnumT> {};
234 /// ```
235 template <typename EnumT, class CharT = char>
236 struct enum_formatter : fmt::formatter<std::underlying_type_t<EnumT>, CharT> {
237 template <class Context>
formatenum_formatter238 typename Context::iterator format(EnumT value, Context& ctx) const {
239 return fmt::formatter<std::underlying_type_t<EnumT>, CharT>::format(
240 static_cast<std::underlying_type_t<EnumT>>(value), ctx);
241 }
242 };
243
244 /// Default formatter implementation for formatting
245 /// values of type T for which a string conversion function
246 /// T_to_str is implemented.
247 ///
248 /// Enable this formatter in the code by declaring:
249 /// ```
250 /// template<>
251 /// struct fmt::formatter<T> : string_formatter<T, &T_to_str> {};
252 /// ```
253 template <typename T, std::string (*F)(const T&), class CharT = char>
254 struct string_formatter : fmt::formatter<std::string> {
255 template <class Context>
formatstring_formatter256 typename Context::iterator format(const T& value, Context& ctx) const {
257 return fmt::formatter<std::string>::format(F(value), ctx);
258 }
259 };
260
261 } // namespace fmt
262