use serde::Serialize;
use super::{LogEvent, StructuredLog, emit_event_with_seq};
use crate::daemon::{
clock_parameters::ClockParameters, logging::ffevents::types::ClockParametersLog,
receiver_stream::RoutableEvent,
};
pub mod types;
#[derive(Serialize)]
struct Init {
message: &'static str,
}
impl LogEvent for Init {
const STREAM: StructuredLog = StructuredLog::FFEvents;
const EVENT: &'static str = "init";
}
#[derive(Serialize)]
struct Disruption {
message: &'static str,
}
impl LogEvent for Disruption {
const STREAM: StructuredLog = StructuredLog::FFEvents;
const EVENT: &'static str = "disruption";
}
#[derive(Serialize)]
struct Feed {
input: types::RoutableEventLog,
output: Option<types::ClockParametersLog>,
}
impl LogEvent for Feed {
const STREAM: StructuredLog = StructuredLog::FFEvents;
const EVENT: &'static str = "feed";
}
pub(crate) fn log_init(seq: u64) {
emit_event_with_seq(
&Init {
message: "Clock Sync Algorithm initialized",
},
seq,
);
}
pub(crate) fn log_disruption(seq: u64) {
emit_event_with_seq(
&Disruption {
message: "Disruption occurred",
},
seq,
);
}
pub(crate) fn log_feed(input: &RoutableEvent, output: Option<&ClockParameters>, seq: u64) {
emit_event_with_seq(
&Feed {
input: input.into(),
output: output.map(ClockParametersLog::from),
},
seq,
);
}
#[cfg(test)]
mod tests {
use crate::daemon::{
clock_parameters::ClockParameters,
event::{Ntp, NtpData, Phc, PhcData, Stratum},
logging::{
StructuredLog,
test_support::{assert_matches_expected, with_layer},
},
receiver_stream::RoutableEvent,
time::{Duration, Instant, TscCount, tsc::Period},
};
use indoc::indoc;
const TEST_VERSION: &str = "3.0.0-test";
fn sample_ntp_event() -> Ntp {
Ntp::builder()
.counter_pre(TscCount::new(100))
.counter_post(TscCount::new(200))
.ntp_data(NtpData {
server_recv_time: Instant::new(1000),
server_send_time: Instant::new(2000),
root_delay: Duration::new(50),
root_dispersion: Duration::new(25),
stratum: Stratum::ONE,
})
.build()
.unwrap()
}
fn sample_phc_event() -> Phc {
Phc::builder()
.counter_pre(TscCount::new(300))
.counter_post(TscCount::new(400))
.data(PhcData {
time: Instant::new(5000),
clock_error_bound: Duration::new(100),
})
.build()
.unwrap()
}
fn sample_output() -> ClockParameters {
ClockParameters {
tsc_count: TscCount::new(150),
time: Instant::new(1500),
clock_error_bound: Duration::new(75),
period: Period::from_seconds(4.0e-10),
period_max_error: Period::from_seconds(1.0e-16),
as_of_monotonic: Instant::new(0),
}
}
fn with_ffevents_layer(f: impl FnOnce()) -> (String, tempfile::TempDir) {
with_layer(StructuredLog::FFEvents, TEST_VERSION, f)
}
#[test]
fn init_event() {
let (output, _tmp) = with_ffevents_layer(|| {
super::log_init(0);
});
assert_matches_expected(
output.lines().next().unwrap(),
indoc! {r#"
{
"timestamp": "",
"event": "init",
"seq": 0,
"init": {
"message": "Clock Sync Algorithm initialized"
},
"version": "3.0.0-test"
}
"#},
);
}
#[test]
fn disruption_event() {
let (output, _tmp) = with_ffevents_layer(|| {
super::log_disruption(7);
});
assert_matches_expected(
output.lines().next().unwrap(),
indoc! {r#"
{
"timestamp": "",
"event": "disruption",
"seq": 7,
"disruption": {
"message": "Disruption occurred"
},
"version": "3.0.0-test"
}
"#},
);
}
#[test]
fn feed_ntp_no_output() {
let (output, _tmp) = with_ffevents_layer(|| {
let routable = RoutableEvent::AmazonTimeSync(sample_ntp_event());
super::log_feed(&routable, None, 1);
});
assert_matches_expected(
output.lines().next().unwrap(),
indoc! {r#"
{
"timestamp": "",
"event": "feed",
"seq": 1,
"feed": {
"input": {
"type": "ntp",
"identifier": "169.254.169.123:123",
"counter_pre": 100,
"counter_post": 200,
"data": {
"server_recv_time_ns": 1000,
"server_send_time_ns": 2000,
"root_delay_ns": 50,
"root_dispersion_ns": 25,
"stratum": 1
}
},
"output": null
},
"version": "3.0.0-test"
}
"#},
);
}
#[test]
fn feed_phc_with_output() {
let output_params = sample_output();
let (output, _tmp) = with_ffevents_layer(|| {
let routable = RoutableEvent::Phc(
crate::daemon::clock_sync_algorithm::source::DevicePath::from("/dev/ptp0"),
sample_phc_event(),
);
super::log_feed(&routable, Some(&output_params), 2);
});
assert_matches_expected(
output.lines().next().unwrap(),
indoc! {r#"
{
"timestamp": "",
"event": "feed",
"seq": 2,
"feed": {
"input": {
"type": "phc",
"identifier": "/dev/ptp0",
"counter_pre": 300,
"counter_post": 400,
"data": {
"time_ns": 5000,
"clock_error_bound_ns": 100
}
},
"output": {
"counter_sync": 150,
"time_sync_ns": 1500,
"clock_error_bound_ns": 75,
"period_s": 4.0e-10,
"period_max_error_s": 1.0e-16
}
},
"version": "3.0.0-test"
}
"#},
);
}
}