Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 124 additions & 2 deletions linkerd/app/inbound/src/http/router/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use linkerd_http_prom::{
pub(super) fn layer<N>(
InboundMetrics {
request_count,
request_body_data,
response_body_data,
..
}: &InboundMetrics,
Expand All @@ -34,11 +35,16 @@ pub(super) fn layer<N>(
NewRecordResponseBodyData::layer_via(extract)
};

svc::layer::mk(move |inner| count.layer(body.layer(inner)))
let request = {
let extract = ExtractRequestBodyDataParams(request_body_data.clone());
NewRecordRequestBodyData::new(extract)
};

svc::layer::mk(move |inner| count.layer(body.layer(request.layer(inner))))
}

/// An `N`-typed service instrumented with metrics middleware.
type Instrumented<N> = NewCountRequests<NewRecordResponseBodyData<N>>;
type Instrumented<N> = NewCountRequests<NewRecordResponseBodyData<NewRecordRequestBodyData<N>>>;

/// An `N`-typed `NewService<T>` instrumented with request counting metrics.
type NewCountRequests<N> = count_reqs::NewCountRequests<ExtractRequestCount, N>;
Expand Down Expand Up @@ -75,6 +81,30 @@ pub struct ResponseBodyDataLabels {
#[derive(Clone, Debug)]
pub struct ExtractResponseBodyDataMetrics(ResponseBodyFamilies);

/// An `N`-typed `NewService<T>` instrumented with request body metrics.
type NewRecordRequestBodyData<N> = body_data::request::NewRecordBodyData<
N,
ExtractRequestBodyDataParams,
ExtractRequestBodyDataMetrics,
>;

#[derive(Clone, Debug)]
pub struct RequestBodyFamilies {
grpc: body_data::request::RequestBodyFamilies<RequestBodyDataLabels>,
http: body_data::request::RequestBodyFamilies<RequestBodyDataLabels>,
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct RequestBodyDataLabels {
route: RouteLabels,
}

#[derive(Clone, Debug)]
pub struct ExtractRequestBodyDataParams(RequestBodyFamilies);

#[derive(Clone, Debug)]
pub struct ExtractRequestBodyDataMetrics(BodyDataMetrics);

// === impl RequestCountFamilies ===

impl RequestCountFamilies {
Expand Down Expand Up @@ -236,3 +266,95 @@ where
family.metrics(&ResponseBodyDataLabels { route })
}
}

// === impl RequestBodyFamilies ===

impl RequestBodyFamilies {
/// Registers a new [`RequestBodyDataFamilies`] with the given registry.
pub fn register(reg: &mut prom::Registry) -> Self {
let grpc = {
let reg = reg.sub_registry_with_prefix("grpc");
body_data::request::RequestBodyFamilies::register(reg)
};

let http = {
let reg = reg.sub_registry_with_prefix("http");
body_data::request::RequestBodyFamilies::register(reg)
};

Self { grpc, http }
}

/// Fetches the proper body frame metrics family, given a permitted target.
fn family(
&self,
variant: PermitVariant,
) -> &body_data::request::RequestBodyFamilies<RequestBodyDataLabels> {
let Self { grpc, http } = self;
match variant {
PermitVariant::Grpc => grpc,
PermitVariant::Http => http,
}
}
}

// === impl RequestBodyDataLabels ===

impl EncodeLabelSetMut for RequestBodyDataLabels {
fn encode_label_set(&self, enc: &mut LabelSetEncoder<'_>) -> std::fmt::Result {
use encoding::EncodeLabel as _;

let Self {
route:
RouteLabels {
server: ServerLabel(parent, port),
route,
},
} = self;

("parent_group", parent.group()).encode(enc.encode_label())?;
("parent_kind", parent.kind()).encode(enc.encode_label())?;
("parent_name", parent.name()).encode(enc.encode_label())?;
("parent_port", *port).encode(enc.encode_label())?;

("route_group", route.group()).encode(enc.encode_label())?;
("route_kind", route.kind()).encode(enc.encode_label())?;
("route_name", route.name()).encode(enc.encode_label())?;

Ok(())
}
}

Comment on lines +303 to +327
Copy link
Member

@olix0r olix0r Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we need a new labels impl. Can we call RouteLabels::encode?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

within the scope of this pull request, because i'm following the same pattern that was established in #4127 and #4165.

zooming out further, because linkerd_app_core's RouteLabels structure does not implement EncodeLabelSet. you may be thinking of the similarly named types in linkerd/app/outbound/src/opaq/logical/route.rs and linkerd/app/outbound/src/tls/logical/route.rs?

i do, fwiw, think that there's an unfortunately high amount of boilerplate in each of these layers. do you think we should follow the example to the outbound proxy, and introduce a RouteLabels structure here that does offer an encode() implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should reduce the boilerplate but it's not a blocker.

impl EncodeLabelSet for RequestBodyDataLabels {
fn encode(&self, mut enc: LabelSetEncoder<'_>) -> std::fmt::Result {
self.encode_label_set(&mut enc)
}
}

// === impl ExtractRequestBodyDataParams ===

impl<T> svc::ExtractParam<ExtractRequestBodyDataMetrics, T> for ExtractRequestBodyDataParams
where
T: svc::Param<PermitVariant> + svc::Param<RouteLabels>,
{
fn extract_param(&self, target: &T) -> ExtractRequestBodyDataMetrics {
let Self(families) = self;
let variant = target.param();
let route = target.param();

let family = families.family(variant);
let metrics = family.metrics(&RequestBodyDataLabels { route });

ExtractRequestBodyDataMetrics(metrics)
}
}

// === impl ExtractRequestBodyDataMetrics ===

impl<T> svc::ExtractParam<BodyDataMetrics, T> for ExtractRequestBodyDataMetrics {
fn extract_param(&self, _: &T) -> BodyDataMetrics {
let Self(metrics) = self;

metrics.clone()
}
}
5 changes: 4 additions & 1 deletion linkerd/app/inbound/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
pub(crate) mod authz;
pub(crate) mod error;

use crate::http::router::{RequestCountFamilies, ResponseBodyFamilies};
use crate::http::router::{RequestBodyFamilies, RequestCountFamilies, ResponseBodyFamilies};
pub use linkerd_app_core::metrics::*;

/// Holds LEGACY inbound proxy metrics.
Expand All @@ -30,6 +30,7 @@ pub struct InboundMetrics {
pub detect: crate::detect::MetricsFamilies,
pub direct: crate::direct::MetricsFamilies,
pub request_count: RequestCountFamilies,
pub request_body_data: RequestBodyFamilies,
pub response_body_data: ResponseBodyFamilies,
}

Expand All @@ -41,6 +42,7 @@ impl InboundMetrics {
reg.sub_registry_with_prefix("tcp_transport_header"),
);
let request_count = RequestCountFamilies::register(reg);
let request_body_data = RequestBodyFamilies::register(reg);
let response_body_data = ResponseBodyFamilies::register(reg);

Self {
Expand All @@ -52,6 +54,7 @@ impl InboundMetrics {
detect,
direct,
request_count,
request_body_data,
response_body_data,
}
}
Expand Down
Loading