From 28e0c72e0380786ead30eaa3d19ca85899ae5893 Mon Sep 17 00:00:00 2001 From: Marcin Radomski Date: Thu, 17 Aug 2023 16:11:56 +0000 Subject: Enable default-initializing liblog_rust to write to logcat on Android Add default_log_impl cfg that, when enabled, makes `liblog_rust` use `android_logger` instead of `NopLogger` by default. This makes it possible to embed android_logger as mod inside liblog_rust crate, so that AndroidLogger can be used as default logger instead of a NopLogger. Changing that default prevents dropping logs when the logger is uninitialized. This can happen by accident when an application doesn't intialize the logger in all linker namespaces it pulls libraries from. See discussion at b/294216366#comment7. Bug: 275290559 Test: compile test app from aosp/2717614 Test: run it on a Cuttlefish device Test: observe logcat logs on all level from FFI call Test: observe all logs on non-FFI call without initializing the logger Test: observe set log filter applying only to non-FFI call Change-Id: I04dd334c66e5a2be8cfb19e87be3afb9146e5aa6 --- Android.bp | 12 +++ ...ult-initializing-liblog_rust-to-write-to-.patch | 115 +++++++++++++++++++++ src/android_logger.rs | 1 + src/lib.rs | 23 +++++ 4 files changed, 151 insertions(+) create mode 100644 patches/0001-Enable-default-initializing-liblog_rust-to-write-to-.patch create mode 120000 src/android_logger.rs diff --git a/Android.bp b/Android.bp index e6ff3cf..81c9175 100644 --- a/Android.bp +++ b/Android.bp @@ -60,6 +60,18 @@ rust_library { product_available: true, vendor_available: true, min_sdk_version: "29", + target: { + android: { + cfgs: ["default_log_impl"], + rustlibs: [ + "libandroid_log_sys", + "libonce_cell", + ], + shared_libs: [ + "liblog", + ], + } + } } rust_library_rlib { diff --git a/patches/0001-Enable-default-initializing-liblog_rust-to-write-to-.patch b/patches/0001-Enable-default-initializing-liblog_rust-to-write-to-.patch new file mode 100644 index 0000000..0c8c38a --- /dev/null +++ b/patches/0001-Enable-default-initializing-liblog_rust-to-write-to-.patch @@ -0,0 +1,115 @@ +From cd85e49c438e3aa9dbe2989f91e5ad7d3f4816de Mon Sep 17 00:00:00 2001 +From: Marcin Radomski +Date: Thu, 17 Aug 2023 16:11:56 +0000 +Subject: [PATCH] Enable default-initializing liblog_rust to write to logcat on + Android + +Add default_log_impl cfg that, when enabled, makes `liblog_rust` use +`android_logger` instead of `NopLogger` by default. + +This makes it possible to embed android_logger as mod inside liblog_rust +crate, so that AndroidLogger can be used as default logger instead of a +NopLogger. + +Changing that default prevents dropping logs when the logger is +uninitialized. This can happen by accident when an application doesn't +intialize the logger in all linker namespaces it pulls libraries from. +See discussion at b/294216366#comment7. + +Bug: 275290559 +Test: compile test app from aosp/2717614 +Test: run it on a Cuttlefish device +Test: observe logcat logs on all level from FFI call +Test: observe all logs on non-FFI call without initializing the logger +Test: observe set log filter applying only to non-FFI call +Change-Id: I04dd334c66e5a2be8cfb19e87be3afb9146e5aa6 +--- + Android.bp | 12 ++++++++++++ + src/android_logger.rs | 1 + + src/lib.rs | 23 +++++++++++++++++++++++ + 3 files changed, 36 insertions(+) + create mode 120000 src/android_logger.rs + +diff --git a/Android.bp b/Android.bp +index e6ff3cf..81c9175 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -60,6 +60,18 @@ rust_library { + product_available: true, + vendor_available: true, + min_sdk_version: "29", ++ target: { ++ android: { ++ cfgs: ["default_log_impl"], ++ rustlibs: [ ++ "libandroid_log_sys", ++ "libonce_cell", ++ ], ++ shared_libs: [ ++ "liblog", ++ ], ++ } ++ } + } + + rust_library_rlib { +diff --git a/src/android_logger.rs b/src/android_logger.rs +new file mode 120000 +index 0000000..84b8625 +--- /dev/null ++++ b/src/android_logger.rs +@@ -0,0 +1 @@ ++../../android_logger/src/lib.rs +\ No newline at end of file +diff --git a/src/lib.rs b/src/lib.rs +index 4ead826..8eb1c50 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -344,6 +344,11 @@ mod serde; + #[cfg(feature = "kv_unstable")] + pub mod kv; + ++#[cfg(default_log_impl)] ++extern crate once_cell; ++#[cfg(default_log_impl)] ++mod android_logger; ++ + #[cfg(has_atomics)] + use std::sync::atomic::{AtomicUsize, Ordering}; + +@@ -405,7 +410,10 @@ const UNINITIALIZED: usize = 0; + const INITIALIZING: usize = 1; + const INITIALIZED: usize = 2; + ++#[cfg(not(default_log_impl))] + static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); ++#[cfg(default_log_impl)] ++static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(5); + + static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; + +@@ -1572,6 +1580,21 @@ impl error::Error for ParseLevelError {} + /// If a logger has not been set, a no-op implementation is returned. + pub fn logger() -> &'static dyn Log { + if STATE.load(Ordering::SeqCst) != INITIALIZED { ++ #[cfg(default_log_impl)] ++ { ++ // On Android, default to logging to logcat if not explicitly initialized. This ++ // prevents logs from being dropped by default, which may happen unexpectedly in case ++ // of using libraries from multiple linker namespaces and failing to initialize the ++ // logger in each namespace. See b/294216366#comment7. ++ use android_logger::{AndroidLogger, Config}; ++ use std::sync::OnceLock; ++ static ANDROID_LOGGER: OnceLock = OnceLock::new(); ++ return ++ ANDROID_LOGGER.get_or_init(|| { ++ // Pass all logs down to liblog - it does its own filtering. ++ AndroidLogger::new(Config::default().with_max_level(LevelFilter::Trace)) ++ }); ++ } + static NOP: NopLogger = NopLogger; + &NOP + } else { +-- +2.42.0.rc1.204.g551eb34607-goog + diff --git a/src/android_logger.rs b/src/android_logger.rs new file mode 120000 index 0000000..84b8625 --- /dev/null +++ b/src/android_logger.rs @@ -0,0 +1 @@ +../../android_logger/src/lib.rs \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 4ead826..8eb1c50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -344,6 +344,11 @@ mod serde; #[cfg(feature = "kv_unstable")] pub mod kv; +#[cfg(default_log_impl)] +extern crate once_cell; +#[cfg(default_log_impl)] +mod android_logger; + #[cfg(has_atomics)] use std::sync::atomic::{AtomicUsize, Ordering}; @@ -405,7 +410,10 @@ const UNINITIALIZED: usize = 0; const INITIALIZING: usize = 1; const INITIALIZED: usize = 2; +#[cfg(not(default_log_impl))] static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); +#[cfg(default_log_impl)] +static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(5); static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; @@ -1572,6 +1580,21 @@ impl error::Error for ParseLevelError {} /// If a logger has not been set, a no-op implementation is returned. pub fn logger() -> &'static dyn Log { if STATE.load(Ordering::SeqCst) != INITIALIZED { + #[cfg(default_log_impl)] + { + // On Android, default to logging to logcat if not explicitly initialized. This + // prevents logs from being dropped by default, which may happen unexpectedly in case + // of using libraries from multiple linker namespaces and failing to initialize the + // logger in each namespace. See b/294216366#comment7. + use android_logger::{AndroidLogger, Config}; + use std::sync::OnceLock; + static ANDROID_LOGGER: OnceLock = OnceLock::new(); + return + ANDROID_LOGGER.get_or_init(|| { + // Pass all logs down to liblog - it does its own filtering. + AndroidLogger::new(Config::default().with_max_level(LevelFilter::Trace)) + }); + } static NOP: NopLogger = NopLogger; &NOP } else { -- cgit v1.2.3