network_protocol/utils/
logging.rs1use std::sync::Once;
2use tracing::Level;
3use tracing_appender::rolling;
4use tracing_subscriber::{
5 fmt::{self, format::FmtSpan},
6 prelude::*,
7 registry, EnvFilter,
8};
9
10static INIT: Once = Once::new();
11
12#[derive(Clone, Debug)]
14pub struct LogConfig {
15 pub app_name: String,
17 pub log_level: Level,
19 pub json_format: bool,
21 pub log_dir: Option<String>,
23 pub log_to_stdout: bool,
25}
26
27impl Default for LogConfig {
28 fn default() -> Self {
29 Self {
30 app_name: "network-protocol".to_string(),
31 log_level: Level::INFO,
32 json_format: false,
33 log_dir: None,
34 log_to_stdout: true,
35 }
36 }
37}
38
39pub fn init_logging(config: &LogConfig) {
55 INIT.call_once(|| {
56 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
57 EnvFilter::new(format!(
58 "{},{app_name}={level}",
59 std::env::var("RUST_LOG").unwrap_or_default(),
60 app_name = config.app_name,
61 level = config.log_level
62 ))
63 });
64
65 let registry = registry().with(filter);
66
67 match (&config.log_dir, config.log_to_stdout) {
68 (Some(log_dir), true) => {
70 let file_appender = rolling::daily(log_dir, format!("{}.log", config.app_name));
71 let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
72
73 if config.json_format {
74 let file_layer = fmt::layer()
75 .json()
76 .with_writer(non_blocking)
77 .with_span_events(FmtSpan::CLOSE);
78
79 let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
80
81 registry.with(file_layer).with(stdout_layer).init();
82 } else {
83 let file_layer = fmt::layer()
84 .with_writer(non_blocking)
85 .with_span_events(FmtSpan::CLOSE);
86
87 let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
88
89 registry.with(file_layer).with(stdout_layer).init();
90 }
91 }
92
93 (Some(log_dir), false) => {
95 let file_appender = rolling::daily(log_dir, format!("{}.log", config.app_name));
96 let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
97
98 if config.json_format {
99 let file_layer = fmt::layer()
100 .json()
101 .with_writer(non_blocking)
102 .with_span_events(FmtSpan::CLOSE);
103
104 registry.with(file_layer).init();
105 } else {
106 let file_layer = fmt::layer()
107 .with_writer(non_blocking)
108 .with_span_events(FmtSpan::CLOSE);
109
110 registry.with(file_layer).init();
111 }
112 }
113
114 (None, true) => {
116 if config.json_format {
117 let stdout_layer = fmt::layer()
118 .json()
119 .with_writer(std::io::stdout)
120 .with_span_events(FmtSpan::CLOSE);
121
122 registry.with(stdout_layer).init();
123 } else {
124 let stdout_layer = fmt::layer()
125 .with_writer(std::io::stdout)
126 .with_ansi(true)
127 .with_span_events(FmtSpan::CLOSE);
128
129 registry.with(stdout_layer).init();
130 }
131 }
132
133 (None, false) => {
135 let stdout_layer = fmt::layer().with_writer(std::io::stdout).with_ansi(true);
136
137 registry.with(stdout_layer).init();
138
139 tracing::warn!("No log output configured, defaulting to stdout");
140 }
141 }
142
143 tracing::info!("Logging initialized at {} level", config.log_level);
144 });
145}
146
147pub fn setup_default_logging() {
149 init_logging(&LogConfig::default());
150}