Squashed commit of the following:

commit 2a9f2f20e9c6d88c2f96fd40589740e1f236f64a
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 11:07:31 2025 +0800

    fix: improve message handling in CommandEvent logging with CompactString

commit c77fc18accefeaf471594035d61bd13e235c87d6
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 10:47:16 2025 +0800

    fix: optimize shared writer locking in CommandEvent handling

commit d5286ee5f1612f17b7a97eead84d430669816d98
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 10:30:19 2025 +0800

    feat: integrate CompactString for improved logging and dependency management

commit 951fb2b120ce159c00dc57d43c5a519990f34cee
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 09:39:29 2025 +0800

    refactor: remove write_sidecar_log function and streamline logging in CommandEvent handling

commit fd48d66c55a2c62fd32741fd3c65cc06d4cc693f
Author: Tunglies <77394545+Tunglies@users.noreply.github.com>
Date:   Mon Oct 13 09:38:05 2025 +0800

    Revert "refactor(core): stabilize 'static backing for sidecar logging"

    This reverts commit fe7eb59f18.
This commit is contained in:
Tunglies
2025-10-13 11:08:44 +08:00
parent 51ba1d1e34
commit ca3fa869d5
6 changed files with 82 additions and 47 deletions

35
src-tauri/Cargo.lock generated
View File

@@ -962,6 +962,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]]
name = "castaway"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.40" version = "1.2.40"
@@ -1101,6 +1110,7 @@ dependencies = [
"chrono", "chrono",
"clash_verge_logger", "clash_verge_logger",
"clash_verge_service_ipc", "clash_verge_service_ipc",
"compact_str",
"console-subscriber", "console-subscriber",
"criterion", "criterion",
"dashmap 6.1.0", "dashmap 6.1.0",
@@ -1232,7 +1242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1245,6 +1255,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "compact_str"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
dependencies = [
"castaway 0.2.4",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"serde",
"static_assertions",
]
[[package]] [[package]]
name = "concat-idents" name = "concat-idents"
version = "1.1.5" version = "1.1.5"
@@ -3196,7 +3221,7 @@ dependencies = [
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.5.10", "socket2 0.6.0",
"system-configuration", "system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
@@ -3648,7 +3673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
dependencies = [ dependencies = [
"async-channel 1.9.0", "async-channel 1.9.0",
"castaway", "castaway 0.1.2",
"crossbeam-utils", "crossbeam-utils",
"curl", "curl",
"curl-sys", "curl-sys",
@@ -5644,7 +5669,7 @@ dependencies = [
"quinn-udp", "quinn-udp",
"rustc-hash", "rustc-hash",
"rustls", "rustls",
"socket2 0.5.10", "socket2 0.6.0",
"thiserror 2.0.17", "thiserror 2.0.17",
"tokio", "tokio",
"tracing", "tracing",
@@ -5681,7 +5706,7 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
"once_cell", "once_cell",
"socket2 0.5.10", "socket2 0.6.0",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]

View File

@@ -88,6 +88,7 @@ clash_verge_logger = { version = "0.1.0", git = "https://github.com/clash-verge-
clash_verge_service_ipc = { version = "2.0.15", features = [ clash_verge_service_ipc = { version = "2.0.15", features = [
"client", "client",
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" } ], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }
compact_str = { version = "0.9.0", features = ["serde"] }
# clash_verge_service_ipc = { version = "2.0.14", features = [ # clash_verge_service_ipc = { version = "2.0.14", features = [
# "client", # "client",
# ], path = "../../clash-verge-service-ipc" } # ], path = "../../clash-verge-service-ipc" }

View File

@@ -6,6 +6,7 @@ use crate::{
core::{CoreManager, handle}, core::{CoreManager, handle},
}; };
use crate::{config::*, feat, logging, utils::logging::Type, wrap_err}; use crate::{config::*, feat, logging, utils::logging::Type, wrap_err};
use compact_str::CompactString;
use serde_yaml_ng::Mapping; use serde_yaml_ng::Mapping;
/// 复制Clash环境变量 /// 复制Clash环境变量
@@ -285,7 +286,7 @@ pub async fn validate_dns_config() -> CmdResult<(bool, String)> {
} }
#[tauri::command] #[tauri::command]
pub async fn get_clash_logs() -> CmdResult<VecDeque<String>> { pub async fn get_clash_logs() -> CmdResult<VecDeque<CompactString>> {
let logs = CoreManager::global() let logs = CoreManager::global()
.get_clash_logs() .get_clash_logs()
.await .await

View File

@@ -2,7 +2,7 @@ use crate::AsyncHandler;
use crate::core::logger::ClashLogger; use crate::core::logger::ClashLogger;
use crate::process::CommandChildGuard; use crate::process::CommandChildGuard;
use crate::utils::init::sidecar_writer; use crate::utils::init::sidecar_writer;
use crate::utils::logging::SharedWriter; use crate::utils::logging::{SharedWriter, write_sidecar_log};
use crate::{ use crate::{
config::*, config::*,
core::{ core::{
@@ -17,9 +17,9 @@ use crate::{
}, },
}; };
use anyhow::Result; use anyhow::Result;
use compact_str::CompactString;
use flexi_logger::DeferredNow; use flexi_logger::DeferredNow;
use flexi_logger::writers::LogWriter; use log::Level;
use log::Record;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::{fmt, path::PathBuf, sync::Arc}; use std::{fmt, path::PathBuf, sync::Arc};
@@ -58,29 +58,6 @@ impl fmt::Display for RunningMode {
use crate::config::IVerge; use crate::config::IVerge;
fn write_sidecar_log(
writer: &dyn LogWriter,
now: &mut DeferredNow,
level: log::Level,
message: String,
) -> String {
let boxed = message.into_boxed_str();
let leaked: &'static mut str = Box::leak(boxed);
let leaked_ptr = leaked as *mut str;
{
let _ = writer.write(
now,
&Record::builder()
.args(format_args!("{}", &*leaked))
.level(level)
.target("sidecar")
.build(),
);
}
// SAFETY: `leaked` originated from `Box::leak` above; reboxing frees it immediately after use.
unsafe { String::from(Box::from_raw(leaked_ptr)) }
}
impl CoreManager { impl CoreManager {
/// 检查文件是否为脚本文件 /// 检查文件是否为脚本文件
fn is_script_file(&self, path: &str) -> Result<bool> { fn is_script_file(&self, path: &str) -> Result<bool> {
@@ -767,30 +744,35 @@ impl CoreManager {
AsyncHandler::spawn(|| async move { AsyncHandler::spawn(|| async move {
while let Some(event) = rx.recv().await { while let Some(event) = rx.recv().await {
let w = shared_writer.lock().await;
match event { match event {
tauri_plugin_shell::process::CommandEvent::Stdout(line) => { tauri_plugin_shell::process::CommandEvent::Stdout(line) => {
let mut now = DeferredNow::default(); let mut now = DeferredNow::default();
let message = String::from_utf8_lossy(&line).into_owned(); let message =
let message = write_sidecar_log(&*w, &mut now, log::Level::Error, message); CompactString::from(String::from_utf8_lossy(&line).into_owned());
let w = shared_writer.lock().await;
write_sidecar_log(w, &mut now, Level::Error, &message);
ClashLogger::global().append_log(message); ClashLogger::global().append_log(message);
} }
tauri_plugin_shell::process::CommandEvent::Stderr(line) => { tauri_plugin_shell::process::CommandEvent::Stderr(line) => {
let mut now = DeferredNow::default(); let mut now = DeferredNow::default();
let message = String::from_utf8_lossy(&line).into_owned(); let message =
let message = write_sidecar_log(&*w, &mut now, log::Level::Error, message); CompactString::from(String::from_utf8_lossy(&line).into_owned());
let w = shared_writer.lock().await;
write_sidecar_log(w, &mut now, Level::Error, &message);
ClashLogger::global().append_log(message); ClashLogger::global().append_log(message);
} }
tauri_plugin_shell::process::CommandEvent::Terminated(term) => { tauri_plugin_shell::process::CommandEvent::Terminated(term) => {
let mut now = DeferredNow::default(); let mut now = DeferredNow::default();
let message = if let Some(code) = term.code { let message = if let Some(code) = term.code {
format!("Process terminated with code: {}", code) CompactString::from(format!("Process terminated with code: {}", code))
} else if let Some(signal) = term.signal { } else if let Some(signal) = term.signal {
format!("Process terminated by signal: {}", signal) CompactString::from(format!("Process terminated by signal: {}", signal))
} else { } else {
"Process terminated".to_string() CompactString::from("Process terminated")
}; };
write_sidecar_log(&*w, &mut now, log::Level::Info, message); let w = shared_writer.lock().await;
write_sidecar_log(w, &mut now, Level::Info, &message);
ClashLogger::global().clear_logs();
break; break;
} }
_ => {} _ => {}
@@ -899,10 +881,15 @@ impl CoreManager {
Ok(()) Ok(())
} }
pub async fn get_clash_logs(&self) -> Result<VecDeque<String>> { pub async fn get_clash_logs(&self) -> Result<VecDeque<CompactString>> {
logging!(info, Type::Core, "get clash logs"); logging!(info, Type::Core, "get clash logs");
let logs = match self.get_running_mode() { let logs = match self.get_running_mode() {
RunningMode::Service => service::get_clash_logs_by_service().await?, // TODO 服务端也完成 CompactString 迁移
RunningMode::Service => service::get_clash_logs_by_service()
.await?
.into_iter()
.map(CompactString::from)
.collect::<VecDeque<CompactString>>(),
RunningMode::Sidecar => ClashLogger::global().get_logs().clone(), RunningMode::Sidecar => ClashLogger::global().get_logs().clone(),
_ => VecDeque::new(), _ => VecDeque::new(),
}; };

View File

@@ -1,12 +1,13 @@
use std::{collections::VecDeque, sync::Arc}; use std::{collections::VecDeque, sync::Arc};
use compact_str::CompactString;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::{RwLock, RwLockReadGuard}; use parking_lot::{RwLock, RwLockReadGuard};
const LOGS_QUEUE_LEN: usize = 100; const LOGS_QUEUE_LEN: usize = 100;
pub struct ClashLogger { pub struct ClashLogger {
logs: Arc<RwLock<VecDeque<String>>>, logs: Arc<RwLock<VecDeque<CompactString>>>,
} }
impl ClashLogger { impl ClashLogger {
@@ -18,11 +19,11 @@ impl ClashLogger {
}) })
} }
pub fn get_logs(&self) -> RwLockReadGuard<'_, VecDeque<String>> { pub fn get_logs(&self) -> RwLockReadGuard<'_, VecDeque<CompactString>> {
self.logs.read() self.logs.read()
} }
pub fn append_log(&self, text: String) { pub fn append_log(&self, text: CompactString) {
let mut logs = self.logs.write(); let mut logs = self.logs.write();
if logs.len() > LOGS_QUEUE_LEN { if logs.len() > LOGS_QUEUE_LEN {
logs.pop_front(); logs.pop_front();

View File

@@ -1,10 +1,13 @@
use compact_str::CompactString;
use flexi_logger::writers::FileLogWriter; use flexi_logger::writers::FileLogWriter;
use flexi_logger::writers::LogWriter;
#[cfg(not(feature = "tauri-dev"))] #[cfg(not(feature = "tauri-dev"))]
use flexi_logger::{DeferredNow, filter::LogLineFilter}; use flexi_logger::{DeferredNow, filter::LogLineFilter};
use log::Level;
#[cfg(not(feature = "tauri-dev"))] #[cfg(not(feature = "tauri-dev"))]
use log::Record; use log::Record;
use std::{fmt, sync::Arc}; use std::{fmt, sync::Arc};
use tokio::sync::Mutex; use tokio::sync::{Mutex, MutexGuard};
pub type SharedWriter = Arc<Mutex<FileLogWriter>>; pub type SharedWriter = Arc<Mutex<FileLogWriter>>;
@@ -134,6 +137,23 @@ macro_rules! logging_error {
}; };
} }
pub fn write_sidecar_log(
writer: MutexGuard<'_, FileLogWriter>,
now: &mut DeferredNow,
level: Level,
message: &CompactString,
) {
let args = format_args!("{}", message);
let record = Record::builder()
.args(args)
.level(level)
.target("sidecar")
.build();
let _ = writer.write(now, &record);
}
#[cfg(not(feature = "tauri-dev"))] #[cfg(not(feature = "tauri-dev"))]
pub struct NoModuleFilter<'a>(pub &'a [&'a str]); pub struct NoModuleFilter<'a>(pub &'a [&'a str]);