512 lines
16 KiB
Diff
512 lines
16 KiB
Diff
From 0ce2ca0cfe650b88bccdd8e9b62a9b233e794f34 Mon Sep 17 00:00:00 2001
|
|
From: licunlong <licunlong1@huawei.com>
|
|
Date: Wed, 28 Jun 2023 16:35:55 +0800
|
|
Subject: [PATCH] fix: several fixes on logger
|
|
|
|
1. Also use our own logger to log message to console, now log4rs can be removed
|
|
2. remove 'rolling_file', as it can only save log file in 644, we can't set the mode
|
|
3. change 'console' to 'console-syslog', when users want to print message to console
|
|
we will also record the message in syslog
|
|
4. check the returned value more
|
|
---
|
|
core/bin/manager/config.rs | 4 +-
|
|
libs/basic/src/logger.rs | 311 +++++++++++++++++++++++++------------
|
|
3 files changed, 215 insertions(+), 109 deletions(-)
|
|
|
|
diff --git a/core/bin/manager/config.rs b/core/bin/manager/config.rs
|
|
index fc4c028..691b692 100644
|
|
--- a/core/bin/manager/config.rs
|
|
+++ b/core/bin/manager/config.rs
|
|
@@ -26,7 +26,7 @@ pub struct ManagerConfig {
|
|
|
|
#[config(default = "info")]
|
|
pub LogLevel: log::LevelFilter,
|
|
- #[config(default = "console")]
|
|
+ #[config(default = "syslog")]
|
|
pub LogTarget: String,
|
|
#[config(default = "")]
|
|
pub LogFile: String,
|
|
@@ -57,7 +57,7 @@ impl Default for ManagerConfig {
|
|
DefaultRestartSec: 100,
|
|
DefaultTimeoutSec: 90,
|
|
LogLevel: log::LevelFilter::Debug,
|
|
- LogTarget: "console".to_string(),
|
|
+ LogTarget: "syslog".to_string(),
|
|
LogFile: String::new(),
|
|
LogFileSize: 10240,
|
|
LogFileNumber: 10,
|
|
diff --git a/libs/basic/src/logger.rs b/libs/basic/src/logger.rs
|
|
index 06f4ff2..8ee3ffa 100644
|
|
--- a/libs/basic/src/logger.rs
|
|
+++ b/libs/basic/src/logger.rs
|
|
@@ -12,9 +12,9 @@
|
|
|
|
//!
|
|
use std::{
|
|
- fs::{File, OpenOptions},
|
|
+ fs::{self, File, OpenOptions},
|
|
io::Write,
|
|
- os::unix::prelude::OpenOptionsExt,
|
|
+ os::unix::prelude::{OpenOptionsExt, PermissionsExt},
|
|
path::{Path, PathBuf},
|
|
sync::Mutex,
|
|
};
|
|
@@ -37,6 +37,8 @@ use log4rs::{
|
|
use nix::libc;
|
|
use time::UtcOffset;
|
|
|
|
+use crate::Error;
|
|
+
|
|
/// sysmaster log parttern:
|
|
///
|
|
/// ```rust,ignore
|
|
@@ -65,6 +67,36 @@ impl log::Log for LogPlugin {
|
|
}
|
|
}
|
|
|
|
+fn write_msg_common(writer: &mut impl Write, 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 = match now.format(&format) {
|
|
+ Err(_) => "[unknown time]".to_string(),
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+
|
|
+ /* 1. Write time */
|
|
+ if let Err(e) = writer.write(format!("{now} ").as_bytes()) {
|
|
+ println!("Failed to log time message: {e}");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* 2. Write module */
|
|
+ if let Err(e) = writer.write((module.to_string() + " ").as_bytes()) {
|
|
+ println!("Failed to log module message: {e}");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* 3. Write message */
|
|
+ if let Err(e) = writer.write((msg + "\n").as_bytes()) {
|
|
+ println!("Failed to log message: {e}");
|
|
+ }
|
|
+}
|
|
+
|
|
+fn write_msg_file(writer: &mut File, module: &str, msg: String) {
|
|
+ write_msg_common(writer, module, msg);
|
|
+}
|
|
+
|
|
struct SysLogger;
|
|
|
|
/* This is an extremely simple implementation, and only
|
|
@@ -92,6 +124,25 @@ impl log::Log for SysLogger {
|
|
fn flush(&self) {}
|
|
}
|
|
|
|
+struct ConsoleLogger;
|
|
+
|
|
+impl log::Log for ConsoleLogger {
|
|
+ fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
|
+ true
|
|
+ }
|
|
+
|
|
+ fn log(&self, record: &log::Record) {
|
|
+ let mut stdout = std::io::stdout();
|
|
+ let module_path = match record.module_path() {
|
|
+ None => "unknown",
|
|
+ Some(v) => v,
|
|
+ };
|
|
+ write_msg_common(&mut stdout, module_path, record.args().to_string());
|
|
+ }
|
|
+
|
|
+ fn flush(&self) {}
|
|
+}
|
|
+
|
|
struct FileLogger {
|
|
level: log::Level,
|
|
file_path: PathBuf,
|
|
@@ -113,30 +164,55 @@ impl log::Log for FileLogger {
|
|
}
|
|
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;
|
|
+ let mut file = match self.file.lock() {
|
|
+ Err(_) => return,
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+
|
|
+ let module_path = match record.module_path() {
|
|
+ None => "unknown",
|
|
+ Some(v) => v,
|
|
+ };
|
|
+ write_msg_file(&mut file, module_path, record.args().to_string());
|
|
+ current_size = match file.metadata() {
|
|
+ Err(_) => return,
|
|
+ Ok(v) => v.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);
|
|
+ let file = match self.file.lock() {
|
|
+ Err(_) => return,
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ if let Err(e) = self.rotate() {
|
|
+ println!("Failed to rotate log file: {e}");
|
|
+ }
|
|
+ if let Err(e) = file.set_len(0) {
|
|
+ println!("Failed to clear log file: {e}");
|
|
+ }
|
|
}
|
|
}
|
|
|
|
fn flush(&self) {
|
|
- let mut file = self.file.lock().unwrap();
|
|
- let _ = file.flush();
|
|
+ let mut file = match self.file.lock() {
|
|
+ Err(_) => return,
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ if let Err(e) = file.flush() {
|
|
+ println!("Failed to flush log file: {e}");
|
|
+ }
|
|
}
|
|
}
|
|
|
|
impl FileLogger {
|
|
fn file_open(file_path: &PathBuf, file_mode: u32) -> File {
|
|
+ /* Panic if we cann't open a log file. */
|
|
+ let dir = file_path.parent().unwrap();
|
|
+ if !dir.exists() {
|
|
+ fs::create_dir_all(dir).unwrap();
|
|
+ }
|
|
+ fs::set_permissions(dir, fs::Permissions::from_mode(0o700)).unwrap();
|
|
OpenOptions::new()
|
|
.write(true)
|
|
.create(true)
|
|
@@ -146,22 +222,6 @@ impl FileLogger {
|
|
.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,
|
|
@@ -180,49 +240,70 @@ impl FileLogger {
|
|
}
|
|
}
|
|
|
|
- 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}");
|
|
+ fn mv_file_in_dir(src: &str, dst: Option<&str>, dir: &Path) {
|
|
+ let src = dir.join(src);
|
|
+ if dst.is_none() {
|
|
+ if let Err(e) = fs::remove_file(src) {
|
|
+ println!("Failed to remove old log file: {e}");
|
|
+ }
|
|
return;
|
|
}
|
|
-
|
|
- /* 2. Write module */
|
|
- if let Err(e) = file.write((module.to_string() + " ").as_bytes()) {
|
|
- println!("Failed to log module message: {e}");
|
|
- return;
|
|
+ let dst = dir.join(dst.unwrap()); /* safe here */
|
|
+ if let Err(e) = fs::rename(src, dst) {
|
|
+ println!("Failed to rotate log file: {e}");
|
|
}
|
|
+ }
|
|
|
|
- /* 3. Write message */
|
|
- if let Err(e) = file.write((msg + "\n").as_bytes()) {
|
|
- println!("Failed to log message: {e}");
|
|
+ fn cp_file_in_dir(src: &str, dst: &str, dir: &Path) {
|
|
+ let src = dir.join(src);
|
|
+ let dst = dir.join(dst);
|
|
+ if let Err(e) = fs::copy(src, &dst) {
|
|
+ println!("Failed to create sysmaster.log.1: {e}");
|
|
+ }
|
|
+ if let Err(e) = fs::set_permissions(dst, fs::Permissions::from_mode(0o400)) {
|
|
+ println!("Failed to set log file mode: {e}");
|
|
}
|
|
}
|
|
|
|
- fn rotate(&self) {
|
|
- let dir = self.file_path.parent().unwrap();
|
|
- let file_name = self
|
|
- .file_path
|
|
- .file_name()
|
|
- .unwrap()
|
|
- .to_string_lossy()
|
|
- .to_string();
|
|
+ fn rotate(&self) -> Result<(), Error> {
|
|
+ let dir = match self.file_path.parent() {
|
|
+ None => {
|
|
+ return Err(Error::Other {
|
|
+ msg: "Cannot determine the parent directory of log file".to_string(),
|
|
+ })
|
|
+ }
|
|
+ Some(v) => v,
|
|
+ };
|
|
+ let file_name = match self.file_path.file_name() {
|
|
+ None => {
|
|
+ return Err(Error::Other {
|
|
+ msg: "Cannot determine the file name of log file".to_string(),
|
|
+ })
|
|
+ }
|
|
+ Some(v) => v.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() {
|
|
+ /* Walk through the parent directory, save the suffix rotate number in num_list */
|
|
+ let mut num_list: Vec<usize> = Vec::new();
|
|
+ let read_dir = match dir.read_dir() {
|
|
+ Err(e) => return Err(Error::Io { source: e }),
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ for de in read_dir {
|
|
let de = match de {
|
|
Err(_) => continue,
|
|
Ok(v) => v,
|
|
};
|
|
- if !de.file_type().unwrap().is_file() {
|
|
+
|
|
+ let file_type = match de.file_type() {
|
|
+ Err(_) => continue,
|
|
+ Ok(v) => v,
|
|
+ };
|
|
+ if !file_type.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>() {
|
|
@@ -238,17 +319,16 @@ impl FileLogger {
|
|
|
|
/* 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 rotated_num in num_list.iter().skip(file_number - 1) {
|
|
- let src = String::from(&file_name_dot) + &rotated_num.to_string();
|
|
+ while num_list.len() > (self.file_number - 1) as usize {
|
|
+ let num = num_list.pop().unwrap(); /* safe here */
|
|
+ let src = String::from(&file_name_dot) + &num.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();
|
|
+ while let Some(num) = num_list.pop() {
|
|
+ let src = String::from(&file_name_dot) + &num.to_string();
|
|
+ let dst = String::from(&file_name_dot) + &(num + 1).to_string();
|
|
Self::mv_file_in_dir(&src, Some(&dst), dir);
|
|
}
|
|
|
|
@@ -256,37 +336,38 @@ impl FileLogger {
|
|
let src = String::from(&file_name);
|
|
let dst = String::from(&file_name_dot) + "1";
|
|
Self::cp_file_in_dir(&src, &dst, dir);
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
-fn append_log(
|
|
- app_name: &str,
|
|
- level: LevelFilter,
|
|
- target: &str,
|
|
- file_path: &str,
|
|
- file_size: u32,
|
|
- file_number: u32,
|
|
-) {
|
|
- if target == "syslog" {
|
|
- let _ = log::set_boxed_logger(Box::new(SysLogger));
|
|
- log::set_max_level(level);
|
|
- return;
|
|
+struct CombinedLogger {
|
|
+ loggers: Vec<Box<dyn log::Log>>,
|
|
+}
|
|
+
|
|
+impl log::Log for CombinedLogger {
|
|
+ fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
|
+ true
|
|
}
|
|
- 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;
|
|
+
|
|
+ fn log(&self, record: &log::Record) {
|
|
+ for logger in &self.loggers {
|
|
+ logger.log(record);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn flush(&self) {}
|
|
+}
|
|
+
|
|
+impl CombinedLogger {
|
|
+ fn empty() -> Self {
|
|
+ Self {
|
|
+ loggers: Vec::new(),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fn push(&mut self, logger: Box<dyn Log>) {
|
|
+ self.loggers.push(logger)
|
|
}
|
|
- 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);
|
|
- let _ = log::set_boxed_logger(Box::new(LogPlugin(logger)));
|
|
}
|
|
|
|
/// Init and set the sub unit manager's log
|
|
@@ -297,7 +378,11 @@ fn append_log(
|
|
///
|
|
/// target: log target
|
|
///
|
|
-/// file_path: file path if the target is set to file
|
|
+/// file_path: file path (valid when target == "file")
|
|
+///
|
|
+/// file_size: the maximum size of an active log file (valid when target == "file")
|
|
+///
|
|
+/// file_number: the maximum number of rotated log files (valid when target == "file")
|
|
pub fn init_log_for_subum(
|
|
app_name: &str,
|
|
level: LevelFilter,
|
|
@@ -309,7 +394,7 @@ pub fn init_log_for_subum(
|
|
/* We should avoid calling init_log here, or we will get many "attempted
|
|
* to set a logger after the logging system was already initialized" error
|
|
* message. */
|
|
- append_log(app_name, level, target, file_path, file_size, file_number);
|
|
+ init_log(app_name, level, target, file_path, file_size, file_number);
|
|
}
|
|
|
|
/// Init and set the log target to console
|
|
@@ -318,7 +403,7 @@ pub fn init_log_for_subum(
|
|
///
|
|
/// level: maximum log level
|
|
pub fn init_log_to_console(app_name: &str, level: LevelFilter) {
|
|
- init_log(app_name, level, "console", "", 0, 0);
|
|
+ init_log(app_name, level, "console-syslog", "", 0, 0);
|
|
}
|
|
|
|
/// Init and set the log target to file
|
|
@@ -327,7 +412,11 @@ pub fn init_log_to_console(app_name: &str, level: LevelFilter) {
|
|
///
|
|
/// level: maximum log level
|
|
///
|
|
-/// file_path: log to which file
|
|
+/// file_path: file path
|
|
+///
|
|
+/// file_size: the maximum size of an active log file
|
|
+///
|
|
+/// file_number: the maximum number of rotated log files
|
|
pub fn init_log_to_file(
|
|
app_name: &str,
|
|
level: LevelFilter,
|
|
@@ -346,20 +435,42 @@ pub fn init_log_to_file(
|
|
///
|
|
/// target: log target
|
|
///
|
|
-/// file_path: file path if the target is set to file
|
|
+/// file_path: file path (valid when target == "file")
|
|
+///
|
|
+/// file_size: the maximum size of an active log file (valid when target == "file")
|
|
+///
|
|
+/// file_number: the maximum number of rotated log files (valid when target == "file")
|
|
pub fn init_log(
|
|
- app_name: &str,
|
|
+ _app_name: &str,
|
|
level: LevelFilter,
|
|
target: &str,
|
|
file_path: &str,
|
|
file_size: u32,
|
|
file_number: u32,
|
|
) {
|
|
+ let mut target = target;
|
|
+ if target == "file" && (file_path.is_empty() || file_size == 0 || file_number == 0) {
|
|
+ println!(
|
|
+ "LogTarget is configured to `file`, but configuration is invalid, changing the \
|
|
+ LogTarget to `console`, file: {file_path}, file_size: {file_size}, file_number: \
|
|
+ {file_number}"
|
|
+ );
|
|
+ target = "syslog";
|
|
+ }
|
|
+
|
|
if target == "syslog" {
|
|
let _ = log::set_boxed_logger(Box::new(SysLogger));
|
|
log::set_max_level(level);
|
|
return;
|
|
}
|
|
+ if target == "console-syslog" {
|
|
+ let mut logger = CombinedLogger::empty();
|
|
+ logger.push(Box::new(ConsoleLogger));
|
|
+ logger.push(Box::new(SysLogger));
|
|
+ let _ = log::set_boxed_logger(Box::new(logger));
|
|
+ log::set_max_level(level);
|
|
+ return;
|
|
+ }
|
|
if target == "file" {
|
|
let _ = log::set_boxed_logger(Box::new(FileLogger::new(
|
|
log::Level::Debug,
|
|
@@ -371,13 +482,9 @@ pub fn init_log(
|
|
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 {
|
|
- println!("{e}");
|
|
- }
|
|
}
|
|
|
|
+#[allow(unused)]
|
|
fn build_log_config(
|
|
app_name: &str,
|
|
level: LevelFilter,
|
|
--
|
|
2.33.0
|
|
|