mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 11:02:08 +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:
@@ -317,6 +317,7 @@ impl CoreManager {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(status = tracing::field::Empty, tun_disabled = false, readiness_generation = tracing::field::Empty))]
|
||||
pub async fn continue_with_sidecar(&self) -> Result<()> {
|
||||
if !self.try_start_config_update() {
|
||||
anyhow::bail!("configuration update is already running");
|
||||
@@ -329,6 +330,7 @@ impl CoreManager {
|
||||
let config_write = Config::lock_config_write().await;
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
let status = SERVICE_MANAGER.current().await;
|
||||
tracing::Span::current().record("status", tracing::field::debug(&status));
|
||||
let mode = self.get_running_mode();
|
||||
if !can_allow_sidecar_for_session(&mode, &status) {
|
||||
anyhow::bail!("Sidecar continuation is not allowed from {mode:?} / {status:?}");
|
||||
@@ -342,6 +344,7 @@ impl CoreManager {
|
||||
.state()
|
||||
.tun_should_be_disabled(tun_enabled)
|
||||
{
|
||||
tracing::Span::current().record("tun_disabled", true);
|
||||
Config::disable_tun_and_persist().await?;
|
||||
}
|
||||
Config::generate().await
|
||||
@@ -361,6 +364,7 @@ impl CoreManager {
|
||||
{
|
||||
anyhow::bail!("Sidecar did not become ready");
|
||||
}
|
||||
tracing::Span::current().record("readiness_generation", self.current_core_readiness_generation());
|
||||
self.apply_proxy_after_start().await
|
||||
}
|
||||
.await;
|
||||
@@ -376,6 +380,7 @@ impl CoreManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(readiness_generation = tracing::field::Empty))]
|
||||
pub async fn uninstall_service_and_start_sidecar(&self) -> Result<()> {
|
||||
if !self.try_start_config_update() {
|
||||
anyhow::bail!("configuration update is already running");
|
||||
@@ -413,6 +418,7 @@ impl CoreManager {
|
||||
{
|
||||
anyhow::bail!("Sidecar did not become ready after service uninstall");
|
||||
}
|
||||
tracing::Span::current().record("readiness_generation", self.current_core_readiness_generation());
|
||||
self.apply_proxy_after_start().await
|
||||
},
|
||||
)
|
||||
@@ -456,6 +462,7 @@ impl CoreManager {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(mode = ?*self.get_running_mode(), readiness_generation = self.current_core_readiness_generation(), owner_generation = crate::core::service::owner_monitor_generation()))]
|
||||
pub(crate) async fn apply_proxy_after_start(&self) -> Result<()> {
|
||||
let expectation = ProxyRestoreExpectation::capture(
|
||||
*self.get_running_mode(),
|
||||
@@ -519,6 +526,7 @@ impl CoreManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(decision = tracing::field::Empty))]
|
||||
async fn start_core_inner(&self) -> Result<()> {
|
||||
if Handle::global().is_exiting() {
|
||||
return Ok(());
|
||||
@@ -526,7 +534,7 @@ impl CoreManager {
|
||||
|
||||
if !matches!(*self.get_running_mode(), RunningMode::NotRunning) {
|
||||
logging!(
|
||||
info,
|
||||
debug,
|
||||
Type::Core,
|
||||
"start_core called while a core is running; treated as no-op"
|
||||
);
|
||||
@@ -534,6 +542,7 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
let startup = self.prepare_startup().await;
|
||||
tracing::Span::current().record("decision", tracing::field::debug(&startup));
|
||||
if matches!(startup, StartupDecision::Wait) {
|
||||
self.rollback_failed_start().await;
|
||||
return Ok(());
|
||||
@@ -563,6 +572,7 @@ impl CoreManager {
|
||||
result
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(mode = ?*self.get_running_mode()))]
|
||||
pub async fn stop_core(&self) -> Result<()> {
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
self.controlled_stop_core_inner().await
|
||||
@@ -594,7 +604,7 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
async fn stop_core_unprepared_inner(&self) -> Result<()> {
|
||||
CLASH_LOGGER.clear_logs().await;
|
||||
CLASH_LOGGER.clear_logs();
|
||||
match *self.get_running_mode() {
|
||||
RunningMode::Service => self.stop_core_by_service().await,
|
||||
RunningMode::Sidecar => {
|
||||
@@ -605,6 +615,7 @@ impl CoreManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info")]
|
||||
pub async fn restart_core(&self) -> Result<()> {
|
||||
if !self.try_start_config_update() {
|
||||
anyhow::bail!("configuration update is already running");
|
||||
@@ -617,7 +628,6 @@ impl CoreManager {
|
||||
|
||||
pub(crate) async fn restart_core_during_config_update(&self) -> Result<()> {
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
logging!(info, Type::Core, "Restarting core");
|
||||
let proxy_intent = self.proxy_stop_intent().await;
|
||||
run_core_replacement_transition(
|
||||
|| self.controlled_stop_core_with_intent(proxy_intent),
|
||||
@@ -635,6 +645,7 @@ impl CoreManager {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, level = "info", fields(core = %clash_core))]
|
||||
pub async fn change_core(&self, clash_core: &String) -> Result<()> {
|
||||
if !IVerge::VALID_CLASH_CORES.contains(&clash_core.as_str()) {
|
||||
anyhow::bail!("invalid clash core: {clash_core}");
|
||||
@@ -757,6 +768,7 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[tracing::instrument(skip_all, level = "debug", fields(outcome = tracing::field::Empty))]
|
||||
async fn try_handoff_sidecar_to_service(&self) -> HandoffOutcome {
|
||||
// Before probing: a probe reply records an observation, which clears sidecar_allowed.
|
||||
if crate::core::runstate::RUN_STATE.state().sidecar_allowed {
|
||||
|
||||
Reference in New Issue
Block a user