sfo_log/
lib.rs

1use std::path::{Path, PathBuf};
2use std::thread;
3use flexi_logger::{Cleanup, Criterion, DeferredNow, Duplicate, FileSpec, FlexiLoggerError, Naming, Record};
4
5fn custom_format(writer: &mut dyn std::io::Write, now: &mut DeferredNow, record: &Record) -> std::io::Result<()> {
6    let file = match record.file() {
7        None => {
8            "<unknown>".to_string()
9        }
10        Some(path) => {
11            Path::new(path).file_name().map(|v| v.to_string_lossy().to_string()).unwrap_or("<unknown>".to_string())
12        }
13    };
14    write!(
15        writer,
16        "{} [{}] [{}:{}] [{}] - {}",
17        now.format("%Y-%m-%d %H:%M:%S"),
18        record.level(),
19        file,
20        record.line().unwrap_or(0),
21        thread::current().name().unwrap_or(format!("{:?}", thread::current().id()).as_str()),
22        &record.args()
23    )
24}
25pub struct Logger {
26    app_name: String,
27    log_level: String,
28    log_to_file: bool,
29    log_path: PathBuf,
30    log_file_size: u64,
31    log_file_count: usize,
32    instance_id: String,
33    output_console: bool,
34}
35
36impl Logger {
37    pub fn new(app_name: &str) -> Self {
38        Self {
39            app_name: app_name.to_string(),
40            log_level: "info".to_string(),
41            log_to_file: false,
42            log_path: std::env::current_dir().unwrap().join("logs"),
43            log_file_size: 10 * 1024 * 1024,
44            log_file_count: 10,
45            instance_id: "".to_string(),
46            output_console: true,
47        }
48    }
49
50    pub fn set_instance_id(mut self, instance_id: &str) -> Self {
51        self.instance_id = instance_id.to_string();
52        self
53    }
54
55    pub fn set_process_id_to_instance_id(mut self) -> Self {
56        self.instance_id = format!("{}", std::process::id());
57        self
58    }
59
60    pub fn set_log_level(mut self, level: &str) -> Self {
61        self.log_level = level.to_string();
62        self
63    }
64
65    pub fn set_log_to_file(mut self, to_file: bool) -> Self {
66        self.log_to_file = to_file;
67        self
68    }
69
70    pub fn set_log_path(mut self, path: &str) -> Self {
71        self.log_path = PathBuf::from(path);
72        self
73    }
74
75    pub fn set_log_file_size(mut self, size: u64) -> Self {
76        self.log_file_size = size;
77        self
78    }
79
80    pub fn set_log_file_count(mut self, count: usize) -> Self {
81        self.log_file_count = count;
82        self
83    }
84
85    pub fn set_output_to_console(mut self, output_console: bool) -> Self {
86        self.output_console = output_console;
87        self
88    }
89
90    pub fn start(&self) -> Result<(), FlexiLoggerError> {
91        let mut logger = flexi_logger::Logger::try_with_env_or_str(self.log_level.as_str())?;
92        if self.log_to_file {
93            let mut base_name = self.app_name.clone();
94            if !self.instance_id.is_empty() {
95                base_name = format!("{}_{}", self.app_name, self.instance_id);
96            }
97            logger = logger.log_to_file(FileSpec::default().directory(self.log_path.as_path()).basename(base_name.as_str()))
98                .rotate(Criterion::Size(self.log_file_size), // 文件大小达到 10MB 时轮转
99                        Naming::Numbers, // 使用数字命名轮转文件
100                        Cleanup::KeepLogFiles(self.log_file_count), // 保留最近 7 个日志文件
101                );
102        }
103        if !self.output_console {
104            logger = logger.duplicate_to_stderr(Duplicate::None);
105        } else {
106            logger = logger.duplicate_to_stderr(Duplicate::All);
107        }
108        logger.format(custom_format)
109            .start()?;
110        Ok(())
111    }
112}