-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(app): Backend response frame count metrics
this introduces a new tower middleware for Prometheus metrics, used for instrumenting HTTP and gRPC response bodies, and observing (a) the number of frames yielded by a body, and (b) the number of bytes included in body frames. this middleware allows operators to reason about how large or small the packets being served in a backend's response bodies are. a route-level middleware that instruments request bodies will be added in a follow-on PR. ### 📝 changes an overview of changes made here: * the `linkerd-http-prom` has a new `body_data` submodule. it exposes `request` and `response` halves, to be explicit about which body is being instrumented on a `tower::Service`. * the `linkerd-http-prom` crate now has a collection of new dependencies: `bytes` is added as a dependency in order to inspect the data chunk when the inner body yields a new frame. `futures-util` and `http-body` are added as dev-dependencies for the accompanying test coverage. * body metrics are affixed to the `RouteBackendMetrics<L>` structure, and registered at startup. Signed-off-by: katelyn martin <[email protected]>
- Loading branch information
Showing
11 changed files
with
467 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pub mod request; | ||
pub mod response; | ||
|
||
mod body; | ||
mod metrics; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
use super::metrics::BodyDataMetrics; | ||
use http::HeaderMap; | ||
use http_body::SizeHint; | ||
use pin_project::pin_project; | ||
use std::{ | ||
pin::Pin, | ||
task::{Context, Poll}, | ||
}; | ||
|
||
/// An instrumented body. | ||
#[pin_project] | ||
pub struct Body<B> { | ||
/// The inner body. | ||
#[pin] | ||
inner: B, | ||
/// Metrics with which the inner body will be instrumented. | ||
metrics: BodyDataMetrics, | ||
} | ||
|
||
impl<B> Body<B> { | ||
/// Returns a new, instrumented body. | ||
pub(crate) fn new(body: B, metrics: BodyDataMetrics) -> Self { | ||
Self { | ||
inner: body, | ||
metrics, | ||
} | ||
} | ||
} | ||
|
||
impl<B> http_body::Body for Body<B> | ||
where | ||
B: http_body::Body, | ||
{ | ||
type Data = B::Data; | ||
type Error = B::Error; | ||
|
||
/// Attempt to pull out the next data buffer of this stream. | ||
fn poll_data( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
) -> Poll<Option<Result<Self::Data, Self::Error>>> { | ||
let this = self.project(); | ||
let inner = this.inner; | ||
let BodyDataMetrics { | ||
frames_total, | ||
frames_bytes, | ||
} = this.metrics; | ||
|
||
let data = std::task::ready!(inner.poll_data(cx)); | ||
|
||
if let Some(Ok(data)) = data.as_ref() { | ||
// We've polled and yielded a new chunk! Increment our telemetry. | ||
// | ||
// NB: We're careful to call `remaining()` rather than `chunk()`, which | ||
// "can return a shorter slice (this allows non-continuous internal representation)." | ||
let bytes = <B::Data as bytes::Buf>::remaining(data) | ||
.try_into() | ||
.unwrap_or(u64::MAX); | ||
frames_bytes.inc_by(bytes.into()); | ||
Check failure on line 59 in linkerd/http/prom/src/body_data/body.rs GitHub Actions / rust
|
||
frames_total.inc(); | ||
} | ||
|
||
Poll::Ready(data) | ||
} | ||
|
||
fn poll_trailers( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
) -> Poll<Result<Option<HeaderMap>, Self::Error>> { | ||
self.project().inner.poll_trailers(cx) | ||
} | ||
|
||
fn is_end_stream(&self) -> bool { | ||
self.inner.is_end_stream() | ||
} | ||
|
||
fn size_hint(&self) -> SizeHint { | ||
self.inner.size_hint() | ||
} | ||
} |
Oops, something went wrong.