use crate::daemon::{
logging::{LogEvent, StructuredLog},
time::{Duration, Instant, tsc::Period},
};
#[derive(Debug, Clone, serde::Serialize)]
pub(crate) struct TraceUncorrectedClock {
pub p_estimate: Period,
pub k_ns: Instant,
}
impl LogEvent for TraceUncorrectedClock {
const STREAM: StructuredLog = StructuredLog::AlgoAnalysis;
const EVENT: &'static str = "uncorrected_clock";
}
#[derive(Debug, Clone, serde::Serialize)]
pub(crate) struct TraceTheta {
pub theta_ns: Duration,
pub clock_error_bound_ns: Duration,
pub delay_bound_ns: Duration,
pub samples_used: usize,
pub samples_rejected: usize,
pub offset_ns: Duration,
pub weight: f64,
pub weight_sum: f64,
}
impl LogEvent for TraceTheta {
const STREAM: StructuredLog = StructuredLog::AlgoAnalysis;
const EVENT: &'static str = "theta";
}
#[derive(Debug, Clone, serde::Serialize)]
pub(crate) struct TraceLocalPeriod {
pub period_local: Period,
pub period_local_error: Period,
pub samples: usize,
pub baseline_ns: Duration,
pub age_ns: Duration,
}
impl LogEvent for TraceLocalPeriod {
const STREAM: StructuredLog = StructuredLog::AlgoAnalysis;
const EVENT: &'static str = "local_period";
}
#[cfg(test)]
mod tests {
use serde_json::{Map, Value};
use super::*;
use crate::daemon::{
algo_trace,
logging::{StructuredLog, test_support::with_layer},
};
const TEST_VERSION: &str = "test-version";
fn emit_theta() {
algo_trace!(TraceTheta {
theta_ns: Duration::from(-1_348),
clock_error_bound_ns: Duration::from(27_528),
delay_bound_ns: Duration::from(4_112),
samples_used: 98,
samples_rejected: 2,
offset_ns: Duration::from(-350),
weight: 0.8,
weight_sum: 61.5,
});
}
fn parse_single_record(output: &str) -> Map<String, Value> {
let mut lines = output.lines();
let record = serde_json::from_str(lines.next().expect("expected one record"))
.expect("expected valid JSON");
assert!(lines.next().is_none(), "expected exactly one record");
record
}
#[test]
fn theta_record_inside_a_feed_span() {
let (output, _tmp) = with_layer(StructuredLog::AlgoAnalysis, TEST_VERSION, || {
let _feed = tracing::trace_span!(
target: StructuredLog::AlgoAnalysis.target(),
"feed",
seq = 42_u64
)
.entered();
emit_theta();
});
let record = parse_single_record(&output);
let keys: Vec<&str> = record.keys().map(String::as_str).collect();
assert_eq!(keys, ["timestamp", "event", "seq", "theta", "version"]);
assert_eq!(record["event"], "theta");
assert_eq!(record["seq"], Value::from(42_u64));
let body = record["theta"]
.as_object()
.expect("theta body is a JSON object");
let body_keys: Vec<&str> = body.keys().map(String::as_str).collect();
assert_eq!(
body_keys,
[
"theta_ns",
"clock_error_bound_ns",
"delay_bound_ns",
"samples_used",
"samples_rejected",
"offset_ns",
"weight",
"weight_sum",
]
);
assert_eq!(body["theta_ns"], Value::from(-1_348_i64));
assert_eq!(body["delay_bound_ns"], Value::from(4_112_i64));
assert_eq!(body["offset_ns"], Value::from(-350_i64));
assert_eq!(body["weight"], Value::from(0.8));
assert_eq!(body["weight_sum"], Value::from(61.5));
}
#[test]
fn record_outside_a_span_has_no_seq() {
let (output, _tmp) = with_layer(StructuredLog::AlgoAnalysis, TEST_VERSION, emit_theta);
let record = parse_single_record(&output);
assert!(!record.contains_key("seq"));
}
#[test]
fn local_period_record() {
let (output, _tmp) = with_layer(StructuredLog::AlgoAnalysis, TEST_VERSION, || {
algo_trace!(TraceLocalPeriod {
period_local: Period::from_seconds(4.166e-10),
period_local_error: Period::from_seconds(1e-16),
samples: 512,
baseline_ns: Duration::from_secs(45),
age_ns: Duration::from_secs(3),
});
});
let record = parse_single_record(&output);
assert_eq!(record["event"], "local_period");
assert_eq!(record["local_period"]["samples"], Value::from(512_u64));
assert_eq!(
record["local_period"]["baseline_ns"],
Value::from(45_000_000_000_i64)
);
assert_eq!(
record["local_period"]["age_ns"],
Value::from(3_000_000_000_i64)
);
}
#[test]
fn uncorrected_clock_record() {
let (output, _tmp) = with_layer(StructuredLog::AlgoAnalysis, TEST_VERSION, || {
algo_trace!(TraceUncorrectedClock {
p_estimate: Period::from_seconds(3.846e-10),
k_ns: Instant::from_secs(1_700_000_000),
});
});
let record = parse_single_record(&output);
assert_eq!(record["event"], "uncorrected_clock");
assert_eq!(
record["uncorrected_clock"]["p_estimate"],
Value::from(3.846e-10)
);
assert_eq!(
record["uncorrected_clock"]["k_ns"],
Value::from(1_700_000_000_000_000_000_i64)
);
}
}