dango/system/logging/
appender.rs1use std::path::{Path, PathBuf};
8
9use uninode::UniNode;
10
11use tracing::Metadata;
12use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
13use tracing_appender::rolling::{RollingFileAppender, Rotation};
14use tracing_subscriber::fmt::writer::{BoxMakeWriter, MakeWriter};
15
16use super::LoggerError;
17
18struct NonBlokingWriter {
19 writer: NonBlocking,
20 _guard: WorkerGuard,
21}
22
23impl<'a> MakeWriter<'a> for NonBlokingWriter {
24 type Writer = NonBlocking;
25
26 fn make_writer(&self) -> Self::Writer {
27 self.writer.make_writer()
28 }
29
30 fn make_writer_for(&'a self, meta: &Metadata) -> Self::Writer {
31 self.writer.make_writer_for(meta)
32 }
33}
34
35fn create_console_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
36 if cfg.find_bool("sync").unwrap_or(false) {
37 Ok(BoxMakeWriter::new(std::io::stdout))
38 } else {
39 let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
40 let writer = NonBlokingWriter {
41 writer: non_blocking,
42 _guard: guard,
43 };
44 Ok(BoxMakeWriter::new(writer))
45 }
46}
47
48struct BlockingFileWriter {
49 rotation: Rotation,
50 directory: PathBuf,
51 file_name_prefix: PathBuf,
52}
53
54impl<'a> MakeWriter<'a> for BlockingFileWriter {
55 type Writer = RollingFileAppender;
56
57 fn make_writer(&self) -> Self::Writer {
58 RollingFileAppender::new(
59 self.rotation.clone(),
60 self.directory.clone(),
61 self.file_name_prefix.clone(),
62 )
63 }
64}
65
66fn create_file_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
67 let file_path = cfg
68 .find_str("path")
69 .map(Path::new)
70 .ok_or(LoggerError::InvalidOuputPath)?;
71
72 let dir = file_path
73 .parent()
74 .and_then(|dir| dir.canonicalize().ok())
75 .ok_or(LoggerError::InvalidOuputPath)?;
76
77 let file = file_path
78 .file_name()
79 .map(Path::new)
80 .ok_or(LoggerError::InvalidOuputPath)?;
81
82 let rotation = cfg
83 .find_str("rotation")
84 .map(|v| v.to_lowercase())
85 .unwrap_or_else(|| String::from("never"));
86
87 let rotation = match rotation.as_str() {
88 "minutely" => Rotation::MINUTELY,
89 "hourly" => Rotation::HOURLY,
90 "daily" => Rotation::DAILY,
91 _ => Rotation::NEVER,
92 };
93
94 if cfg.find_bool("sync").unwrap_or(false) {
95 let writer = BlockingFileWriter {
96 rotation,
97 directory: dir,
98 file_name_prefix: file.to_path_buf(),
99 };
100 Ok(BoxMakeWriter::new(writer))
101 } else {
102 let file_appender = RollingFileAppender::new(rotation, dir, file);
103 let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
104 Ok(BoxMakeWriter::new(NonBlokingWriter {
105 writer: non_blocking,
106 _guard: guard,
107 }))
108 }
109}
110
111pub fn create_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
112 let appender = cfg
113 .find_str("appender")
114 .map(|v| v.to_lowercase())
115 .ok_or(LoggerError::NotDefineAppender)?;
116
117 match appender.as_str() {
118 "file" => create_file_appender(cfg),
119 "console" => create_console_appender(cfg),
120 _ => Err(LoggerError::UnknownAppender(appender)),
121 }
122}