Skip to main content

qlog_dancer/reports/
events.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use qlog::events::http3::Http3Frame;
28use qlog::events::quic::QuicFrame;
29use qlog::events::EventData;
30use tabled::Table;
31use tabled::Tabled;
32
33use crate::category_and_type_from_event;
34use crate::category_and_type_from_name;
35
36macro_rules! printy {
37    ($k:expr, $value:expr, $s:expr) => {{
38        $s += &format!("{}={}, ", $k, $value);
39    }};
40}
41
42macro_rules! printyo {
43    ($k:expr, $value:expr, $s:expr) => {{
44        if let Some(v) = $value {
45            $s += &format!("{}={}, ", $k, v);
46        }
47    }};
48}
49
50macro_rules! printy_json {
51    ($k:expr, $value:expr, $s:expr) => {{
52        $s += &format!(
53            "{}={}, ",
54            $k,
55            &serde_json::to_string(&$value).unwrap().replace("\"", "")
56        );
57    }};
58}
59
60macro_rules! printyo_json {
61    ($k:expr, $value:expr, $s:expr) => {{
62        if let Some(v) = $value {
63            $s += &format!(
64                "{}={}, ",
65                $k,
66                &serde_json::to_string(&v).unwrap().replace("\"", "")
67            );
68        }
69    }};
70}
71
72#[derive(Debug, Default, Tabled)]
73struct PrintableEvent {
74    pub time: f64,
75    pub category: String,
76    #[tabled(rename = "Type")]
77    pub ty: String,
78    pub details: String,
79}
80
81pub fn frames_to_string(frames: &[QuicFrame]) -> String {
82    let mut s = String::new();
83    for f in frames {
84        match f {
85            QuicFrame::Padding { raw, .. } => {
86                s += &format!(" PADDING {{raw={raw:?}}}");
87            },
88            QuicFrame::Ping { .. } => {
89                s += " PING";
90            },
91            QuicFrame::Ack { acked_ranges, .. } => {
92                s += " ACK {";
93                if let Some(acked_ranges) = acked_ranges {
94                    for range in acked_ranges {
95                        s += &format!("{}, ", range);
96                    }
97                }
98                s += "}";
99            },
100            QuicFrame::ResetStream { stream_id, error, error_code, final_size, .. } => {
101                s += &format!(" RESET_STREAM {{id={stream_id}, error={error:?}, error_code={error_code:?}, final_size={final_size}}}");
102            },
103            QuicFrame::StopSending { stream_id, error, error_code, ..} => {
104                s += &format!(" STOP_SENDING {{id={stream_id}, error={error:?}, error_code={error_code:?}}}");
105            },
106            QuicFrame::Crypto { offset, raw } => {
107                s += &format!(" CRYPTO {{off={offset}, raw={raw:?}}}");
108            },
109            QuicFrame::NewToken { token , ..} => {
110               s += " NEW_TOKEN ";
111               if let Some(ty) = &token.ty {
112                    s += &format!("{{ty={ty:?}}}");
113               }
114            },
115            QuicFrame::Stream { stream_id, offset, fin, raw } => {
116                s += &format!(" STREAM {{id={stream_id}, off={offset:?}, raw={raw:?}");
117                if let Some(f) = fin {
118                    s += &format!(", fin={f}")
119                }
120                s += "}";
121            },
122            QuicFrame::MaxData { maximum, .. } => {
123                s += &format!(" MAX_DATA {{max={maximum}}}");
124            },
125            QuicFrame::MaxStreamData { stream_id, maximum, .. } => {
126                s += &format!(" MAX_STREAM_DATA {{id={stream_id}, max={maximum}}}");
127            },
128            QuicFrame::MaxStreams { stream_type, maximum, .. } => {
129                s += &format!(" MAX_STREAMS {{ty={stream_type:?}, max={maximum}}}");
130            },
131            QuicFrame::DataBlocked { limit, .. } => {
132                s += &format!(" DATA_BLOCKED {{limit={limit}}}");
133            },
134            QuicFrame::StreamDataBlocked { stream_id, limit, .. } => {
135                s += &format!(" STREAM_DATA_BLOCKED {{id={stream_id}, limit={limit}}}");
136            },
137            QuicFrame::StreamsBlocked { stream_type, limit , ..} => {
138                s += &format!(" STREAMS_BLOCKED {{ty={stream_type:?}, limit={limit}}}");
139            },
140            QuicFrame::NewConnectionId { /*sequence_number, retire_prior_to, connection_id_length, connection_id, stateless_reset_token*/ .. } => {
141                s += " NEW_CONNECTION_ID {{todo='todo'}}";
142            },
143            QuicFrame::RetireConnectionId { sequence_number , ..} => {
144                s += &format!(" RETIRE_CONNECION_ID {{sn={sequence_number}}}");
145            },
146            QuicFrame::PathChallenge { /*data*/ .. } => {
147                s += " PATH_CHALLENGE {{todo='todo'}}";
148            },
149            QuicFrame::PathResponse { /*data*/ .. } => {
150                s += " PATH_RESPONSE {{todo='todo'}}";
151            },
152            QuicFrame::ConnectionClose { error_space, error_code, reason, .. } => {
153               s += " CONNECTION_CLOSE {";
154               if let Some(es) = error_space {
155                    s += &format!(" ty={es:?},");
156               }
157               printyo!(" code", error_code, s);
158               printyo!(" reason", reason, s);
159               s += "}";
160            },
161            QuicFrame::HandshakeDone { .. } => {
162                s += " HANDSHAKE_DONE";
163            },
164            QuicFrame::Datagram { raw, .. } => {
165               s += &format!(" DATAGRAM {{raw={raw:?}}}");
166            },
167            QuicFrame::Unknown { frame_type_bytes, .. } => {
168               s += &format!(" UNKNOWN {{frame_type_bytes={frame_type_bytes:?}}}");
169            },
170        }
171    }
172
173    s
174}
175
176fn http_frame_to_string(frame: &Http3Frame) -> String {
177    let mut s = String::new();
178
179    match frame {
180        Http3Frame::Data { raw } =>{
181            s += " DATA";
182            if let Some(r) = raw {
183                printyo!("len", r.length, s);
184            }
185        },
186        Http3Frame::Headers { headers, .. } => {
187            s += " HEADERS {";
188
189            for header in headers {
190                let name = header.name.as_deref().unwrap_or("<binary>");
191                let value = header.value.as_deref().unwrap_or("<binary>");
192                s += &format!("{}: {}, ", name, value);
193            }
194
195            s += "}";
196        },
197        Http3Frame::CancelPush { push_id , ..} => {
198            s += &format!(" CANCEL_PUSH {{id={push_id}}}");
199        },
200        Http3Frame::Settings { /* settings */ ..} => {
201            s += " SETTINGS {{todo}}";
202        }
203        Http3Frame::PushPromise { /*push_id, headers */ ..} => {
204            s += " PUSH_PROMISE {{todo}}";
205        }
206        Http3Frame::Goaway { id , ..} => {
207            s += &format!(" GOAWAY {{id={id}}}");
208        }
209        Http3Frame::MaxPushId { push_id , ..} => {
210            s += &format!(" MAX_PUSH_ID {{id={push_id}}}");
211        }
212        Http3Frame::PriorityUpdate { /*target_stream_type, prioritized_element_id, priority_field_value*/ .. } => {
213            s += " PRIORITY_UPDATE {{todo}}";
214        },
215        Http3Frame::Reserved { /*length*/ .. } => {
216            s += " GREASE {{todo}}";
217        },
218        Http3Frame::Unknown { frame_type_bytes, .. } => {
219            s += &format!(" UNKNOWN {{ty={frame_type_bytes}}}");
220        }
221    }
222
223    s
224}
225
226pub fn sqlog_event_list(
227    events: &[qlog::reader::Event],
228) -> tabled::builder::Builder {
229    let mut pp = vec![];
230
231    for event in events {
232        match event {
233            qlog::reader::Event::Qlog(ev) => {
234                let (cat, ty) = category_and_type_from_event(ev);
235
236                let mut p = PrintableEvent {
237                    time: ev.time,
238                    ty,
239                    category: cat,
240                    ..Default::default()
241                };
242
243                match &ev.data {
244                    EventData::QuicConnectionStarted(v) => {
245                        printyo!("local_ip_v4", &v.local.ip_v4, p.details);
246                        printyo!("local_port_v4", &v.local.port_v4, p.details);
247                        printyo!("local_ip_v6", &v.local.ip_v6, p.details);
248                        printyo!("local_port_v6", &v.local.port_v6, p.details);
249                        printy!(
250                            "local_cids",
251                            format!("{:?}", &v.local.connection_ids),
252                            p.details
253                        );
254                        printyo!("remote_ip_v4", &v.remote.ip_v4, p.details);
255                        printyo!("remote_port_v4", &v.remote.port_v4, p.details);
256                        printyo!("remote_ip_v6", &v.remote.ip_v6, p.details);
257                        printyo!("remote_port_v6", &v.remote.port_v6, p.details);
258                        printy!(
259                            "remote_cid",
260                            format!("{:?}", &v.local.connection_ids),
261                            p.details
262                        );
263                    },
264                    EventData::QuicConnectionClosed(v) => {
265                        printyo_json!("initiator", &v.initiator, p.details);
266                        printyo_json!(
267                            "connection_code",
268                            &v.connection_error,
269                            p.details
270                        );
271                        printyo_json!(
272                            "application_error",
273                            &v.application_error,
274                            p.details
275                        );
276                        printyo!("internal_code", &v.internal_code, p.details);
277                        printyo!("reason", &v.reason, p.details);
278                        printyo_json!("trigger", &v.trigger, p.details);
279                    },
280                    EventData::QuicParametersSet(v) => {
281                        printyo_json!("initiator", &v.initiator, p.details);
282                        printyo!(
283                            "resumption_allowed",
284                            &v.resumption_allowed,
285                            p.details
286                        );
287                        printyo!(
288                            "early_data_enabled",
289                            &v.early_data_enabled,
290                            p.details
291                        );
292                        printyo!("tls_cipher", &v.tls_cipher, p.details);
293                        printyo!(
294                            "odcid",
295                            &v.original_destination_connection_id,
296                            p.details
297                        );
298                        printyo!(
299                            "initial_scid",
300                            &v.initial_source_connection_id,
301                            p.details
302                        );
303                        printyo!(
304                            "retry_scid",
305                            &v.retry_source_connection_id,
306                            p.details
307                        );
308                        printyo!(
309                            "stateless_reset_token",
310                            &v.stateless_reset_token,
311                            p.details
312                        );
313                        printyo!(
314                            "disable_active_migration",
315                            &v.disable_active_migration,
316                            p.details
317                        );
318                        printyo!(
319                            "max_idle_timeout",
320                            &v.max_idle_timeout,
321                            p.details
322                        );
323                        printyo!(
324                            "max_udp_payload_size",
325                            &v.max_udp_payload_size,
326                            p.details
327                        );
328                        printyo!("max_ack_delay", &v.max_ack_delay, p.details);
329                        printyo!(
330                            "active_connection_id_limit",
331                            &v.active_connection_id_limit,
332                            p.details
333                        );
334                        printyo!(
335                            "initial_max_data",
336                            &v.initial_max_data,
337                            p.details
338                        );
339                        printyo!(
340                            "initial_max_stream_data_bidi_local",
341                            &v.initial_max_stream_data_bidi_local,
342                            p.details
343                        );
344                        printyo!(
345                            "initial_max_stream_data_bidi_remote",
346                            &v.initial_max_stream_data_bidi_remote,
347                            p.details
348                        );
349                        printyo!(
350                            "initial_max_stream_data_uni",
351                            &v.initial_max_stream_data_uni,
352                            p.details
353                        );
354                        printyo!(
355                            "initial_max_streams_bidi",
356                            &v.initial_max_streams_bidi,
357                            p.details
358                        );
359                        printyo!(
360                            "initial_max_streams_uni",
361                            &v.initial_max_streams_uni,
362                            p.details
363                        );
364                        printyo_json!(
365                            "preferred_address",
366                            &v.preferred_address,
367                            p.details
368                        );
369                        // TODO: v.unknown_parameters
370                    },
371                    EventData::QuicPacketSent(v) => {
372                        p.details +=
373                            &serde_json::to_string(&v.header.packet_type)
374                                .unwrap()
375                                .replace("\"", "");
376                        printyo!(" pn", v.header.packet_number, p.details);
377
378                        if let Some(frames) = &v.frames {
379                            p.details += &frames_to_string(frames);
380                        }
381                    },
382                    EventData::QuicPacketReceived(v) => {
383                        p.details +=
384                            &serde_json::to_string(&v.header.packet_type)
385                                .unwrap()
386                                .replace("\"", "");
387                        printyo!(" pn", v.header.packet_number, p.details);
388
389                        if let Some(frames) = &v.frames {
390                            p.details += &frames_to_string(frames);
391                        }
392                    },
393                    EventData::QuicStreamDataMoved(v) => {
394                        printyo!("id", v.stream_id, p.details);
395                        printyo!("off", v.offset, p.details);
396                        if let Some(raw) = &v.raw {
397                            printyo!("len", raw.length, p.details);
398                        }
399                        printyo_json!("from", &v.from, p.details);
400                        printyo_json!("to", &v.to, p.details);
401                    },
402                    EventData::QuicMetricsUpdated(v) => {
403                        printyo!("min_rtt", v.min_rtt, p.details);
404                        printyo!("smoothed_rtt", v.smoothed_rtt, p.details);
405                        printyo!("latest_rtt", v.latest_rtt, p.details);
406                        printyo!("rtt_variance", v.rtt_variance, p.details);
407                        printyo!("pto_count", v.pto_count, p.details);
408                        printyo!("cwnd", v.congestion_window, p.details);
409                        printyo!("bytes_in_flight", v.bytes_in_flight, p.details);
410                        printyo!("ssthresh", v.ssthresh, p.details);
411                        printyo!(
412                            "packets_in_flight",
413                            v.packets_in_flight,
414                            p.details
415                        );
416                        printyo!("pacing_rate", v.pacing_rate, p.details);
417                    },
418                    EventData::Http3StreamTypeSet(ev) => {
419                        printy!("id", &ev.stream_id, p.details);
420                        printyo_json!("initiator", &ev.initiator, p.details);
421                        printy_json!("ty", &ev.stream_type, p.details);
422                    },
423                    EventData::Http3FrameCreated(v) => {
424                        printy!("id", v.stream_id, p.details);
425                        printyo!("len", v.length, p.details);
426                        p.details += &http_frame_to_string(&v.frame);
427                    },
428                    EventData::Http3FrameParsed(v) => {
429                        printy!("id", v.stream_id, p.details);
430                        printyo!("len", v.length, p.details);
431                        p.details += &http_frame_to_string(&v.frame);
432                    },
433
434                    _ => {
435                        p.details += &serde_json::to_string(&ev.data).unwrap();
436                    },
437                }
438
439                pp.push(p);
440            },
441
442            qlog::reader::Event::Json(ev) => {
443                let (cat, ty) = category_and_type_from_name(&ev.name);
444
445                let p = PrintableEvent {
446                    time: ev.time,
447                    ty,
448                    category: cat,
449                    details: serde_json::to_string(&ev.data).unwrap(),
450                };
451
452                pp.push(p);
453            },
454        }
455    }
456
457    Table::builder(pp)
458}