risingwave_common_metrics/
relabeled_metric.rs

1// Copyright 2024 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use prometheus::core::{Collector, MetricVec, MetricVecBuilder};
16use prometheus::{HistogramVec, IntCounterVec};
17
18use crate::{
19    LabelGuardedHistogramVec, LabelGuardedIntCounterVec, LabelGuardedIntGaugeVec,
20    LabelGuardedMetric, LabelGuardedMetricVec, LazyLabelGuardedMetrics, MetricLevel,
21};
22
23/// For all `Relabeled*Vec` below,
24/// - when `metric_level` <= `relabel_threshold`, they behave exactly the same as their inner
25///   metric.
26/// - when `metric_level` > `relabel_threshold`, the first `relabel_num` labels are rewrite to "" when
27///   calling `with_label_values`. That's means the metric vec is aggregated into a single metric.
28///
29/// These wrapper classes add a `metric_level` field to corresponding metric.
30/// We could have use one single struct to represent all `MetricVec<T: MetricVecBuilder>`, rather
31/// than specializing them one by one. However, that's undoable because prometheus crate doesn't
32/// export `MetricVecBuilder` implementation like `HistogramVecBuilder`.
33///
34/// ## Note
35///
36/// CAUTION! Relabelling might cause expected result!
37///
38/// For counters (including histogram because it uses counters internally), it's usually natural
39/// to sum up the count from multiple labels.
40///
41/// For the rest (such as Gauge), the semantics becomes "any/last of the recorded value". Please be
42/// cautious.
43#[derive(Clone, Debug)]
44pub struct RelabeledMetricVec<M> {
45    relabel_threshold: MetricLevel,
46    metric_level: MetricLevel,
47    metric: M,
48
49    /// The first `relabel_num` labels will be relabeled to empty string
50    ///
51    /// For example, if `relabel_num` is 1, and the input labels are `["actor_id",
52    /// "fragment_id", "table_id"]`, when threshold is reached, the label values will be
53    /// `["", "<original_fragment_id>", "<original_table_id>"]`.
54    relabel_num: usize,
55}
56
57impl<M> RelabeledMetricVec<M> {
58    pub fn with_metric_level(
59        metric_level: MetricLevel,
60        metric: M,
61        relabel_threshold: MetricLevel,
62    ) -> Self {
63        Self {
64            relabel_threshold,
65            metric_level,
66            metric,
67            relabel_num: usize::MAX,
68        }
69    }
70
71    pub fn with_metric_level_relabel_n(
72        metric_level: MetricLevel,
73        metric: M,
74        relabel_threshold: MetricLevel,
75        relabel_num: usize,
76    ) -> Self {
77        Self {
78            relabel_threshold,
79            metric_level,
80            metric,
81            relabel_num,
82        }
83    }
84
85    fn relabel_impl<V: AsRef<str> + std::fmt::Debug>(&self, vals: &[V]) -> Option<Vec<String>> {
86        if self.metric_level > self.relabel_threshold {
87            // relabel first n labels to empty string
88            let mut relabeled_vals = vals
89                .iter()
90                .map(|v| v.as_ref().to_owned())
91                .collect::<Vec<_>>();
92            for label in relabeled_vals.iter_mut().take(self.relabel_num) {
93                *label = String::new();
94            }
95            Some(relabeled_vals)
96        } else {
97            None
98        }
99    }
100}
101
102#[easy_ext::ext(MetricVecRelabelExt)]
103impl<M> M
104where
105    M: Sized,
106{
107    /// Equivalent to [`RelabeledMetricVec::with_metric_level`].
108    pub fn relabel(
109        self,
110        metric_level: MetricLevel,
111        relabel_threshold: MetricLevel,
112    ) -> RelabeledMetricVec<M> {
113        RelabeledMetricVec::with_metric_level(metric_level, self, relabel_threshold)
114    }
115
116    /// Equivalent to [`RelabeledMetricVec::with_metric_level_relabel_n`].
117    pub fn relabel_n(
118        self,
119        metric_level: MetricLevel,
120        relabel_threshold: MetricLevel,
121        relabel_num: usize,
122    ) -> RelabeledMetricVec<M> {
123        RelabeledMetricVec::with_metric_level_relabel_n(
124            metric_level,
125            self,
126            relabel_threshold,
127            relabel_num,
128        )
129    }
130
131    /// Equivalent to [`RelabeledMetricVec::with_metric_level_relabel_n`] with `metric_level` set to
132    /// `MetricLevel::Debug` and `relabel_num` set to 1.
133    pub fn relabel_debug_1(self, relabel_threshold: MetricLevel) -> RelabeledMetricVec<M> {
134        RelabeledMetricVec::with_metric_level_relabel_n(
135            MetricLevel::Debug,
136            self,
137            relabel_threshold,
138            1,
139        )
140    }
141}
142
143impl<T: MetricVecBuilder> RelabeledMetricVec<MetricVec<T>> {
144    pub fn with_label_values<V: AsRef<str> + std::fmt::Debug>(&self, vals: &[V]) -> T::M {
145        if let Some(relabeled_vals) = self.relabel_impl(vals) {
146            return self.metric.with_label_values(&relabeled_vals);
147        }
148        self.metric.with_label_values(vals)
149    }
150}
151
152impl<T: MetricVecBuilder> RelabeledMetricVec<LabelGuardedMetricVec<T>> {
153    pub fn with_guarded_label_values<V: AsRef<str> + std::fmt::Debug>(
154        &self,
155        vals: &[V],
156    ) -> LabelGuardedMetric<T::M> {
157        if let Some(relabeled_vals) = self.relabel_impl(vals) {
158            return self.metric.with_guarded_label_values(&relabeled_vals);
159        }
160        self.metric.with_guarded_label_values(vals)
161    }
162
163    pub fn lazy_guarded_metrics(&self, labels: Vec<String>) -> LazyLabelGuardedMetrics<T> {
164        if let Some(relabeled_vals) = self.relabel_impl(labels.as_slice()) {
165            return self.metric.clone().lazy_guarded_metrics(relabeled_vals);
166        }
167        self.metric.clone().lazy_guarded_metrics(labels)
168    }
169}
170
171impl<T: Collector> Collector for RelabeledMetricVec<T> {
172    fn desc(&self) -> Vec<&prometheus::core::Desc> {
173        self.metric.desc()
174    }
175
176    fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {
177        self.metric.collect()
178    }
179}
180
181pub type RelabeledCounterVec = RelabeledMetricVec<IntCounterVec>;
182pub type RelabeledHistogramVec = RelabeledMetricVec<HistogramVec>;
183
184pub type RelabeledGuardedHistogramVec = RelabeledMetricVec<LabelGuardedHistogramVec>;
185pub type RelabeledGuardedIntCounterVec = RelabeledMetricVec<LabelGuardedIntCounterVec>;
186
187/// CAUTION! Relabelling a Gauge might cause expected result!
188///
189/// See [`RelabeledMetricVec`] for details.
190pub type RelabeledGuardedIntGaugeVec = RelabeledMetricVec<LabelGuardedIntGaugeVec>;