Skip to main content

elph_core/logger/
mod.rs

1mod options;
2mod rotation;
3
4pub use options::{LogRotation, LoggingOptions};
5use tracing_appender::non_blocking::{NonBlockingBuilder, WorkerGuard};
6use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
7
8pub use rotation::build_writer;
9
10/// Bounded queue for the async file writer. Caps memory under sustained log bursts
11/// (default upstream limit is 128_000 lines).
12const FILE_WRITER_BUFFER_LINES: usize = 16_384;
13
14/// Initializes the global tracing subscriber.
15///
16/// Returns a [`WorkerGuard`] that must be kept alive for the process lifetime so
17/// the non-blocking file writer can flush buffered records.
18pub fn init(options: LoggingOptions) -> Option<WorkerGuard> {
19    if cfg!(test) {
20        return None;
21    }
22
23    install_subscriber(&options)
24}
25
26fn non_blocking_writer(options: &LoggingOptions) -> (tracing_appender::non_blocking::NonBlocking, WorkerGuard) {
27    let writer = build_writer(&options.logs_dir, options.app_name, options.rotation, options.max_files)
28        .expect("failed to initialize rolling log writer");
29    NonBlockingBuilder::default()
30        .buffered_lines_limit(FILE_WRITER_BUFFER_LINES)
31        .finish(writer)
32}
33
34fn install_subscriber(options: &LoggingOptions) -> Option<WorkerGuard> {
35    let env_filter = EnvFilter::new(options.level.clone());
36
37    match (options.file_enabled, options.console_enabled) {
38        (true, true) => {
39            let (non_blocking, guard) = non_blocking_writer(options);
40
41            tracing_subscriber::registry()
42                .with(env_filter)
43                .with(
44                    fmt::layer()
45                        .json()
46                        .with_writer(non_blocking)
47                        .with_ansi(false)
48                        .with_target(true),
49                )
50                .with(
51                    fmt::layer()
52                        .with_writer(std::io::stdout)
53                        .with_target(true)
54                        .with_ansi(true),
55                )
56                .init();
57
58            Some(guard)
59        }
60        (true, false) => {
61            let (non_blocking, guard) = non_blocking_writer(options);
62
63            tracing_subscriber::registry()
64                .with(env_filter)
65                .with(
66                    fmt::layer()
67                        .json()
68                        .with_writer(non_blocking)
69                        .with_ansi(false)
70                        .with_target(true),
71                )
72                .init();
73
74            Some(guard)
75        }
76        (false, true) => {
77            tracing_subscriber::registry()
78                .with(env_filter)
79                .with(
80                    fmt::layer()
81                        .with_writer(std::io::stdout)
82                        .with_target(true)
83                        .with_ansi(true),
84                )
85                .init();
86
87            None
88        }
89        (false, false) => {
90            tracing_subscriber::registry().with(env_filter).init();
91            None
92        }
93    }
94}