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.0005, 0.0010, 0.0025, 0.0050, 0.0075, 0.0100, 0.0250, 0.0500, 0.0750, 0.1000, 0.2500, 0.5000, 0.7500, 1.0000, ];
25
26pub 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}