Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Logging with tracing for Android and iOS #1372

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[profile.default]
retries = 3
2 changes: 1 addition & 1 deletion .github/workflows/test-ffi-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ jobs:
- name: Run cargo nextest on FFI bindings
run: |
export CLASSPATH="${{ env.CLASSPATH }}"
cargo nextest run --manifest-path bindings_ffi/Cargo.toml --test-threads 2
cargo nextest --config-file ".cargo/nextest.toml" run --manifest-path bindings_ffi/Cargo.toml --test-threads 2
4 changes: 2 additions & 2 deletions .github/workflows/test-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ jobs:
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: build tests
run: cargo nextest run --no-run --workspace --tests --exclude xmtpv3 --exclude bindings_node --exclude bindings_wasm
run: cargo nextest --config-file ".cargo/nextest.toml" run --no-run --workspace --tests --exclude xmtpv3 --exclude bindings_node --exclude bindings_wasm
- name: cargo test
run: cargo nextest run --workspace --test-threads 2 --exclude xmtpv3 --exclude bindings_node --exclude bindings_wasm
run: cargo nextest --config-file ".cargo/nextest.toml" run --workspace --test-threads 2 --exclude xmtpv3 --exclude bindings_node --exclude bindings_wasm
106 changes: 94 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions bindings_ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ crate-type = ["lib", "cdylib", "staticlib"]

[dependencies]
futures.workspace = true
log = { version = "0.4", features = ["std"] }
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["registry", "env-filter", "fmt", "json"] }
parking_lot.workspace = true
thiserror.workspace = true
thread-id = "5.0.0"
tokio = { workspace = true, features = ["macros"] }
uniffi = { version = "0.28.0", default-features = false, features = ["tokio"] }
xmtp_api_grpc = { path = "../xmtp_api_grpc" }
Expand All @@ -23,6 +23,12 @@ xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] }
xmtp_user_preferences = { path = "../xmtp_user_preferences" }
xmtp_v2 = { path = "../xmtp_v2" }

[target.'cfg(target_os = "android")'.dependencies]
paranoid-android = "0.2"

[target.'cfg(target_os = "ios")'.dependencies]
tracing-oslog = "0.2"

[build-dependencies]
uniffi = { version = "0.28.0", features = ["build"] }

Expand Down
3 changes: 2 additions & 1 deletion bindings_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ pub mod v2;

pub use crate::inbox_owner::SigningError;
use inbox_owner::FfiInboxOwner;
use logger::FfiLogger;
pub use mls::*;
use std::error::Error;

extern crate tracing as log;

pub use ffi::*;
#[allow(clippy::all)]
mod ffi {
Expand Down
111 changes: 82 additions & 29 deletions bindings_ffi/src/logger.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,96 @@
use log::{LevelFilter, Metadata, Record};
use log::Subscriber;
use std::sync::Once;
use tracing_subscriber::{
layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt, Layer,
};

pub trait FfiLogger: Send + Sync {
fn log(&self, level: u32, level_label: String, message: String);
#[cfg(target_os = "android")]
pub use android::*;
#[cfg(target_os = "android")]
mod android {
use super::*;
pub fn native_layer<S>() -> impl Layer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
paranoid_android::layer(env!("CARGO_PKG_NAME")).with_thread_names(true)
}
}

struct RustLogger {
logger: parking_lot::Mutex<Box<dyn FfiLogger>>,
#[cfg(target_os = "ios")]
pub use ios::*;
#[cfg(target_os = "ios")]
mod ios {
use super::*;
// use tracing_subscriber::Layer;
pub fn native_layer<S>() -> impl Layer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
use tracing_oslog::OsLogger;
let subsystem = format!("org.xmtp.{}", env!("CARGO_PKG_NAME"));
OsLogger::new(subsystem, "default")
}
}

impl log::Log for RustLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
#[cfg(not(any(target_os = "ios", target_os = "android")))]
pub use other::*;
#[cfg(not(any(target_os = "ios", target_os = "android")))]
mod other {
use super::*;

fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
// TODO handle errors
self.logger.lock().log(
record.level() as u32,
record.level().to_string(),
format!("[libxmtp][t:{}] {}", thread_id::get(), record.args()),
);
}
}
pub fn native_layer<S>() -> impl Layer<S>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
use tracing_subscriber::{
fmt::{self, format},
EnvFilter, Layer,
};
let structured = std::env::var("STRUCTURED");
let is_structured = matches!(structured, Ok(s) if s == "true" || s == "1");

fn flush(&self) {}
let filter = || {
EnvFilter::builder()
.with_default_directive(tracing::metadata::LevelFilter::INFO.into())
.from_env_lossy()
};

vec![
// structured JSON logger
is_structured
.then(|| {
tracing_subscriber::fmt::layer()
.json()
.flatten_event(true)
.with_level(true)
.with_filter(filter())
})
.boxed(),
// default logger
(!is_structured)
.then(|| {
fmt::layer()
.compact()
.fmt_fields({
format::debug_fn(move |writer, field, value| {
if field.name() == "message" {
write!(writer, "{:?}", value)?;
}
Ok(())
})
})
.with_filter(filter())
})
.boxed(),
]
}
}

static LOGGER_INIT: Once = Once::new();
pub fn init_logger(logger: Box<dyn FfiLogger>) {
// TODO handle errors
pub fn init_logger() {
LOGGER_INIT.call_once(|| {
let logger = RustLogger {
logger: parking_lot::Mutex::new(logger),
};
log::set_boxed_logger(Box::new(logger))
.map(|()| log::set_max_level(LevelFilter::Info))
.expect("Failed to initialize logger");
log::info!("Logger initialized");
let native_layer = native_layer();
tracing_subscriber::registry().with(native_layer).init()
});
}
Loading
Loading