vector_core/metrics/
label_filter.rs

1use std::collections::HashSet;
2use std::sync::LazyLock;
3
4use metrics::{KeyName, Label};
5use metrics_tracing_context::LabelFilter;
6
7/// A label name that should be preserved on metric keys when present as a tracing-span field.
8///
9/// Both Vector's own built-in global labels and downstream-registered labels go through this
10/// type. Registrations are collected at link time via [`inventory`]. Downstream crates use the
11/// [`register_extra_span_field!`](crate::register_extra_span_field) macro to add one.
12#[derive(Debug)]
13pub struct MetricLabel(pub &'static str);
14
15inventory::collect!(MetricLabel);
16
17// Vector's own global labels are registered through the same mechanism so the filter only has
18// one allowlist to consult.
19inventory::submit!(MetricLabel("component_id"));
20inventory::submit!(MetricLabel("component_type"));
21inventory::submit!(MetricLabel("component_kind"));
22inventory::submit!(MetricLabel("buffer_type"));
23
24/// Snapshot of every registered [`MetricLabel`], built on first access. `inventory` populates
25/// all submissions before `main`, so the snapshot is guaranteed to capture every entry — the
26/// hot path is then a single set lookup against this static.
27pub static LABELS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
28    inventory::iter::<MetricLabel>()
29        .map(|label| label.0)
30        .collect()
31});
32
33#[derive(Debug, Clone)]
34pub(crate) struct VectorLabelFilter;
35
36impl LabelFilter for VectorLabelFilter {
37    fn should_include_label(&self, metric_key: &KeyName, label: &Label) -> bool {
38        let label_key = label.key();
39        // HTTP Server-specific labels
40        if metric_key.as_str().starts_with("http_server_")
41            && (label_key == "method" || label_key == "path")
42        {
43            return true;
44        }
45        // gRPC Server-specific labels
46        if metric_key.as_str().starts_with("grpc_server_")
47            && (label_key == "grpc_method" || label_key == "grpc_service")
48        {
49            return true;
50        }
51        // Globally-registered labels: Vector's own built-ins plus any registered by downstream
52        // crates via `register_extra_span_field!`.
53        LABELS.contains(label_key)
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use metrics::{KeyName, Label};
60    use metrics_tracing_context::LabelFilter;
61
62    use super::VectorLabelFilter;
63
64    // Use the macro so that removing either arm (MetricLabel or SpanField) from
65    // register_extra_span_field! would cause the tests below to fail, locking the
66    // dual-registration contract.
67    crate::register_extra_span_field!("test_extra_label");
68
69    fn key(name: &'static str) -> KeyName {
70        KeyName::from_const_str(name)
71    }
72
73    #[test]
74    fn includes_globally_registered_label() {
75        let filter = VectorLabelFilter;
76        let label = Label::new("test_extra_label", "value");
77        assert!(filter.should_include_label(&key("any_metric"), &label));
78    }
79
80    #[test]
81    fn excludes_unregistered_label() {
82        let filter = VectorLabelFilter;
83        let label = Label::new("not_registered", "value");
84        assert!(!filter.should_include_label(&key("any_metric"), &label));
85    }
86
87    #[test]
88    fn includes_built_in_global_labels() {
89        let filter = VectorLabelFilter;
90        for builtin in [
91            "component_id",
92            "component_type",
93            "component_kind",
94            "buffer_type",
95        ] {
96            let label = Label::new(builtin, "value");
97            assert!(
98                filter.should_include_label(&key("any_metric"), &label),
99                "expected built-in label `{builtin}` to remain included",
100            );
101        }
102    }
103}