Skip to main content

leyline/trace/
summary.rs

1use std::fmt;
2use std::time::Duration;
3
4use http::StatusCode;
5use url::Url;
6
7use super::ms;
8use crate::profile::Browser;
9use crate::{Error, HttpVersion};
10
11#[non_exhaustive]
12pub struct Summary<'a> {
13    pub id: u64,
14    pub method: &'a str,
15    pub url: Option<&'a Url>,
16    pub original_url: &'a str,
17    pub redirects: usize,
18    pub status: Option<StatusCode>,
19    pub version: Option<HttpVersion>,
20    pub attempts: u32,
21    pub elapsed: Duration,
22    pub outcome: Result<(), &'a Error>,
23    pub streamed: bool,
24    pub tag: Option<&'a str>,
25    pub proxy: Option<String>,
26    pub browser: Option<Browser>,
27}
28
29impl fmt::Debug for Summary<'_> {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("Summary")
32            .field("id", &self.id)
33            .field("method", &self.method)
34            .field("url", &self.url.map(|u| crate::util::redact(u.as_str())))
35            .field("original_url", &crate::util::redact(self.original_url))
36            .field("redirects", &self.redirects)
37            .field("status", &self.status)
38            .field("version", &self.version)
39            .field("attempts", &self.attempts)
40            .field("elapsed", &self.elapsed)
41            .field("outcome", &self.outcome)
42            .field("streamed", &self.streamed)
43            .field("tag", &self.tag)
44            .field("proxy", &self.proxy)
45            .field("browser", &self.browser)
46            .finish()
47    }
48}
49
50pub(crate) struct Finish<'a> {
51    pub(crate) method: &'a str,
52    pub(crate) original_url: &'a str,
53    pub(crate) attempts: u32,
54    pub(crate) streamed: bool,
55}
56
57pub(crate) fn summary(finish: &Finish<'_>, out: Result<&crate::Response, &Error>) {
58    super::with(|ctx| {
59        let notes = super::notes::take(&ctx.notes);
60        let parsed;
61        let (url, redirects, status, version, outcome) = match out {
62            Ok(r) => (
63                Some(r.url()),
64                r.redirect_chain().len(),
65                Some(r.status()),
66                Some(r.version()),
67                Ok(()),
68            ),
69            Err(e) => {
70                parsed = Url::parse(finish.original_url).ok();
71                (parsed.as_ref(), 0, None, None, Err(e))
72            }
73        };
74        ctx.hook.summary(&Summary {
75            id: ctx.id,
76            method: finish.method,
77            url,
78            original_url: finish.original_url,
79            redirects,
80            status,
81            version,
82            attempts: finish.attempts,
83            elapsed: ctx.start.elapsed(),
84            outcome,
85            streamed: finish.streamed,
86            tag: notes.tag.as_deref(),
87            proxy: notes.proxy,
88            browser: notes.browser,
89        });
90    });
91}
92
93pub(super) fn render(ev: &Summary<'_>) {
94    let url = crate::util::redact(ev.url.map_or(ev.original_url, Url::as_str));
95    let status = ev.status.map(|s| s.as_u16());
96    let version = ev.version.map(tracing::field::debug);
97    let elapsed_ms = ms(ev.elapsed);
98    let tag = ev.tag;
99    let proxy = ev.proxy.as_deref();
100    let browser = ev.browser.map(tracing::field::debug);
101    match ev.outcome {
102        Ok(()) => {
103            tracing::info!(target: "leyline::trace", id = ev.id, method = ev.method, url = %url, redirects = ev.redirects, status, version, attempts = ev.attempts, elapsed_ms, streamed = ev.streamed, tag, proxy, browser, outcome = "ok", "request")
104        }
105        Err(e) => {
106            tracing::info!(target: "leyline::trace", id = ev.id, method = ev.method, url = %url, redirects = ev.redirects, status, version, attempts = ev.attempts, elapsed_ms, streamed = ev.streamed, tag, proxy, browser, outcome = "error", kind = ?e.kind(), error = %e, "request")
107        }
108    }
109}