diff --git a/Cargo.lock b/Cargo.lock index 3981845e464..e3466dbf0d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6402,6 +6402,8 @@ dependencies = [ "paste", "thiserror", "tokio 1.37.0", + "tracing", + "tracing-subscriber", "typetag", "virtual-fs", "wasmer", diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index 2197cce14f5..1cc1a2a3489 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -43,6 +43,11 @@ thiserror = "1" typetag = { version = "0.1", optional = true } paste = "1.0" tokio = { version = "1", features = [ "rt", "rt-multi-thread", "io-util", "sync", "macros"], default_features = false } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = [ + "env-filter", + "fmt", +] } [dev-dependencies] field-offset = "0.3.3" diff --git a/lib/c-api/src/lib.rs b/lib/c-api/src/lib.rs index 5b59ab71a35..82a3fa12c2f 100644 --- a/lib/c-api/src/lib.rs +++ b/lib/c-api/src/lib.rs @@ -29,4 +29,5 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod error; +pub mod tracing; pub mod wasm_c_api; diff --git a/lib/c-api/src/tracing.rs b/lib/c-api/src/tracing.rs new file mode 100644 index 00000000000..e63e356835c --- /dev/null +++ b/lib/c-api/src/tracing.rs @@ -0,0 +1,87 @@ +//! Utilities to set up tracing and logging. +//! +//! By default, wasmer does not generate any tracing output. To +//! enable tracing, one must call the `wasmer_setup_tracing()` +//! function during the initialization stage of their program. +//! +//! Tracing levels can be enabled/disabled using the [RUST_LOG +//! env var](https://docs.rs/env_logger/latest/env_logger/#enabling-logging). +//! +//! # Example +//! +//! ```rust +//! # use wasmer_inline_c::assert_c; +//! # fn main() { +//! # (assert_c! { +//! # #include "tests/wasmer.h" +//! # +//! int main() { +//! // This can go up to 4, which is the most verbose +//! int verbosity_level = 0; +//! // Whether to use colors when logging information +//! int use_colors = 1; +//! wasmer_setup_tracing(verbosity_level, use_colors); +//! +//! return 0; +//! } +//! # }) +//! # .success(); +//! # } + +use std::ffi::c_int; + +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; + +const WHITELISTED_LOG_TARGETS: &[&str] = &["wasmer", "wasmer_wasix", "virtual_fs"]; + +#[no_mangle] +pub extern "C" fn wasmer_setup_tracing(verbosity_level: c_int, use_color: c_int) { + let fmt_layer = fmt::layer() + .with_target(true) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_ansi(use_color > 0) + .with_thread_ids(true) + .with_writer(std::io::stderr); + + let filter_layer = { + let default_filters = [ + LevelFilter::OFF, + LevelFilter::WARN, + LevelFilter::INFO, + LevelFilter::DEBUG, + ]; + + // First, we set up the default log level. + let default_level = default_filters + .get(verbosity_level as usize) + .copied() + .unwrap_or(LevelFilter::TRACE); + let mut filter = EnvFilter::builder() + .with_default_directive(default_level.into()) + .from_env_lossy(); + + // Next we add level-specific directives, where verbosity=0 means don't + // override anything. Note that these are shifted one level up so we'll + // get something like RUST_LOG="warn,wasmer_wasix=info" + let specific_filters = [LevelFilter::WARN, LevelFilter::INFO, LevelFilter::DEBUG]; + if verbosity_level > 0 { + let level = specific_filters + .get(verbosity_level as usize) + .copied() + .unwrap_or(LevelFilter::TRACE); + + for target in WHITELISTED_LOG_TARGETS { + let directive = format!("{target}={level}").parse().unwrap(); + filter = filter.add_directive(directive); + } + } + + filter + }; + + tracing_subscriber::registry() + .with(filter_layer) + .with(fmt_layer.compact().with_target(true)) + .init(); +}