284 lines
8.7 KiB
Diff
284 lines
8.7 KiB
Diff
From f70fcf7c4389ee9eb81f62e8b763e095ed50aea9 Mon Sep 17 00:00:00 2001
|
|
From: licunlong <licunlong1@huawei.com>
|
|
Date: Tue, 27 Jun 2023 20:15:04 +0800
|
|
Subject: [PATCH] feature: use our own file logger
|
|
|
|
This implements a simple file logger which supports log rorating. we set
|
|
the log file mode to 600 forcely
|
|
---
|
|
libs/basic/Cargo.toml | 1 +
|
|
libs/basic/src/logger.rs | 203 ++++++++++++++++++++++++++++++++++++++-
|
|
2 files changed, 202 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml
|
|
index 7557d37..6ee7162 100644
|
|
--- a/libs/basic/Cargo.toml
|
|
+++ b/libs/basic/Cargo.toml
|
|
@@ -21,6 +21,7 @@ lazy_static = "1.4.0"
|
|
bitflags = "1.3.2"
|
|
pkg-config = "0.3"
|
|
rand = "0.4.6"
|
|
+time = {version = "=0.3.10", features = ["formatting", "macros"] }
|
|
|
|
[dev-dependencies]
|
|
libtests = { path = "../libtests" }
|
|
diff --git a/libs/basic/src/logger.rs b/libs/basic/src/logger.rs
|
|
index 56def43..3bf579b 100644
|
|
--- a/libs/basic/src/logger.rs
|
|
+++ b/libs/basic/src/logger.rs
|
|
@@ -11,6 +11,14 @@
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
//!
|
|
+use std::{
|
|
+ fs::{File, OpenOptions},
|
|
+ io::Write,
|
|
+ os::unix::prelude::OpenOptionsExt,
|
|
+ path::{Path, PathBuf},
|
|
+ sync::Mutex,
|
|
+};
|
|
+
|
|
use log::{LevelFilter, Log};
|
|
use log4rs::{
|
|
append::{
|
|
@@ -27,6 +35,7 @@ use log4rs::{
|
|
encode::pattern::PatternEncoder,
|
|
};
|
|
use nix::libc;
|
|
+use time::UtcOffset;
|
|
|
|
/// sysmaster log parttern:
|
|
///
|
|
@@ -83,6 +92,174 @@ impl log::Log for SysLogger {
|
|
fn flush(&self) {}
|
|
}
|
|
|
|
+struct FileLogger {
|
|
+ level: log::Level,
|
|
+ file_path: PathBuf,
|
|
+ #[allow(dead_code)]
|
|
+ file_mode: u32,
|
|
+ file_number: u32,
|
|
+ max_size: u32,
|
|
+ file: Mutex<File>,
|
|
+}
|
|
+
|
|
+impl log::Log for FileLogger {
|
|
+ fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
+ metadata.level() <= self.level
|
|
+ }
|
|
+
|
|
+ fn log(&self, record: &log::Record) {
|
|
+ if !self.enabled(record.metadata()) {
|
|
+ return;
|
|
+ }
|
|
+ let current_size: u32;
|
|
+ {
|
|
+ let mut file = self.file.lock().unwrap();
|
|
+ self.write(
|
|
+ &mut file,
|
|
+ record.module_path().unwrap(),
|
|
+ record.args().to_string(),
|
|
+ );
|
|
+ current_size = file.metadata().unwrap().len() as u32;
|
|
+ /* file is automatically unlocked. */
|
|
+ }
|
|
+ if current_size > self.max_size {
|
|
+ self.rotate();
|
|
+ let file = self.file.lock().unwrap();
|
|
+ let _ = file.set_len(0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn flush(&self) {
|
|
+ let mut file = self.file.lock().unwrap();
|
|
+ let _ = file.flush();
|
|
+ }
|
|
+}
|
|
+
|
|
+impl FileLogger {
|
|
+ fn file_open(file_path: &PathBuf, file_mode: u32) -> File {
|
|
+ OpenOptions::new()
|
|
+ .write(true)
|
|
+ .create(true)
|
|
+ .append(true)
|
|
+ .mode(file_mode)
|
|
+ .open(file_path)
|
|
+ .unwrap()
|
|
+ }
|
|
+
|
|
+ fn mv_file_in_dir(src: &str, dst: Option<&str>, dir: &Path) {
|
|
+ let src = dir.join(src);
|
|
+ if dst.is_none() {
|
|
+ let _ = std::fs::remove_file(src);
|
|
+ return;
|
|
+ }
|
|
+ let dst = dir.join(dst.unwrap());
|
|
+ let _ = std::fs::rename(src, dst);
|
|
+ }
|
|
+
|
|
+ fn cp_file_in_dir(src: &str, dst: &str, dir: &Path) {
|
|
+ let src = dir.join(src);
|
|
+ let dst = dir.join(dst);
|
|
+ let _ = std::fs::copy(src, dst);
|
|
+ }
|
|
+
|
|
+ fn new(
|
|
+ level: log::Level,
|
|
+ file_path: PathBuf,
|
|
+ file_mode: u32,
|
|
+ max_size: u32,
|
|
+ file_number: u32,
|
|
+ ) -> Self {
|
|
+ let file = Self::file_open(&file_path, file_mode);
|
|
+ Self {
|
|
+ level,
|
|
+ file_path,
|
|
+ file_mode,
|
|
+ file_number,
|
|
+ max_size: max_size * 1024,
|
|
+ file: Mutex::new(file),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn write(&self, file: &mut File, module: &str, msg: String) {
|
|
+ let now = time::OffsetDateTime::now_utc().to_offset(UtcOffset::UTC);
|
|
+ let format =
|
|
+ time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
|
|
+ let now = now.format(&format).unwrap();
|
|
+
|
|
+ /* 1. Write time */
|
|
+ if let Err(e) = file.write(format!("{now} ").as_bytes()) {
|
|
+ println!("Failed to log time message: {e}");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* 2. Write module */
|
|
+ if let Err(e) = file.write((module.to_string() + " ").as_bytes()) {
|
|
+ println!("Failed to log module message: {e}");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* 3. Write message */
|
|
+ if let Err(e) = file.write((msg + "\n").as_bytes()) {
|
|
+ println!("Failed to log message: {e}");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn rotate(&self) {
|
|
+ let dir = self.file_path.parent().unwrap();
|
|
+ let file_name = self
|
|
+ .file_path
|
|
+ .file_name()
|
|
+ .unwrap()
|
|
+ .to_string_lossy()
|
|
+ .to_string();
|
|
+ let file_name_dot = String::from(&file_name) + ".";
|
|
+ let mut num_list: Vec<usize> = Vec::new();
|
|
+
|
|
+ for de in dir.read_dir().unwrap() {
|
|
+ let de = match de {
|
|
+ Err(_) => continue,
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ if !de.file_type().unwrap().is_file() {
|
|
+ continue;
|
|
+ }
|
|
+ let de_file_name = de.file_name().to_string_lossy().to_string();
|
|
+ let rotated_num = de_file_name.trim_start_matches(&file_name_dot);
|
|
+ let rotated_num = match rotated_num.parse::<usize>() {
|
|
+ Err(_) => {
|
|
+ continue;
|
|
+ }
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ num_list.push(rotated_num);
|
|
+ }
|
|
+
|
|
+ num_list.sort();
|
|
+
|
|
+ /* 1. delete surplus rotated file */
|
|
+ /* We only keep (file_number - 1) rotated files, because we will generate a new one later. */
|
|
+ let file_number = self.file_number as usize;
|
|
+ for i in file_number - 1..num_list.len() {
|
|
+ let src = String::from(&file_name_dot) + &num_list[i].to_string();
|
|
+ Self::mv_file_in_dir(&src, None, dir);
|
|
+ }
|
|
+
|
|
+ let end = std::cmp::min(num_list.len(), file_number);
|
|
+ /* 2. {sysmaster.log.1, sysmaster.log.2, ...} => {sysmaster.log.2, sysmaster.log.3, ...} */
|
|
+ for i in (0..end).rev() {
|
|
+ let src = String::from(&file_name_dot) + &num_list[i].to_string();
|
|
+ let dst = String::from(&file_name_dot) + &(num_list[i] + 1).to_string();
|
|
+ Self::mv_file_in_dir(&src, Some(&dst), dir);
|
|
+ }
|
|
+
|
|
+ /* 3. **copy** sysmaster.log => sysmaster.log.1 */
|
|
+ let src = String::from(&file_name);
|
|
+ let dst = String::from(&file_name_dot) + "1";
|
|
+ Self::cp_file_in_dir(&src, &dst, dir);
|
|
+ }
|
|
+}
|
|
+
|
|
fn append_log(
|
|
app_name: &str,
|
|
level: LevelFilter,
|
|
@@ -96,6 +273,17 @@ fn append_log(
|
|
log::set_max_level(level);
|
|
return;
|
|
}
|
|
+ if target == "file" {
|
|
+ log::set_max_level(level);
|
|
+ let _ = log::set_boxed_logger(Box::new(FileLogger::new(
|
|
+ log::Level::Debug,
|
|
+ PathBuf::from(file_path),
|
|
+ 0o600,
|
|
+ file_size,
|
|
+ file_number,
|
|
+ )));
|
|
+ return;
|
|
+ }
|
|
let config = build_log_config(app_name, level, target, file_path, file_size, file_number);
|
|
let logger = log4rs::Logger::new(config);
|
|
log::set_max_level(level);
|
|
@@ -173,6 +361,17 @@ pub fn init_log(
|
|
log::set_max_level(level);
|
|
return;
|
|
}
|
|
+ if target == "file" {
|
|
+ let _ = log::set_boxed_logger(Box::new(FileLogger::new(
|
|
+ log::Level::Debug,
|
|
+ PathBuf::from(&file_path),
|
|
+ 0o600,
|
|
+ file_size,
|
|
+ file_number,
|
|
+ )));
|
|
+ log::set_max_level(level);
|
|
+ return;
|
|
+ }
|
|
let config = build_log_config(app_name, level, target, file_path, file_size, file_number);
|
|
let r = log4rs::init_config(config);
|
|
if let Err(e) = r {
|
|
@@ -190,7 +389,7 @@ fn build_log_config(
|
|
) -> Config {
|
|
let mut target = target;
|
|
/* If the file is configured to None, use console forcely. */
|
|
- if (file_path.is_empty() || file_size == 0 || file_number == 0) && target == "file" {
|
|
+ if (file_path.is_empty() || file_size == 0 || file_number == 0) && target == "rolling_file" {
|
|
println!(
|
|
"LogTarget is configured to `file`, but configuration is invalid, changing the \
|
|
LogTarget to `console`, file: {file_path}, file_size: {file_size}, file_number: \
|
|
@@ -206,7 +405,7 @@ fn build_log_config(
|
|
.target(Target::Stdout)
|
|
.build(),
|
|
),
|
|
- "file" => {
|
|
+ "rolling_file" => {
|
|
let pattern = file_path.to_string() + ".{}";
|
|
let policy = Box::new(CompoundPolicy::new(
|
|
Box::new(SizeTrigger::new(file_size as u64 * 1024)),
|
|
--
|
|
2.33.0
|
|
|