From e08cbed3c13100b912b8209c706b42e5cfbabf53 Mon Sep 17 00:00:00 2001 From: Henri Chataing <henrichataing@google.com> Date: Fri, 20 Oct 2023 09:06:51 -0700 Subject: [PATCH] system: fmtlib logger implementation - The fmtlib logger is implemented in <bluetooth/log.h>. The header defines the following templated logs function: template<typename T...> log::fatal(fmt::format_string<T...> fmt, T...args); log::error(..); log::warn(..); log::info(..); log::debug(..); log::verbose(..); - Front-end, logs are printed out by invoking these macros with the macro LOG_TAG defined _before_ the inclusion of #include <bluetooth/log.h> - Back-end, a single method must be implemented for all supported platforms (android, floss, host): namespace log_internal { void vlog(Level level, char const *tag, char const *file_name, int line, fmt::string_view fmt, fmt::format_args vargs); } - Default implementations are provided: + vlog_android: outputs to <log/log.h> __android_log_write_log_message + vlog_syslog: outputs to <syslog.h> syslog Bug: 305066880 Test: m libbluetooth_log Test: atest libbluetooth_log_test Flag: EXEMPT, logging utils Change-Id: Ic8a80f113b25d874c372d7dce8252d5428842ee8 --- README.md | 2 +- build.py | 1 + floss/build/Dockerfile | 1 + system/BUILD.gn | 8 + system/build/dpkg/floss/build-dpkg | 2 +- system/build/dpkg/floss/install-dependencies | 2 +- .../build/dpkg/floss/package/DEBIAN/control | 2 +- system/log/Android.bp | 31 ++++ system/log/BUILD.gn | 36 +++++ system/log/include/bluetooth/log.h | 146 ++++++++++++++++++ system/log/src/truncating_buffer.h | 69 +++++++++ system/log/src/truncating_buffer_test.cc | 83 ++++++++++ system/log/src/vlog_android.cc | 53 +++++++ system/log/src/vlog_syslog.cc | 66 ++++++++ system/log/src/vlog_test.cc | 114 ++++++++++++++ 15 files changed, 612 insertions(+), 4 deletions(-) create mode 100644 system/log/Android.bp create mode 100644 system/log/BUILD.gn create mode 100644 system/log/include/bluetooth/log.h create mode 100644 system/log/src/truncating_buffer.h create mode 100644 system/log/src/truncating_buffer_test.cc create mode 100644 system/log/src/vlog_android.cc create mode 100644 system/log/src/vlog_syslog.cc create mode 100644 system/log/src/vlog_test.cc diff --git a/README.md b/README.md index 60daa85761d..3df5e04b6d5 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ sudo apt-get install repo git-core gnupg flex bison gperf build-essential \ libgl1-mesa-dev libxml2-utils xsltproc unzip liblz4-tool libssl-dev \ libc++-dev libevent-dev \ flatbuffers-compiler libflatbuffers1 openssl \ - libflatbuffers-dev libtinyxml2-dev \ + libflatbuffers-dev libfmt-dev libtinyxml2-dev \ libglib2.0-dev libevent-dev libnss3-dev libdbus-1-dev \ libprotobuf-dev ninja-build generate-ninja protobuf-compiler \ libre2-9 debmake \ diff --git a/build.py b/build.py index 5e03c6388c7..d47005a41f2 100755 --- a/build.py +++ b/build.py @@ -127,6 +127,7 @@ REQUIRED_APT_PACKAGES = [ 'liblz4-tool', 'libncurses5', 'libnss3-dev', + 'libfmt-dev', 'libprotobuf-dev', 'libre2-9', 'libre2-dev', diff --git a/floss/build/Dockerfile b/floss/build/Dockerfile index e0e48248564..2be32777732 100644 --- a/floss/build/Dockerfile +++ b/floss/build/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update && \ libdouble-conversion-dev \ libevent-dev \ libflatbuffers-dev \ + libfmt-dev \ libgl1-mesa-dev \ libglib2.0-dev \ libgtest-dev \ diff --git a/system/BUILD.gn b/system/BUILD.gn index 02b92d245b2..62ced34597e 100644 --- a/system/BUILD.gn +++ b/system/BUILD.gn @@ -174,6 +174,10 @@ config("external_flatbuffers") { libs = [ "flatbuffers" ] } +config("external_fmtlib") { + configs = [ ":pkg_fmtlib" ] +} + # Package configurations to extract dependencies from env pkg_config("pkg_gtest") { pkg_deps = [ "gtest" ] @@ -203,6 +207,10 @@ pkg_config("pkg_tinyxml2") { pkg_deps = [ "tinyxml2" ] } +pkg_config("pkd_fmtlib") { + pkg_deps = [ "fmt" ] +} + # To include ChroemOS-specific libraries and build dependencies. if (target_os == "chromeos") { config("external_chromeos") { diff --git a/system/build/dpkg/floss/build-dpkg b/system/build/dpkg/floss/build-dpkg index e9cb70b9feb..fececbc6895 100755 --- a/system/build/dpkg/floss/build-dpkg +++ b/system/build/dpkg/floss/build-dpkg @@ -48,7 +48,7 @@ export PATH="${PATH}:${BIN_DIR}" # Check dependencies # libchrome requires modp_b64 -APT_REQUIRED="modp-b64 libchrome flatbuffers-compiler flex g++-multilib gcc-multilib generate-ninja gnupg gperf libc++-dev libdbus-1-dev libevent-dev libevent-dev libflatbuffers-dev libflatbuffers1 libgl1-mesa-dev libglib2.0-dev liblz4-tool libncurses5 libnss3-dev libprotobuf-dev libre2-9 libssl-dev libtinyxml2-dev libx11-dev libxml2-utils ninja-build openssl protobuf-compiler unzip x11proto-core-dev xsltproc zip zlib1g-dev" +APT_REQUIRED="modp-b64 libchrome flatbuffers-compiler flex g++-multilib gcc-multilib generate-ninja gnupg gperf libc++-dev libdbus-1-dev libevent-dev libevent-dev libflatbuffers-dev libflatbuffers1 libfmt-dev libgl1-mesa-dev libglib2.0-dev liblz4-tool libncurses5 libnss3-dev libprotobuf-dev libre2-9 libssl-dev libtinyxml2-dev libx11-dev libxml2-utils ninja-build openssl protobuf-compiler unzip x11proto-core-dev xsltproc zip zlib1g-dev" # SPEED UP TEST, REMOVE ME APT_REQUIRED="modp-b64 libchrome flatbuffers-compiler" diff --git a/system/build/dpkg/floss/install-dependencies b/system/build/dpkg/floss/install-dependencies index a1b33a74a66..e215399f6fa 100755 --- a/system/build/dpkg/floss/install-dependencies +++ b/system/build/dpkg/floss/install-dependencies @@ -24,7 +24,7 @@ function ctrl_c() { # APT dependencies APT_REQUIRED="git curl wget flatbuffers-compiler flex g++-multilib gcc-multilib generate-ninja \ -gnupg gperf libc++-dev libdbus-1-dev libevent-dev libflatbuffers-dev libflatbuffers1 \ +gnupg gperf libc++-dev libdbus-1-dev libevent-dev libflatbuffers-dev libfmt-dev libflatbuffers1 \ libgl1-mesa-dev libglib2.0-dev liblz4-tool libncurses5 libnss3-dev libprotobuf-dev libre2-9 \ libssl-dev libtinyxml2-dev libx11-dev libxml2-utils ninja-build openssl protobuf-compiler unzip \ x11proto-core-dev xsltproc zip zlib1g-dev libc++abi-dev cmake debmake ninja-build libgtest-dev \ diff --git a/system/build/dpkg/floss/package/DEBIAN/control b/system/build/dpkg/floss/package/DEBIAN/control index 3bb5d35eb9f..d5f4520c6cf 100644 --- a/system/build/dpkg/floss/package/DEBIAN/control +++ b/system/build/dpkg/floss/package/DEBIAN/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Martin Brabham <optedoblivion@google.com> Version: 0.1 Homepage: https://www.google.com -Depends: debmake, ninja-build, flatbuffers-compiler, flex, g++-multilib, gcc-multilib, generate-ninja, gnupg, gperf, libc++-dev, libdbus-1-dev, libevent-dev, libevent-dev, libflatbuffers-dev, libflatbuffers1, libgl1-mesa-dev, libglib2.0-dev, liblz4-tool, libncurses5, libnss3-dev, libprotobuf-dev, libre2-9, libssl-dev, libtinyxml2-dev, libx11-dev, libxml2-utils, ninja-build, openssl, protobuf-compiler, unzip, x11proto-core-dev, xsltproc, zip, zlib1g-dev, modp-b64, libchrome +Depends: debmake, ninja-build, flatbuffers-compiler, flex, g++-multilib, gcc-multilib, generate-ninja, gnupg, gperf, libc++-dev, libdbus-1-dev, libevent-dev, libevent-dev, libflatbuffers-dev, libflatbuffers1, libfmt-dev, libfmt9, libgl1-mesa-dev, libglib2.0-dev, liblz4-tool, libncurses5, libnss3-dev, libprotobuf-dev, libre2-9, libssl-dev, libtinyxml2-dev, libx11-dev, libxml2-utils, ninja-build, openssl, protobuf-compiler, unzip, x11proto-core-dev, xsltproc, zip, zlib1g-dev, modp-b64, libchrome Architecture: all Essential: no Installed-Size: 490MB diff --git a/system/log/Android.bp b/system/log/Android.bp new file mode 100644 index 00000000000..10c7e730db8 --- /dev/null +++ b/system/log/Android.bp @@ -0,0 +1,31 @@ +cc_library { + name: "libbluetooth_log", + host_supported: true, + min_sdk_version: "33", + apex_available: [ + "com.android.btservices", + ], + export_include_dirs: [ + "include", + ], + shared_libs: [ + "libbase", + "liblog", + ], + srcs: [ + "src/vlog_android.cc", + ], +} + +cc_test { + name: "libbluetooth_log_test", + host_supported: true, + srcs: [ + "src/truncating_buffer_test.cc", + "src/vlog_test.cc", + ], + shared_libs: [ + "libbase", + "libbluetooth_log", + ], +} diff --git a/system/log/BUILD.gn b/system/log/BUILD.gn new file mode 100644 index 00000000000..b7b94e7deed --- /dev/null +++ b/system/log/BUILD.gn @@ -0,0 +1,36 @@ +# +# Copyright 2024 Google, Inc. +# +# 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. +# + +config("log_defaults") { + include_dirs = [ + "//bt/system/log/include", + ] +} + +static_library("libbluetooth_log") { + cflags = [ + "-fvisibility=default", + ] + sources = [ + "include/bluetooth/log.h", + "src/truncating_buffer.h", + "src/vlog_syslog.cc", + ] + configs += [ + "//bt/system:target_defaults", + ":log_defaults", + ] +} diff --git a/system/log/include/bluetooth/log.h b/system/log/include/bluetooth/log.h new file mode 100644 index 00000000000..3ed77aa7a44 --- /dev/null +++ b/system/log/include/bluetooth/log.h @@ -0,0 +1,146 @@ +/* + * 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, +}; + +/// Write a single log line. +/// The implementation of this function is dependent on the backend. +void vlog(Level level, char const* tag, char const* file_name, int line, + char const* function_name, fmt::string_view fmt, + fmt::format_args vargs); + +template <Level level, typename... T> +struct log { + log(fmt::format_string<T...> fmt, T&&... args, + char const* file_name = __builtin_FILE(), int line = __builtin_LINE(), + char const* function_name = __builtin_FUNCTION()) { + vlog(level, LOG_TAG, file_name, line, function_name, + static_cast<fmt::string_view>(fmt), fmt::make_format_args(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 fatal = log_internal::log<log_internal::kFatal, T...>; +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 fatal : log_internal::log<log_internal::kFatal, T...> { + using log_internal::log<log_internal::kFatal, T...>::log; +}; +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> +fatal(fmt::format_string<T...>, T&&...) -> fatal<T...>; +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 + +} // 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); + } +}; + +} // namespace fmt diff --git a/system/log/src/truncating_buffer.h b/system/log/src/truncating_buffer.h new file mode 100644 index 00000000000..c2ad0014a95 --- /dev/null +++ b/system/log/src/truncating_buffer.h @@ -0,0 +1,69 @@ +/* + * 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 <cstddef> + +namespace bluetooth::log_internal { + +/// Truncating write buffer. +/// +/// This buffer can be used with `std::back_insert_iterator` to create +/// an output iterator. All write actions beyond the maximum length of +/// the buffer are silently ignored. +template <int buffer_size> +struct truncating_buffer { + using value_type = char; + + void push_back(char c) { + if (len < buffer_size - 1) { + buffer[len++] = c; + } + } + + char const* c_str() { + if (len == buffer_size - 1) { + // Inspect the last 4 bytes of the buffer to check if + // the last character was truncated. Remove the character + // entirely if that's the case. + for (size_t n = 0; n < 4; n++) { + char c = buffer[len - n - 1]; + if ((c & 0b11000000) == 0b10000000) { + continue; + } + size_t char_len = (c & 0b10000000) == 0b00000000 ? 1 + : (c & 0b11100000) == 0b11000000 ? 2 + : (c & 0b11110000) == 0b11100000 ? 3 + : (c & 0b11111000) == 0b11110000 ? 4 + : 0; + if ((n + 1) < char_len) { + len -= n + 1; + } + break; + } + } + + buffer[len] = '\0'; + return buffer; + } + + private: + char buffer[buffer_size]; + size_t len{0}; +}; + +} // namespace bluetooth::log_internal diff --git a/system/log/src/truncating_buffer_test.cc b/system/log/src/truncating_buffer_test.cc new file mode 100644 index 00000000000..5790270cdc6 --- /dev/null +++ b/system/log/src/truncating_buffer_test.cc @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#define LOG_TAG "test" + +#include "truncating_buffer.h" + +#include <fmt/format.h> +#include <gtest/gtest.h> +#include <log/log.h> + +using namespace bluetooth::log_internal; + +TEST(TruncatingBufferTest, 1byte) { + EXPECT_EQ(sizeof("ab"), 3); + truncating_buffer<2> buffer_1; + truncating_buffer<3> buffer_2; + fmt::format_to(std::back_insert_iterator(buffer_1), "ab"); + fmt::format_to(std::back_insert_iterator(buffer_2), "ab"); + EXPECT_STREQ(buffer_1.c_str(), "a"); + EXPECT_STREQ(buffer_2.c_str(), "ab"); +} + +TEST(TruncatingBufferTest, 2bytes) { + EXPECT_EQ(sizeof("αβ"), 5); + truncating_buffer<3> buffer_1; + truncating_buffer<4> buffer_2; + truncating_buffer<5> buffer_3; + fmt::format_to(std::back_insert_iterator(buffer_1), "αβ"); + fmt::format_to(std::back_insert_iterator(buffer_2), "αβ"); + fmt::format_to(std::back_insert_iterator(buffer_3), "αβ"); + EXPECT_STREQ(buffer_1.c_str(), "α"); + EXPECT_STREQ(buffer_2.c_str(), "α"); + EXPECT_STREQ(buffer_3.c_str(), "αβ"); +} + +TEST(TruncatingBufferTest, 3bytes) { + EXPECT_EQ(sizeof("ພຮ"), 7); + truncating_buffer<4> buffer_1; + truncating_buffer<5> buffer_2; + truncating_buffer<6> buffer_3; + truncating_buffer<7> buffer_4; + fmt::format_to(std::back_insert_iterator(buffer_1), "ພຮ"); + fmt::format_to(std::back_insert_iterator(buffer_2), "ພຮ"); + fmt::format_to(std::back_insert_iterator(buffer_3), "ພຮ"); + fmt::format_to(std::back_insert_iterator(buffer_4), "ພຮ"); + EXPECT_STREQ(buffer_1.c_str(), "ພ"); + EXPECT_STREQ(buffer_2.c_str(), "ພ"); + EXPECT_STREQ(buffer_3.c_str(), "ພ"); + EXPECT_STREQ(buffer_4.c_str(), "ພຮ"); +} + +TEST(TruncatingBufferTest, 4bytes) { + EXPECT_EQ(sizeof("ðŽ¡ðŽª"), 9); + truncating_buffer<5> buffer_1; + truncating_buffer<6> buffer_2; + truncating_buffer<7> buffer_3; + truncating_buffer<8> buffer_4; + truncating_buffer<9> buffer_5; + fmt::format_to(std::back_insert_iterator(buffer_1), "ðŽ¡ðŽª"); + fmt::format_to(std::back_insert_iterator(buffer_2), "ðŽ¡ðŽª"); + fmt::format_to(std::back_insert_iterator(buffer_3), "ðŽ¡ðŽª"); + fmt::format_to(std::back_insert_iterator(buffer_4), "ðŽ¡ðŽª"); + fmt::format_to(std::back_insert_iterator(buffer_5), "ðŽ¡ðŽª"); + EXPECT_STREQ(buffer_1.c_str(), "ðŽ¡"); + EXPECT_STREQ(buffer_2.c_str(), "ðŽ¡"); + EXPECT_STREQ(buffer_3.c_str(), "ðŽ¡"); + EXPECT_STREQ(buffer_4.c_str(), "ðŽ¡"); + EXPECT_STREQ(buffer_5.c_str(), "ðŽ¡ðŽª"); +} diff --git a/system/log/src/vlog_android.cc b/system/log/src/vlog_android.cc new file mode 100644 index 00000000000..38930ca4291 --- /dev/null +++ b/system/log/src/vlog_android.cc @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include <log/log.h> + +#include "bluetooth/log.h" +#include "truncating_buffer.h" + +namespace bluetooth::log_internal { + +static constexpr size_t kBufferSize = 1024; + +void vlog(Level level, char const* tag, char const* file_name, int line, + char const* function_name, fmt::string_view fmt, + fmt::format_args vargs) { + // Check if log is enabled. + if (!__android_log_is_loggable(level, tag, ANDROID_LOG_DEFAULT) && + !__android_log_is_loggable(level, "bluetooth", ANDROID_LOG_DEFAULT)) { + return; + } + + // Format to stack buffer. + truncating_buffer<kBufferSize> buffer; + fmt::format_to(std::back_insert_iterator(buffer), "{}: ", function_name); + fmt::vformat_to(std::back_insert_iterator(buffer), fmt, vargs); + + // Send message to liblog. + struct __android_log_message message = { + .struct_size = sizeof(__android_log_message), + .buffer_id = LOG_ID_MAIN, + .priority = static_cast<android_LogPriority>(level), + .tag = tag, + .file = file_name, + .line = static_cast<uint32_t>(line), + .message = buffer.c_str(), + }; + __android_log_write_log_message(&message); +} + +} // namespace bluetooth::log_internal diff --git a/system/log/src/vlog_syslog.cc b/system/log/src/vlog_syslog.cc new file mode 100644 index 00000000000..9d7dad1a965 --- /dev/null +++ b/system/log/src/vlog_syslog.cc @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <syslog.h> + +#include "bluetooth/log.h" +#include "truncating_buffer.h" + +namespace bluetooth::log_internal { + +// Default value for $MaxMessageSize for rsyslog. +static constexpr size_t kBufferSize = 8192; + +void vlog(Level level, char const* tag, char const* file_name, int line, + char const* function_name, fmt::string_view fmt, + fmt::format_args vargs) { + // Convert the level to syslog severity. + int severity = LOG_DEBUG; + switch (level) { + case Level::kVerbose: + case Level::kDebug: + default: + severity = LOG_DEBUG; + break; + case Level::kInfo: + severity = LOG_INFO; + break; + case Level::kWarn: + severity = LOG_WARNING; + break; + case Level::kError: + severity = LOG_ERR; + break; + case Level::kFatal: + severity = LOG_CRIT; + break; + } + + // Prepare bounded stack buffer. + truncating_buffer<kBufferSize> buffer; + + // Format file, line. + fmt::format_to(std::back_insert_iterator(buffer), "{} {}:{} {}: ", tag, + file_name, line, function_name); + + // Format message. + fmt::vformat_to(std::back_insert_iterator(buffer), fmt, vargs); + + // Print to vsyslog. + syslog(LOG_USER | severity, "%s", buffer.c_str()); +} + +} // namespace bluetooth::log_internal diff --git a/system/log/src/vlog_test.cc b/system/log/src/vlog_test.cc new file mode 100644 index 00000000000..026dd737081 --- /dev/null +++ b/system/log/src/vlog_test.cc @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#define LOG_TAG "test" + +#include <gtest/gtest.h> +#include <log/log.h> + +#include "bluetooth/log.h" +#include "truncating_buffer.h" + +/// Captures the latest message generated by the android vlog +/// implementation. +static std::optional<__android_log_message> androidLogMessage; + +/// Mask the implementation from liblog. +int __android_log_is_loggable(int /*prio*/, const char* /*tag*/, + int /*default_prio*/) { + return true; +} + +/// Mask the implementation from liblog. +void __android_log_write_log_message( + struct __android_log_message* log_message) { + if (log_message != nullptr) { + log_message->message = strdup(log_message->message); + androidLogMessage.emplace(*log_message); + } +} + +using namespace bluetooth; + +TEST(BluetoothLoggerTest, verbose) { + androidLogMessage.reset(); + + log::verbose("verbose test"); + + ASSERT_TRUE(androidLogMessage.has_value()); + EXPECT_EQ(androidLogMessage->priority, ANDROID_LOG_VERBOSE); + EXPECT_STREQ(androidLogMessage->tag, LOG_TAG); + EXPECT_STREQ(androidLogMessage->file, + "packages/modules/Bluetooth/system/log/src/vlog_test.cc"); + EXPECT_EQ(androidLogMessage->line, 49); + EXPECT_STREQ(androidLogMessage->message, "TestBody: verbose test"); +} + +TEST(BluetoothLoggerTest, debug) { + androidLogMessage.reset(); + + log::debug("debug test"); + + ASSERT_TRUE(androidLogMessage.has_value()); + EXPECT_EQ(androidLogMessage->priority, ANDROID_LOG_DEBUG); + EXPECT_STREQ(androidLogMessage->tag, LOG_TAG); + EXPECT_STREQ(androidLogMessage->file, + "packages/modules/Bluetooth/system/log/src/vlog_test.cc"); + EXPECT_EQ(androidLogMessage->line, 63); + EXPECT_STREQ(androidLogMessage->message, "TestBody: debug test"); +} + +TEST(BluetoothLoggerTest, info) { + androidLogMessage.reset(); + + log::info("info test"); + + ASSERT_TRUE(androidLogMessage.has_value()); + EXPECT_EQ(androidLogMessage->priority, ANDROID_LOG_INFO); + EXPECT_STREQ(androidLogMessage->tag, LOG_TAG); + EXPECT_STREQ(androidLogMessage->file, + "packages/modules/Bluetooth/system/log/src/vlog_test.cc"); + EXPECT_EQ(androidLogMessage->line, 77); + EXPECT_STREQ(androidLogMessage->message, "TestBody: info test"); +} + +TEST(BluetoothLoggerTest, warn) { + androidLogMessage.reset(); + + log::warn("warn test"); + + ASSERT_TRUE(androidLogMessage.has_value()); + EXPECT_EQ(androidLogMessage->priority, ANDROID_LOG_WARN); + EXPECT_STREQ(androidLogMessage->tag, LOG_TAG); + EXPECT_STREQ(androidLogMessage->file, + "packages/modules/Bluetooth/system/log/src/vlog_test.cc"); + EXPECT_EQ(androidLogMessage->line, 91); + EXPECT_STREQ(androidLogMessage->message, "TestBody: warn test"); +} + +TEST(BluetoothLoggerTest, error) { + androidLogMessage.reset(); + + log::error("error test"); + + ASSERT_TRUE(androidLogMessage.has_value()); + EXPECT_EQ(androidLogMessage->priority, ANDROID_LOG_ERROR); + EXPECT_STREQ(androidLogMessage->tag, LOG_TAG); + EXPECT_STREQ(androidLogMessage->file, + "packages/modules/Bluetooth/system/log/src/vlog_test.cc"); + EXPECT_EQ(androidLogMessage->line, 105); + EXPECT_STREQ(androidLogMessage->message, "TestBody: error test"); +} -- GitLab