1use std::collections::HashMap;
5use tracing::{Span, debug, info, warn};
6
7use super::{ConnectionRole, LogEvent, logger};
8use crate::{ConnectionId, Duration, Instant};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ConnectionState {
13 Initiated,
15 Handshaking,
17 Established,
19 Migrating,
21 Closing,
23 Closed,
25 Lost,
27}
28
29pub struct ConnectionLifecycle {
31 pub conn_id: ConnectionId,
32 pub role: ConnectionRole,
33 pub state: ConnectionState,
34 pub initiated_at: Instant,
35 pub handshake_started_at: Option<Instant>,
36 pub established_at: Option<Instant>,
37 pub closed_at: Option<Instant>,
38 pub close_reason: Option<String>,
39 pub total_bytes_sent: u64,
40 pub total_bytes_received: u64,
41 pub total_packets_sent: u64,
42 pub total_packets_received: u64,
43}
44
45impl ConnectionLifecycle {
46 pub fn new(conn_id: ConnectionId, role: ConnectionRole) -> Self {
48 Self {
49 conn_id,
50 role,
51 state: ConnectionState::Initiated,
52 initiated_at: Instant::now(),
53 handshake_started_at: None,
54 established_at: None,
55 closed_at: None,
56 close_reason: None,
57 total_bytes_sent: 0,
58 total_bytes_received: 0,
59 total_packets_sent: 0,
60 total_packets_received: 0,
61 }
62 }
63
64 pub fn update_state(&mut self, new_state: ConnectionState) {
66 let old_state = self.state;
67 self.state = new_state;
68
69 match new_state {
70 ConnectionState::Handshaking => {
71 self.handshake_started_at = Some(Instant::now());
72 }
73 ConnectionState::Established => {
74 self.established_at = Some(Instant::now());
75 }
76 ConnectionState::Closed | ConnectionState::Lost => {
77 self.closed_at = Some(Instant::now());
78 }
79 _ => {}
80 }
81
82 self.log_state_transition(old_state, new_state);
83 }
84
85 fn log_state_transition(&self, old_state: ConnectionState, new_state: ConnectionState) {
87 let mut fields = HashMap::new();
88 fields.insert("conn_id".to_string(), format!("{:?}", self.conn_id));
89 fields.insert("role".to_string(), format!("{:?}", self.role));
90 fields.insert("old_state".to_string(), format!("{old_state:?}"));
91 fields.insert("new_state".to_string(), format!("{new_state:?}"));
92
93 if let Some(duration) = self.duration_in_state(old_state) {
95 fields.insert("duration_ms".to_string(), duration.as_millis().to_string());
96 }
97
98 let level = match new_state {
99 ConnectionState::Lost => tracing::Level::WARN,
100 ConnectionState::Established => tracing::Level::INFO,
101 _ => tracing::Level::DEBUG,
102 };
103
104 logger().log_event(LogEvent {
105 timestamp: Instant::now(),
106 level,
107 target: "ant_quic::connection::lifecycle".to_string(),
108 message: "connection_state_changed".to_string(),
109 fields,
110 span_id: None,
111 });
112 }
113
114 fn duration_in_state(&self, state: ConnectionState) -> Option<Duration> {
116 match state {
117 ConnectionState::Initiated => {
118 let end = self.handshake_started_at.unwrap_or_else(Instant::now);
119 Some(end.duration_since(self.initiated_at))
120 }
121 ConnectionState::Handshaking => {
122 if let Some(start) = self.handshake_started_at {
123 let end = self.established_at.unwrap_or_else(Instant::now);
124 Some(end.duration_since(start))
125 } else {
126 None
127 }
128 }
129 ConnectionState::Established => {
130 if let Some(start) = self.established_at {
131 let end = self.closed_at.unwrap_or_else(Instant::now);
132 Some(end.duration_since(start))
133 } else {
134 None
135 }
136 }
137 _ => None,
138 }
139 }
140
141 pub fn log_summary(&self) {
143 let total_duration = self
144 .closed_at
145 .unwrap_or_else(Instant::now)
146 .duration_since(self.initiated_at);
147
148 let mut fields = HashMap::new();
149 fields.insert("conn_id".to_string(), format!("{:?}", self.conn_id));
150 fields.insert("role".to_string(), format!("{:?}", self.role));
151 fields.insert(
152 "total_duration_ms".to_string(),
153 total_duration.as_millis().to_string(),
154 );
155 fields.insert("bytes_sent".to_string(), self.total_bytes_sent.to_string());
156 fields.insert(
157 "bytes_received".to_string(),
158 self.total_bytes_received.to_string(),
159 );
160 fields.insert(
161 "packets_sent".to_string(),
162 self.total_packets_sent.to_string(),
163 );
164 fields.insert(
165 "packets_received".to_string(),
166 self.total_packets_received.to_string(),
167 );
168
169 if let Some(handshake_duration) = self.duration_in_state(ConnectionState::Handshaking) {
170 fields.insert(
171 "handshake_duration_ms".to_string(),
172 handshake_duration.as_millis().to_string(),
173 );
174 }
175
176 if let Some(established_duration) = self.duration_in_state(ConnectionState::Established) {
177 fields.insert(
178 "established_duration_ms".to_string(),
179 established_duration.as_millis().to_string(),
180 );
181 }
182
183 if let Some(reason) = &self.close_reason {
184 fields.insert("close_reason".to_string(), reason.clone());
185 }
186
187 logger().log_event(LogEvent {
188 timestamp: Instant::now(),
189 level: tracing::Level::INFO,
190 target: "ant_quic::connection::lifecycle".to_string(),
191 message: "connection_summary".to_string(),
192 fields,
193 span_id: None,
194 });
195 }
196}
197
198pub fn log_connection_initiated(
200 conn_id: &ConnectionId,
201 role: ConnectionRole,
202 remote_addr: std::net::SocketAddr,
203) {
204 info!(
205 target: "ant_quic::connection::lifecycle",
206 conn_id = ?conn_id,
207 role = ?role,
208 remote_addr = %remote_addr,
209 "Connection initiated"
210 );
211}
212
213pub fn log_handshake_started(conn_id: &ConnectionId) {
214 debug!(
215 target: "ant_quic::connection::lifecycle",
216 conn_id = ?conn_id,
217 "Handshake started"
218 );
219}
220
221pub fn log_handshake_completed(conn_id: &ConnectionId, duration: Duration) {
222 info!(
223 target: "ant_quic::connection::lifecycle",
224 conn_id = ?conn_id,
225 duration_ms = duration.as_millis(),
226 "Handshake completed"
227 );
228}
229
230pub fn log_connection_established(conn_id: &ConnectionId, negotiated_version: u32) {
231 info!(
232 target: "ant_quic::connection::lifecycle",
233 conn_id = ?conn_id,
234 negotiated_version = format!("0x{:08x}", negotiated_version),
235 "Connection established"
236 );
237}
238
239pub fn log_connection_migration(conn_id: &ConnectionId, old_path: &str, new_path: &str) {
240 info!(
241 target: "ant_quic::connection::lifecycle",
242 conn_id = ?conn_id,
243 old_path = old_path,
244 new_path = new_path,
245 "Connection migrated to new path"
246 );
247}
248
249pub fn log_connection_closed(conn_id: &ConnectionId, reason: &str, error_code: Option<u64>) {
250 let mut fields = HashMap::new();
251 fields.insert("conn_id".to_string(), format!("{conn_id:?}"));
252 fields.insert("reason".to_string(), reason.to_string());
253
254 if let Some(code) = error_code {
255 fields.insert("error_code".to_string(), format!("0x{code:x}"));
256 }
257
258 logger().log_event(LogEvent {
259 timestamp: Instant::now(),
260 level: tracing::Level::DEBUG,
261 target: "ant_quic::connection::lifecycle".to_string(),
262 message: "connection_closed".to_string(),
263 fields,
264 span_id: None,
265 });
266}
267
268pub fn log_connection_lost(conn_id: &ConnectionId, reason: &str) {
269 warn!(
270 target: "ant_quic::connection::lifecycle",
271 conn_id = ?conn_id,
272 reason = reason,
273 "Connection lost"
274 );
275}
276
277pub fn create_connection_lifetime_span(conn_id: &ConnectionId, role: ConnectionRole) -> Span {
279 tracing::span!(
280 tracing::Level::INFO,
281 "connection_lifetime",
282 conn_id = %format!("{:?}", conn_id),
283 role = ?role,
284 )
285}