Skip to content

Commit 0705151

Browse files
author
Marcin Radomski
committed
Use __android_log_is_loggable in AndroidLogger::enabled
Android's C logging API determines the effective log level based on a combination [1] of a global variable, system-wide properties [2], and call-specific default. log + android_logger crates add another layer of log filtering on top of that, independent from the C API. ``` .-----. | app | '-----' | v .-----------. filter by STATIC_MAX_LEVEL [3] + | log crate | - MAX_LOG_LEVEL_FILTER [4], '-----------' overrideable via log::set_max_level | v .----------------------. | android_logger crate | - filter by Config::max_level [5] '----------------------' | v .--------. | liblog | - filter by global state or system-wide properties [2] '--------' ``` The result is that in mixed C/C++ + Rust applications, logs originating from Rust use the least verbose level configured, which sometimes results in unexpected loss of logs. In addition, adjusting the log level globally via system properties, very useful in work on the AOSP itself, currently works only up to the level android_logger defaults to. This change makes AndroidLogger completely delegate log filtering to the underlying liblog when Config::max_level is unset by using __android_log_is_loggable. Setting Config::max_level, or calling log::set_max_level still keep the existing behavior of filtering the logs before they reach liblog, but the default is now to have the log level consistent between Rust and C/C++ logs. It also removes one TODO from the code :) Tested by: - running a test app that uses Rust logs of all levels on a rooted device, using both Android's liblog C API and android_logger ones - adjusting the log.tag system property [2] via adb shell setprop - verifying the message filtering changes according to log.tag changes consistently for logs from both languages [1] https://cs.android.com/android/platform/superproject/main/+/main:system/logging/liblog/properties.cpp;l=243;drc=b74a506c1b69f5b295a8cdfd7e2da3b16db15934 [2] https://cs.android.com/android/platform/superproject/main/+/main:system/logging/logd/README.property;l=45;drc=99c545d3098018a544cb292e1501daca694bee0f [3] https://github.com/rust-lang/log/blob/0551261bb4588b7f8afc8be05640347c97b67e10/src/lib.rs#L1536 [4] https://github.com/rust-lang/log/blob/0551261bb4588b7f8afc8be05640347c97b67e10/src/lib.rs#L469 [5] https://github.com/rust-mobile/android_logger-rs/blob/d51b7ffdacf20fb09fd36a6b309b50240ef50728/src/lib.rs#L198
1 parent 151aefc commit 0705151

File tree

4 files changed

+74
-11
lines changed

4 files changed

+74
-11
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ targets = [
2525
[features]
2626
default = ["regex"]
2727
regex = ["env_filter/regex"]
28+
android-api-30 = []
2829

2930
[dependencies.log]
3031
version = "0.4"
3132

3233
[dependencies.android_log-sys]
33-
version = "0.3"
34+
version = "0.3.2"
3435

3536
[dependencies.env_filter]
3637
version = "0.1"

src/config.rs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,67 @@ impl fmt::Debug for Config {
2020
.field("buf_id", &self.buf_id)
2121
.field("filter", &self.filter)
2222
.field("tag", &self.tag)
23-
.field("custom_format", match &self.custom_format {
24-
Some(_) => &"Some(_)",
25-
None => &"None",
26-
})
23+
.field(
24+
"custom_format",
25+
match &self.custom_format {
26+
Some(_) => &"Some(_)",
27+
None => &"None",
28+
},
29+
)
2730
.finish()
2831
}
2932
}
3033

34+
#[cfg(all(target_os = "android", feature = "android-api-30"))]
35+
fn android_log_priority_from_level(level: Level) -> android_log_sys::LogPriority {
36+
match level {
37+
Level::Warn => android_log_sys::LogPriority::WARN,
38+
Level::Info => android_log_sys::LogPriority::INFO,
39+
Level::Debug => android_log_sys::LogPriority::DEBUG,
40+
Level::Error => android_log_sys::LogPriority::ERROR,
41+
Level::Trace => android_log_sys::LogPriority::VERBOSE,
42+
}
43+
}
44+
45+
/// Asks Android liblog if a message with given `tag` and `priority` should be logged, using
46+
/// `default_prio` as the level filter in case no system- or process-wide overrides are set.
47+
#[cfg(all(target_os = "android", feature = "android-api-30"))]
48+
fn android_is_loggable_len(
49+
prio: log_ffi::LogPriority,
50+
tag: &str,
51+
default_prio: log_ffi::LogPriority,
52+
) -> bool {
53+
// SAFETY: tag points to a valid string tag.len() bytes long.
54+
unsafe {
55+
log_ffi::__android_log_is_loggable_len(
56+
prio as log_ffi::c_int,
57+
tag.as_ptr() as *const log_ffi::c_char,
58+
tag.len() as log_ffi::c_size_t,
59+
default_prio as log_ffi::c_int,
60+
) != 0
61+
}
62+
}
63+
64+
#[cfg(not(all(target_os = "android", feature = "android-api-30")))]
65+
fn default_is_loggable(_tag: &str, record_level: Level, config_level: Option<LevelFilter>) -> bool {
66+
record_level <= config_level.unwrap_or_else(log::max_level)
67+
}
68+
69+
#[cfg(all(target_os = "android", feature = "android-api-30"))]
70+
fn android_is_loggable(tag: &str, record_level: Level, config_level: Option<LevelFilter>) -> bool {
71+
let prio = android_log_priority_from_level(record_level);
72+
// Priority to use in case no system-wide or process-wide overrides are set.
73+
let default_prio = match config_level {
74+
Some(level_filter) => match level_filter.to_level() {
75+
Some(level) => android_log_priority_from_level(level),
76+
// LevelFilter::to_level() returns None only for LevelFilter::Off
77+
None => android_log_sys::LogPriority::SILENT,
78+
},
79+
None => android_log_sys::LogPriority::INFO,
80+
};
81+
android_is_loggable_len(prio, tag, default_prio)
82+
}
83+
3184
impl Config {
3285
/// Changes the maximum log level.
3386
///
@@ -61,9 +114,13 @@ impl Config {
61114
}
62115
}
63116

64-
pub(crate) fn is_loggable(&self, level: Level) -> bool {
65-
// todo: consider __android_log_is_loggable.
66-
level <= self.log_level.unwrap_or_else(log::max_level)
117+
pub(crate) fn is_loggable(&self, tag: &str, level: Level) -> bool {
118+
#[cfg(all(target_os = "android", feature = "android-api-30"))]
119+
use android_is_loggable as is_loggable;
120+
#[cfg(not(all(target_os = "android", feature = "android-api-30")))]
121+
use default_is_loggable as is_loggable;
122+
123+
is_loggable(tag, level, self.log_level)
67124
}
68125

69126
pub fn with_filter(mut self, filter: env_filter::Filter) -> Self {

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ const LOGGING_MSG_MAX_LEN: usize = 4000;
146146

147147
impl Log for AndroidLogger {
148148
fn enabled(&self, metadata: &Metadata) -> bool {
149-
self.config().is_loggable(metadata.level())
149+
self.config()
150+
.is_loggable(metadata.target(), metadata.level())
150151
}
151152

152153
fn log(&self, record: &Record) {

src/platform_log_writer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::arrays::slice_assume_init_ref;
2-
use crate::{LOGGING_MSG_MAX_LEN, LogId, android_log, uninit_array};
2+
use crate::{android_log, uninit_array, LogId, LOGGING_MSG_MAX_LEN};
33
use log::Level;
44
#[cfg(target_os = "android")]
55
use log_ffi::LogPriority;
@@ -153,7 +153,11 @@ impl fmt::Write for PlatformLogWriter<'_> {
153153
.enumerate()
154154
.fold(None, |acc, (i, (output, input))| {
155155
output.write(*input);
156-
if *input == b'\n' { Some(i) } else { acc }
156+
if *input == b'\n' {
157+
Some(i)
158+
} else {
159+
acc
160+
}
157161
});
158162

159163
// update last \n index

0 commit comments

Comments
 (0)