1use anyhow::Result;
11use backbeat::{
12 record::RecordView,
13 ring::walk,
14 schema::{FieldRole, Phase},
15 wire::{DumpReader, OwnedSchema},
16};
17use bytes::Bytes;
18use std::{collections::BTreeMap, io::Write};
19
20pub fn inspect(bytes: impl Into<Bytes>, out: &mut impl Write) -> Result<()> {
22 let bytes = bytes.into();
23 let total_len = bytes.len();
24 let reader = DumpReader::new(bytes).map_err(|e| anyhow::anyhow!("{e}"))?;
25 let schemas = reader.schemas().map_err(|e| anyhow::anyhow!("{e}"))?;
26 let intern = reader.intern_table().map_err(|e| anyhow::anyhow!("{e}"))?;
27 let shards = reader.shards().map_err(|e| anyhow::anyhow!("{e}"))?;
28
29 let by_id: BTreeMap<u64, &OwnedSchema> = schemas.iter().map(|s| (s.id.get(), s)).collect();
31
32 writeln!(out, "envelope")?;
33 writeln!(out, " size: {total_len} bytes")?;
34 writeln!(out, " flags: {:#06x}", reader.flags())?;
35 writeln!(out, " sections: {}", reader.section_count())?;
36
37 writeln!(out, "\nschema registry ({} events)", schemas.len())?;
38 for s in &schemas {
39 let phase = match s.phase {
40 Phase::Enter => " [span enter]",
41 Phase::Exit => " [span exit]",
42 _ => "",
43 };
44 writeln!(
45 out,
46 " {:#018x} {} ({} bytes, {} fields){}{}",
47 s.id.get(),
48 s.qualified_name,
49 s.record_size,
50 s.fields.len(),
51 phase,
52 s.description
53 .as_deref()
54 .map(|d| format!(" — {d}"))
55 .unwrap_or_default()
56 )?;
57 for f in &s.fields {
58 let marker = match f.role {
60 FieldRole::Key => "*",
61 FieldRole::SpanId => "$",
62 FieldRole::ParentSpanId => "^",
63 _ => " ",
64 };
65 writeln!(
66 out,
67 " {}{}: {:?}{}",
68 marker,
69 f.name,
70 f.ty,
71 f.unit
72 .as_deref()
73 .map(|u| format!(" [{u}]"))
74 .unwrap_or_default()
75 )?;
76 }
77 }
78 writeln!(out, " (* = key, $ = span id, ^ = parent span id)")?;
79
80 writeln!(out, "\nintern table: {} entries", intern.len())?;
81
82 writeln!(out, "\nshards ({})", shards.len())?;
86 let mut total = 0usize;
87 let mut per_event: BTreeMap<String, usize> = BTreeMap::new();
88
89 for shard in &shards {
90 let mut count = 0usize;
91 walk(
92 &shard.region,
93 shard.head as usize,
94 shard.capacity as usize,
95 |payload| match RecordView::parse(&payload[..]) {
96 Some(rec) => match by_id.get(&rec.event_id.get()) {
97 Some(schema) if rec.fields.len() == schema.record_size as usize => {
98 *per_event.entry(schema.qualified_name.clone()).or_default() += 1;
99 count += 1;
100 true
101 }
102 _ => false,
103 },
104 None => false,
105 },
106 );
107 total += count;
108 writeln!(
109 out,
110 " shard {:>3}: head {:>10} capacity {:>10} {} records",
111 shard.shard_id, shard.head, shard.capacity, count
112 )?;
113 }
114
115 writeln!(out, "\nrecords: {total} total")?;
116 for (name, n) in &per_event {
117 writeln!(out, " {name}: {n}")?;
118 }
119
120 Ok(())
121}