use std::time::Instant;
use tracing::{Level, Span, span};
pub struct ResponseTracker {
span: Span,
start_time: Instant,
}
impl ResponseTracker {
pub fn start(response_format: &str, response_size: Option<u64>) -> Self {
let span = span!(
Level::DEBUG,
"response_processing",
format = response_format,
input_size = response_size.unwrap_or(0),
parsing_duration_ms = tracing::field::Empty,
validation_duration_ms = tracing::field::Empty,
total_duration_ms = tracing::field::Empty,
success = tracing::field::Empty,
);
{
let _enter = span.enter();
tracing::debug!("Starting response processing");
}
Self {
span,
start_time: Instant::now(),
}
}
pub fn parsing_complete(&self) {
let parsing_duration = self.start_time.elapsed();
let parsing_duration_ms = parsing_duration.as_millis() as u64;
self.span.record("parsing_duration_ms", parsing_duration_ms);
}
pub fn validation_complete(&self) {
let total_duration = self.start_time.elapsed();
let validation_duration_ms = total_duration.as_millis() as u64;
self.span
.record("validation_duration_ms", validation_duration_ms);
}
pub fn success(self) {
let duration = self.start_time.elapsed();
let duration_ms = duration.as_millis() as u64;
self.span.record("total_duration_ms", duration_ms);
self.span.record("success", true);
let _enter = self.span.enter();
tracing::debug!("Response processing completed successfully");
}
pub fn error(self, error: &str) {
let duration = self.start_time.elapsed();
let duration_ms = duration.as_millis() as u64;
self.span.record("total_duration_ms", duration_ms);
self.span.record("success", false);
let _enter = self.span.enter();
tracing::error!(error = error, "Response processing failed");
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use tracing_test::traced_test;
#[traced_test]
#[test]
fn test_response_tracker() {
let tracker = ResponseTracker::start("json", Some(1024));
std::thread::sleep(Duration::from_millis(5));
tracker.parsing_complete();
std::thread::sleep(Duration::from_millis(3));
tracker.validation_complete();
tracker.success();
assert!(logs_contain("Starting response processing"));
assert!(logs_contain("Response processing completed successfully"));
}
#[traced_test]
#[test]
fn test_response_tracker_error() {
let tracker = ResponseTracker::start("xml", Some(512));
tracker.error("Parse error: invalid XML structure");
assert!(logs_contain("Starting response processing"));
assert!(logs_contain("Response processing failed"));
assert!(logs_contain("Parse error: invalid XML structure"));
}
#[traced_test]
#[test]
fn test_response_tracker_with_none_size() {
let tracker = ResponseTracker::start("xml", None);
tracker.parsing_complete();
tracker.success();
assert!(logs_contain("Starting response processing"));
assert!(logs_contain("Response processing completed successfully"));
}
#[traced_test]
#[test]
fn test_response_tracker_validation_timing() {
let tracker = ResponseTracker::start("binary", Some(2048));
std::thread::sleep(Duration::from_millis(2));
tracker.parsing_complete();
std::thread::sleep(Duration::from_millis(3));
tracker.validation_complete();
std::thread::sleep(Duration::from_millis(1));
tracker.success();
assert!(logs_contain("Starting response processing"));
assert!(logs_contain("Response processing completed successfully"));
}
}