resolved/
metrics.rs

1use axum::{http::StatusCode, routing};
2use prometheus::{
3    opts, register_histogram_vec, register_int_counter, register_int_counter_vec,
4    register_int_gauge, HistogramVec, IntCounter, IntCounterVec, IntGauge, TextEncoder,
5};
6use std::net::SocketAddr;
7
8pub const RESPONSE_TIME_BUCKETS: &[f64] = &[
9    0.0001, // 0.1 ms
10    0.0005, // 0.5 ms
11    0.0010, // 1   ms
12    0.0025, // 2.5 ms
13    0.0050, // 5   ms
14    0.0075, // 7.5 ms
15    0.0100, // 10  ms
16    0.0250, // 25  ms
17    0.0500, // 50  ms
18    0.0750, // 75  ms
19    0.1000, // 100 ms
20    0.2500, // 250 ms
21    0.5000, // 500 ms
22    0.7500, // 750 ms
23    1.0000, // 1    s
24];
25
26// separate const because we may want to change this in the future to
27// get more granularity on the lower end
28pub const PROCESSING_TIME_BUCKETS: &[f64] = RESPONSE_TIME_BUCKETS;
29
30pub const REFUSED_FOR_MULTIPLE_QUESTIONS: &str = "multiple_questions";
31pub const REFUSED_FOR_UNKNOWN_QTYPE_OR_QCLASS: &str = "unknown_qtype_or_qclass";
32
33pub static DNS_REQUESTS_TOTAL: std::sync::LazyLock<IntCounterVec> =
34    std::sync::LazyLock::new(|| {
35        register_int_counter_vec!(
36            opts!(
37                "dns_requests_total",
38                "Total number of DNS requests received, whether valid or invalid."
39            ),
40            &["protocol"]
41        )
42        .unwrap()
43    });
44
45pub static DNS_REQUESTS_REFUSED_TOTAL: std::sync::LazyLock<IntCounterVec> =
46    std::sync::LazyLock::new(|| {
47        register_int_counter_vec!(
48            opts!(
49                "dns_requests_refused_total",
50                "Total number of DNS requests refused."
51            ),
52            &["reason"]
53        )
54        .unwrap()
55    });
56
57pub static DNS_RESPONSES_TOTAL: std::sync::LazyLock<IntCounterVec> =
58    std::sync::LazyLock::new(|| {
59        register_int_counter_vec!(
60            opts!("dns_responses_total", "Total number of DNS responses sent."),
61            &["aa", "tc", "rd", "ra", "rcode"]
62        )
63        .unwrap()
64    });
65
66pub static DNS_RESPONSE_TIME_SECONDS: std::sync::LazyLock<HistogramVec> =
67    std::sync::LazyLock::new(|| {
68        register_histogram_vec!(
69            "dns_response_time_seconds",
70            "Response time of DNS requests, whether valid or invalid.",
71            &["protocol"],
72            RESPONSE_TIME_BUCKETS.to_vec()
73        )
74        .unwrap()
75    });
76
77pub static DNS_QUESTIONS_TOTAL: std::sync::LazyLock<IntCounterVec> =
78    std::sync::LazyLock::new(|| {
79        register_int_counter_vec!(
80            opts!(
81                "dns_questions_total",
82                "Total number of DNS questions received (a request may have multiple questions)."
83            ),
84            &["rd", "qtype", "qclass"]
85        )
86        .unwrap()
87    });
88
89pub static DNS_QUESTION_PROCESSING_TIME_SECONDS: std::sync::LazyLock<HistogramVec> =
90    std::sync::LazyLock::new(|| {
91        register_histogram_vec!(
92            "dns_question_processing_time_seconds",
93            "Time spent processing a DNS question (a request may have multiple questions).",
94            &["rd", "qtype", "class"],
95            PROCESSING_TIME_BUCKETS.to_vec()
96        )
97        .unwrap()
98    });
99
100pub static DNS_RESOLVER_AUTHORITATIVE_HIT_TOTAL: std::sync::LazyLock<IntCounter> =
101    std::sync::LazyLock::new(|| {
102        register_int_counter!(opts!(
103            "dns_resolver_authoritative_hit_total",
104            "Total number of hits of local authoritative data (not including blocked domains)."
105        ),)
106        .unwrap()
107    });
108
109pub static DNS_RESOLVER_OVERRIDE_HIT_TOTAL: std::sync::LazyLock<IntCounter> =
110    std::sync::LazyLock::new(|| {
111        register_int_counter!(opts!(
112            "dns_resolver_override_hit_total",
113            "Total number of hits of local override data (not including blocked domains)."
114        ),)
115        .unwrap()
116    });
117
118pub static DNS_RESOLVER_BLOCKED_TOTAL: std::sync::LazyLock<IntCounter> =
119    std::sync::LazyLock::new(|| {
120        register_int_counter!(opts!(
121            "dns_resolver_blocked_total",
122            "Total number of queries which have been blocked."
123        ),)
124        .unwrap()
125    });
126
127pub static DNS_RESOLVER_CACHE_HIT_TOTAL: std::sync::LazyLock<IntCounter> =
128    std::sync::LazyLock::new(|| {
129        register_int_counter!(opts!(
130            "dns_resolver_cache_hit_total",
131            "Total number of cache hits."
132        ),)
133        .unwrap()
134    });
135
136pub static DNS_RESOLVER_CACHE_MISS_TOTAL: std::sync::LazyLock<IntCounter> =
137    std::sync::LazyLock::new(|| {
138        register_int_counter!(opts!(
139            "dns_resolver_cache_miss_total",
140            "Total number of cache misses."
141        ),)
142        .unwrap()
143    });
144
145pub static DNS_RESOLVER_NAMESERVER_HIT_TOTAL: std::sync::LazyLock<IntCounter> =
146    std::sync::LazyLock::new(|| {
147        register_int_counter!(opts!(
148            "dns_resolver_nameserver_hit_total",
149            "Total number of hits when calling an upstream nameserver."
150        ),)
151        .unwrap()
152    });
153
154pub static DNS_RESOLVER_NAMESERVER_MISS_TOTAL: std::sync::LazyLock<IntCounter> =
155    std::sync::LazyLock::new(|| {
156        register_int_counter!(opts!(
157            "dns_resolver_nameserver_miss_total",
158            "Total number of misses when calling an upstream nameserver."
159        ),)
160        .unwrap()
161    });
162
163pub static CACHE_SIZE: std::sync::LazyLock<IntGauge> = std::sync::LazyLock::new(|| {
164    register_int_gauge!(opts!("cache_size", "Number of records in the cache.")).unwrap()
165});
166
167pub static CACHE_OVERFLOW_COUNT: std::sync::LazyLock<IntCounter> = std::sync::LazyLock::new(|| {
168    register_int_counter!(opts!(
169        "cache_overflow_count",
170        "Number of times the cache has overflowed."
171    ))
172    .unwrap()
173});
174
175pub static CACHE_EXPIRED_TOTAL: std::sync::LazyLock<IntCounter> = std::sync::LazyLock::new(|| {
176    register_int_counter!(opts!(
177        "cache_expired_total",
178        "Number of records which have been expired from the cache."
179    ))
180    .unwrap()
181});
182
183pub static CACHE_PRUNED_TOTAL: std::sync::LazyLock<IntCounter> = std::sync::LazyLock::new(|| {
184    register_int_counter!(opts!(
185        "cache_pruned_total",
186        "Number of records which have been pruned from the cache due to overflow."
187    ))
188    .unwrap()
189});
190
191async fn get_metrics() -> (StatusCode, String) {
192    match TextEncoder::new().encode_to_string(&prometheus::gather()) {
193        Ok(metrics_str) => (StatusCode::OK, metrics_str),
194        Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()),
195    }
196}
197
198pub async fn serve_prometheus_endpoint_task(address: SocketAddr) -> std::io::Result<()> {
199    let app = axum::Router::new().route("/metrics", routing::get(get_metrics));
200    let listener = tokio::net::TcpListener::bind(address).await?;
201    axum::serve(listener, app).await?;
202
203    Ok(())
204}