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