mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 04:53:44 +08:00
perf: Improve kernel management logic & add more dev mode logs
This commit is contained in:
parent
ebe0899eb1
commit
ff2cf30238
@ -3,7 +3,7 @@
|
|||||||
"version": "2.0.4-alpha",
|
"version": "2.0.4-alpha",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env RUST_BACKTRACE=1 tauri dev",
|
"dev": "cross-env RUST_BACKTRACE=1 tauri dev --features verge-dev",
|
||||||
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
|
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
|
||||||
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
|
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
|
||||||
"tauri": "tauri",
|
"tauri": "tauri",
|
||||||
|
@ -10,10 +10,14 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
use super::process_lock::ProcessLock;
|
||||||
|
use super::health_check::HealthChecker;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CoreManager {
|
pub struct CoreManager {
|
||||||
running: Arc<Mutex<bool>>,
|
running: Arc<Mutex<bool>>,
|
||||||
|
process_lock: Arc<Mutex<Option<ProcessLock>>>,
|
||||||
|
health_checker: HealthChecker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreManager {
|
impl CoreManager {
|
||||||
@ -21,11 +25,19 @@ impl CoreManager {
|
|||||||
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
|
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
|
||||||
CORE_MANAGER.get_or_init(|| CoreManager {
|
CORE_MANAGER.get_or_init(|| CoreManager {
|
||||||
running: Arc::new(Mutex::new(false)),
|
running: Arc::new(Mutex::new(false)),
|
||||||
|
process_lock: Arc::new(Mutex::new(None)),
|
||||||
|
health_checker: HealthChecker::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init(&self) -> Result<()> {
|
pub async fn init(&self) -> Result<()> {
|
||||||
log::trace!("run core start");
|
log::trace!("run core start");
|
||||||
|
|
||||||
|
// 初始化进程锁
|
||||||
|
let process_lock = ProcessLock::new()?;
|
||||||
|
process_lock.acquire()?;
|
||||||
|
*self.process_lock.lock().await = Some(process_lock);
|
||||||
|
|
||||||
// 启动clash
|
// 启动clash
|
||||||
log_err!(Self::global().start_core().await);
|
log_err!(Self::global().start_core().await);
|
||||||
log::trace!("run core end");
|
log::trace!("run core end");
|
||||||
@ -56,37 +68,38 @@ impl CoreManager {
|
|||||||
|
|
||||||
/// 停止核心运行
|
/// 停止核心运行
|
||||||
pub async fn stop_core(&self) -> Result<()> {
|
pub async fn stop_core(&self) -> Result<()> {
|
||||||
let mut running = self.running.lock().await;
|
log::info!(target: "app", "Stopping core");
|
||||||
|
|
||||||
if !*running {
|
|
||||||
log::debug!("core is not running");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭tun模式
|
// 关闭tun模式
|
||||||
let mut disable = Mapping::new();
|
let mut disable = Mapping::new();
|
||||||
let mut tun = Mapping::new();
|
let mut tun = Mapping::new();
|
||||||
tun.insert("enable".into(), false.into());
|
tun.insert("enable".into(), false.into());
|
||||||
disable.insert("tun".into(), tun.into());
|
disable.insert("tun".into(), tun.into());
|
||||||
log::debug!(target: "app", "disable tun mode");
|
log::debug!(target: "app", "Disabling TUN mode");
|
||||||
log_err!(clash_api::patch_configs(&disable).await);
|
let _ = clash_api::patch_configs(&disable).await;
|
||||||
|
|
||||||
// 服务模式
|
// 直接尝试停止服务,不预先检查状态
|
||||||
if service::check_service().await.is_ok() {
|
log::info!(target: "app", "Attempting to stop service");
|
||||||
log::info!(target: "app", "stop the core by service");
|
let _ = service::stop_core_by_service().await;
|
||||||
service::stop_core_by_service().await?;
|
|
||||||
|
// 设置运行状态
|
||||||
|
*self.running.lock().await = false;
|
||||||
|
|
||||||
|
// 释放进程锁
|
||||||
|
let mut process_lock = self.process_lock.lock().await;
|
||||||
|
if let Some(lock) = process_lock.take() {
|
||||||
|
log::info!(target: "app", "Releasing process lock");
|
||||||
|
let _ = lock.release();
|
||||||
}
|
}
|
||||||
*running = false;
|
|
||||||
|
log::info!(target: "app", "Core stopped successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 启动核心
|
/// 启动核心
|
||||||
pub async fn start_core(&self) -> Result<()> {
|
pub async fn start_core(&self) -> Result<()> {
|
||||||
let mut running = self.running.lock().await;
|
// 检查端口占用
|
||||||
if *running {
|
self.health_checker.check_ports().await?;
|
||||||
log::info!("core is running");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let config_path = Config::generate_file(ConfigType::Run)?;
|
let config_path = Config::generate_file(ConfigType::Run)?;
|
||||||
|
|
||||||
@ -95,12 +108,23 @@ impl CoreManager {
|
|||||||
log::info!(target: "app", "try to run core in service mode");
|
log::info!(target: "app", "try to run core in service mode");
|
||||||
service::run_core_by_service(&config_path).await?;
|
service::run_core_by_service(&config_path).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动健康检查
|
||||||
|
let checker = Arc::new(self.health_checker.clone());
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
sleep(Duration::from_secs(30)).await;
|
||||||
|
if let Err(e) = checker.check_service_health().await {
|
||||||
|
log::error!(target: "app", "Health check failed: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 流量订阅
|
// 流量订阅
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
log_err!(Tray::global().subscribe_traffic().await);
|
log_err!(Tray::global().subscribe_traffic().await);
|
||||||
|
|
||||||
*running = true;
|
*self.running.lock().await = true;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
src-tauri/src/core/health_check.rs
Normal file
53
src-tauri/src/core/health_check.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use sysinfo::{Pid, System};
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::core::service;
|
||||||
|
use port_scanner::local_port_available;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HealthChecker;
|
||||||
|
|
||||||
|
impl HealthChecker {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_ports(&self) -> Result<()> {
|
||||||
|
let verge = Config::verge();
|
||||||
|
let verge_config = verge.latest();
|
||||||
|
let mixed_port = verge_config.verge_mixed_port.unwrap_or(7897);
|
||||||
|
let socks_port = verge_config.verge_socks_port.unwrap_or(7890);
|
||||||
|
let http_port = verge_config.verge_port.unwrap_or(7891);
|
||||||
|
|
||||||
|
if !local_port_available(mixed_port) {
|
||||||
|
bail!("Mixed port {} is already in use", mixed_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if verge_config.verge_socks_enabled.unwrap_or(true) && !local_port_available(socks_port) {
|
||||||
|
bail!("Socks port {} is already in use", socks_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if verge_config.verge_http_enabled.unwrap_or(true) && !local_port_available(http_port) {
|
||||||
|
bail!("Http port {} is already in use", http_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_service_health(&self) -> Result<()> {
|
||||||
|
if let Ok(response) = service::check_service().await {
|
||||||
|
if let Some(body) = response.data {
|
||||||
|
let sys = System::new_all();
|
||||||
|
if let Ok(pid) = body.bin_path.parse::<u32>() {
|
||||||
|
if let Some(process) = sys.process(Pid::from(pid as usize)) {
|
||||||
|
if !process.name().to_string_lossy().contains("mihomo") {
|
||||||
|
log::warn!(target: "app", "Found non-mihomo process using service port");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -9,5 +9,6 @@ pub mod sysopt;
|
|||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod tray;
|
pub mod tray;
|
||||||
pub mod win_uwp;
|
pub mod win_uwp;
|
||||||
|
pub mod process_lock;
|
||||||
|
pub mod health_check;
|
||||||
pub use self::core::*;
|
pub use self::core::*;
|
||||||
|
191
src-tauri/src/core/process_lock.rs
Normal file
191
src-tauri/src/core/process_lock.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use sysinfo::{Pid, System, Signal};
|
||||||
|
use crate::utils::dirs;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
const TERM_WAIT: Duration = Duration::from_millis(500);
|
||||||
|
const KILL_WAIT: Duration = Duration::from_millis(500);
|
||||||
|
const FINAL_WAIT: Duration = Duration::from_millis(1000);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProcessLock {
|
||||||
|
pid_file: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessLock {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let pid_file = dirs::app_home_dir()?.join("mihomo.pid");
|
||||||
|
println!("Creating ProcessLock with PID file: {:?}", pid_file);
|
||||||
|
log::info!(target: "app", "Creating ProcessLock with PID file: {:?}", pid_file);
|
||||||
|
Ok(Self { pid_file })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_target_process(process_name: &str) -> bool {
|
||||||
|
(process_name.contains("mihomo") || process_name.contains("clash"))
|
||||||
|
&& !process_name.contains("clash-verge")
|
||||||
|
&& !process_name.contains("clash.meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_process(pid: Pid, process: &sysinfo::Process) -> bool {
|
||||||
|
let process_name = process.name().to_string_lossy().to_lowercase();
|
||||||
|
let process_pid = pid.as_u32();
|
||||||
|
|
||||||
|
println!("Terminating clash core process (PID: {}, Name: {})", process_pid, process_name);
|
||||||
|
log::info!(target: "app", "Terminating clash core process (PID: {}, Name: {})", process_pid, process_name);
|
||||||
|
|
||||||
|
// 首先尝试正常终止
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
println!("Sending SIGTERM to process {}", process_pid);
|
||||||
|
log::info!(target: "app", "Sending SIGTERM to process {}", process_pid);
|
||||||
|
let _ = process.kill_with(Signal::Term);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
println!("Killing process {}", process_pid);
|
||||||
|
log::info!(target: "app", "Killing process {}", process_pid);
|
||||||
|
process.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(TERM_WAIT);
|
||||||
|
|
||||||
|
// 检查进程是否还在运行
|
||||||
|
let mut new_sys = System::new();
|
||||||
|
new_sys.refresh_all();
|
||||||
|
if let Some(p) = new_sys.process(pid) {
|
||||||
|
println!("Process {} still running, trying force kill", process_pid);
|
||||||
|
log::info!(target: "app", "Process {} still running, trying force kill", process_pid);
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
println!("Sending SIGKILL to process {}", process_pid);
|
||||||
|
log::info!(target: "app", "Sending SIGKILL to process {}", process_pid);
|
||||||
|
let _ = p.kill_with(Signal::Kill);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
println!("Force killing process {}", process_pid);
|
||||||
|
log::info!(target: "app", "Force killing process {}", process_pid);
|
||||||
|
p.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(KILL_WAIT);
|
||||||
|
|
||||||
|
// 再次检查进程是否存在
|
||||||
|
new_sys.refresh_all();
|
||||||
|
if new_sys.process(pid).is_some() {
|
||||||
|
println!("Failed to terminate process {}", process_pid);
|
||||||
|
log::error!(target: "app", "Failed to terminate process {}", process_pid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Process {} has been terminated", process_pid);
|
||||||
|
log::info!(target: "app", "Process {} has been terminated", process_pid);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_other_processes(include_current: bool) -> Result<()> {
|
||||||
|
println!("Starting process cleanup (include_current: {})", include_current);
|
||||||
|
log::info!(target: "app", "Starting process cleanup (include_current: {})", include_current);
|
||||||
|
|
||||||
|
let mut sys = System::new();
|
||||||
|
sys.refresh_all();
|
||||||
|
|
||||||
|
let current_pid = std::process::id();
|
||||||
|
println!("Current process ID: {}", current_pid);
|
||||||
|
log::info!(target: "app", "Current process ID: {}", current_pid);
|
||||||
|
|
||||||
|
let mut killed = false;
|
||||||
|
let mut failed_kills = Vec::new();
|
||||||
|
|
||||||
|
for (pid, process) in sys.processes() {
|
||||||
|
let process_name = process.name().to_string_lossy().to_lowercase();
|
||||||
|
|
||||||
|
if Self::is_target_process(&process_name) {
|
||||||
|
let process_pid = pid.as_u32();
|
||||||
|
if include_current || process_pid != current_pid {
|
||||||
|
if !Self::kill_process(*pid, process) {
|
||||||
|
failed_kills.push((process_pid, process_name.clone()));
|
||||||
|
}
|
||||||
|
killed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if killed {
|
||||||
|
std::thread::sleep(FINAL_WAIT);
|
||||||
|
|
||||||
|
// 最终检查
|
||||||
|
let mut final_sys = System::new();
|
||||||
|
final_sys.refresh_all();
|
||||||
|
|
||||||
|
let remaining: Vec<_> = final_sys.processes()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, process)| {
|
||||||
|
let name = process.name().to_string_lossy().to_lowercase();
|
||||||
|
Self::is_target_process(&name)
|
||||||
|
})
|
||||||
|
.map(|(pid, process)| {
|
||||||
|
(pid.as_u32(), process.name().to_string_lossy().to_string())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !remaining.is_empty() {
|
||||||
|
log::error!(target: "app", "Failed to terminate some processes: {:?}", remaining);
|
||||||
|
bail!("Failed to terminate processes: {:?}", remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Process cleanup completed");
|
||||||
|
log::info!(target: "app", "Process cleanup completed");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire(&self) -> Result<()> {
|
||||||
|
println!("Attempting to acquire process lock");
|
||||||
|
log::info!(target: "app", "Attempting to acquire process lock");
|
||||||
|
|
||||||
|
// 首先尝试终止其他进程
|
||||||
|
Self::kill_other_processes(false)?;
|
||||||
|
|
||||||
|
if self.pid_file.exists() {
|
||||||
|
fs::remove_file(&self.pid_file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(&self.pid_file, std::process::id().to_string())?;
|
||||||
|
println!("Process lock acquired successfully");
|
||||||
|
log::info!(target: "app", "Process lock acquired successfully");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(&self) -> Result<()> {
|
||||||
|
println!("Starting release process");
|
||||||
|
log::info!(target: "app", "Starting release process");
|
||||||
|
|
||||||
|
Self::kill_other_processes(true)?;
|
||||||
|
|
||||||
|
if self.pid_file.exists() {
|
||||||
|
println!("Removing PID file");
|
||||||
|
log::info!(target: "app", "Removing PID file");
|
||||||
|
fs::remove_file(&self.pid_file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Release process completed");
|
||||||
|
log::info!(target: "app", "Release process completed");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ProcessLock {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// 只在 PID 文件还存在时执行释放
|
||||||
|
if self.pid_file.exists() {
|
||||||
|
println!("ProcessLock being dropped");
|
||||||
|
log::info!(target: "app", "ProcessLock being dropped");
|
||||||
|
let _ = self.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -161,20 +161,49 @@ pub async fn reinstall_service() -> Result<()> {
|
|||||||
|
|
||||||
/// check the windows service status
|
/// check the windows service status
|
||||||
pub async fn check_service() -> Result<JsonResponse> {
|
pub async fn check_service() -> Result<JsonResponse> {
|
||||||
|
log::info!(target: "app", "Checking service status");
|
||||||
|
println!("Checking service status");
|
||||||
|
|
||||||
let url = format!("{SERVICE_URL}/get_clash");
|
let url = format!("{SERVICE_URL}/get_clash");
|
||||||
let response = reqwest::ClientBuilder::new()
|
log::debug!(target: "app", "Sending request to {}", url);
|
||||||
|
println!("Sending request to {}", url);
|
||||||
|
|
||||||
|
let client = reqwest::ClientBuilder::new()
|
||||||
.no_proxy()
|
.no_proxy()
|
||||||
.timeout(Duration::from_secs(3))
|
.timeout(Duration::from_secs(3))
|
||||||
.build()?
|
.build()?;
|
||||||
.get(url)
|
|
||||||
.send()
|
// 重试3次
|
||||||
.await
|
for i in 0..3 {
|
||||||
.context("failed to connect to the Clash Verge Service")?
|
match client.get(&url).send().await {
|
||||||
.json::<JsonResponse>()
|
Ok(resp) => {
|
||||||
.await
|
match resp.json::<JsonResponse>().await {
|
||||||
.context("failed to parse the Clash Verge Service response")?;
|
Ok(json) => {
|
||||||
|
log::info!(target: "app", "Service check response: {:?}", json);
|
||||||
Ok(response)
|
println!("Service check response: {:?}", json);
|
||||||
|
return Ok(json);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to parse service response (attempt {}): {}", i + 1, e);
|
||||||
|
println!("Failed to parse service response (attempt {}): {}", i + 1, e);
|
||||||
|
if i == 2 {
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to connect to service (attempt {}): {}", i + 1, e);
|
||||||
|
println!("Failed to connect to service (attempt {}): {}", i + 1, e);
|
||||||
|
if i == 2 {
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("Failed to check service after 3 attempts")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// start the clash by service
|
/// start the clash by service
|
||||||
@ -219,14 +248,32 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
|||||||
|
|
||||||
/// stop the clash by service
|
/// stop the clash by service
|
||||||
pub(super) async fn stop_core_by_service() -> Result<()> {
|
pub(super) async fn stop_core_by_service() -> Result<()> {
|
||||||
|
log::info!(target: "app", "Attempting to stop core through service");
|
||||||
|
|
||||||
let url = format!("{SERVICE_URL}/stop_clash");
|
let url = format!("{SERVICE_URL}/stop_clash");
|
||||||
let _ = reqwest::ClientBuilder::new()
|
let client = reqwest::ClientBuilder::new()
|
||||||
.no_proxy()
|
.no_proxy()
|
||||||
.build()?
|
.timeout(Duration::from_secs(3))
|
||||||
.post(url)
|
.build()?;
|
||||||
.send()
|
|
||||||
.await
|
// 重试2次
|
||||||
.context("failed to connect to the Clash Verge Service")?;
|
for i in 0..2 {
|
||||||
|
match client.post(&url).send().await {
|
||||||
Ok(())
|
Ok(_) => {
|
||||||
|
log::info!(target: "app", "Successfully sent stop request to service");
|
||||||
|
// 等待服务停止
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(target: "app", "Failed to send stop request (attempt {}): {}", i + 1, e);
|
||||||
|
if i == 1 {
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("Failed to stop service after 2 attempts")
|
||||||
}
|
}
|
||||||
|
@ -135,10 +135,36 @@ pub fn toggle_tun_mode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn quit(code: Option<i32>) {
|
pub fn quit(code: Option<i32>) {
|
||||||
|
println!("Starting application quit process");
|
||||||
|
log::info!(target: "app", "Starting application quit process");
|
||||||
|
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
handle::Handle::global().set_is_exiting();
|
handle::Handle::global().set_is_exiting();
|
||||||
resolve::resolve_reset();
|
|
||||||
log_err!(handle::Handle::global().get_window().unwrap().close());
|
// 立即关闭窗口,让用户感知到退出开始
|
||||||
|
if let Some(window) = handle::Handle::global().get_window() {
|
||||||
|
let _ = window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后台执行所有清理工作
|
||||||
|
let app_handle_clone = app_handle.clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
// 1. 发送停止内核指令
|
||||||
|
let _ = CoreManager::global().stop_core().await;
|
||||||
|
|
||||||
|
// 2. 重置系统代理
|
||||||
|
resolve::resolve_reset();
|
||||||
|
|
||||||
|
// 3. 保存窗口状态
|
||||||
|
let _ = app_handle_clone.save_window_state(StateFlags::default());
|
||||||
|
|
||||||
|
println!("Cleanup tasks completed in background");
|
||||||
|
log::info!(target: "app", "Cleanup tasks completed in background");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 主线程立即退出
|
||||||
|
println!("Exiting application with code: {:?}", code);
|
||||||
|
log::info!(target: "app", "Exiting application with code: {:?}", code);
|
||||||
app_handle.exit(code.unwrap_or(0));
|
app_handle.exit(code.unwrap_or(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,47 +15,67 @@ use tauri_plugin_shell::ShellExt;
|
|||||||
|
|
||||||
/// initialize this instance's log file
|
/// initialize this instance's log file
|
||||||
fn init_log() -> Result<()> {
|
fn init_log() -> Result<()> {
|
||||||
|
println!("Starting log initialization...");
|
||||||
|
|
||||||
let log_dir = dirs::app_logs_dir()?;
|
let log_dir = dirs::app_logs_dir()?;
|
||||||
if !log_dir.exists() {
|
if !log_dir.exists() {
|
||||||
|
println!("Creating log directory: {:?}", log_dir);
|
||||||
let _ = fs::create_dir_all(&log_dir);
|
let _ = fs::create_dir_all(&log_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
let log_level = Config::verge().data().get_log_level();
|
let log_level = Config::verge().data().get_log_level();
|
||||||
|
println!("Current log level: {:?}", log_level);
|
||||||
|
|
||||||
if log_level == LevelFilter::Off {
|
if log_level == LevelFilter::Off {
|
||||||
|
println!("Logging is disabled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
let log_pattern = match log_level {
|
let log_pattern = match log_level {
|
||||||
LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}",
|
LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}",
|
||||||
_ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}",
|
_ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}",
|
||||||
};
|
};
|
||||||
|
|
||||||
let encode = Box::new(PatternEncoder::new(log_pattern));
|
let encode = Box::new(PatternEncoder::new(log_pattern));
|
||||||
|
|
||||||
let stdout = ConsoleAppender::builder().encoder(encode.clone()).build();
|
let stdout = ConsoleAppender::builder().encoder(encode.clone()).build();
|
||||||
|
|
||||||
|
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);
|
||||||
|
println!("Log file path: {:?}", log_file);
|
||||||
|
|
||||||
let tofile = FileAppender::builder().encoder(encode).build(log_file)?;
|
let tofile = FileAppender::builder().encoder(encode).build(log_file)?;
|
||||||
|
|
||||||
let mut logger_builder = Logger::builder();
|
let mut logger_builder = Logger::builder();
|
||||||
let mut root_builder = Root::builder();
|
let mut root_builder = Root::builder();
|
||||||
|
|
||||||
let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug;
|
let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug;
|
||||||
|
logger_builder = logger_builder.appenders(["stdout", "file"]);
|
||||||
logger_builder = logger_builder.appenders(["file"]);
|
root_builder = root_builder.appender("stdout");
|
||||||
if log_more {
|
if log_more {
|
||||||
root_builder = root_builder.appenders(["file"]);
|
root_builder = root_builder.appender("file");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (config, _) = log4rs::config::Config::builder()
|
println!("Building log config...");
|
||||||
|
let config = log4rs::config::Config::builder()
|
||||||
.appender(Appender::builder().build("stdout", Box::new(stdout)))
|
.appender(Appender::builder().build("stdout", Box::new(stdout)))
|
||||||
.appender(Appender::builder().build("file", Box::new(tofile)))
|
.appender(Appender::builder().build("file", Box::new(tofile)))
|
||||||
.logger(logger_builder.additive(false).build("app", log_level))
|
.logger(logger_builder.additive(false).build("app", log_level))
|
||||||
.build_lossy(root_builder.build(log_level));
|
.build(root_builder.build(log_level))
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to build log config: {}", e))?;
|
||||||
|
|
||||||
log4rs::init_config(config)?;
|
println!("Initializing log config...");
|
||||||
|
match log4rs::init_config(config) {
|
||||||
|
Ok(_) => println!("Log system initialized successfully"),
|
||||||
|
Err(e) => println!("Failed to initialize log system: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试日志系统
|
||||||
|
log::error!(target: "app", "Test error log message");
|
||||||
|
log::warn!(target: "app", "Test warning log message");
|
||||||
|
log::info!(target: "app", "Test info log message");
|
||||||
|
log::debug!(target: "app", "Test debug log message");
|
||||||
|
log::trace!(target: "app", "Test trace log message");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user