use std::borrow::Cow;
use std::marker::PhantomData;
#[cfg(feature = "prometheus")]
use metrics_exporter_prometheus::PrometheusHandle;
use crate::{set_prefix, GenericMetricLayer, MakeDefaultHandle, Traffic};
#[doc(hidden)]
mod sealed {
use super::{LayerOnly, Paired};
pub trait Sealed {}
impl Sealed for LayerOnly {}
impl Sealed for Paired {}
}
pub trait MetricBuilderState: sealed::Sealed {}
pub enum Paired {}
pub enum LayerOnly {}
impl MetricBuilderState for Paired {}
impl MetricBuilderState for LayerOnly {}
#[derive(Default, Clone)]
pub enum EndpointLabel {
Exact,
#[default]
MatchedPath,
MatchedPathWithFallbackFn(for<'f> fn(&'f str) -> String),
}
#[derive(Clone, Default)]
pub struct MetricLayerBuilder<'a, T, M, S: MetricBuilderState> {
pub(crate) traffic: Traffic<'a>,
pub(crate) metric_handle: Option<T>,
pub(crate) metric_prefix: Option<String>,
pub(crate) enable_body_size: bool,
pub(crate) no_initialize_metrics: bool,
pub(crate) _marker: PhantomData<(S, M)>,
}
impl<'a, T, M, S> MetricLayerBuilder<'a, T, M, S>
where
S: MetricBuilderState,
{
pub fn with_ignore_pattern(mut self, ignore_pattern: &'a str) -> Self {
self.traffic.with_ignore_pattern(ignore_pattern);
self
}
pub fn with_ignore_patterns(mut self, ignore_patterns: &'a [&'a str]) -> Self {
self.traffic.with_ignore_patterns(ignore_patterns);
self
}
pub fn with_allow_pattern(mut self, allow_pattern: &'a str) -> Self {
self.traffic.with_allow_pattern(allow_pattern);
self
}
pub fn with_allow_patterns(mut self, allow_patterns: &'a [&'a str]) -> Self {
self.traffic.with_allow_patterns(allow_patterns);
self
}
pub fn with_group_patterns_as(
mut self,
group_pattern: &'a str,
patterns: &'a [&'a str],
) -> Self {
self.traffic.with_group_patterns_as(group_pattern, patterns);
self
}
pub fn with_endpoint_label_type(mut self, endpoint_label: EndpointLabel) -> Self {
self.traffic.with_endpoint_label_type(endpoint_label);
self
}
pub fn enable_response_body_size(mut self, enable: bool) -> Self {
self.enable_body_size = enable;
self
}
pub fn no_initialize_metrics(mut self) -> Self {
self.no_initialize_metrics = true;
self
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, LayerOnly> {
pub fn new() -> MetricLayerBuilder<'a, T, M, LayerOnly> {
MetricLayerBuilder {
_marker: PhantomData,
traffic: Traffic::new(),
metric_handle: None,
no_initialize_metrics: false,
metric_prefix: None,
enable_body_size: false,
}
}
pub fn with_prefix(mut self, prefix: impl Into<Cow<'a, str>>) -> Self {
self.metric_prefix = Some(prefix.into().into_owned());
self
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, LayerOnly>
where
M: MakeDefaultHandle<Out = T>,
{
pub fn build(self) -> GenericMetricLayer<'a, T, M> {
GenericMetricLayer::from_builder(self)
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, LayerOnly>
where
M: MakeDefaultHandle<Out = T> + Default,
{
pub fn with_default_metrics(self) -> MetricLayerBuilder<'a, T, M, Paired> {
let mut builder = MetricLayerBuilder::<'_, _, _, Paired>::from_layer_only(self);
builder.metric_handle = Some(M::make_default_handle(M::default()));
builder
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, LayerOnly> {
pub fn with_metrics_from_fn(
self,
f: impl FnOnce() -> T,
) -> MetricLayerBuilder<'a, T, M, Paired> {
let mut builder = MetricLayerBuilder::<'_, _, _, Paired>::from_layer_only(self);
builder.metric_handle = Some(f());
builder
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, Paired> {
pub(crate) fn from_layer_only(layer_only: MetricLayerBuilder<'a, T, M, LayerOnly>) -> Self {
if let Some(prefix) = layer_only.metric_prefix.as_ref() {
set_prefix(prefix);
}
if !layer_only.no_initialize_metrics {
describe_metrics(layer_only.enable_body_size);
}
MetricLayerBuilder {
_marker: PhantomData,
traffic: layer_only.traffic,
metric_handle: layer_only.metric_handle,
no_initialize_metrics: layer_only.no_initialize_metrics,
metric_prefix: layer_only.metric_prefix,
enable_body_size: layer_only.enable_body_size,
}
}
}
impl<'a, T, M> MetricLayerBuilder<'a, T, M, Paired>
where
M: MakeDefaultHandle<Out = T> + Default,
{
pub fn build_pair(self) -> (GenericMetricLayer<'a, T, M>, T) {
GenericMetricLayer::pair_from_builder(self)
}
}
#[cfg(feature = "prometheus")]
pub type PrometheusMetricLayerBuilder<'a, S> =
MetricLayerBuilder<'a, PrometheusHandle, crate::Handle, S>;
fn describe_metrics(enable_body_size: bool) {
metrics::describe_counter!(
crate::utils::requests_total_name(),
metrics::Unit::Count,
"The number of times a HTTP request was processed."
);
metrics::describe_gauge!(
crate::utils::requests_pending_name(),
metrics::Unit::Count,
"The number of currently in-flight requests."
);
metrics::describe_histogram!(
crate::utils::requests_duration_name(),
metrics::Unit::Seconds,
"The distribution of HTTP response times."
);
if enable_body_size {
metrics::describe_histogram!(
crate::utils::response_body_size_name(),
metrics::Unit::Count,
"The distribution of HTTP response body sizes."
);
}
}