Skip to main content

glass/browser/session/
diagnostic.rs

1//! Scoped diagnostic evidence collection.
2//!
3//! Produces a bounded [`DiagnosticReport`] with console messages, network
4//! evidence, and page context. All secrets are redacted before output.
5
6use super::*;
7
8impl BrowserSession {
9    /// Collect explicitly scoped, bounded, secret-redacted browser evidence.
10    pub async fn diagnostics(&self, duration: Duration) -> BrowserResult<DiagnosticReport> {
11        if duration.is_zero() || duration > MAX_DIAGNOSTIC_DURATION {
12            return Err("diagnostic duration must be between 1 ms and 30 seconds".into());
13        }
14        self.cdp
15            .with_current_route(async {
16                let (target_id, frame_id) = self.route_identity().await?;
17                let route_session_id = self.cdp.current_session_id();
18                let mut events = self.cdp.subscribe_events_with_params();
19                let mut guard = DiagnosticDomainGuard::acquire(
20                    self.cdp.clone(),
21                    Arc::clone(&self.network_wait_leases),
22                    Arc::clone(&self.diagnostic_leases),
23                )
24                .await?;
25                let started = tokio::time::Instant::now();
26                let deadline = started + duration;
27                let mut console = Vec::new();
28                let mut network = Vec::new();
29                let mut request_indexes = HashMap::new();
30                let mut dropped_events = 0_u64;
31                loop {
32                    tokio::select! {
33                        _ = tokio::time::sleep_until(deadline) => break,
34                        event = events.recv() => match event {
35                            Ok(event) if event.session_id == route_session_id => collect_diagnostic_event(
36                                &event,
37                                &mut console,
38                                &mut network,
39                                &mut request_indexes,
40                                &mut dropped_events,
41                            ),
42                            Ok(_) => {}
43                            Err(tokio::sync::broadcast::error::RecvError::Lagged(count)) => {
44                                dropped_events = dropped_events.saturating_add(count);
45                            }
46                            Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
47                        }
48                    }
49                }
50                guard.disable().await?;
51                Ok(DiagnosticReport {
52                    target_id,
53                    frame_id,
54                    duration_ms: started.elapsed().as_millis() as u64,
55                    console,
56                    network,
57                    dropped_events,
58                })
59            })
60            .await
61    }
62}