1use std::fmt;
18use std::path::Path;
19use std::str::FromStr;
20use std::sync::OnceLock;
21
22use rocketmq_error::RocketMQError;
23use rocketmq_error::RocketMQResult;
24use tracing_appender::non_blocking::WorkerGuard;
25use tracing_appender::rolling;
26use tracing_subscriber::fmt::writer::MakeWriterExt;
27use tracing_subscriber::layer::SubscriberExt;
28use tracing_subscriber::util::SubscriberInitExt;
29use tracing_subscriber::Layer;
30
31static WORKER_GUARD: OnceLock<WorkerGuard> = OnceLock::new();
34
35pub fn init_logger() -> RocketMQResult<()> {
46 let info_level = std::env::var("RUST_LOG").unwrap_or_else(|_| String::from("INFO"));
47 let level = tracing::Level::from_str(&info_level).map_err(|_| {
48 RocketMQError::illegal_argument(format!("Invalid log level: {}", info_level))
49 })?;
50
51 tracing_subscriber::fmt()
52 .with_thread_names(true)
53 .with_level(true)
54 .with_line_number(true)
55 .with_thread_ids(true)
56 .with_max_level(level)
57 .try_init()
58 .map_err(|e| RocketMQError::Internal(format!("Failed to initialize logger: {}", e)))?;
59
60 Ok(())
61}
62
63pub fn init_logger_with_level(level: Level) -> RocketMQResult<()> {
77 let tracing_level = level.to_tracing_level()?;
78
79 tracing_subscriber::fmt()
80 .with_thread_names(true)
81 .with_level(true)
82 .with_line_number(true)
83 .with_thread_ids(true)
84 .with_max_level(tracing_level)
85 .try_init()
86 .map_err(|e| RocketMQError::Internal(format!("Failed to initialize logger: {}", e)))?;
87
88 Ok(())
89}
90
91pub fn init_logger_with_file(
112 level: Level,
113 directory: impl AsRef<Path>,
114 file_name_prefix: impl AsRef<Path>,
115) -> RocketMQResult<()> {
116 let max_level = tracing_subscriber::filter::LevelFilter::from_str(level.as_str())
118 .map_err(|_| RocketMQError::illegal_argument(format!("Invalid log level: {}", level)))?;
119
120 let file_appender = rolling::daily(directory, file_name_prefix);
122 let (file_writer, guard) = tracing_appender::non_blocking(file_appender);
123
124 let console_layer = tracing_subscriber::fmt::layer()
126 .with_writer(std::io::stdout)
127 .with_thread_names(true)
128 .with_thread_ids(true)
129 .with_line_number(true)
130 .with_level(true)
131 .with_ansi(true)
132 .with_filter(max_level);
133
134 let file_layer = tracing_subscriber::fmt::layer()
136 .with_writer(file_writer)
137 .with_thread_names(true)
138 .with_thread_ids(true)
139 .with_line_number(true)
140 .with_level(true)
141 .with_ansi(false)
142 .with_filter(max_level);
143
144 tracing_subscriber::registry()
146 .with(console_layer)
147 .with(file_layer)
148 .try_init()
149 .map_err(|e| RocketMQError::Internal(format!("Failed to initialize logger: {}", e)))?;
150
151 WORKER_GUARD
154 .set(guard)
155 .map_err(|_| RocketMQError::Internal("Logger already initialized".to_string()))?;
156
157 Ok(())
158}
159
160#[derive(Clone, Debug, PartialEq, Eq, Hash)]
165pub struct Level(&'static str);
166
167impl Level {
168 pub const ERROR: Level = Level("ERROR");
170
171 pub const WARN: Level = Level("WARN");
173
174 pub const INFO: Level = Level("INFO");
176
177 pub const DEBUG: Level = Level("DEBUG");
179
180 pub const TRACE: Level = Level("TRACE");
182
183 #[inline]
185 pub fn as_str(&self) -> &'static str {
186 self.0
187 }
188
189 pub fn to_tracing_level(&self) -> RocketMQResult<tracing::Level> {
196 tracing::Level::from_str(self.0)
197 .map_err(|_| RocketMQError::illegal_argument(format!("Invalid log level: {}", self.0)))
198 }
199}
200
201impl From<&'static str> for Level {
202 fn from(level: &'static str) -> Self {
203 match level {
204 "ERROR" | "WARN" | "INFO" | "DEBUG" | "TRACE" => Level(level),
205 _ => panic!("Invalid log level: {level}"),
206 }
207 }
208}
209
210impl FromStr for Level {
211 type Err = String;
212
213 fn from_str(s: &str) -> Result<Self, Self::Err> {
214 match s {
215 "ERROR" => Ok(Level::ERROR),
216 "WARN" => Ok(Level::WARN),
217 "INFO" => Ok(Level::INFO),
218 "DEBUG" => Ok(Level::DEBUG),
219 "TRACE" => Ok(Level::TRACE),
220 _ => Err(format!("Invalid log level: {s}")),
221 }
222 }
223}
224
225impl fmt::Display for Level {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 f.pad(self.0)
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn level_as_str_returns_correct_value() {
237 assert_eq!(Level::ERROR.as_str(), "ERROR");
238 assert_eq!(Level::WARN.as_str(), "WARN");
239 assert_eq!(Level::INFO.as_str(), "INFO");
240 assert_eq!(Level::DEBUG.as_str(), "DEBUG");
241 assert_eq!(Level::TRACE.as_str(), "TRACE");
242 }
243
244 #[test]
245 fn level_from_str_creates_correct_level() {
246 assert_eq!(Level::from("ERROR"), Level::ERROR);
247 assert_eq!(Level::from("WARN"), Level::WARN);
248 assert_eq!(Level::from("INFO"), Level::INFO);
249 assert_eq!(Level::from("DEBUG"), Level::DEBUG);
250 assert_eq!(Level::from("TRACE"), Level::TRACE);
251 }
252
253 #[test]
254 fn level_from_str_trait_works() {
255 assert_eq!("ERROR".parse::<Level>().unwrap(), Level::ERROR);
256 assert_eq!("INFO".parse::<Level>().unwrap(), Level::INFO);
257 assert!("invalid".parse::<Level>().is_err());
258 }
259
260 #[test]
261 fn level_display_formats_correctly() {
262 assert_eq!(format!("{}", Level::ERROR), "ERROR");
263 assert_eq!(format!("{}", Level::WARN), "WARN");
264 assert_eq!(format!("{}", Level::INFO), "INFO");
265 assert_eq!(format!("{}", Level::DEBUG), "DEBUG");
266 assert_eq!(format!("{}", Level::TRACE), "TRACE");
267 }
268
269 #[test]
270 fn level_to_tracing_level_success() {
271 assert!(Level::ERROR.to_tracing_level().is_ok());
272 assert!(Level::WARN.to_tracing_level().is_ok());
273 assert!(Level::INFO.to_tracing_level().is_ok());
274 assert!(Level::DEBUG.to_tracing_level().is_ok());
275 assert!(Level::TRACE.to_tracing_level().is_ok());
276 }
277
278 #[test]
279 fn level_clone_works() {
280 let level = Level::INFO;
281 let cloned = level.clone();
282 assert_eq!(level, cloned);
283 }
284
285 #[test]
286 fn level_hash_works() {
287 use std::collections::HashSet;
288 let mut set = HashSet::new();
289 set.insert(Level::ERROR);
290 set.insert(Level::INFO);
291 assert!(set.contains(&Level::ERROR));
292 assert!(!set.contains(&Level::DEBUG));
293 }
294}