use dial9::core::{Encodable, ThreadLocalEncoder, clock_monotonic_ns};
use dial9::format::{InternedString, TraceEvent};
use dial9::{Dial9HandleTokioExt, DiskBuffer, TokioAttachOptions, recorder};
use std::time::Duration;
#[derive(TraceEvent)]
struct RequestCompleted {
#[traceevent(timestamp)]
timestamp_ns: u64,
status_code: u32,
#[traceevent(unit = "us", kind = "gauge")]
latency_us: u64,
error_message: Option<String>,
}
struct HttpRequest {
timestamp_ns: u64,
method: String,
status: u32,
}
#[derive(TraceEvent)]
struct HttpRequestWire {
#[traceevent(timestamp)]
timestamp_ns: u64,
method: InternedString,
status: u32,
}
impl Encodable for HttpRequest {
fn encode(&self, enc: &mut ThreadLocalEncoder<'_>) {
let method = enc.intern_string(&self.method);
enc.encode(&HttpRequestWire {
timestamp_ns: self.timestamp_ns,
method,
status: self.status,
});
}
}
fn main() -> std::io::Result<()> {
let dir = tempfile::tempdir()?;
let trace_path = dir.path().join("trace.bin");
let writer = DiskBuffer::single_file(&trace_path)?;
let recorder = recorder(writer).build();
let mut builder = tokio::runtime::Builder::new_multi_thread();
builder.enable_all().worker_threads(2);
let rt = recorder
.handle()
.attach_tokio_runtime(builder, TokioAttachOptions::default())
.expect("build tokio runtime");
let handle = recorder.handle();
rt.block_on(async {
for i in 0..10 {
let error_message = if i % 4 == 3 {
Some(format!("timeout after {}ms", 100 + i))
} else {
None
};
handle.record_event(RequestCompleted {
timestamp_ns: clock_monotonic_ns(),
status_code: if error_message.is_some() { 500 } else { 200 },
latency_us: 100 + i,
error_message,
});
}
for _ in 0..10 {
handle.record_event(HttpRequest {
timestamp_ns: clock_monotonic_ns(),
method: "GET".into(),
status: 200,
});
}
tokio::time::sleep(Duration::from_millis(100)).await;
});
drop(rt);
drop(recorder);
let sealed = dir.path().join("trace.0.bin");
let data = std::fs::read(&sealed)?;
let mut decoder =
dial9::format::Decoder::new(&data).ok_or_else(|| std::io::Error::other("invalid trace"))?;
let mut request_completed = 0u32;
let mut http_request = 0u32;
decoder
.for_each_event(|ev| match ev.name {
"RequestCompleted" => request_completed += 1,
"HttpRequestWire" => http_request += 1,
_ => {}
})
.map_err(|e| std::io::Error::other(e.to_string()))?;
println!("RequestCompleted events: {request_completed}");
println!("HttpRequestWire events: {http_request}");
assert_eq!(request_completed, 10);
assert_eq!(http_request, 10);
println!("✓ All custom events recorded successfully");
Ok(())
}