-
Notifications
You must be signed in to change notification settings - Fork 81
perf: 自动清理运行日志支持删除更多日志 #334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,10 @@ const ZIP_CENTRAL_DIR_FIXED_BYTES: u64 = 46; | |
| const MAX_EXPORTS_TO_KEEP: usize = 10; | ||
| /// 需要随日志一起清空的调试产物子目录(内容由 MaaFW 的 save_on_error / save_draw 写入)。 | ||
| const DEBUG_ARTIFACT_DIRS: [&str; 2] = ["on_error", "vision"]; | ||
| /// 日志目录名(位于应用数据目录下)。 | ||
| const DEBUG_DIR: &str = "debug"; | ||
| /// 日志导出产物目录名,与 `debug/` 同级,避免下次导出把上次的产物扫进去。 | ||
| const DEBUG_EXPORTS_DIR: &str = "debug_exports"; | ||
|
|
||
| #[derive(Clone)] | ||
| struct ExportEntry { | ||
|
|
@@ -296,10 +300,10 @@ pub fn get_data_dir() -> Result<String, String> { | |
| Ok(data_dir.to_string_lossy().to_string()) | ||
| } | ||
|
|
||
| /// 清空调试产物目录内容,保留目录本身。返回成功删除的条目数。 | ||
| /// 清空目录内容,保留目录本身。返回成功删除的条目数(子目录整体计 1 条)。 | ||
| /// | ||
| /// 保留目录本身是因为 MaaFW 的 save_on_error 直接往 `on_error/` 写文件,父目录缺失会写入失败。 | ||
| fn clear_debug_artifact_dir(dir: &Path) -> u64 { | ||
| /// 保留根目录只是为了不改动既有目录结构;MaaFW 写调试产物前会自建父目录,缺失也不会写入失败。 | ||
| fn clear_dir_contents(dir: &Path) -> u64 { | ||
| let entries = match std::fs::read_dir(dir) { | ||
| Ok(entries) => entries, | ||
| Err(e) => { | ||
|
|
@@ -326,26 +330,28 @@ fn clear_debug_artifact_dir(dir: &Path) -> u64 { | |
| deleted | ||
| } | ||
|
|
||
| /// 删除 debug 目录中的 .log 文件,并清空 on_error/、vision/ 内的调试产物(保留这两个目录本身)。 | ||
| /// 可选择排除一个当前正在使用的日志文件。返回删除的文件与调试产物总数。 | ||
| #[tauri::command] | ||
| pub fn clear_log_files(exclude_file_name: Option<String>) -> Result<u64, String> { | ||
| let debug_dir = get_app_data_dir()?.join("debug"); | ||
|
|
||
| if !debug_dir.exists() { | ||
| return Ok(0); | ||
| } | ||
| /// 递归删除 `dir` 及其所有子目录下的 .log 文件,返回删除数量。 | ||
| /// `exclude_file_name` 匹配的文件名会被跳过(当前会话正在写入的日志)。 | ||
| /// 只删文件不回收空目录,避免改动既有目录结构。 | ||
| fn remove_log_files_recursively(dir: &Path, exclude_file_name: Option<&str>) -> u64 { | ||
| let entries = match std::fs::read_dir(dir) { | ||
| Ok(entries) => entries, | ||
| Err(e) => { | ||
| log::debug!("Failed to read log dir [{}]: {}", dir.display(), e); | ||
| return 0; | ||
| } | ||
| }; | ||
|
|
||
| let mut deleted = 0_u64; | ||
| let entries = std::fs::read_dir(&debug_dir) | ||
| .map_err(|e| format!("读取日志目录失败 [{}]: {}", debug_dir.display(), e))?; | ||
|
|
||
| for entry in entries { | ||
| let entry = match entry { | ||
| Ok(entry) => entry, | ||
| Err(_) => continue, | ||
| }; | ||
| for entry in entries.flatten() { | ||
| let path = entry.path(); | ||
|
|
||
| if path.is_dir() { | ||
| deleted = | ||
| deleted.saturating_add(remove_log_files_recursively(&path, exclude_file_name)); | ||
| continue; | ||
| } | ||
|
|
||
| if !path.is_file() { | ||
| continue; | ||
| } | ||
|
|
@@ -354,11 +360,7 @@ pub fn clear_log_files(exclude_file_name: Option<String>) -> Result<u64, String> | |
| continue; | ||
| }; | ||
|
|
||
| if !name.ends_with(".log") { | ||
| continue; | ||
| } | ||
|
|
||
| if exclude_file_name.as_deref() == Some(name) { | ||
| if !name.ends_with(".log") || exclude_file_name == Some(name) { | ||
| continue; | ||
| } | ||
|
Comment on lines
+363
to
365
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick (bug_risk): 排除逻辑仅基于文件名,因此与活动根日志文件同名的嵌套日志也会被跳过。因此,当两个目录包含同名日志文件时,递归清理会留下无关的过期日志。 触发条件: 嵌套的 debug 目录包含一个与当前会话日志同名但实际不同的日志文件时。 建议修复: 传递并比较活动日志的规范路径/完整路径,或者将基于文件名的排除限制在已知的活动日志位置。 Original comment in Englishnitpick (bug_risk): The exclusion is based only on the basename, so a nested log with the same filename as the active root log is skipped as well. Recursive cleanup therefore leaves unrelated stale logs whenever two directories contain the same log filename. Triggers: When a nested debug directory contains a different log file with the same basename as the current session's log. Suggested fix: Pass and compare the active log's canonical/full path, or restrict the basename exclusion to the known active-log location. |
||
|
|
||
|
|
@@ -368,15 +370,40 @@ pub fn clear_log_files(exclude_file_name: Option<String>) -> Result<u64, String> | |
| } | ||
| } | ||
|
|
||
| deleted | ||
| } | ||
|
|
||
| /// `clear_log_files` 的可测核心,接收具体目录而不依赖应用数据目录。 | ||
| fn clear_log_dirs(debug_dir: &Path, exports_dir: &Path, exclude_file_name: Option<&str>) -> u64 { | ||
| let mut deleted = remove_log_files_recursively(debug_dir, exclude_file_name); | ||
|
|
||
| for dir_name in DEBUG_ARTIFACT_DIRS { | ||
| let artifact_dir = debug_dir.join(dir_name); | ||
| if !artifact_dir.is_dir() { | ||
| continue; | ||
| } | ||
| deleted = deleted.saturating_add(clear_debug_artifact_dir(&artifact_dir)); | ||
| deleted = deleted.saturating_add(clear_dir_contents(&artifact_dir)); | ||
| } | ||
|
|
||
| Ok(deleted) | ||
| if exports_dir.is_dir() { | ||
| deleted = deleted.saturating_add(clear_dir_contents(exports_dir)); | ||
|
Comment on lines
+388
to
+389
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): 触发条件: 手动清除日志的操作与正在进行的日志导出重叠时。 建议修复: 使用共享锁串行化清除和导出操作,或者避免在导出完成前删除当前活动的导出目录。 Original comment in Englishissue (bug_risk): Triggers: When a manual log clear overlaps an in-progress log export. Suggested fix: Serialize clearing and exporting with a shared lock, or avoid deleting the currently active export directory until the export completes. |
||
| } | ||
|
|
||
| deleted | ||
| } | ||
|
|
||
| /// 递归删除 debug 目录(含所有子目录)中的 .log 文件,清空 on_error/、vision/ 内的调试产物 | ||
| /// 以及 debug_exports/ 下的日志导出产物(保留这几个目录本身)。 | ||
| /// 可选择排除一个当前正在使用的日志文件。返回删除的文件与产物总数。 | ||
| #[tauri::command] | ||
| pub fn clear_log_files(exclude_file_name: Option<String>) -> Result<u64, String> { | ||
| let data_dir = get_app_data_dir()?; | ||
|
|
||
| Ok(clear_log_dirs( | ||
| &data_dir.join(DEBUG_DIR), | ||
| &data_dir.join(DEBUG_EXPORTS_DIR), | ||
| exclude_file_name.as_deref(), | ||
| )) | ||
| } | ||
|
|
||
| /// 获取当前工作目录 | ||
|
|
@@ -573,7 +600,7 @@ fn export_logs_blocking( | |
|
|
||
| // 日志在数据目录下(macOS: ~/Library/Application Support/MXU/debug) | ||
| let data_dir = get_app_data_dir()?; | ||
| let debug_dir = data_dir.join("debug"); | ||
| let debug_dir = data_dir.join(DEBUG_DIR); | ||
|
|
||
| if !debug_dir.exists() { | ||
| return Err("日志目录不存在".to_string()); | ||
|
|
@@ -589,7 +616,7 @@ fn export_logs_blocking( | |
| format!("{}-logs-{}-{}", name, version, date_str) | ||
| }; | ||
| // 产物放在 debug_exports/ 下而不是 debug/,避免下次导出把上次的产物扫进去。 | ||
| let exports_root = data_dir.join("debug_exports"); | ||
| let exports_root = data_dir.join(DEBUG_EXPORTS_DIR); | ||
| let out_dir = exports_root.join(&dir_name); | ||
| std::fs::create_dir_all(&out_dir) | ||
| .map_err(|e| format!("创建导出目录失败 [{}]: {}", out_dir.display(), e))?; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security):
path.is_dir()会跟随目录符号链接,因此debug内部的符号链接会导致递归遍历进入符号链接的目标,并删除应用程序 debug 目录之外匹配的.log文件。触发条件:
debug下存在意外的或用户创建的目录符号链接时。建议修复: 使用支持符号链接识别的元数据检查条目,并跳过符号链接,而不是递归进入其中。
Original comment in English
🚨 issue (security):
path.is_dir()follows directory symlinks, so a symlink insidedebugcauses the recursive traversal to enter the symlink target and delete matching.logfiles outside the application's debug directory.Triggers: When an unexpected or user-created directory symlink exists below
debug.Suggested fix: Inspect the entry with symlink-aware metadata and skip symlinks instead of recursing into them.