Skip to main content

ferrox_logger/
lib.rs

1//! # Ferrox Logger (`ferrox-logger`)
2//!
3//! `ferrox-logger` sets up production-ready, structured JSON logging and Sentry telemetry for Ferrox applications.
4//! Built on top of `tracing-subscriber` and `sentry`, it provides unified log formatting, environmental level filtering,
5//! and automated crash reports.
6//!
7//! ## Architectural Context
8//! Containerized environments (Kubernetes, AWS ECS, Docker) require logs to be emitted in structured JSON format via stdout
9//! for centralized aggregation (Elasticsearch, Datadog, Loki). `ferrox-logger` ensures all log events retain trace IDs,
10//! timestamps, and module metadata.
11//!
12//! ## Key Features
13//! - 📝 **Structured JSON Output**: Standardized log format with timestamp, severity level, target module, and dynamic fields.
14//! - 🚨 **Sentry Integration**: Automatic reporting of critical error events to Sentry APM.
15//! - 🎛️ **Environment-Driven Filtering**: Configurable log level thresholds via `RUST_LOG` environment variables.
16//!
17//! ## Example Usage
18//! ```rust,no_run
19//! use ferrox_logger::{setup_logger, LoggerConfig};
20//!
21//! fn main() -> Result<(), Box<dyn std::error::Error>> {
22//!     let _sentry_guard = setup_logger(LoggerConfig::default())?;
23//!     tracing::info!("Application booted successfully");
24//!     Ok(())
25//! }
26//! ```
27
28use opentelemetry::KeyValue;
29use opentelemetry_otlp::WithExportConfig;
30use opentelemetry_sdk::trace::{self, Sampler};
31use opentelemetry_sdk::Resource;
32use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
33use ferrox_errors::AppError;
34
35pub mod sanitizer;
36pub mod dp;
37pub mod weekly_report;
38pub mod merkle;
39
40pub struct LoggerConfig {
41    pub service_name: String,
42    pub environment: String,
43    pub otlp_endpoint: Option<String>,
44    pub sentry_dsn: Option<String>,
45    pub log_level: String,
46}
47
48impl Default for LoggerConfig {
49    fn default() -> Self {
50        Self {
51            service_name: "ferrox-app".to_string(),
52            environment: "development".to_string(),
53            otlp_endpoint: None,
54            sentry_dsn: None,
55            log_level: "info".to_string(),
56        }
57    }
58}
59
60pub fn setup_logger(config: LoggerConfig) -> Result<Option<sentry::ClientInitGuard>, AppError> {
61    let env_filter = EnvFilter::try_from_default_env()
62        .unwrap_or_else(|_| EnvFilter::new(&config.log_level));
63    
64    // Default JSON formatting for standard output
65    let formatting_layer = tracing_subscriber::fmt::layer()
66        .json()
67        .with_file(true)
68        .with_line_number(true)
69        .with_target(false);
70
71    let subscriber = Registry::default().with(env_filter).with(formatting_layer);
72
73    // If Sentry DSN is provided, setup Sentry
74    let mut sentry_guard = None;
75    if let Some(dsn) = config.sentry_dsn {
76        sentry_guard = Some(sentry::init((
77            dsn,
78            sentry::ClientOptions {
79                release: sentry::release_name!(),
80                traces_sample_rate: 1.0,
81                environment: Some(config.environment.into()),
82                ..Default::default()
83            },
84        )));
85    }
86
87    let _ = subscriber.try_init();
88
89    tracing::info!("ferrox-logger initialized: Structured JSON logging enabled for service '{}'.", config.service_name);
90    Ok(sentry_guard)
91}