/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #ifndef LOG_TAG #define LOG_TAG "bluetooth" #endif // LOG_TAG namespace bluetooth::log_internal { /// Android framework log priority levels. /// They are defined in system/logging/liblog/include/android/log.h by /// the Android Framework code. enum Level { kVerbose = 2, kDebug = 3, kInfo = 4, kWarn = 5, kError = 6, kFatal = 7, }; /// Information about the location a log is printed from. /// Passing this parameter by default value will fill in /// the correct information. struct source_location { source_location(char const* file_name = __builtin_FILE(), int line = __builtin_LINE(), char const* function_name = __builtin_FUNCTION()) : line(line), file_name(file_name), function_name(function_name) {} int line; char const* file_name; char const* function_name; }; /// Write a single log line. /// The implementation of this function is dependent on the backend. void vlog(Level level, char const* tag, source_location location, std::string_view fmt, std::format_args vargs); /// Capture invalid parameter values that would cause runtime /// formatting errors. template [[maybe_unused]] static inline T& format_replace(T& arg) { return arg; } /// Specialization of format_replace for nullptr string parameters. template <> char const*& format_replace(char const*& arg) { static char const* nullptr_str = "(nullptr)"; if (arg) { return arg; } return nullptr_str; } /// Specialization of format_replace for nullptr string parameters. template <> char*& format_replace(char*& arg) { static char* nullptr_str = (char*)"(nullptr)"; if (arg) { return arg; } return nullptr_str; } template struct log { log(std::format_string fmt, T&&... args, source_location location = source_location()) { vlog(level, LOG_TAG, location, fmt.get(), std::make_format_args(format_replace(args)...)); } }; #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__)) template log(std::format_string, T&&...) -> log; #endif } // namespace bluetooth::log_internal namespace bluetooth::log { #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__)) template using error = log_internal::log; template using warning = log_internal::log; template using info = log_internal::log; template using debug = log_internal::log; template using verbose = log_internal::log; #else template struct error : log_internal::log { using log_internal::log::log; }; template struct warn : log_internal::log { using log_internal::log::log; }; template struct info : log_internal::log { using log_internal::log::log; }; template struct debug : log_internal::log { using log_internal::log::log; }; template struct verbose : log_internal::log { using log_internal::log::log; }; template error(std::format_string, T&&...) -> error; template warn(std::format_string, T&&...) -> warn; template info(std::format_string, T&&...) -> info; template debug(std::format_string, T&&...) -> debug; template verbose(std::format_string, T&&...) -> verbose; #endif // GCC / C++20 [[noreturn]] [[maybe_unused]] static void fatal( std::format_string<> fmt, log_internal::source_location location = log_internal::source_location()) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args()); std::abort(); // Enforce [[noreturn]] } template [[noreturn]] [[maybe_unused]] static void fatal( std::format_string fmt, T0&& arg0, log_internal::source_location location = log_internal::source_location()) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args(log_internal::format_replace(arg0))); std::abort(); // Enforce [[noreturn]] } template [[noreturn]] [[maybe_unused]] static void fatal( std::format_string fmt, T0&& arg0, T1&& arg1, log_internal::source_location location = log_internal::source_location()) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1))); std::abort(); // Enforce [[noreturn]] } template [[noreturn]] [[maybe_unused]] static void fatal( std::format_string fmt, T0&& arg0, T1&& arg1, T2&& arg2, log_internal::source_location location = log_internal::source_location()) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1), log_internal::format_replace(arg2))); std::abort(); // Enforce [[noreturn]] } template [[noreturn]] [[maybe_unused]] static void fatal( std::format_string fmt, T0&& arg0, T1&& arg1, T2&& arg2, T3&& arg3, log_internal::source_location location = log_internal::source_location()) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1), log_internal::format_replace(arg2), log_internal::format_replace(arg3))); std::abort(); // Enforce [[noreturn]] } template struct assert_that { assert_that(bool cond, std::format_string fmt, T&&... args, log_internal::source_location location = log_internal::source_location()) { if (!cond) { vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args(log_internal::format_replace(args)...)); } } }; template assert_that(bool, std::format_string, T&&...) -> assert_that; } // namespace bluetooth::log namespace std { /// Helper to format a pointer value as the memory address. /// Use this helper as `std::format("{}", std::format_ptr(value));`. template const void* format_ptr(T* ptr) { return reinterpret_cast(ptr); } /// Derive formatter for std::atomic types where T is formattable. /// The formatter uses the default memory order `std::memory_order_seq_cst` /// for reading the value. template struct formatter, CharT> : formatter { template auto format(const std::atomic& v, Context& ctx) const -> decltype(ctx.out()) { return formatter::format(v.load(), ctx); } }; /// Default formatter implementation for formatting /// types overloading the ostream `operator<<`. /// /// Enable this formatter in the code by declaring: /// ``` /// template<> /// struct std::formatter : ostream_formatter {}; /// ``` template struct basic_ostream_formatter : formatter, CharT> { void set_debug_format() = delete; template auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { auto&& output = std::basic_stringstream(); output.imbue(std::locale::classic()); // The default is always unlocalized. output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); return formatter, CharT>::format(output.str(), ctx); } }; using ostream_formatter = basic_ostream_formatter; /// Default formatter implementation for formatting /// enum class values to the underlying type. /// /// Enable this formatter in the code by declaring: /// ``` /// template<> /// struct std::formatter : enum_formatter {}; /// ``` template struct enum_formatter : std::formatter, CharT> { template typename Context::iterator format(EnumT value, Context& ctx) const { return std::formatter, CharT>::format( static_cast>(value), ctx); } }; /// Default formatter implementation for formatting /// values of type T for which a string conversion function /// T_to_str is implemented. /// /// Enable this formatter in the code by declaring: /// ``` /// template<> /// struct std::formatter : string_formatter {}; /// ``` template struct string_formatter : std::formatter { template typename Context::iterator format(const T& value, Context& ctx) const { return std::formatter::format(F(value), ctx); } }; } // namespace std