tower_http/trace/
on_eos.rs

1use super::{Latency, DEFAULT_MESSAGE_LEVEL};
2use crate::{classify::grpc_errors_as_failures::ParsedGrpcStatus, LatencyUnit};
3use http::header::HeaderMap;
4use std::time::Duration;
5use tracing::{Level, Span};
6
7/// Trait used to tell [`Trace`] what to do when a stream closes.
8///
9/// See the [module docs](../trace/index.html#on_eos) for details on exactly when the `on_eos`
10/// callback is called.
11///
12/// [`Trace`]: super::Trace
13pub trait OnEos {
14    /// Do the thing.
15    ///
16    /// `stream_duration` is the duration since the response was sent.
17    ///
18    /// `span` is the `tracing` [`Span`], corresponding to this request, produced by the closure
19    /// passed to [`TraceLayer::make_span_with`]. It can be used to [record field values][record]
20    /// that weren't known when the span was created.
21    ///
22    /// [`Span`]: https://docs.rs/tracing/latest/tracing/span/index.html
23    /// [record]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html#method.record
24    /// [`TraceLayer::make_span_with`]: crate::trace::TraceLayer::make_span_with
25    fn on_eos(self, trailers: Option<&HeaderMap>, stream_duration: Duration, span: &Span);
26}
27
28impl OnEos for () {
29    #[inline]
30    fn on_eos(self, _: Option<&HeaderMap>, _: Duration, _: &Span) {}
31}
32
33impl<F> OnEos for F
34where
35    F: FnOnce(Option<&HeaderMap>, Duration, &Span),
36{
37    fn on_eos(self, trailers: Option<&HeaderMap>, stream_duration: Duration, span: &Span) {
38        self(trailers, stream_duration, span)
39    }
40}
41
42/// The default [`OnEos`] implementation used by [`Trace`].
43///
44/// [`Trace`]: super::Trace
45#[derive(Clone, Debug)]
46pub struct DefaultOnEos {
47    level: Level,
48    latency_unit: LatencyUnit,
49}
50
51impl Default for DefaultOnEos {
52    fn default() -> Self {
53        Self {
54            level: DEFAULT_MESSAGE_LEVEL,
55            latency_unit: LatencyUnit::Millis,
56        }
57    }
58}
59
60impl DefaultOnEos {
61    /// Create a new [`DefaultOnEos`].
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    /// Set the [`Level`] used for [tracing events].
67    ///
68    /// Defaults to [`Level::DEBUG`].
69    ///
70    /// [tracing events]: https://docs.rs/tracing/latest/tracing/#events
71    /// [`Level::DEBUG`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.DEBUG
72    pub fn level(mut self, level: Level) -> Self {
73        self.level = level;
74        self
75    }
76
77    /// Set the [`LatencyUnit`] latencies will be reported in.
78    ///
79    /// Defaults to [`LatencyUnit::Millis`].
80    pub fn latency_unit(mut self, latency_unit: LatencyUnit) -> Self {
81        self.latency_unit = latency_unit;
82        self
83    }
84}
85
86impl OnEos for DefaultOnEos {
87    fn on_eos(self, trailers: Option<&HeaderMap>, stream_duration: Duration, _span: &Span) {
88        let stream_duration = Latency {
89            unit: self.latency_unit,
90            duration: stream_duration,
91        };
92        let status = trailers.and_then(|trailers| {
93            match crate::classify::grpc_errors_as_failures::classify_grpc_metadata(
94                trailers,
95                crate::classify::GrpcCode::Ok.into_bitmask(),
96            ) {
97                ParsedGrpcStatus::Success
98                | ParsedGrpcStatus::HeaderNotString
99                | ParsedGrpcStatus::HeaderNotInt => Some(0),
100                ParsedGrpcStatus::NonSuccess(status) => Some(status.get()),
101                ParsedGrpcStatus::GrpcStatusHeaderMissing => None,
102            }
103        });
104
105        event_dynamic_lvl!(self.level, %stream_duration, status, "end of stream");
106    }
107}