use std::time::Duration;
use dial9::Dial9Handle;
use dial9::DiskBuffer;
use dial9::core::dump::DumpError;
const TRACE_DIR: &str = "/tmp/dial9-on-trigger-dump";
fn sealed_segments() -> usize {
std::fs::read_dir(TRACE_DIR)
.map(|rd| {
rd.flatten()
.filter(|e| e.file_name().to_string_lossy().ends_with(".bin"))
.count()
})
.unwrap_or(0)
}
#[dial9::main(config = || {
use dial9::{Dial9HandleTokioExt, RecorderPipelineExt, TokioAttachOptions};
let _ = std::fs::remove_dir_all(TRACE_DIR);
let _ = std::fs::create_dir_all(TRACE_DIR);
let writer = DiskBuffer::builder()
.base_path(TRACE_DIR)
// Fast-rotating writer so the demo seals a segment within a couple of
// seconds instead of waiting on the default rotation period.
.max_file_size(4 * 1024)
.max_total_size(10 * 1024 * 1024)
.rotation_period(Duration::from_millis(500))
.build()
.expect("open trace writer");
let recorder = dial9::recorder(writer)
// The pipeline is whatever you would run continuously (here: gzip +
// write_back); `with_dump_trigger(...)` only changes *when* it runs. The
// debounce gate folds a burst of re-trips into a single dump.
.with_custom_pipeline(|p| p.gzip().write_back())
.with_dump_trigger(|t| t.debounce(Duration::from_secs(30)))
.build();
let mut builder = tokio::runtime::Builder::new_multi_thread();
builder.enable_all().worker_threads(2);
let runtime = recorder.handle().attach_tokio_runtime(
builder,
TokioAttachOptions::builder().task_tracking_enabled(true).build(),
)?;
Ok((recorder, runtime))
})]
async fn main() {
let trigger = Dial9Handle::current()
.dump_trigger()
.expect("on-demand mode enabled");
for id in 0..8 {
dial9::spawn(async move {
for _ in 0..200 {
tokio::time::sleep(Duration::from_millis(25)).await;
std::hint::black_box(id);
}
});
}
while sealed_segments() == 0 {
tokio::time::sleep(Duration::from_millis(50)).await;
}
println!(
"monitor: incident detected, {} sealed segment(s) buffered",
sealed_segments()
);
let mut receipt = None;
for tick in 0..3 {
match trigger
.dump_current_data()
.with_metadata("reason", "idle-ratio-drop")
.await
{
Ok(r) => {
println!(
"monitor: tick {tick}: dump {} captured {} segment(s)",
r.dump_id, r.segments_processed
);
receipt.get_or_insert(r);
}
Err(DumpError::Coalesced { into }) => {
println!("monitor: tick {tick}: re-trip folded into dump {into}, skipping");
}
Err(e) => println!("monitor: tick {tick}: dump error: {e}"),
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
let receipt = receipt.expect("at least one dump ran");
println!("dump complete:");
println!(" dump_id = {}", receipt.dump_id);
println!(" segments_processed = {}", receipt.segments_processed);
println!(" time_range = {:?}", receipt.time_range);
println!("processed to disk: run `ls {TRACE_DIR}/*.bin.gz`");
}