blob: f7e923cf2e596b23d75aed2d8f7bd8f2f17f0ee8 [file]
/*
* 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 <fmt/core.h>
#include <fmt/format.h>
#include <fmt/std.h>
#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,
fmt::string_view fmt, fmt::format_args vargs);
/// Capture invalid parameter values that would cause runtime
/// formatting errors.
template <class T>
[[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 <Level level, typename... T>
struct log {
log(fmt::format_string<T...> fmt, T&&... args,
source_location location = source_location()) {
vlog(level, LOG_TAG, location, static_cast<fmt::string_view>(fmt),
fmt::make_format_args(format_replace(args)...));
}
};
#if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
template <int level, typename... T>
log(fmt::format_string<T...>, T&&...) -> log<level, T...>;
#endif
} // namespace bluetooth::log_internal
namespace bluetooth::log {
#if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
template <typename... T>
using error = log_internal::log<log_internal::kError, T...>;
template <typename... T>
using warning = log_internal::log<log_internal::kWarning, T...>;
template <typename... T>
using info = log_internal::log<log_internal::kInfo, T...>;
template <typename... T>
using debug = log_internal::log<log_internal::kDebug, T...>;
template <typename... T>
using verbose = log_internal::log<log_internal::kVerbose, T...>;
#else
template <typename... T>
struct error : log_internal::log<log_internal::kError, T...> {
using log_internal::log<log_internal::kError, T...>::log;
};
template <typename... T>
struct warn : log_internal::log<log_internal::kWarn, T...> {
using log_internal::log<log_internal::kWarn, T...>::log;
};
template <typename... T>
struct info : log_internal::log<log_internal::kInfo, T...> {
using log_internal::log<log_internal::kInfo, T...>::log;
};
template <typename... T>
struct debug : log_internal::log<log_internal::kDebug, T...> {
using log_internal::log<log_internal::kDebug, T...>::log;
};
template <typename... T>
struct verbose : log_internal::log<log_internal::kVerbose, T...> {
using log_internal::log<log_internal::kVerbose, T...>::log;
};
template <typename... T>
error(fmt::format_string<T...>, T&&...) -> error<T...>;
template <typename... T>
warn(fmt::format_string<T...>, T&&...) -> warn<T...>;
template <typename... T>
info(fmt::format_string<T...>, T&&...) -> info<T...>;
template <typename... T>
debug(fmt::format_string<T...>, T&&...) -> debug<T...>;
template <typename... T>
verbose(fmt::format_string<T...>, T&&...) -> verbose<T...>;
#endif // GCC / C++20
[[noreturn]] [[maybe_unused]] static void fatal(
fmt::format_string<> fmt,
log_internal::source_location location = log_internal::source_location()) {
vlog(log_internal::kFatal, LOG_TAG, location,
static_cast<fmt::string_view>(fmt), fmt::make_format_args());
std::abort(); // Enforce [[noreturn]]
}
template <typename T0>
[[noreturn]] [[maybe_unused]] static void fatal(
fmt::format_string<T0> fmt, T0&& arg0,
log_internal::source_location location = log_internal::source_location()) {
vlog(log_internal::kFatal, LOG_TAG, location,
static_cast<fmt::string_view>(fmt),
fmt::make_format_args(log_internal::format_replace(arg0)));
std::abort(); // Enforce [[noreturn]]
}
template <typename T0, typename T1>
[[noreturn]] [[maybe_unused]] static void fatal(
fmt::format_string<T0, T1> fmt, T0&& arg0, T1&& arg1,
log_internal::source_location location = log_internal::source_location()) {
vlog(log_internal::kFatal, LOG_TAG, location,
static_cast<fmt::string_view>(fmt),
fmt::make_format_args(log_internal::format_replace(arg0),
log_internal::format_replace(arg1)));
std::abort(); // Enforce [[noreturn]]
}
template <typename T0, typename T1, typename T2>
[[noreturn]] [[maybe_unused]] static void fatal(
fmt::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
log_internal::source_location location = log_internal::source_location()) {
vlog(log_internal::kFatal, LOG_TAG, location,
static_cast<fmt::string_view>(fmt),
fmt::make_format_args(log_internal::format_replace(arg0),
log_internal::format_replace(arg1),
log_internal::format_replace(arg2)));
std::abort(); // Enforce [[noreturn]]
}
template <typename T0, typename T1, typename T2, typename T3>
[[noreturn]] [[maybe_unused]] static void fatal(
fmt::format_string<T0, T1, T2, T3> 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,
static_cast<fmt::string_view>(fmt),
fmt::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 <typename... T>
struct assert_that {
assert_that(bool cond, fmt::format_string<T...> fmt, T&&... args,
log_internal::source_location location =
log_internal::source_location()) {
if (!cond) {
vlog(log_internal::kFatal, LOG_TAG, location,
static_cast<fmt::string_view>(fmt),
fmt::make_format_args(log_internal::format_replace(args)...));
}
}
};
template <typename... T>
assert_that(bool, fmt::format_string<T...>, T&&...) -> assert_that<T...>;
} // namespace bluetooth::log
namespace fmt {
/// Default formatter implementation for formatting
/// enum class values to the underlying type.
///
/// Enable this formatter in the code by declaring:
/// ```
/// template<>
/// struct fmt::formatter<EnumT> : enum_formatter<EnumT> {};
/// ```
template <typename EnumT, class CharT = char>
struct enum_formatter : fmt::formatter<std::underlying_type_t<EnumT>, CharT> {
template <class Context>
typename Context::iterator format(EnumT value, Context& ctx) const {
return fmt::formatter<std::underlying_type_t<EnumT>, CharT>::format(
static_cast<std::underlying_type_t<EnumT>>(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 fmt::formatter<T> : string_formatter<T, &T_to_str> {};
/// ```
template <typename T, std::string (*F)(const T&), class CharT = char>
struct string_formatter : fmt::formatter<std::string> {
template <class Context>
typename Context::iterator format(const T& value, Context& ctx) const {
return fmt::formatter<std::string>::format(F(value), ctx);
}
};
} // namespace fmt