diff --git a/Cargo.lock b/Cargo.lock index 9cbf740f7..85442f2db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4407,6 +4407,7 @@ dependencies = [ "dsa", "ecdsa", "encoding_rs", + "env_logger", "flate2", "getrandom 0.2.17", "globwalk", @@ -4490,13 +4491,11 @@ dependencies = [ "dunce", "enable-ansi-support", "encoding_rs", - "env_logger", "figment", "globwalk", "home", "indicatif", "itertools 0.14.0", - "log", "predicates", "protobuf", "regex", diff --git a/capi/Cargo.toml b/capi/Cargo.toml index 7cdf6d499..1b9c92f01 100644 --- a/capi/Cargo.toml +++ b/capi/Cargo.toml @@ -50,6 +50,23 @@ rules-profiling = ["yara-x/rules-profiling"] # This feature is disabled by default. magic-module = ["yara-x/magic-module"] +# Enables internal logging and automatically writes log messages to stderr. +# +# When this feature is enabled, log output can be configured via the `YRX_LOG` +# environment variable set to any of the log levels: `error`, `warn`, `info`, +# `debug`, or `trace`. +# +# You can also specify different log levels for specific targets/modules using +# comma-separated `target=level` directives: +# +# YRX_LOG=info +# YRX_LOG=yara_x=debug +# YRX_LOG=warn,yara_x::compiler=trace +# +# This feature is disabled by default. +stderr-logs = ["yara-x/stderr-logs"] + + [lib] name = "yara_x_capi" crate-type = ["staticlib", "cdylib"] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 0bbedc2a0..cf8881798 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -28,8 +28,8 @@ test = false # Enable the "debug" command for developers. debug-cmd = [] -# When this feature is enabled the CLI program prints debug logs if -# the RUST_LOG environment variable is set to any of the debug levels: +# When this feature is enabled the CLI program prints debug logs to stderr if +# the YRX_LOG environment variable is set to any of the log levels: # # error # warn @@ -37,8 +37,13 @@ debug-cmd = [] # debug # trace # -# Example: RUST_LOG=info ./yr scan some_rule.yar some_file -logging = ["dep:log", "dep:env_logger"] +# You can also specify different log levels for specific targets/modules using +# comma-separated `target=level` directives: +# +# YRX_LOG=info ./yr scan some_rule.yar some_file +# YRX_LOG=yara_x=debug ./yr scan some_rule.yar some_file +# YRX_LOG=warn,yara_x::compiler=trace ./yr scan some_rule.yar some_file +stderr-logs = ["yara-x/stderr-logs"] # Enables rules profiling. Notice that profiling has an impact on scan # performance. @@ -58,8 +63,6 @@ figment = { workspace = true, features = ["toml"] } globwalk = { workspace = true } home = { workspace = true } itertools = { workspace = true } -env_logger = { workspace = true, optional = true, features = ["auto-color"] } -log = { workspace = true, optional = true } protobuf = { workspace = true } regex = { workspace = true } serde_json = { workspace = true, features = ["preserve_order"] } diff --git a/cli/src/main.rs b/cli/src/main.rs index 3c7fe115f..ba9d7b0dc 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -35,9 +35,6 @@ fn main() -> anyhow::Result<()> { println!("could not enable ANSI support: {err}") } - #[cfg(feature = "logging")] - env_logger::init(); - // If stdout is not a tty (for example, because it was redirected to a // file) turn off colors. This way you can redirect the output to a file // without ANSI escape codes messing up the file content. diff --git a/cli/src/walk.rs b/cli/src/walk.rs index 674618895..4b7732b52 100644 --- a/cli/src/walk.rs +++ b/cli/src/walk.rs @@ -539,10 +539,10 @@ impl<'a> ParWalker<'a> { } })); - // `multi_progress` will be `None` if the `logging` feature is - // enabled or if either stdout or stderr is not a tty (for example - // when any of them are redirected to a file). - let multi_progress = if cfg!(feature = "logging") { + // `multi_progress` will be `None` if the `stderr-logs` feature + // is enabled or if either stdout or stderr is not a tty (for + // example when any of them are redirected to a file). + let multi_progress = if cfg!(feature = "stderr-logs") { None } else if io::stdout().is_tty() { Some(MultiProgress::new()) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index f7949c7a6..ac694cea6 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -34,6 +34,35 @@ rustdoc-args = ["--cfg", "docsrs"] # actually computes the expression. constant-folding = [] +# Enables internal log statements inside YARA-X via the standard `log` crate +# facade. +# +# When this feature is enabled, YARA-X emits internal log messages (using +# `log::info!`,`log::error!`, etc.). However, it does not initialize any +# concrete logger implementation. The host application remains responsible +# for configuring and initializing its own logger (e.g., `env_logger`, +# `tracing-subscriber`, or a custom logger). +logging = ["dep:log", "dep:quanta"] + +# Enables internal logging and automatically logs messages to stderr. +# +# Unlike `logging` (which only emits log events and relies on the host +# application to set up a logger), `stderr-logs` actually writes log messages +# to stderr. This is particularly useful when YARA-X is embedded in non-Rust +# host binaries, allowing log output to be configured via the `YRX_LOG` +# environment variable. +# +# Log output can be configured by setting `YRX_LOG` to any of the log levels: +# `error`, `warn`, `info`, `debug`, or `trace`. +# +# You can also specify different log levels for specific targets/modules using +# comma-separated `target=level` directives: +# +# YRX_LOG=info # Global level set to info +# YRX_LOG=yara_x=debug # Debug level for the yara_x crate +# YRX_LOG=warn,yara_x::compiler=trace # Global warn, trace for yara_x::compiler +stderr-logs = ["dep:env_logger", "logging"] + # Enables the use of exact atoms for speeding up matches. Exact atoms are those # that don't require further verification, the sole presence of the atom # indicates that the pattern containing the atom matches. For instance, in @@ -74,9 +103,6 @@ generate-module-docs = ["protoc"] # plugin. Follow the instructions in: https://lib.rs/crates/protobuf-codegen3 protoc = [] -# Enables debug logs. -logging = ["dep:log", "dep:quanta"] - # When enabled, the serialization of compiled rules include native code for # the platform in which the rules where compiled. This reduces the load time, # as the native code is already included in the serialized rules and doesn't @@ -310,6 +336,7 @@ digest = { workspace = true, optional = true } dsa = { workspace = true, optional = true } ecdsa = { workspace = true, optional = true } encoding_rs = { workspace = true, optional = true } +env_logger = { workspace = true, optional = true } flate2 = { workspace = true, optional = true } memmap2 = { workspace = true } indexmap = { workspace = true, features = ["serde"] } diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index 18a9c6e38..34ef44a2f 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -500,6 +500,8 @@ pub struct Compiler<'a> { impl<'a> Compiler<'a> { /// Creates a new YARA compiler. pub fn new() -> Self { + crate::init_logger(); + let mut ident_pool = StringPool::new(); let mut symbol_table = StackedSymbolTable::new(); diff --git a/lib/src/compiler/rules.rs b/lib/src/compiler/rules.rs index 548226cb0..1617bc61a 100644 --- a/lib/src/compiler/rules.rs +++ b/lib/src/compiler/rules.rs @@ -323,6 +323,8 @@ impl Rules { }); } + crate::init_logger(); + #[cfg(feature = "logging")] let start = Instant::now(); @@ -597,7 +599,7 @@ impl Rules { let rule = self.get(rule_id); - info!( + warn!( "Very short atom in pattern `{}` in rule `{}:{}` (length: {})", self.ident_pool.get(pattern_ident_id).unwrap(), self.ident_pool.get(rule.namespace_ident_id).unwrap(), diff --git a/lib/src/lib.rs b/lib/src/lib.rs index a463d0ee8..b11723575 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -180,3 +180,33 @@ pub unsafe fn finalize() { wasm::free_engine(); } } + +#[cfg(feature = "stderr-logs")] +/// Initializes the `env_logger` backend for logging output to stdout/stderr. +/// +/// This function is called automatically when creating a [`Compiler`] or +/// [`Scanner`] if the `stderr-logs` feature is enabled. It uses +/// `env_logger::try_init()`, which reads the `YRX_LOG` environment variable +/// and safely ignores initialization if a logger was already registered. +pub(crate) fn init_logger() { + static INIT_LOGGER: std::sync::Once = std::sync::Once::new(); + INIT_LOGGER.call_once(|| { + let mut builder = env_logger::Builder::from_env("YRX_LOG"); + + for noisy_module in [ + "cranelift_codegen", + "cranelift_frontend", + "wasmtime", + "wasmtime_internal_cranelift", + "walrus", + ] { + builder.filter_module(noisy_module, log::LevelFilter::Info); + } + + let _ = builder.try_init(); + }); +} + +#[cfg(not(feature = "stderr-logs"))] +#[inline] +pub(crate) fn init_logger() {} diff --git a/lib/src/scanner/context.rs b/lib/src/scanner/context.rs index 57fac1903..076ed00a8 100644 --- a/lib/src/scanner/context.rs +++ b/lib/src/scanner/context.rs @@ -744,7 +744,7 @@ impl ScanContext<'_, '_> { let rule = self.compiled_rules.get(rule_id); #[cfg(feature = "logging")] - log::info!( + log::debug!( "Rule match: {}:{} {:?}", self.compiled_rules .ident_pool() @@ -2056,6 +2056,8 @@ impl From for RuntimeObjectHandle { pub fn create_wasm_store_and_ctx<'r>( rules: &'r Rules, ) -> Pin>>> { + crate::init_logger(); + let num_rules = rules.num_rules() as u32; let num_patterns = rules.num_patterns() as u32; diff --git a/ls/editors/code/package-lock.json b/ls/editors/code/package-lock.json index ead3b61a4..aa884c11b 100644 --- a/ls/editors/code/package-lock.json +++ b/ls/editors/code/package-lock.json @@ -2444,9 +2444,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "dev": true, "funding": [ {