sysmaster/backport-fix-several-fixes-on-logger.patch
huyubiao 8936fa02c5 sync patches from upstream,change the path of the unit,modify permissions for some directories and files
(cherry picked from commit ce9ff469b57f60130621bc293783bd3ac1fc92f2)
2023-08-05 18:15:53 +08:00

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