1mod rotating;
10
11use std::path::PathBuf;
12
13use tracing_appender::non_blocking::WorkerGuard;
14use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
15
16pub use rotating::SizeRotatingWriter;
17
18pub struct LogConfig {
20 pub log_dir: PathBuf,
22 pub file_name: String,
24 pub default_filter: String,
26 pub max_file_size: u64,
28 pub max_files: usize,
30 pub foreground: bool,
32}
33
34impl Default for LogConfig {
35 fn default() -> Self {
36 Self {
37 log_dir: PathBuf::from("."),
38 file_name: "app.log".to_string(),
39 default_filter: "info".to_string(),
40 max_file_size: 10 * 1024 * 1024,
41 max_files: 5,
42 foreground: false,
43 }
44 }
45}
46
47pub struct LogGuard {
50 _file_guard: WorkerGuard,
51}
52
53impl LogGuard {
54 pub fn flush(self) {
57 drop(self);
59 }
60}
61
62pub fn init(config: LogConfig) -> LogGuard {
71 std::fs::create_dir_all(&config.log_dir).expect("failed to create log directory");
74
75 let rotating_writer = SizeRotatingWriter::new(
76 config.log_dir.join(&config.file_name),
77 config.max_file_size,
78 config.max_files,
79 );
80
81 let (non_blocking, file_guard) = tracing_appender::non_blocking(rotating_writer);
82
83 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
84 .unwrap_or_else(|_| config.default_filter.into());
85
86 let file_layer = tracing_subscriber::fmt::layer()
88 .json()
89 .with_target(true)
90 .with_writer(non_blocking);
91
92 let stderr_layer = config.foreground.then(|| {
94 tracing_subscriber::fmt::layer()
95 .with_target(false)
96 .with_writer(std::io::stderr)
97 });
98
99 tracing_subscriber::registry()
100 .with(env_filter)
101 .with(file_layer)
102 .with(stderr_layer)
103 .init();
104
105 LogGuard {
106 _file_guard: file_guard,
107 }
108}
109
110#[cfg(feature = "sentry")]
115pub fn init_with_sentry(config: LogConfig) -> LogGuard {
116 std::fs::create_dir_all(&config.log_dir).expect("failed to create log directory");
117
118 let rotating_writer = SizeRotatingWriter::new(
119 config.log_dir.join(&config.file_name),
120 config.max_file_size,
121 config.max_files,
122 );
123
124 let (non_blocking, file_guard) = tracing_appender::non_blocking(rotating_writer);
125
126 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
127 .unwrap_or_else(|_| config.default_filter.into());
128
129 let file_layer = tracing_subscriber::fmt::layer()
130 .json()
131 .with_target(true)
132 .with_writer(non_blocking);
133
134 let stderr_layer = config.foreground.then(|| {
135 tracing_subscriber::fmt::layer()
136 .with_target(false)
137 .with_writer(std::io::stderr)
138 });
139
140 let sentry_layer = sentry::integrations::tracing::layer();
141
142 tracing_subscriber::registry()
143 .with(env_filter)
144 .with(file_layer)
145 .with(stderr_layer)
146 .with(sentry_layer)
147 .init();
148
149 LogGuard {
150 _file_guard: file_guard,
151 }
152}