saf_core/logging/subscriber/mod.rs
1//! Subscriber initialization for SAF structured debug logging.
2//!
3//! Provides `init()` which reads `SAF_LOG` and `SAF_LOG_FILE` environment
4//! variables and returns a `SafLogLayer` for composition into a
5//! `tracing_subscriber::Registry`.
6
7pub mod filter;
8pub mod layer;
9
10use std::fs::File;
11
12use self::filter::SafLogFilter;
13use self::layer::SafLogLayer;
14
15/// Initialize the SAF log layer from environment variables.
16///
17/// Reads:
18/// - `SAF_LOG`: filter specification (if absent, returns a no-op layer)
19/// - `SAF_LOG_FILE`: output file path (if absent, writes to stderr)
20///
21/// Returns a `SafLogLayer` to compose into a tracing subscriber:
22///
23/// ```ignore
24/// use tracing_subscriber::prelude::*;
25/// let saf_layer = saf_core::logging::subscriber::init();
26/// tracing_subscriber::registry()
27/// .with(tracing_subscriber::fmt::layer())
28/// .with(saf_layer)
29/// .init();
30/// ```
31///
32/// # Panics
33///
34/// Panics if `SAF_LOG_FILE` is set but the file cannot be created.
35pub fn init() -> SafLogLayer {
36 let filter = match std::env::var("SAF_LOG") {
37 Ok(spec) => SafLogFilter::parse(&spec),
38 Err(_) => SafLogFilter::none(),
39 };
40
41 let writer: Box<dyn std::io::Write + Send> = match std::env::var("SAF_LOG_FILE") {
42 Ok(path) => Box::new(
43 File::create(&path)
44 .unwrap_or_else(|e| panic!("SAF_LOG_FILE: failed to create {path}: {e}")),
45 ),
46 Err(_) => Box::new(std::io::stderr()),
47 };
48
49 SafLogLayer::new(filter, writer)
50}