ds_common_logger_rs_lib/
lib.rs

1//!
2//! # DS Common Logger
3//!
4//! A high-performance, production-ready logging library for Rust applications that provides
5//! structured logging with comprehensive tracing capabilities.
6//!
7//! ## Overview
8//!
9//! This library provides a simple yet powerful interface for initializing global tracing
10//! subscribers with support for both JSON and compact output formats, environment-driven
11//! filtering, and comprehensive error context capture.
12//!
13//! ## Features
14//!
15//! - **Environment-driven filtering** - Respects `RUST_LOG` environment variable
16//! - **Dual output formats** - JSON and compact formats via `LOG_FORMAT` environment variable
17//! - **Error context capture** - Automatic span trace capture for better debugging
18//! - **Panic logging** - Converts panics to structured log events
19//! - **Thread-safe initialization** - Idempotent initialization safe for concurrent use
20//! - **Zero-cost abstractions** - Minimal runtime overhead when logging is disabled
21//!
22//! ## Quick Start
23//!
24//! ```rust
25//! use ds_common_logger_rs_lib::init_tracing;
26//!
27//! // Initialize the global tracing subscriber
28//! init_tracing();
29//!
30//! // Start logging
31//! tracing::info!("Application started");
32//! tracing::warn!("This is a warning");
33//! tracing::error!("This is an error");
34//! ```
35//!
36//! ## Configuration
37//!
38//! ### Log Level Filtering
39//!
40//! Control log levels using the `RUST_LOG` environment variable:
41//!
42//! ```bash
43//! # Show all logs at info level and above
44//! RUST_LOG=info cargo run
45//!
46//! # Show only error logs
47//! RUST_LOG=error cargo run
48//!
49//! # Show debug logs for specific modules
50//! RUST_LOG=debug,my_crate::module=info cargo run
51//! ```
52//!
53//! ### Output Format
54//!
55//! Switch between compact and JSON output using the `LOG_FORMAT` environment variable:
56//!
57//! ```bash
58//! # Compact format (default)
59//! cargo run
60//!
61//! # JSON format
62//! LOG_FORMAT=json cargo run
63//! ```
64//!
65//! ## Thread Safety
66//!
67//! This library is designed to be thread-safe and can be safely called from multiple threads.
68//! The initialization is idempotent, meaning it's safe to call `init_tracing()` multiple times.
69//!
70//! ## Performance
71//!
72//! The library is designed for minimal performance impact:
73//! - Zero-cost when logging is disabled via environment filters
74//! - Efficient string formatting and serialization
75//! - Minimal memory allocations during normal operation
76//!
77use std::sync::Once;
78use tracing::{error, info};
79use tracing_error::ErrorLayer;
80use tracing_subscriber::{
81    filter::EnvFilter,
82    fmt::{self, format::FmtSpan},
83    prelude::*,
84    registry::Registry,
85    util::SubscriberInitExt,
86};
87
88static INIT: Once = Once::new();
89
90/// # Logger Module
91///
92/// This module provides functionality for initializing and configuring the global tracing subscriber.
93///
94/// The logger is designed to be initialized once per application and provides comprehensive
95/// logging capabilities including structured logging, error context capture, and panic logging.
96/// Initializes a global [`tracing`] subscriber exactly **once**.
97///
98/// This function sets up a comprehensive tracing subscriber with the following features:
99///
100/// - **Environment-driven filtering** - Uses `RUST_LOG` environment variable (defaults to `info`)
101/// - **Flexible output format** - JSON or compact format based on `LOG_FORMAT` environment variable
102/// - **Error context capture** - Adds [`ErrorLayer`] to capture span traces for better debugging
103/// - **Panic logging** - Replaces the default panic hook to log panics as structured events
104/// - **Idempotent initialization** - Safe to call multiple times from any thread
105///
106/// # Environment Variables
107///
108/// - `RUST_LOG`: Controls log level filtering (e.g., `info`, `debug`, `error`)
109/// - `LOG_FORMAT`: Controls output format (`json` for JSON format, anything else for compact)
110///
111/// # Thread Safety
112///
113/// This function is thread-safe and can be called from any thread. Subsequent calls after
114/// the first successful initialization will be no-ops.
115///
116/// # Panics
117///
118/// This function will not panic, but if the tracing subscriber fails to initialize,
119/// subsequent calls to tracing macros will be no-ops.
120///
121/// # Examples
122///
123/// Basic usage:
124///
125/// ```rust
126/// use ds_common_logger_rs_lib::init_tracing;
127///
128/// init_tracing();
129/// tracing::info!("Application started");
130/// ```
131///
132/// With environment variables:
133///
134/// ```bash
135/// RUST_LOG=debug LOG_FORMAT=json cargo run
136/// ```
137///
138/// In tests:
139///
140/// ```rust
141/// #[cfg(test)]
142/// mod tests {
143///     use super::*;
144///
145///     #[test]
146///     fn test_logging() {
147///         init_tracing(); // Safe to call in tests
148///         tracing::info!("Test log message");
149///     }
150/// }
151/// ```
152pub fn init_tracing() {
153    INIT.call_once(|| {
154        // 1. Filtering via env
155        let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
156
157        // 2. Decide on format layer
158        let use_json = std::env::var("LOG_FORMAT")
159            .map(|v| v.eq_ignore_ascii_case("json"))
160            .unwrap_or(false);
161
162        // Base registry
163        let base = Registry::default().with(env_filter).with(ErrorLayer::default());
164
165        // 4. Attach stdout layer + init
166        if use_json {
167            base.with(
168                fmt::layer()
169                    .json()
170                    .with_target(true)
171                    .with_thread_ids(true)
172                    .with_thread_names(true)
173                    .with_current_span(true)
174                    .with_span_list(true)
175                    .with_span_events(FmtSpan::ENTER | FmtSpan::EXIT | FmtSpan::CLOSE),
176            )
177            .init();
178        } else {
179            base.with(
180                fmt::layer()
181                    .compact()
182                    .with_target(true)
183                    .with_thread_ids(true)
184                    .with_thread_names(true)
185                    .with_span_events(FmtSpan::ENTER | FmtSpan::EXIT | FmtSpan::CLOSE),
186            )
187            .init();
188        }
189
190        // 5. Log panics
191        std::panic::set_hook(Box::new(|panic_info| {
192            let location = panic_info
193                .location()
194                .map(|l| format!("{}:{}", l.file(), l.line()))
195                .unwrap_or_else(|| "unknown".to_string());
196
197            error!(
198                %location,
199                payload = %panic_info.to_string(),
200                "Application panicked"
201            );
202        }));
203
204        info!(
205            format = %if use_json { "json" } else { "compact" },
206            "Tracing initialized"
207        );
208    });
209}
210
211// endregion: <-- Logger