1use tracing_appender::non_blocking::WorkerGuard;
17use tracing_subscriber::fmt;
18use tracing_subscriber::prelude::*;
19use tracing_subscriber::EnvFilter;
20
21#[derive(Debug, Clone, Copy, PartialEq)]
23pub enum LogFormat {
24 Json,
26 Pretty,
28}
29
30impl LogFormat {
31 pub fn from_str(s: &str) -> Self {
32 match s.to_lowercase().as_str() {
33 "pretty" | "text" | "human" => LogFormat::Pretty,
34 _ => LogFormat::Json,
35 }
36 }
37}
38
39pub fn init(log_level: &str, format: &str, log_file_dir: Option<&str>) -> LogGuard {
51 let format = LogFormat::from_str(format);
52
53 let filter = EnvFilter::try_from_env("AXON_LOG")
55 .unwrap_or_else(|_| {
56 EnvFilter::try_new(log_level)
57 .unwrap_or_else(|_| EnvFilter::new("info"))
58 });
59
60 let (stdout_writer, stdout_guard) = tracing_appender::non_blocking(std::io::stdout());
62
63 let file_guard = match format {
65 LogFormat::Json => {
66 let stdout_layer = fmt::layer()
67 .json()
68 .with_writer(stdout_writer)
69 .with_target(true)
70 .with_thread_ids(false)
71 .with_file(true)
72 .with_line_number(true)
73 .with_span_list(true);
74
75 match log_file_dir {
76 Some(dir) => {
77 let file_appender = tracing_appender::rolling::daily(dir, "axon-server.log");
78 let (file_writer, fguard) = tracing_appender::non_blocking(file_appender);
79 let file_layer = fmt::layer()
80 .json()
81 .with_writer(file_writer)
82 .with_target(true)
83 .with_thread_ids(true)
84 .with_thread_names(true)
85 .with_file(true)
86 .with_line_number(true)
87 .with_span_list(true);
88
89 let subscriber = tracing_subscriber::registry()
90 .with(filter)
91 .with(stdout_layer)
92 .with(file_layer);
93 tracing::subscriber::set_global_default(subscriber)
94 .expect("Failed to set global tracing subscriber");
95 Some(fguard)
96 }
97 None => {
98 let subscriber = tracing_subscriber::registry()
99 .with(filter)
100 .with(stdout_layer);
101 tracing::subscriber::set_global_default(subscriber)
102 .expect("Failed to set global tracing subscriber");
103 None
104 }
105 }
106 }
107 LogFormat::Pretty => {
108 let stdout_layer = fmt::layer()
109 .pretty()
110 .with_writer(stdout_writer)
111 .with_target(true)
112 .with_thread_ids(false)
113 .with_file(true)
114 .with_line_number(true);
115
116 match log_file_dir {
117 Some(dir) => {
118 let file_appender = tracing_appender::rolling::daily(dir, "axon-server.log");
119 let (file_writer, fguard) = tracing_appender::non_blocking(file_appender);
120 let file_layer = fmt::layer()
121 .json()
122 .with_writer(file_writer)
123 .with_target(true)
124 .with_thread_ids(true)
125 .with_thread_names(true)
126 .with_file(true)
127 .with_line_number(true)
128 .with_span_list(true);
129
130 let subscriber = tracing_subscriber::registry()
131 .with(filter)
132 .with(stdout_layer)
133 .with(file_layer);
134 tracing::subscriber::set_global_default(subscriber)
135 .expect("Failed to set global tracing subscriber");
136 Some(fguard)
137 }
138 None => {
139 let subscriber = tracing_subscriber::registry()
140 .with(filter)
141 .with(stdout_layer);
142 tracing::subscriber::set_global_default(subscriber)
143 .expect("Failed to set global tracing subscriber");
144 None
145 }
146 }
147 }
148 };
149
150 LogGuard {
151 _stdout_guard: stdout_guard,
152 _file_guard: file_guard,
153 }
154}
155
156pub struct LogGuard {
159 _stdout_guard: WorkerGuard,
160 _file_guard: Option<WorkerGuard>,
161}
162
163#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_log_format_from_str() {
171 assert_eq!(LogFormat::from_str("json"), LogFormat::Json);
172 assert_eq!(LogFormat::from_str("JSON"), LogFormat::Json);
173 assert_eq!(LogFormat::from_str("pretty"), LogFormat::Pretty);
174 assert_eq!(LogFormat::from_str("text"), LogFormat::Pretty);
175 assert_eq!(LogFormat::from_str("human"), LogFormat::Pretty);
176 assert_eq!(LogFormat::from_str("unknown"), LogFormat::Json);
177 }
178
179 #[test]
180 fn test_log_format_default_is_json() {
181 assert_eq!(LogFormat::from_str(""), LogFormat::Json);
182 }
183}