diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs
index 164a8293b7d1283b7bc70b58ff235ad75479ba7a..1440aa8a156df67270bf131cd89715347905857f 100644
--- a/src-tauri/src/config/verge.rs
+++ b/src-tauri/src/config/verge.rs
@@ -1,5 +1,6 @@
 use crate::utils::{dirs, help};
 use anyhow::Result;
+use log::LevelFilter;
 use serde::{Deserialize, Serialize};
 
 /// ### `verge.yaml` schema
@@ -8,6 +9,10 @@ pub struct IVerge {
     /// app listening port for app singleton
     pub app_singleton_port: Option<u16>,
 
+    /// app log level
+    /// `trace` `debug` `info` `warn` `error`
+    pub app_log_level: Option<String>,
+
     // i18n
     pub language: Option<String>,
 
@@ -144,6 +149,7 @@ impl IVerge {
             };
         }
 
+        patch!(app_log_level);
         patch!(language);
         patch!(theme_mode);
         patch!(theme_blur);
@@ -182,4 +188,20 @@ impl IVerge {
             Err(_) => SERVER_PORT, // 这里就不log错误了
         }
     }
+
+    /// 获取日志等级
+    pub fn get_log_level(&self) -> LevelFilter {
+        if let Some(level) = self.app_log_level.as_ref() {
+            match level.to_lowercase().as_str() {
+                "trace" => LevelFilter::Trace,
+                "debug" => LevelFilter::Debug,
+                "info" => LevelFilter::Info,
+                "warn" => LevelFilter::Warn,
+                "error" => LevelFilter::Error,
+                _ => LevelFilter::Info,
+            }
+        } else {
+            LevelFilter::Info
+        }
+    }
 }
diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs
index 6a3c375135a6a84118470cffc8652011d79e9c8e..291e7aab160f589ec27d3cd0e4f3585d3b48b3e9 100644
--- a/src-tauri/src/utils/init.rs
+++ b/src-tauri/src/utils/init.rs
@@ -5,7 +5,7 @@ use chrono::Local;
 use log::LevelFilter;
 use log4rs::append::console::ConsoleAppender;
 use log4rs::append::file::FileAppender;
-use log4rs::config::{Appender, Config, Logger, Root};
+use log4rs::config::{Appender, Logger, Root};
 use log4rs::encode::pattern::PatternEncoder;
 use std::fs;
 use tauri::PackageInfo;
@@ -17,35 +17,49 @@ fn init_log() -> Result<()> {
         let _ = fs::create_dir_all(&log_dir);
     }
 
+    let log_level = Config::verge().data().get_log_level();
+
     let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string();
     let log_file = format!("{}.log", local_time);
     let log_file = log_dir.join(log_file);
 
-    #[cfg(feature = "verge-dev")]
-    let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {M} {m}{n}";
-    #[cfg(not(feature = "verge-dev"))]
-    let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}";
+    let log_pattern = match log_level {
+        LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}",
+        _ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}",
+    };
 
-    let encode = Box::new(PatternEncoder::new(time_format));
+    let encode = Box::new(PatternEncoder::new(log_pattern));
 
     let stdout = ConsoleAppender::builder().encoder(encode.clone()).build();
     let tofile = FileAppender::builder().encoder(encode).build(log_file)?;
 
+    let mut logger_builder = Logger::builder();
+    let mut root_builder = Root::builder();
+
+    let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug;
+
     #[cfg(feature = "verge-dev")]
-    let level = LevelFilter::Debug;
+    {
+        logger_builder = logger_builder.appenders(["file", "stdout"]);
+        if log_more {
+            root_builder = root_builder.appenders(["file", "stdout"]);
+        } else {
+            root_builder = root_builder.appenders(["stdout"]);
+        }
+    }
     #[cfg(not(feature = "verge-dev"))]
-    let level = LevelFilter::Info;
+    {
+        logger_builder = logger_builder.appenders(["file"]);
+        if log_more {
+            root_builder = root_builder.appenders(["file"]);
+        }
+    }
 
-    let config = Config::builder()
+    let (config, _) = log4rs::config::Config::builder()
         .appender(Appender::builder().build("stdout", Box::new(stdout)))
         .appender(Appender::builder().build("file", Box::new(tofile)))
-        .logger(
-            Logger::builder()
-                .appenders(["file", "stdout"])
-                .additive(false)
-                .build("app", level),
-        )
-        .build(Root::builder().appender("stdout").build(LevelFilter::Info))?;
+        .logger(logger_builder.additive(false).build("app", log_level))
+        .build_lossy(root_builder.build(log_level));
 
     log4rs::init_config(config)?;
 
diff --git a/src/components/setting/mods/misc-viewer.tsx b/src/components/setting/mods/misc-viewer.tsx
index 70d1d6d1773f5621efb31bc3d9bf8fbf813fb14f..2bd884c7ed218303a7c4ea072367bee2eb00123e 100644
--- a/src/components/setting/mods/misc-viewer.tsx
+++ b/src/components/setting/mods/misc-viewer.tsx
@@ -19,6 +19,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
 
   const [open, setOpen] = useState(false);
   const [values, setValues] = useState({
+    appLogLevel: "info",
     autoCloseConnection: false,
     enableClashFields: true,
     enableBuiltinEnhanced: true,
@@ -30,6 +31,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
     open: () => {
       setOpen(true);
       setValues({
+        appLogLevel: verge?.app_log_level ?? "info",
         autoCloseConnection: verge?.auto_close_connection ?? false,
         enableClashFields: verge?.enable_clash_fields ?? true,
         enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true,
@@ -43,6 +45,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
   const onSave = useLockFn(async () => {
     try {
       await patchVerge({
+        app_log_level: values.appLogLevel,
         auto_close_connection: values.autoCloseConnection,
         enable_clash_fields: values.enableClashFields,
         enable_builtin_enhanced: values.enableBuiltinEnhanced,
@@ -67,6 +70,27 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
       onOk={onSave}
     >
       <List>
+        <ListItem sx={{ padding: "5px 2px" }}>
+          <ListItemText primary="App Log Level" />
+          <Select
+            size="small"
+            sx={{ width: 100, "> div": { py: "7.5px" } }}
+            value={values.appLogLevel}
+            onChange={(e) => {
+              setValues((v) => ({
+                ...v,
+                appLogLevel: e.target.value as string,
+              }));
+            }}
+          >
+            {["trace", "debug", "info", "warn", "error"].map((i) => (
+              <MenuItem value={i} key={i}>
+                {i}
+              </MenuItem>
+            ))}
+          </Select>
+        </ListItem>
+
         <ListItem sx={{ padding: "5px 2px" }}>
           <ListItemText primary="Auto Close Connections" />
           <Switch
diff --git a/src/services/types.d.ts b/src/services/types.d.ts
index ccccc01d08ce0d507a6e81117ffee5431650a168..819c5d07263438e25af4f0188ab8ab70801b6a81 100644
--- a/src/services/types.d.ts
+++ b/src/services/types.d.ts
@@ -153,6 +153,7 @@ interface IProfilesConfig {
 }
 
 interface IVergeConfig {
+  app_log_level?: "trace" | "debug" | "info" | "warn" | "error" | string;
   language?: string;
   clash_core?: string;
   theme_mode?: "light" | "dark" | "system";