Skip to main content

leyline/trace/
body.rs

1use std::time::Duration;
2
3use bytes::Bytes;
4
5use super::{CURRENT, Ctx, ms};
6
7#[derive(Debug)]
8#[non_exhaustive]
9pub enum BodyOutcome<'a> {
10    Complete,
11    Failed(&'a std::io::Error),
12    Dropped,
13}
14
15#[derive(Debug)]
16#[non_exhaustive]
17pub struct BodyEnd<'a> {
18    pub id: u64,
19    pub bytes: u64,
20    pub elapsed: Duration,
21    pub outcome: BodyOutcome<'a>,
22}
23
24pub(crate) struct BodyWatch {
25    ctx: Ctx,
26    bytes: u64,
27    ended: bool,
28}
29
30impl BodyWatch {
31    pub(crate) fn begin() -> Option<Self> {
32        CURRENT
33            .try_with(|ctx| Self {
34                ctx: ctx.clone(),
35                bytes: 0,
36                ended: false,
37            })
38            .ok()
39    }
40
41    pub(crate) fn observe(&mut self, item: Option<&std::io::Result<Bytes>>) {
42        match item {
43            Some(Ok(chunk)) => self.bytes += chunk.len() as u64,
44            Some(Err(err)) => self.end(BodyOutcome::Failed(err)),
45            None => self.end(BodyOutcome::Complete),
46        }
47    }
48
49    fn end(&mut self, outcome: BodyOutcome<'_>) {
50        if self.ended {
51            return;
52        }
53        self.ended = true;
54        let (id, start) = self.ctx.root;
55        self.ctx.hook.body(&BodyEnd {
56            id,
57            bytes: self.bytes,
58            elapsed: start.elapsed(),
59            outcome,
60        });
61    }
62}
63
64impl Drop for BodyWatch {
65    fn drop(&mut self) {
66        if !std::thread::panicking() {
67            self.end(BodyOutcome::Dropped);
68        }
69    }
70}
71
72pub(super) fn render(ev: &BodyEnd<'_>) {
73    let elapsed_ms = ms(ev.elapsed);
74    match ev.outcome {
75        BodyOutcome::Complete => {
76            tracing::info!(target: "leyline::trace", id = ev.id, bytes = ev.bytes, elapsed_ms, outcome = "complete", "body")
77        }
78        BodyOutcome::Failed(err) => {
79            tracing::info!(target: "leyline::trace", id = ev.id, bytes = ev.bytes, elapsed_ms, outcome = "error", kind = ?err.kind(), error = %err, "body")
80        }
81        BodyOutcome::Dropped => {
82            tracing::info!(target: "leyline::trace", id = ev.id, bytes = ev.bytes, elapsed_ms, outcome = "dropped", "body")
83        }
84    }
85}