Skip to content

Commit 164ca5e

Browse files
authored
Merge pull request #4255 from Byron/log-retention
better logs
2 parents dcfa6f5 + bb880cf commit 164ca5e

File tree

2 files changed

+133
-31
lines changed

2 files changed

+133
-31
lines changed

Cargo.lock

Lines changed: 27 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/gitbutler-tauri/src/logs.rs

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use std::path::Path;
12
use std::{fs, net::Ipv4Addr, time::Duration};
2-
33
use tauri::{AppHandle, Manager};
4-
use tracing::{metadata::LevelFilter, subscriber::set_global_default};
4+
use tracing::{instrument, metadata::LevelFilter, subscriber::set_global_default};
55
use tracing_appender::rolling::{RollingFileAppender, Rotation};
66
use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, Layer};
77

@@ -12,13 +12,22 @@ pub fn init(app_handle: &AppHandle) {
1212
.expect("failed to get logs dir");
1313
fs::create_dir_all(&logs_dir).expect("failed to create logs dir");
1414

15+
let log_prefix = "GitButler";
16+
let log_suffix = "log";
17+
let max_log_files = 14;
18+
remove_old_logs(&logs_dir).ok();
1519
let file_appender = RollingFileAppender::builder()
1620
.rotation(Rotation::DAILY)
17-
.max_log_files(14)
18-
.filename_prefix("GitButler.log")
21+
.max_log_files(max_log_files)
22+
.filename_prefix(log_prefix)
23+
.filename_suffix(log_suffix)
1924
.build(&logs_dir)
2025
.expect("initializing rolling file appender failed");
2126
let (file_writer, guard) = tracing_appender::non_blocking(file_appender);
27+
// As the file-writer only checks `max_log_files` on file rotation, it bascially never happens.
28+
// Run it now.
29+
prune_old_logs(&logs_dir, Some(log_prefix), Some(log_suffix), max_log_files).ok();
30+
2231
app_handle.manage(guard); // keep the guard alive for the lifetime of the app
2332

2433
let format_for_humans = tracing_subscriber::fmt::format()
@@ -81,3 +90,96 @@ fn get_server_addr(app_handle: &AppHandle) -> (Ipv4Addr, u16) {
8190
};
8291
(Ipv4Addr::LOCALHOST, port)
8392
}
93+
94+
/// Originally based on https://github.com/tokio-rs/tracing/blob/44861cad7a821f08b3c13aba14bb8ddf562b7053/tracing-appender/src/rolling.rs#L571
95+
#[instrument(err(Debug))]
96+
fn prune_old_logs(
97+
log_directory: &Path,
98+
log_filename_prefix: Option<&str>,
99+
log_filename_suffix: Option<&str>,
100+
max_files: usize,
101+
) -> anyhow::Result<()> {
102+
let mut files: Vec<_> = {
103+
let dir = fs::read_dir(log_directory)?;
104+
dir.filter_map(|entry| {
105+
let entry = entry.ok()?;
106+
let metadata = entry.metadata().ok()?;
107+
108+
// the appender only creates files, not directories or symlinks,
109+
// so we should never delete a dir or symlink.
110+
if !metadata.is_file() {
111+
return None;
112+
}
113+
114+
let filename = entry.file_name();
115+
let filename = filename.to_str()?;
116+
if let Some(prefix) = log_filename_prefix {
117+
if !filename.starts_with(prefix) {
118+
return None;
119+
}
120+
}
121+
122+
if let Some(suffix) = log_filename_suffix {
123+
if !filename.ends_with(suffix) {
124+
return None;
125+
}
126+
}
127+
128+
let created = metadata.created().ok()?;
129+
Some((entry, created))
130+
})
131+
.collect()
132+
};
133+
134+
if files.len() < max_files {
135+
return Ok(());
136+
}
137+
138+
// sort the files by their creation timestamps.
139+
files.sort_by_key(|(_, created_at)| *created_at);
140+
141+
// delete files, so that (n-1) files remain, because we will create another log file
142+
for (file, _) in files.iter().take(files.len() - (max_files - 1)) {
143+
if let Err(err) = fs::remove_file(file.path()) {
144+
tracing::warn!(
145+
"Failed to remove extra log file {}: {}",
146+
file.path().display(),
147+
err,
148+
);
149+
}
150+
}
151+
152+
Ok(())
153+
}
154+
155+
#[instrument(err(Debug))]
156+
fn remove_old_logs(log_directory: &Path) -> anyhow::Result<()> {
157+
let dir = fs::read_dir(log_directory)?;
158+
let old_log_files = dir.filter_map(|entry| {
159+
let entry = entry.ok()?;
160+
let metadata = entry.metadata().ok()?;
161+
162+
if !metadata.is_file() {
163+
return None;
164+
}
165+
166+
let filename = entry.file_name();
167+
let filename = filename.to_str()?;
168+
if !filename.starts_with("GitButler.log") {
169+
return None;
170+
}
171+
172+
Some(entry.path())
173+
});
174+
for file_path in old_log_files {
175+
if let Err(err) = fs::remove_file(&file_path) {
176+
tracing::warn!(
177+
"Failed to remove old log file {}: {}",
178+
file_path.display(),
179+
err,
180+
);
181+
}
182+
}
183+
184+
Ok(())
185+
}

0 commit comments

Comments
 (0)