Skip to main content

quinn_proto/connection/
qlog.rs

1// Function bodies in this module are regularly cfg'd out
2#![allow(unused_variables)]
3
4#[cfg(feature = "qlog")]
5use std::sync::{Arc, Mutex};
6
7#[cfg(feature = "qlog")]
8use qlog::{
9    events::{
10        Event, EventData,
11        quic::{
12            PacketHeader, PacketLost, PacketLostTrigger, PacketReceived, PacketSent, PacketType,
13        },
14    },
15    streamer::QlogStreamer,
16};
17#[cfg(feature = "qlog")]
18use tracing::warn;
19
20use crate::{
21    ConnectionId, Instant,
22    connection::{PathData, SentPacket},
23    packet::SpaceId,
24};
25
26/// Shareable handle to a single qlog output stream
27#[cfg(feature = "qlog")]
28#[derive(Clone)]
29pub struct QlogStream(pub(crate) Arc<Mutex<QlogStreamer>>);
30
31#[cfg(feature = "qlog")]
32impl QlogStream {
33    fn emit_event(&self, orig_rem_cid: ConnectionId, event: EventData, now: Instant) {
34        // Time will be overwritten by `add_event_with_instant`
35        let mut event = Event::with_time(0.0, event);
36        event.group_id = Some(orig_rem_cid.to_string());
37
38        let mut qlog_streamer = self.0.lock().unwrap();
39        if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
40            warn!("could not emit qlog event: {e}");
41        }
42    }
43}
44
45/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely
46#[derive(Clone, Default)]
47pub(crate) struct QlogSink {
48    #[cfg(feature = "qlog")]
49    stream: Option<QlogStream>,
50}
51
52impl QlogSink {
53    pub(crate) fn is_enabled(&self) -> bool {
54        #[cfg(feature = "qlog")]
55        {
56            self.stream.is_some()
57        }
58        #[cfg(not(feature = "qlog"))]
59        {
60            false
61        }
62    }
63
64    pub(super) fn emit_recovery_metrics(
65        &self,
66        pto_count: u32,
67        path: &mut PathData,
68        now: Instant,
69        orig_rem_cid: ConnectionId,
70    ) {
71        #[cfg(feature = "qlog")]
72        {
73            let Some(stream) = self.stream.as_ref() else {
74                return;
75            };
76
77            let Some(metrics) = path.qlog_recovery_metrics(pto_count) else {
78                return;
79            };
80
81            stream.emit_event(orig_rem_cid, EventData::MetricsUpdated(metrics), now);
82        }
83    }
84
85    pub(super) fn emit_packet_lost(
86        &self,
87        pn: u64,
88        info: &SentPacket,
89        lost_send_time: Instant,
90        space: SpaceId,
91        now: Instant,
92        orig_rem_cid: ConnectionId,
93    ) {
94        #[cfg(feature = "qlog")]
95        {
96            let Some(stream) = self.stream.as_ref() else {
97                return;
98            };
99
100            let event = PacketLost {
101                header: Some(PacketHeader {
102                    packet_number: Some(pn),
103                    packet_type: packet_type(space, false),
104                    length: Some(info.size),
105                    ..Default::default()
106                }),
107                frames: None,
108                trigger: Some(match info.time_sent <= lost_send_time {
109                    true => PacketLostTrigger::TimeThreshold,
110                    false => PacketLostTrigger::ReorderingThreshold,
111                }),
112            };
113
114            stream.emit_event(orig_rem_cid, EventData::PacketLost(event), now);
115        }
116    }
117
118    pub(super) fn emit_packet_sent(
119        &self,
120        pn: u64,
121        len: usize,
122        space: SpaceId,
123        is_0rtt: bool,
124        now: Instant,
125        orig_rem_cid: ConnectionId,
126    ) {
127        #[cfg(feature = "qlog")]
128        {
129            let Some(stream) = self.stream.as_ref() else {
130                return;
131            };
132
133            let event = PacketSent {
134                header: PacketHeader {
135                    packet_number: Some(pn),
136                    packet_type: packet_type(space, is_0rtt),
137                    length: Some(len as u16),
138                    ..Default::default()
139                },
140                ..Default::default()
141            };
142
143            stream.emit_event(orig_rem_cid, EventData::PacketSent(event), now);
144        }
145    }
146
147    pub(super) fn emit_packet_received(
148        &self,
149        pn: u64,
150        space: SpaceId,
151        is_0rtt: bool,
152        now: Instant,
153        orig_rem_cid: ConnectionId,
154    ) {
155        #[cfg(feature = "qlog")]
156        {
157            let Some(stream) = self.stream.as_ref() else {
158                return;
159            };
160
161            let event = PacketReceived {
162                header: PacketHeader {
163                    packet_number: Some(pn),
164                    packet_type: packet_type(space, is_0rtt),
165                    ..Default::default()
166                },
167                ..Default::default()
168            };
169
170            stream.emit_event(orig_rem_cid, EventData::PacketReceived(event), now);
171        }
172    }
173}
174
175#[cfg(feature = "qlog")]
176impl From<Option<QlogStream>> for QlogSink {
177    fn from(stream: Option<QlogStream>) -> Self {
178        Self { stream }
179    }
180}
181
182#[cfg(feature = "qlog")]
183fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
184    match space {
185        SpaceId::Initial => PacketType::Initial,
186        SpaceId::Handshake => PacketType::Handshake,
187        SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
188        SpaceId::Data => PacketType::OneRtt,
189    }
190}