diff --git a/Cargo.toml b/Cargo.toml index 44b3923c9a431..67e9f1f346a29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -320,6 +320,6 @@ apk_label = "Bevy Example" assets = "assets" res = "assets/android-res" icon = "@mipmap/ic_launcher" - build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] +build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] min_sdk_version = 16 target_sdk_version = 29 diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index fbb479a4f620c..d3c482932ae92 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -17,9 +17,12 @@ keywords = ["bevy"] bevy_app = { path = "../bevy_app", version = "0.3.0" } bevy_utils = { path = "../bevy_utils", version = "0.3.0" } -tracing-subscriber = "0.2.15" +tracing-subscriber = {version = "0.2.15", features = ["registry"]} tracing-chrome = { version = "0.2.0", optional = true } +[target.'cfg(target_os = "android")'.dependencies] +android_log-sys = "0.2.0" + [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.6" -tracing-wasm = "0.1" \ No newline at end of file +tracing-wasm = "0.1" diff --git a/crates/bevy_log/src/android_tracing.rs b/crates/bevy_log/src/android_tracing.rs new file mode 100644 index 0000000000000..5655817da9ab0 --- /dev/null +++ b/crates/bevy_log/src/android_tracing.rs @@ -0,0 +1,108 @@ +use std::fmt::{Debug, Write}; + +use bevy_utils::tracing::{ + field::Field, + span::{Attributes, Record}, + Event, Id, Level, Metadata, Subscriber, +}; +use tracing_subscriber::{field::Visit, layer::Context, registry::LookupSpan, Layer}; + +pub(crate) struct AndroidLayer { + pub(crate) min_level: Level, +} + +struct StringRecorder(String, bool); +impl StringRecorder { + fn new() -> Self { + StringRecorder(String::new(), false) + } +} + +impl Visit for StringRecorder { + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { + if field.name() == "message" { + if !self.0.is_empty() { + self.0 = format!("{:?}\n{}", value, self.0) + } else { + self.0 = format!("{:?}", value) + } + } else { + if self.1 { + // following args + write!(self.0, " ").unwrap(); + } else { + // first arg + self.1 = true; + } + write!(self.0, "{} = {:?};", field.name(), value).unwrap(); + } + } +} + +impl core::fmt::Display for StringRecorder { + fn fmt(&self, mut f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + if !self.0.is_empty() { + write!(&mut f, " {}", self.0) + } else { + Ok(()) + } + } +} + +impl core::default::Default for StringRecorder { + fn default() -> Self { + StringRecorder::new() + } +} + +impl LookupSpan<'a>> Layer for AndroidLayer { + fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { + let level = metadata.level(); + level <= &self.min_level + } + + fn new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + let mut new_debug_record = StringRecorder::new(); + attrs.record(&mut new_debug_record); + + if let Some(span_ref) = ctx.span(id) { + span_ref + .extensions_mut() + .insert::(new_debug_record); + } + } + + fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { + if let Some(span_ref) = ctx.span(id) { + if let Some(debug_record) = span_ref.extensions_mut().get_mut::() { + values.record(debug_record); + } + } + } + + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let mut recorder = StringRecorder::new(); + event.record(&mut recorder); + let meta = event.metadata(); + let level = meta.level(); + if *level < self.min_level { + return; + } + let priority = match *level { + Level::TRACE => android_log_sys::LogPriority::VERBOSE, + Level::DEBUG => android_log_sys::LogPriority::DEBUG, + Level::INFO => android_log_sys::LogPriority::INFO, + Level::WARN => android_log_sys::LogPriority::WARN, + Level::ERROR => android_log_sys::LogPriority::ERROR, + }; + let message = format!("{}\0", recorder); + let tag = format!("{}\0", meta.name()); + unsafe { + android_log_sys::__android_log_write( + priority as android_log_sys::c_int, + tag.as_ptr() as *const android_log_sys::c_char, + message.as_ptr() as *const android_log_sys::c_char, + ); + } + } +} diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 5526110b5c576..7e3bca94cb559 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -1,3 +1,6 @@ +#[cfg(target_os = "android")] +mod android_tracing; + pub mod prelude { pub use bevy_app::{AppBuilder, Plugin}; pub use bevy_utils::tracing::*; @@ -5,7 +8,9 @@ pub mod prelude { pub use bevy_app::{AppBuilder, Plugin}; pub use bevy_utils::tracing::*; #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] -use tracing_subscriber::{fmt, prelude::*, registry::Registry, EnvFilter}; +use tracing_subscriber::{fmt, EnvFilter}; +#[cfg(not(target_arch = "wasm32"))] +use tracing_subscriber::{prelude::*, registry::Registry}; /// Adds logging to Apps. #[derive(Default)] @@ -17,7 +22,7 @@ impl Plugin for LogPlugin { setup_default(_app); #[cfg(target_arch = "wasm32")] setup_wasm(); - #[cfg(target_arch = "android")] + #[cfg(target_os = "android")] setup_android(); } } @@ -53,4 +58,10 @@ fn setup_wasm() { } #[cfg(target_os = "android")] -fn setup_android() {} +fn setup_android() { + let subscriber = Registry::default().with(android_tracing::AndroidLayer { + min_level: bevy_utils::tracing::Level::INFO, + }); + bevy_utils::tracing::subscriber::set_global_default(subscriber) + .expect("Could not set global default tracing subscriber"); +}