Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ztunnel/src/dns/metrics.rs
Line
Count
Source
1
// Copyright Istio Authors
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
15
use hickory_server::server::Request;
16
use prometheus_client::encoding::EncodeLabelSet;
17
use prometheus_client::metrics::counter::Counter;
18
use prometheus_client::metrics::family::Family;
19
use prometheus_client::metrics::histogram::Histogram;
20
use prometheus_client::registry::{Registry, Unit};
21
use std::time::Duration;
22
23
use crate::metrics::{DefaultedUnknown, DeferRecorder, Recorder};
24
25
use crate::state::workload::Workload;
26
use crate::strng;
27
use crate::strng::RichStrng;
28
29
pub struct Metrics {
30
    pub requests: Family<DnsLabels, Counter>,
31
    pub forwarded_requests: Family<DnsLabels, Counter>,
32
    pub forwarded_failures: Family<DnsLabels, Counter>,
33
    pub forwarded_duration: Family<DnsLabels, Histogram>,
34
}
35
36
impl Metrics {
37
0
    pub fn new(registry: &mut Registry) -> Self {
38
0
        let requests = Family::default();
39
0
        registry.register(
40
            "dns_requests",
41
            "Total number of DNS requests (unstable)",
42
0
            requests.clone(),
43
        );
44
45
0
        let forwarded_requests = Family::default();
46
0
        registry.register(
47
            "dns_upstream_requests",
48
            "Total number of DNS requests forwarded to upstream (unstable)",
49
0
            forwarded_requests.clone(),
50
        );
51
52
0
        let forwarded_failures = Family::default();
53
0
        registry.register(
54
            "dns_upstream_failures",
55
            "Total number of DNS requests that failed to forward upstream (unstable)",
56
0
            forwarded_failures.clone(),
57
        );
58
59
0
        let forwarded_duration = Family::<DnsLabels, Histogram>::new_with_constructor(|| {
60
0
            Histogram::new(vec![0.005f64, 0.001, 0.01, 0.1, 1.0, 5.0])
61
0
        });
62
0
        registry.register_with_unit(
63
            "dns_upstream_request_duration",
64
            "Total time in seconds Istio takes to get DNS response from upstream (unstable)",
65
0
            Unit::Seconds,
66
0
            forwarded_duration.clone(),
67
        );
68
69
0
        Self {
70
0
            requests,
71
0
            forwarded_requests,
72
0
            forwarded_failures,
73
0
            forwarded_duration,
74
0
        }
75
0
    }
76
}
77
78
impl DeferRecorder for Metrics {}
79
80
#[derive(Clone, Hash, Debug, PartialEq, Eq, EncodeLabelSet)]
81
pub struct DnsLabels {
82
    request_query_type: DefaultedUnknown<RichStrng>,
83
    request_protocol: RichStrng,
84
85
    // Source workload.
86
    source_canonical_service: DefaultedUnknown<RichStrng>,
87
    source_canonical_revision: DefaultedUnknown<RichStrng>,
88
}
89
90
impl DnsLabels {
91
0
    pub fn new(r: &Request) -> Self {
92
        Self {
93
0
            request_query_type: r
94
0
                .request_info()
95
0
                .map(|q| q.query.query_type().to_string().to_lowercase())
96
0
                .ok()
97
0
                .map(|s| RichStrng::from(strng::new(s)))
98
0
                .into(),
99
0
            request_protocol: r.protocol().to_string().to_lowercase().into(),
100
0
            source_canonical_service: Default::default(),
101
0
            source_canonical_revision: Default::default(),
102
        }
103
0
    }
104
105
0
    pub fn with_source(mut self, w: &Workload) -> Self {
106
0
        self.source_canonical_service = w.canonical_name.clone().into();
107
0
        self.source_canonical_revision = w.canonical_revision.clone().into();
108
109
0
        self
110
0
    }
111
}
112
113
#[derive(Clone)]
114
pub struct DnsRequest<'a> {
115
    pub request: &'a Request,
116
    pub source: Option<&'a Workload>,
117
}
118
119
impl Recorder<DnsRequest<'_>, u64> for Metrics {
120
0
    fn record(&self, reason: &DnsRequest, count: u64) {
121
0
        self.requests
122
0
            .get_or_create(&DnsLabels::from(reason))
123
0
            .inc_by(count);
124
0
    }
125
}
126
127
impl From<&DnsRequest<'_>> for DnsLabels {
128
0
    fn from(value: &DnsRequest) -> Self {
129
0
        let mut labels = Self::new(value.request);
130
0
        if let Some(source) = &value.source {
131
0
            labels = labels.with_source(source)
132
0
        }
133
0
        labels
134
0
    }
135
}
136
137
#[derive(Clone)]
138
pub struct ForwardedRequest<'a> {
139
    pub request: &'a Request,
140
    pub source: Option<&'a Workload>,
141
}
142
143
impl Recorder<ForwardedRequest<'_>, u64> for Metrics {
144
0
    fn record(&self, reason: &ForwardedRequest, count: u64) {
145
0
        self.forwarded_requests
146
0
            .get_or_create(&DnsLabels::from(reason))
147
0
            .inc_by(count);
148
0
    }
149
}
150
151
impl From<&ForwardedRequest<'_>> for DnsLabels {
152
0
    fn from(value: &ForwardedRequest) -> Self {
153
0
        let mut labels = Self::new(value.request);
154
0
        if let Some(source) = &value.source {
155
0
            labels = labels.with_source(source)
156
0
        }
157
0
        labels
158
0
    }
159
}
160
161
#[derive(Clone)]
162
pub struct ForwardedFailure<'a> {
163
    pub request: &'a Request,
164
    pub source: Option<&'a Workload>,
165
}
166
167
impl Recorder<ForwardedFailure<'_>, u64> for Metrics {
168
0
    fn record(&self, reason: &ForwardedFailure, count: u64) {
169
0
        self.forwarded_failures
170
0
            .get_or_create(&DnsLabels::from(reason))
171
0
            .inc_by(count);
172
0
    }
173
}
174
175
impl From<&ForwardedFailure<'_>> for DnsLabels {
176
0
    fn from(value: &ForwardedFailure) -> Self {
177
0
        let mut labels = Self::new(value.request);
178
0
        if let Some(source) = &value.source {
179
0
            labels = labels.with_source(source)
180
0
        }
181
0
        labels
182
0
    }
183
}
184
185
#[derive(Clone)]
186
pub struct ForwardedDuration<'a> {
187
    pub request: &'a Request,
188
    pub source: Option<&'a Workload>,
189
}
190
191
impl Recorder<ForwardedDuration<'_>, Duration> for Metrics {
192
0
    fn record(&self, reason: &ForwardedDuration, duration: Duration) {
193
0
        self.forwarded_duration
194
0
            .get_or_create(&DnsLabels::from(reason))
195
0
            .observe(duration.as_secs_f64());
196
0
    }
197
}
198
199
impl From<&ForwardedDuration<'_>> for DnsLabels {
200
0
    fn from(value: &ForwardedDuration) -> Self {
201
0
        let mut labels = Self::new(value.request);
202
0
        if let Some(source) = &value.source {
203
0
            labels = labels.with_source(source)
204
0
        }
205
0
        labels
206
0
    }
207
}