openzeppelin_relayer/logging/
mod.rs1use chrono::Utc;
9use log::info;
10use simplelog::{Config, LevelFilter, SimpleLogger, WriteLogger};
11use std::{
12 env,
13 fs::{create_dir_all, metadata, File, OpenOptions},
14 path::Path,
15};
16
17pub fn compute_rolled_file_path(base_file_path: &str, date_str: &str, index: u32) -> String {
19 if base_file_path.ends_with(".log") {
20 let trimmed = base_file_path.strip_suffix(".log").unwrap();
21 format!("{}-{}.{}.log", trimmed, date_str, index)
22 } else {
23 format!("{}-{}.{}.log", base_file_path, date_str, index)
24 }
25}
26
27pub fn time_based_rolling(base_file_path: &str, date_str: &str, index: u32) -> String {
30 compute_rolled_file_path(base_file_path, date_str, index)
31}
32
33pub fn space_based_rolling(
41 file_path: &str,
42 base_file_path: &str,
43 date_str: &str,
44 max_size: u64,
45) -> String {
46 let mut final_path = file_path.to_string();
47 let mut index = 1;
48 while let Ok(metadata) = metadata(&final_path) {
49 if metadata.len() > max_size {
50 final_path = compute_rolled_file_path(base_file_path, date_str, index);
51 index += 1;
52 } else {
53 break;
54 }
55 }
56 final_path
57}
58
59pub fn setup_logging() {
61 let log_mode = env::var("LOG_MODE").unwrap_or_else(|_| "stdout".to_string());
62 let log_level = env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string());
63 let level_filter = match log_level.to_lowercase().as_str() {
65 "trace" => LevelFilter::Trace,
66 "debug" => LevelFilter::Debug,
67 "info" => LevelFilter::Info,
68 "warn" => LevelFilter::Warn,
69 "error" => LevelFilter::Error,
70 _ => LevelFilter::Info,
71 };
72
73 if log_mode.to_lowercase() == "file" {
75 info!("Logging to file: {}", log_level);
76
77 let log_dir = if env::var("IN_DOCKER")
79 .map(|val| val == "true")
80 .unwrap_or(false)
81 {
82 "logs/".to_string()
83 } else {
84 env::var("LOG_DATA_DIR").unwrap_or_else(|_| "./logs".to_string())
85 };
86
87 let log_dir = format!("{}/", log_dir.trim_end_matches('/'));
88 let now = Utc::now();
90 let date_str = now.format("%Y-%m-%d").to_string();
91
92 let base_file_path = format!("{}relayer.log", log_dir);
94
95 if Path::new(&base_file_path).exists() {
97 info!(
98 "Base Log file already exists: {}. Proceeding to compute rolled log file path.",
99 base_file_path
100 );
101 }
102
103 let time_based_path = time_based_rolling(&base_file_path, &date_str, 1);
105
106 if let Some(parent) = Path::new(&time_based_path).parent() {
108 create_dir_all(parent).expect("Failed to create log directory");
109 }
110
111 let max_size: u64 = env::var("LOG_MAX_SIZE")
113 .map(|s| {
114 s.parse::<u64>()
115 .expect("LOG_MAX_SIZE must be a valid u64 if set")
116 })
117 .unwrap_or(1_073_741_824);
118
119 let final_path =
120 space_based_rolling(&time_based_path, &base_file_path, &date_str, max_size);
121
122 let log_file = if Path::new(&final_path).exists() {
125 OpenOptions::new()
126 .append(true)
127 .open(&final_path)
128 .unwrap_or_else(|e| panic!("Unable to open log file {}: {}", final_path, e))
129 } else {
130 File::create(&final_path)
131 .unwrap_or_else(|e| panic!("Unable to create log file {}: {}", final_path, e))
132 };
133 WriteLogger::init(level_filter, Config::default(), log_file)
134 .expect("Failed to initialize file logger");
135 } else {
136 SimpleLogger::init(level_filter, Config::default())
138 .expect("Failed to initialize simple logger");
139 }
140
141 info!("Logging is successfully configured (mode: {})", log_mode);
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use std::fs::File;
148 use std::io::Write;
149 use std::sync::Once;
150 use tempfile::tempdir;
151
152 static INIT_LOGGER: Once = Once::new();
154
155 #[test]
156 fn test_compute_rolled_file_path() {
157 let result = compute_rolled_file_path("app.log", "2023-01-01", 1);
159 assert_eq!(result, "app-2023-01-01.1.log");
160
161 let result = compute_rolled_file_path("app", "2023-01-01", 2);
163 assert_eq!(result, "app-2023-01-01.2.log");
164
165 let result = compute_rolled_file_path("logs/app.log", "2023-01-01", 3);
167 assert_eq!(result, "logs/app-2023-01-01.3.log");
168 }
169
170 #[test]
171 fn test_time_based_rolling() {
172 let result = time_based_rolling("app.log", "2023-01-01", 1);
174 assert_eq!(result, "app-2023-01-01.1.log");
175 }
176
177 #[test]
178 fn test_space_based_rolling() {
179 let temp_dir = tempdir().expect("Failed to create temp directory");
181 let base_path = temp_dir
182 .path()
183 .join("test.log")
184 .to_str()
185 .unwrap()
186 .to_string();
187
188 let result = space_based_rolling(&base_path, &base_path, "2023-01-01", 100);
190 assert_eq!(result, base_path);
191
192 {
194 let mut file = File::create(&base_path).expect("Failed to create test file");
195 file.write_all(&[0; 200])
196 .expect("Failed to write to test file");
197 }
198
199 let expected_path = compute_rolled_file_path(&base_path, "2023-01-01", 1);
201 let result = space_based_rolling(&base_path, &base_path, "2023-01-01", 100);
202 assert_eq!(result, expected_path);
203
204 {
206 let mut file = File::create(&expected_path).expect("Failed to create test file");
207 file.write_all(&[0; 200])
208 .expect("Failed to write to test file");
209 }
210
211 let expected_path2 = compute_rolled_file_path(&base_path, "2023-01-01", 2);
213 let result = space_based_rolling(&base_path, &base_path, "2023-01-01", 100);
214 assert_eq!(result, expected_path2);
215 }
216
217 #[test]
218 fn test_logging_configuration() {
219 {
223 env::set_var("LOG_MODE", "stdout");
225 env::set_var("LOG_LEVEL", "debug");
226
227 INIT_LOGGER.call_once(|| {
229 setup_logging();
230 });
231
232 env::remove_var("LOG_MODE");
234 env::remove_var("LOG_LEVEL");
235 }
236
237 {
239 let temp_dir = tempdir().expect("Failed to create temp directory");
241 let log_path = temp_dir
242 .path()
243 .join("test_logs")
244 .to_str()
245 .unwrap()
246 .to_string();
247
248 env::set_var("LOG_MODE", "file");
250 env::set_var("LOG_LEVEL", "info");
251 env::set_var("LOG_DATA_DIR", &log_path);
252 env::set_var("LOG_MAX_SIZE", "1024"); if let Some(parent) = Path::new(&format!("{}/relayer.log", log_path)).parent() {
256 create_dir_all(parent).expect("Failed to create log directory");
257 }
258
259 assert!(Path::new(&log_path).exists());
261
262 env::remove_var("LOG_MODE");
264 env::remove_var("LOG_LEVEL");
265 env::remove_var("LOG_DATA_DIR");
266 env::remove_var("LOG_MAX_SIZE");
267 }
268 }
269}