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}