guts_node/observability/
logging.rs

1//! Structured logging initialization.
2//!
3//! Provides production-ready logging with:
4//! - JSON or pretty format
5//! - Request ID tracking
6//! - Configurable log levels
7//! - Automatic context propagation
8
9use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
10
11/// Log output format.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum LogFormat {
14    /// Human-readable pretty format (for development).
15    Pretty,
16    /// JSON format (for production log aggregation).
17    Json,
18}
19
20impl LogFormat {
21    /// Parse log format from string.
22    pub fn parse(s: &str) -> Self {
23        match s.to_lowercase().as_str() {
24            "json" => LogFormat::Json,
25            _ => LogFormat::Pretty,
26        }
27    }
28}
29
30/// Initialize the logging system.
31///
32/// # Arguments
33///
34/// * `level` - Log level (trace, debug, info, warn, error)
35/// * `json_format` - If true, output logs in JSON format
36///
37/// # Example
38///
39/// ```rust,no_run
40/// use guts_node::observability::init_logging;
41///
42/// init_logging("info", true);
43/// ```
44pub fn init_logging(level: &str, json_format: bool) {
45    let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
46        format!(
47            "guts={level},tower_http=debug,axum::rejection=trace",
48            level = level
49        )
50        .into()
51    });
52
53    let registry = tracing_subscriber::registry().with(env_filter);
54
55    if json_format {
56        registry
57            .with(
58                fmt::layer()
59                    .json()
60                    .with_current_span(true)
61                    .with_span_list(false)
62                    .with_file(true)
63                    .with_line_number(true)
64                    .with_target(true)
65                    .with_thread_ids(false)
66                    .with_thread_names(false),
67            )
68            .init();
69    } else {
70        registry.with(fmt::layer().pretty()).init();
71    }
72
73    tracing::info!(
74        level = %level,
75        format = if json_format { "json" } else { "pretty" },
76        "Logging initialized"
77    );
78}