Selaa lähdekoodia

feat: support auto clean log files

GyDi 1 vuosi sitten
vanhempi
commit
e0d26203dd

+ 6 - 1
src-tauri/src/config/verge.rs

@@ -83,6 +83,10 @@ pub struct IVerge {
     /// proxy 页面布局 列数
     pub proxy_layout_column: Option<i32>,
 
+    /// 日志清理
+    /// 0: 不清理; 1: 7天; 2: 30天; 3: 90天
+    pub auto_log_clean: Option<i32>,
+
     /// window size and position
     #[serde(skip_serializing_if = "Option::is_none")]
     pub window_size_position: Option<Vec<f64>>,
@@ -137,6 +141,7 @@ impl IVerge {
             auto_close_connection: Some(true),
             enable_builtin_enhanced: Some(true),
             enable_clash_fields: Some(true),
+            auto_log_clean: Some(3),
             ..Self::default()
         }
     }
@@ -183,7 +188,7 @@ impl IVerge {
         patch!(enable_builtin_enhanced);
         patch!(proxy_layout_column);
         patch!(enable_clash_fields);
-
+        patch!(auto_log_clean);
         patch!(window_size_position);
     }
 

+ 70 - 2
src-tauri/src/utils/init.rs

@@ -1,13 +1,14 @@
 use crate::config::*;
 use crate::utils::{dirs, help};
 use anyhow::Result;
-use chrono::Local;
+use chrono::{DateTime, Local};
 use log::LevelFilter;
 use log4rs::append::console::ConsoleAppender;
 use log4rs::append::file::FileAppender;
 use log4rs::config::{Appender, Logger, Root};
 use log4rs::encode::pattern::PatternEncoder;
-use std::fs;
+use std::fs::{self, DirEntry};
+use std::str::FromStr;
 use tauri::PackageInfo;
 
 /// initialize this instance's log file
@@ -69,6 +70,72 @@ fn init_log() -> Result<()> {
     Ok(())
 }
 
+/// 删除log文件
+pub fn delete_log() -> Result<()> {
+    let log_dir = dirs::app_logs_dir()?;
+    if !log_dir.exists() {
+        return Ok(());
+    }
+
+    let auto_log_clean = {
+        let verge = Config::verge();
+        let verge = verge.data();
+        verge.auto_log_clean.clone().unwrap_or(0)
+    };
+
+    let day = match auto_log_clean {
+        1 => 7,
+        2 => 30,
+        3 => 90,
+        _ => return Ok(()),
+    };
+
+    log::debug!(target: "app", "try to delete log files, day: {day}");
+
+    // %Y-%m-%d to NaiveDateTime
+    let parse_time_str = |s: &str| {
+        let sa: Vec<&str> = s.split('-').collect();
+        if sa.len() != 4 {
+            return Err(anyhow::anyhow!("invalid time str"));
+        }
+
+        let year = i32::from_str(sa[0])?;
+        let month = u32::from_str(sa[1])?;
+        let day = u32::from_str(sa[2])?;
+        let time = chrono::NaiveDate::from_ymd_opt(year, month, day)
+            .ok_or(anyhow::anyhow!("invalid time str"))?
+            .and_hms_opt(0, 0, 0)
+            .ok_or(anyhow::anyhow!("invalid time str"))?;
+        Ok(time)
+    };
+
+    let process_file = |file: DirEntry| -> Result<()> {
+        let file_name = file.file_name();
+        let file_name = file_name.to_str().unwrap_or_default();
+
+        if file_name.ends_with(".log") {
+            let now = Local::now();
+            let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
+            let file_time = DateTime::<Local>::from_local(created_time, now.offset().clone());
+
+            let duration = now.signed_duration_since(file_time);
+            if duration.num_days() > day {
+                let file_path = file.path();
+                let _ = fs::remove_file(file_path);
+                log::info!(target: "app", "delete log file: {file_name}");
+            }
+        }
+        Ok(())
+    };
+
+    for file in fs::read_dir(&log_dir)? {
+        if let Ok(file) = file {
+            let _ = process_file(file);
+        }
+    }
+    Ok(())
+}
+
 /// Initialize all the config files
 /// before tauri setup
 pub fn init_config() -> Result<()> {
@@ -78,6 +145,7 @@ pub fn init_config() -> Result<()> {
     }
 
     let _ = init_log();
+    let _ = delete_log();
 
     crate::log_err!(dirs::app_home_dir().map(|app_dir| {
         if !app_dir.exists() {

+ 30 - 1
src/components/setting/mods/misc-viewer.tsx

@@ -25,6 +25,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
     enableBuiltinEnhanced: true,
     proxyLayoutColumn: 6,
     defaultLatencyTest: "",
+    autoLogClean: 0,
   });
 
   useImperativeHandle(ref, () => ({
@@ -37,6 +38,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
         enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true,
         proxyLayoutColumn: verge?.proxy_layout_column || 6,
         defaultLatencyTest: verge?.default_latency_test || "",
+        autoLogClean: verge?.auto_log_clean || 0,
       });
     },
     close: () => setOpen(false),
@@ -51,6 +53,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
         enable_builtin_enhanced: values.enableBuiltinEnhanced,
         proxy_layout_column: values.proxyLayoutColumn,
         default_latency_test: values.defaultLatencyTest,
+        auto_log_clean: values.autoLogClean as any,
       });
       setOpen(false);
     } catch (err: any) {
@@ -128,7 +131,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
           <ListItemText primary={t("Proxy Layout Column")} />
           <Select
             size="small"
-            sx={{ width: 100, "> div": { py: "7.5px" } }}
+            sx={{ width: 135, "> div": { py: "7.5px" } }}
             value={values.proxyLayoutColumn}
             onChange={(e) => {
               setValues((v) => ({
@@ -148,6 +151,32 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
           </Select>
         </ListItem>
 
+        <ListItem sx={{ padding: "5px 2px" }}>
+          <ListItemText primary={t("Auto Log Clean")} />
+          <Select
+            size="small"
+            sx={{ width: 135, "> div": { py: "7.5px" } }}
+            value={values.autoLogClean}
+            onChange={(e) => {
+              setValues((v) => ({
+                ...v,
+                autoLogClean: e.target.value as number,
+              }));
+            }}
+          >
+            {[
+              { key: "Never Clean", value: 0 },
+              { key: "Retain 7 Days", value: 1 },
+              { key: "Retain 30 Days", value: 2 },
+              { key: "Retain 90 Days", value: 3 },
+            ].map((i) => (
+              <MenuItem key={i.value} value={i.value}>
+                {t(i.key)}
+              </MenuItem>
+            ))}
+          </Select>
+        </ListItem>
+
         <ListItem sx={{ padding: "5px 2px" }}>
           <ListItemText primary={t("Default Latency Test")} />
           <TextField

+ 7 - 1
src/locales/en.json

@@ -125,5 +125,11 @@
   "Enable Clash Fields Filter": "Enable Clash Fields Filter",
   "Enable Builtin Enhanced": "Enable Builtin Enhanced",
   "Proxy Layout Column": "Proxy Layout Column",
-  "Default Latency Test": "Default Latency Test"
+  "Default Latency Test": "Default Latency Test",
+
+  "Auto Log Clean": "Auto Log Clean",
+  "Never Clean": "Never Clean",
+  "Retain 7 Days": "Retain 7 Days",
+  "Retain 30 Days": "Retain 30 Days",
+  "Retain 90 Days": "Retain 90 Days"
 }

+ 7 - 1
src/locales/zh.json

@@ -125,5 +125,11 @@
   "Enable Clash Fields Filter": "开启Clash字段过滤",
   "Enable Builtin Enhanced": "开启内建增强功能",
   "Proxy Layout Column": "代理页布局列数",
-  "Default Latency Test": "默认测试链接"
+  "Default Latency Test": "默认测试链接",
+
+  "Auto Log Clean": "自动清理日志",
+  "Never Clean": "不清理",
+  "Retain 7 Days": "保留7天",
+  "Retain 30 Days": "保留30天",
+  "Retain 90 Days": "保留90天"
 }

+ 1 - 0
src/services/types.d.ts

@@ -186,6 +186,7 @@ interface IVergeConfig {
   default_latency_test?: string;
   enable_clash_fields?: boolean;
   enable_builtin_enhanced?: boolean;
+  auto_log_clean?: 0 | 1 | 2 | 3;
   proxy_layout_column?: number;
 }