mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Replace file_rotate crate with more robust internal log rotation logic
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -705,16 +705,6 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "file-rotate"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e8e2fa049328a1f3295991407a88585805d126dfaadf74b9fe8c194c730aafc"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"flate2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.1"
|
||||
@@ -2245,7 +2235,6 @@ dependencies = [
|
||||
"async-std",
|
||||
"bitflags 2.9.1",
|
||||
"derivative",
|
||||
"file-rotate",
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@@ -2285,7 +2274,6 @@ dependencies = [
|
||||
"derivative",
|
||||
"dialog",
|
||||
"enum-flags",
|
||||
"file-rotate",
|
||||
"flate2",
|
||||
"fs_extra",
|
||||
"glob",
|
||||
|
||||
@@ -51,7 +51,6 @@ clap = "4.5"
|
||||
chrono = "0.4"
|
||||
wait-timeout = "0.2"
|
||||
strum = { version = "0.27", features = ["derive"] }
|
||||
file-rotate = "0.8"
|
||||
simple-stopwatch = "0.1"
|
||||
enum-flags = "0.4"
|
||||
remove_dir_all = "1.0"
|
||||
|
||||
@@ -58,7 +58,6 @@ bitflags.workspace = true
|
||||
regex.workspace = true
|
||||
normpath.workspace = true
|
||||
simple-stopwatch.workspace = true
|
||||
file-rotate.workspace = true
|
||||
wait-timeout.workspace = true
|
||||
pretty-bytes-rust.workspace = true
|
||||
enum-flags.workspace = true
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
extern crate log;
|
||||
|
||||
use std::{
|
||||
env,
|
||||
os::windows::process::CommandExt,
|
||||
process::{Command as Process, ExitCode},
|
||||
};
|
||||
use velopack::locator::LocationContext;
|
||||
use velopack::logging;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let my_path = std::env::current_exe().unwrap();
|
||||
let default_log_file = {
|
||||
let mut my_dir = env::current_exe().unwrap();
|
||||
my_dir.pop();
|
||||
my_dir.join("Velopack.log")
|
||||
};
|
||||
|
||||
let _ = velopack::logging::init_logging("stub", Some(&default_log_file), false, false, None);
|
||||
let default_log_file = logging::default_logfile_path(LocationContext::IAmUpdateExe);
|
||||
let _ = logging::init_logging("stub", Some(&default_log_file), false, false, None);
|
||||
|
||||
info!("--");
|
||||
info!("Starting Velopack Stub (at {:?})", my_path);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Velopack.Logging
|
||||
public FileVelopackLogger(string filePath, uint processId)
|
||||
{
|
||||
ProcessId = processId;
|
||||
_fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
|
||||
_fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete);
|
||||
_writer = new StreamWriter(_fileStream);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ rust-version.workspace = true
|
||||
default = []
|
||||
async = ["async-std"]
|
||||
typescript = ["ts-rs"]
|
||||
file-logging = ["log-panics", "simplelog", "file-rotate", "time"]
|
||||
file-logging = ["log-panics", "simplelog", "time"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["async"]
|
||||
@@ -59,7 +59,6 @@ async-std = { workspace = true, optional = true }
|
||||
# file logging
|
||||
log-panics = { workspace = true, optional = true }
|
||||
simplelog = { workspace = true, optional = true }
|
||||
file-rotate = { workspace = true, optional = true }
|
||||
time = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
184
src/lib-rust/src/file_rotate.rs
Normal file
184
src/lib-rust/src/file_rotate.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct FileRotate {
|
||||
file: Option<File>,
|
||||
path: PathBuf,
|
||||
max_size: u64,
|
||||
current_size: u64,
|
||||
}
|
||||
|
||||
/// This is a very simple log file rotate implementation. It should implement the Write trait.
|
||||
/// It should fail-safe, and not panic if it can't write logs for some reason.
|
||||
/// It will rotate the "path" to "path.old" when it reaches 1mb, overwriting any previous "path.old"
|
||||
impl FileRotate {
|
||||
pub fn new<P: AsRef<Path>>(path: P, max_size: u64) -> Self {
|
||||
let path = path.as_ref();
|
||||
|
||||
let mut me = Self { file: None, path: path.to_path_buf(), max_size, current_size: 0 };
|
||||
|
||||
me.ensure_log_dir_exists();
|
||||
|
||||
if let Ok(metadata) = fs::metadata(&me.path) {
|
||||
let size = metadata.len();
|
||||
if size > me.max_size {
|
||||
me.rotate();
|
||||
} else {
|
||||
me.current_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
me.open_file();
|
||||
me
|
||||
}
|
||||
|
||||
fn ensure_log_dir_exists(&self) {
|
||||
if let Some(parent) = self.path.parent() {
|
||||
if let Err(_e) = fs::create_dir_all(parent) {
|
||||
// eprintln!("Failed to create log file parent directory: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_file(&mut self) {
|
||||
if let Some(_) = &self.file {
|
||||
return;
|
||||
}
|
||||
|
||||
self.ensure_log_dir_exists();
|
||||
|
||||
let mut o = OpenOptions::new();
|
||||
o.read(true).create(true).append(true);
|
||||
|
||||
match o.open(&self.path) {
|
||||
Ok(file) => self.file = Some(file),
|
||||
Err(_e) => {} //eprintln!("Failed to open log file: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate(&mut self) {
|
||||
self.ensure_log_dir_exists();
|
||||
|
||||
let _ = self.file.take();
|
||||
|
||||
let rotation_path = append_extension(&self.path, "old");
|
||||
|
||||
if let Err(_e) = fs::rename(&self.path, &rotation_path) {
|
||||
// eprintln!("Failed to rotate log file: {e}");
|
||||
} else {
|
||||
self.current_size = 0;
|
||||
}
|
||||
|
||||
self.open_file();
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FileRotate {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
if self.file.is_none() {
|
||||
self.open_file();
|
||||
}
|
||||
|
||||
if let Some(file) = &mut self.file {
|
||||
let written = file.write(buf)?;
|
||||
self.current_size += written as u64;
|
||||
if self.current_size > self.max_size {
|
||||
self.flush()?;
|
||||
self.rotate();
|
||||
}
|
||||
Ok(written)
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::Other, "File not open"))
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
if self.file.is_none() {
|
||||
self.open_file();
|
||||
}
|
||||
|
||||
if let Some(file) = &mut self.file {
|
||||
file.flush()
|
||||
} else {
|
||||
Err(std::io::Error::new(std::io::ErrorKind::Other, "File not open"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn append_extension(path: &Path, ext: &str) -> PathBuf {
|
||||
let mut os_string = path.as_os_str().to_os_string();
|
||||
os_string.push(format!(".{}", ext));
|
||||
PathBuf::from(os_string)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn write_lines(path: &PathBuf, lines: u32) {
|
||||
let mut writer = FileRotate::new(path, 200);
|
||||
for idx in 1..=lines {
|
||||
let _ = writeln!(writer, "Line {}: Hello World!", idx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_rotate() {
|
||||
let tmpdir = tempfile::tempdir().unwrap();
|
||||
|
||||
let path = tmpdir.path().join("test.log");
|
||||
write_lines(&path, 28);
|
||||
|
||||
let content = fs::read_to_string(&path).unwrap();
|
||||
|
||||
let expected = "
|
||||
20: Hello World!
|
||||
Line 21: Hello World!
|
||||
Line 22: Hello World!
|
||||
Line 23: Hello World!
|
||||
Line 24: Hello World!
|
||||
Line 25: Hello World!
|
||||
Line 26: Hello World!
|
||||
Line 27: Hello World!
|
||||
Line 28: Hello World!
|
||||
";
|
||||
|
||||
assert_eq!(content.trim(), expected.trim());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_rotate_case_insensitive() {
|
||||
let tmpdir = tempfile::tempdir().unwrap();
|
||||
|
||||
let path = tmpdir.path().join("test.log");
|
||||
write_lines(&path, 7);
|
||||
write_lines(&path, 7);
|
||||
write_lines(&path, 7);
|
||||
|
||||
let path2 = tmpdir.path().join("Test.log");
|
||||
write_lines(&path2, 7);
|
||||
write_lines(&path2, 7);
|
||||
write_lines(&path2, 7);
|
||||
|
||||
let content = fs::read_to_string(&path).unwrap();
|
||||
let content_old = fs::read_to_string(&append_extension(&path, "old")).unwrap();
|
||||
|
||||
let expected = "
|
||||
Line 6: Hello World!
|
||||
Line 7: Hello World!
|
||||
";
|
||||
|
||||
let expected_old = "
|
||||
Line 3: Hello World!
|
||||
Line 4: Hello World!
|
||||
Line 5: Hello World!
|
||||
Line 6: Hello World!
|
||||
Line 7: Hello World!
|
||||
Line 1: Hello World!
|
||||
Line 2: Hello World!
|
||||
Line 3: Hello World!
|
||||
Line 4: Hello World!
|
||||
Line 5: Hello World!
|
||||
";
|
||||
|
||||
assert_eq!(content.trim(), expected.trim());
|
||||
assert_eq!(content_old.trim(), expected_old.trim());
|
||||
}
|
||||
@@ -82,6 +82,9 @@ mod app;
|
||||
mod manager;
|
||||
mod util;
|
||||
|
||||
// #[cfg(feature = "file-logging")]
|
||||
mod file_rotate;
|
||||
|
||||
/// Utility functions for loading and working with Velopack bundles and manifests.
|
||||
pub mod bundle;
|
||||
|
||||
|
||||
@@ -96,12 +96,9 @@ pub fn init_logging(
|
||||
|
||||
if let Some(f) = file {
|
||||
let file_level = if verbose { LevelFilter::Trace } else { LevelFilter::Info };
|
||||
let writer = file_rotate::FileRotate::new(
|
||||
let writer = super::file_rotate::FileRotate::new(
|
||||
f.clone(),
|
||||
file_rotate::suffix::AppendCount::new(1), // keep 1 old log file
|
||||
file_rotate::ContentLimit::Bytes(1 * 1024 * 1024), // 1MB max log file size
|
||||
file_rotate::compression::Compression::None,
|
||||
None,
|
||||
1 * 1024 * 1024, // 1MB max log file size
|
||||
);
|
||||
loggers.push(WriteLogger::new(file_level, get_config(Some(process_name)), writer));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user