mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-01 03:42:07 +08:00
refactor(log): in-place writer lifecycle and span-based core observability (#7889)
* refactor(log): migrate logging to tracing-estuary pipeline Swap flexi_logger + clash-verge-logger wiring for the tracing-estuary crate: buffered file sink with explicit panic/exit drains, live level switching, and tokio-trace/chrome-trace behind crate features. AsyncLogger moves in-house (VecDeque ring); file format and rotation stay unchanged. * fix(log): reset sidecar writer in place and re-pin service-ipc v2.6.7 Rebuilding the sidecar FileLogWriter on config changes leaked flexi's unstoppable flusher thread plus an fd each time; the writer is now flushed and reset in place, matching what the service crate does. get_clash_logs_by_service drops the CompactString conversion layer now that service-ipc v2.6.7 returns plain Vec<String>, and AsyncLogger is renamed LogRing to match. * refactor(log): span-based observability and log noise cleanup Instrument spans carry the structure flat lines never could: the core-start spine (mode, readiness/owner generations, pid, config file), proxy apply and clear routes with guard generations, service IPC sessions with refusal codes and outcomes, profile-switch terminal outcomes, subscription fetch strategies, core upgrades with per-proxy version probes, backup flows, timer tasks, config generation uids, and the startup setup flow; running-mode changes emit one guarded transition event each. ~90 hand-written noise lines are gone or demoted (entry+success pairs, polled getter echoes, phase brackets, window sub-steps, platform install chatter); errors keep their anyhow chain, operation name, and item id, and swallowed retry results surface per attempt.
This commit is contained in:
@@ -11,7 +11,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use compact_str::CompactString;
|
||||
use serde_yaml_ng::Mapping;
|
||||
use smartstring::alias::String;
|
||||
use tokio::fs;
|
||||
@@ -57,7 +56,6 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<CommandFa
|
||||
|
||||
match CoreManager::global().restart_core().await {
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Core, "core changed and restarted to {clash_core}");
|
||||
handle::Handle::notice_message("config_core::change_success", clash_core);
|
||||
handle::Handle::refresh_clash();
|
||||
Ok(None)
|
||||
@@ -66,7 +64,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<CommandFa
|
||||
let failed = err.context("core changed but failed to restart");
|
||||
let error_msg: String = format!("{failed:#}").into();
|
||||
handle::Handle::notice_message("config_core::change_error", error_msg.clone());
|
||||
logging!(error, Type::Core, "{error_msg}");
|
||||
logging!(error, Type::Core, "core changed but failed to restart: {error_msg}");
|
||||
Ok(Some(proxy_aware_coded_error(&failed, "CORE_CHANGE_FAILED")))
|
||||
}
|
||||
}
|
||||
@@ -108,7 +106,7 @@ pub async fn test_delay(url: String) -> CmdResult<u32> {
|
||||
let result = match feat::test_delay(url).await {
|
||||
Ok(delay) => delay,
|
||||
Err(e) => {
|
||||
logging!(error, Type::Cmd, "{}", e);
|
||||
logging!(error, Type::Cmd, "get clash delay failed: {e:#}");
|
||||
10000u32
|
||||
}
|
||||
};
|
||||
@@ -147,8 +145,6 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
logging!(error, Type::Config, "Failed to parse DNS config: {e}");
|
||||
})?;
|
||||
|
||||
logging!(info, Type::Config, "Applying DNS config from file");
|
||||
|
||||
let mut patch = serde_yaml_ng::Mapping::new();
|
||||
patch.insert("dns".into(), patch_config.into());
|
||||
|
||||
@@ -207,7 +203,7 @@ pub async fn validate_dns_config() -> CmdResult<ValidationOutcome> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_logs() -> CmdResult<Vec<CompactString>> {
|
||||
pub async fn get_clash_logs() -> CmdResult<Vec<std::string::String>> {
|
||||
let logs = CoreManager::global().get_clash_logs().await.unwrap_or_default();
|
||||
Ok(logs)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::CmdResult;
|
||||
use crate::cmd::StringifyErr as _;
|
||||
use crate::core::{proxy_control, sysopt::Sysopt};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
|
||||
use gethostname::gethostname;
|
||||
use network_interface::NetworkInterface;
|
||||
use serde_yaml_ng::Mapping;
|
||||
@@ -10,8 +10,6 @@ use tauri_plugin_clash_verge_sysinfo;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_sys_proxy() -> CmdResult<Mapping> {
|
||||
logging!(debug, Type::Network, "异步获取系统代理配置");
|
||||
|
||||
Sysopt::global().wait_idle().await;
|
||||
// With no network service there is no proxy configured anywhere, which reads as disabled.
|
||||
let sys_proxy = match Sysproxy::get_system_proxy() {
|
||||
@@ -29,15 +27,6 @@ pub async fn get_sys_proxy() -> CmdResult<Mapping> {
|
||||
map.insert("enable".into(), (*enable).into());
|
||||
map.insert("server".into(), format!("{}:{}", host, port).into());
|
||||
map.insert("bypass".into(), bypass.as_str().into());
|
||||
|
||||
logging!(
|
||||
debug,
|
||||
Type::Network,
|
||||
"返回系统代理配置: enable={}, {}:{}",
|
||||
sys_proxy.enable,
|
||||
sys_proxy.host,
|
||||
sys_proxy.port
|
||||
);
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
@@ -53,14 +42,6 @@ pub async fn get_auto_proxy() -> CmdResult<Mapping> {
|
||||
let mut map = Mapping::new();
|
||||
map.insert("enable".into(), (*enable).into());
|
||||
map.insert("url".into(), url.as_str().into());
|
||||
|
||||
logging!(
|
||||
debug,
|
||||
Type::Network,
|
||||
"返回自动代理配置(缓存): enable={}, url={}",
|
||||
auto_proxy.enable,
|
||||
auto_proxy.url
|
||||
);
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ fn profile_import_error(err: &anyhow::Error) -> std::string::String {
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_profiles() -> CmdResult<SharedDraft<IProfiles>> {
|
||||
logging!(debug, Type::Cmd, "获取配置文件列表");
|
||||
let draft = Config::profiles().await;
|
||||
let data = draft.data_arc();
|
||||
Ok(data)
|
||||
@@ -57,45 +56,39 @@ pub async fn enhance_profiles() -> CmdResult<ValidationOutcome> {
|
||||
Ok(outcome)
|
||||
}
|
||||
Err(e) => {
|
||||
logging!(error, Type::Cmd, "{}", e);
|
||||
logging!(error, Type::Cmd, "enhance profiles failed: {e:#}");
|
||||
Err(coded_error("PROFILE_ENHANCE_FAILED", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[tracing::instrument(skip_all, level = "info", fields(url = %help::mask_url(&url), uid = tracing::field::Empty))]
|
||||
pub async fn import_profile(url: std::string::String, option: Option<PrfOption>) -> CmdResult {
|
||||
logging!(info, Type::Cmd, "[导入订阅] 开始导入: {}", help::mask_url(&url));
|
||||
|
||||
let item = &mut match PrfItem::from_url(&url, None, None, option.as_ref()).await {
|
||||
Ok(it) => {
|
||||
logging!(info, Type::Cmd, "[导入订阅] 下载完成,开始保存配置");
|
||||
it
|
||||
}
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
logging!(error, Type::Cmd, "[导入订阅] 下载失败: {}", e);
|
||||
logging!(error, Type::Cmd, "[导入订阅] 下载失败: {e:#}");
|
||||
return Err(coded_error("PROFILE_IMPORT_FAILED", profile_import_error(&e)));
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = profiles_append_item_safe(item).await {
|
||||
logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {}", e);
|
||||
logging!(error, Type::Cmd, "[导入订阅] 保存配置失败: {e:#}");
|
||||
return Err(coded_error("PROFILE_IMPORT_FAILED", e));
|
||||
}
|
||||
|
||||
if let Err(e) = profiles_save_file_safe().await {
|
||||
logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {}", e);
|
||||
logging!(error, Type::Cmd, "[导入订阅] 保存配置文件失败: {e:#}");
|
||||
return Err(coded_error("PROFILE_IMPORT_FAILED", e));
|
||||
}
|
||||
logging!(info, Type::Cmd, "[导入订阅] 配置文件保存成功");
|
||||
logging_error!(Type::Timer, Timer::global().refresh().await);
|
||||
|
||||
if let Some(uid) = &item.uid {
|
||||
logging!(info, Type::Cmd, "[导入订阅] 发送配置变更通知: {}", uid);
|
||||
tracing::Span::current().record("uid", tracing::field::display(uid));
|
||||
handle::Handle::notify_profile_changed(uid);
|
||||
}
|
||||
|
||||
logging!(info, Type::Cmd, "[导入订阅] 导入完成: {}", help::mask_url(&url));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -103,11 +96,17 @@ pub async fn import_profile(url: std::string::String, option: Option<PrfOption>)
|
||||
pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
|
||||
match profiles_reorder_safe(&active_id, &over_id).await {
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Cmd, "重新排序配置文件");
|
||||
logging!(info, Type::Cmd, "重新排序配置文件: {} -> {}", active_id, over_id);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Cmd, "重新排序配置文件失败: {}", err);
|
||||
logging!(
|
||||
error,
|
||||
Type::Cmd,
|
||||
"重新排序配置文件失败: {} -> {}: {err:#}",
|
||||
active_id,
|
||||
over_id
|
||||
);
|
||||
Err(coded_error("PROFILE_REORDER_FAILED", err))
|
||||
}
|
||||
}
|
||||
@@ -122,7 +121,6 @@ pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResu
|
||||
.with_error_code("PROFILE_CREATE_FAILED")?;
|
||||
logging_error!(Type::Timer, Timer::global().refresh().await);
|
||||
if let Some(uid) = &item.uid {
|
||||
logging!(info, Type::Cmd, "[创建订阅] 发送配置变更通知: {}", uid);
|
||||
handle::Handle::notify_profile_changed(uid);
|
||||
}
|
||||
Ok(())
|
||||
@@ -136,7 +134,7 @@ pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResu
|
||||
match feat::update_profile(&index, option.as_ref(), true).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
logging!(error, Type::Cmd, "{}", e);
|
||||
logging!(error, Type::Cmd, "update profile failed: {e:#}");
|
||||
Err(coded_error("PROFILE_UPDATE_FAILED", e))
|
||||
}
|
||||
}
|
||||
@@ -183,16 +181,15 @@ pub async fn delete_profile(index: String) -> CmdResult {
|
||||
drop(profile_write_guard);
|
||||
|
||||
if let Err(e) = Tray::global().update_tooltip().await {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步更新托盘提示失败: {e}");
|
||||
logging!(warn, Type::Cmd, "异步更新托盘提示失败: {e:#}");
|
||||
}
|
||||
|
||||
if let Err(e) = Tray::global().update_menu().await {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步更新托盘菜单失败: {e}");
|
||||
logging!(warn, Type::Cmd, "异步更新托盘菜单失败: {e:#}");
|
||||
}
|
||||
if should_update {
|
||||
handle::Handle::refresh_clash();
|
||||
if let Some(current) = current.as_ref() {
|
||||
logging!(info, Type::Cmd, "[删除订阅] 发送配置变更通知: {}", current);
|
||||
handle::Handle::notify_profile_changed(current);
|
||||
}
|
||||
}
|
||||
@@ -212,10 +209,9 @@ async fn restore_previous_profile(prev_profile: &String) -> CmdResult<()> {
|
||||
Config::profiles().await.apply();
|
||||
crate::process::AsyncHandler::spawn(|| async move {
|
||||
if let Err(e) = profiles_save_file_safe().await {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步保存恢复配置文件失败: {e}");
|
||||
logging!(warn, Type::Cmd, "异步保存恢复配置文件失败: {e:#}");
|
||||
}
|
||||
});
|
||||
logging!(info, Type::Cmd, "成功恢复到之前的配置");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -244,13 +240,12 @@ async fn handle_success(current_value: Option<&String>) -> CmdResult<ValidationO
|
||||
profiles::activate_selected_nodes();
|
||||
|
||||
if let Err(e) = profiles_save_file_safe().await {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步保存配置文件失败: {e}");
|
||||
logging!(warn, Type::Cmd, "异步保存配置文件失败: {e:#}");
|
||||
}
|
||||
|
||||
if let Some(current) = current_value
|
||||
&& WindowManager::get_main_window().is_some()
|
||||
{
|
||||
logging!(info, Type::Cmd, "向前端发送配置变更事件: {}", current);
|
||||
handle::Handle::notify_profile_changed(current);
|
||||
}
|
||||
|
||||
@@ -313,6 +308,7 @@ async fn perform_config_update(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[tracing::instrument(skip_all, level = "info", fields(target = ?profiles.current))]
|
||||
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<ValidationOutcome> {
|
||||
if CURRENT_SWITCHING_PROFILE
|
||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
@@ -325,10 +321,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<ValidationO
|
||||
|
||||
let target_profile = profiles.current.as_ref();
|
||||
|
||||
logging!(info, Type::Cmd, "开始修改配置文件,目标profile: {:?}", target_profile);
|
||||
|
||||
let previous_profile = Config::profiles().await.data_arc().current.clone();
|
||||
logging!(info, Type::Cmd, "当前配置: {:?}", previous_profile);
|
||||
|
||||
Config::profiles().await.edit_draft(|d| d.patch_config(&profiles));
|
||||
|
||||
@@ -338,8 +331,6 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<ValidationO
|
||||
}
|
||||
|
||||
pub async fn patch_profiles_config_by_profile_index(profile_index: String) -> CmdResult<ValidationOutcome> {
|
||||
logging!(info, Type::Cmd, "切换配置到: {}", profile_index);
|
||||
|
||||
let profiles = IProfiles {
|
||||
current: Some(profile_index),
|
||||
items: None,
|
||||
@@ -377,7 +368,7 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||
crate::process::AsyncHandler::spawn(move || async move {
|
||||
logging!(info, Type::Timer, "Timer update settings changed, refreshing timer...");
|
||||
if let Err(e) = crate::core::Timer::global().refresh().await {
|
||||
logging!(error, Type::Timer, "Failed to refresh timer: {}", e);
|
||||
logging!(error, Type::Timer, "Failed to refresh timer: {e:#}");
|
||||
} else {
|
||||
crate::core::handle::Handle::notify_timer_updated(&index);
|
||||
}
|
||||
|
||||
@@ -87,10 +87,10 @@ async fn run_tray_sync_loop() {
|
||||
loop {
|
||||
match Tray::global().update_menu().await {
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Cmd, "Tray proxy selection synced successfully");
|
||||
logging!(debug, Type::Cmd, "Tray proxy selection synced successfully");
|
||||
}
|
||||
Err(e) => {
|
||||
logging!(error, Type::Cmd, "Failed to sync tray proxy selection: {e}");
|
||||
logging!(error, Type::Cmd, "Failed to sync tray proxy selection: {e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ pub async fn update_proxy_chain_config_in_runtime(proxy_chain_config: Option<ser
|
||||
"Failed to apply runtime proxy chain config: {}",
|
||||
outcome
|
||||
),
|
||||
Err(err) => logging!(error, Type::Core, "Failed to apply runtime proxy chain config: {}", err),
|
||||
Err(err) => logging!(error, Type::Core, "Failed to apply runtime proxy chain config: {err:#}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -66,14 +66,6 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
// 保存新的配置文件
|
||||
fs::write(&file_path, &file_data).await.stringify_err()?;
|
||||
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}",
|
||||
file_path_str,
|
||||
is_merge_file
|
||||
);
|
||||
|
||||
let changes_applied = handle_saved_profile_file(
|
||||
&file_path_str,
|
||||
&file_path,
|
||||
@@ -144,18 +136,8 @@ async fn handle_saved_profile_file(
|
||||
(ValidationNoticeTarget::Runtime, "YAML配置文件")
|
||||
};
|
||||
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"[cmd配置save] 开始{}验证: {}",
|
||||
file_type,
|
||||
file_path_str
|
||||
);
|
||||
|
||||
match CoreConfigValidator::validate_config_file_outcome(file_path_str, Some(is_merge_file)).await {
|
||||
Ok(outcome) if outcome.is_valid() => {
|
||||
logging!(info, Type::Config, "[cmd配置save] 文件验证通过: {}", file_path_str);
|
||||
}
|
||||
Ok(outcome) if outcome.is_valid() => {}
|
||||
Ok(outcome) => {
|
||||
logging!(warn, Type::Config, "[cmd配置save] 文件验证失败: {}", outcome);
|
||||
restore_original(file_path, original_content, original_existed).await?;
|
||||
@@ -163,7 +145,7 @@ async fn handle_saved_profile_file(
|
||||
return Ok(outcome);
|
||||
}
|
||||
Err(e) => {
|
||||
logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {}", e);
|
||||
logging!(error, Type::Config, "[cmd配置save] 验证过程发生错误: {e:#}");
|
||||
restore_original(file_path, original_content, original_existed).await?;
|
||||
return Err(e.to_string().into());
|
||||
}
|
||||
@@ -190,7 +172,7 @@ async fn handle_saved_profile_file(
|
||||
Ok(outcome)
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Config, "[cmd配置save] 运行时配置应用错误: {}", err);
|
||||
logging!(error, Type::Config, "[cmd配置save] 运行时配置应用错误: {err:#}");
|
||||
restore_original(file_path, original_content, original_existed).await?;
|
||||
Err(err.to_string().into())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user