From 621fa09361706f3c55269459c7447779881d1284 Mon Sep 17 00:00:00 2001 From: RA <70325462+RAprogramm@users.noreply.github.com> Date: Fri, 3 Oct 2025 07:51:45 +0700 Subject: [PATCH] Fix tracing telemetry retry --- CHANGELOG.md | 7 +++++ Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 4 +-- src/app_error/core.rs | 70 ++++++++++++++++++++++++++++++------------- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 354f400..296c7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [0.24.15] - 2025-10-31 + +### Fixed +- Reworked telemetry flushing so tracing events retry emission when the + subscriber enables interest after an error is constructed, preserving the + expected single event in concurrent test runs. + ## [0.24.14] - 2025-10-30 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 2d0363e..8bafd4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1804,7 +1804,7 @@ dependencies = [ [[package]] name = "masterror" -version = "0.24.14" +version = "0.24.15" dependencies = [ "actix-web", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index c656bee..d521de0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "masterror" -version = "0.24.14" +version = "0.24.15" rust-version = "1.90" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index b468b24..293ae48 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,9 @@ The build script keeps the full feature snippet below in sync with ~~~toml [dependencies] -masterror = { version = "0.24.14", default-features = false } +masterror = { version = "0.24.15", default-features = false } # or with features: -# masterror = { version = "0.24.14", features = [ +# masterror = { version = "0.24.15", features = [ # "std", "axum", "actix", "openapi", # "serde_json", "tracing", "metrics", "backtrace", # "sqlx", "sqlx-migrate", "reqwest", "redis", diff --git a/src/app_error/core.rs b/src/app_error/core.rs index 383de80..452d890 100644 --- a/src/app_error/core.rs +++ b/src/app_error/core.rs @@ -92,7 +92,9 @@ pub struct ErrorInner { pub backtrace: Option, #[cfg(feature = "backtrace")] pub captured_backtrace: OnceLock>, - telemetry_dirty: AtomicBool + telemetry_dirty: AtomicBool, + #[cfg(feature = "tracing")] + tracing_dirty: AtomicBool } #[cfg(feature = "backtrace")] @@ -270,19 +272,33 @@ impl Error { backtrace: None, #[cfg(feature = "backtrace")] captured_backtrace: OnceLock::new(), - telemetry_dirty: AtomicBool::new(true) + telemetry_dirty: AtomicBool::new(true), + #[cfg(feature = "tracing")] + tracing_dirty: AtomicBool::new(true) }) } } fn mark_dirty(&self) { self.telemetry_dirty.store(true, Ordering::Release); + #[cfg(feature = "tracing")] + self.mark_tracing_dirty(); } fn take_dirty(&self) -> bool { self.telemetry_dirty.swap(false, Ordering::AcqRel) } + #[cfg(feature = "tracing")] + fn mark_tracing_dirty(&self) { + self.tracing_dirty.store(true, Ordering::Release); + } + + #[cfg(feature = "tracing")] + fn take_tracing_dirty(&self) -> bool { + self.tracing_dirty.swap(false, Ordering::AcqRel) + } + #[cfg(feature = "backtrace")] fn capture_backtrace(&self) -> Option<&CapturedBacktrace> { if let Some(backtrace) = self.backtrace.as_ref() { @@ -324,27 +340,39 @@ impl Error { ) .increment(1); } + } - #[cfg(feature = "tracing")] - { - let message = self.message.as_deref(); - let retry_seconds = self.retry.map(|value| value.after_seconds); - let trace_id = log_mdc::get("trace_id", |value| value.map(str::to_owned)); - event!( - target: "masterror::error", - Level::ERROR, - code = self.code.as_str(), - category = kind_label(self.kind), - message = message, - retry_seconds, - redactable = matches!(self.edit_policy, MessageEditPolicy::Redact), - metadata_len = self.metadata.len() as u64, - www_authenticate = self.www_authenticate.as_deref(), - trace_id = trace_id.as_deref(), - "app error constructed" - ); - } + #[cfg(feature = "tracing")] + self.flush_tracing(); + } + + #[cfg(feature = "tracing")] + fn flush_tracing(&self) { + if !self.take_tracing_dirty() { + return; } + + if !tracing::event_enabled!(target: "masterror::error", Level::ERROR) { + self.mark_tracing_dirty(); + return; + } + + let message = self.message.as_deref(); + let retry_seconds = self.retry.map(|value| value.after_seconds); + let trace_id = log_mdc::get("trace_id", |value| value.map(str::to_owned)); + event!( + target: "masterror::error", + Level::ERROR, + code = self.code.as_str(), + category = kind_label(self.kind), + message = message, + retry_seconds, + redactable = matches!(self.edit_policy, MessageEditPolicy::Redact), + metadata_len = self.metadata.len() as u64, + www_authenticate = self.www_authenticate.as_deref(), + trace_id = trace_id.as_deref(), + "app error constructed" + ); } /// Create a new [`Error`] with a kind and message.