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;
39pub mod merkle_audit_chain;
40pub use merkle_audit_chain::{AuditLogRecord, MerkleAuditChain};
41
42pub struct LoggerConfig {
43    pub service_name: String,
44    pub environment: String,
45    pub otlp_endpoint: Option<String>,
46    pub sentry_dsn: Option<String>,
47    pub log_level: String,
48}
49
50impl Default for LoggerConfig {
51    fn default() -> Self {
52        Self {
53            service_name: "ferrox-app".to_string(),
54            environment: "development".to_string(),
55            otlp_endpoint: None,
56            sentry_dsn: None,
57            log_level: "info".to_string(),
58        }
59    }
60}
61
62pub fn setup_logger(config: LoggerConfig) -> Result<Option<sentry::ClientInitGuard>, AppError> {
63    let env_filter = EnvFilter::try_from_default_env()
64        .unwrap_or_else(|_| EnvFilter::new(&config.log_level));
65    
66    // Default JSON formatting for standard output
67    let formatting_layer = tracing_subscriber::fmt::layer()
68        .json()
69        .with_file(true)
70        .with_line_number(true)
71        .with_target(false);
72
73    let subscriber = Registry::default().with(env_filter).with(formatting_layer);
74
75    // If Sentry DSN is provided, setup Sentry
76    let mut sentry_guard = None;
77    if let Some(dsn) = config.sentry_dsn {
78        sentry_guard = Some(sentry::init((
79            dsn,
80            sentry::ClientOptions {
81                release: sentry::release_name!(),
82                traces_sample_rate: 1.0,
83                environment: Some(config.environment.into()),
84                ..Default::default()
85            },
86        )));
87    }
88
89    let _ = subscriber.try_init();
90
91    tracing::info!("ferrox-logger initialized: Structured JSON logging enabled for service '{}'.", config.service_name);
92    Ok(sentry_guard)
93}