Skip to content

Commit 7063b5a

Browse files
committed
Add DNS query metrics to Prometheus
- Create metrics/dns.go with counters for total and blocked queries - Integrate metrics collection in stats unit.add() method - Register DNS metrics in web API Prometheus registry - Add comprehensive tests for metrics functionality Metrics exposed: - adguard_dns_queries_total: Total DNS queries processed - adguard_dns_queries_by_result_total: Queries by result type (filtered, not_filtered, etc.) - adguard_dns_queries_blocked_total: Legacy metric for blocked queries The metrics mirror the dashboard counters exactly, providing real-time DNS query statistics for monitoring and alerting.
1 parent a84d24f commit 7063b5a

File tree

6 files changed

+114
-12
lines changed

6 files changed

+114
-12
lines changed

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ require (
4242
howett.net/plist v1.0.1
4343
)
4444

45+
require github.com/prometheus/client_golang v1.22.0
46+
4547
require (
4648
cloud.google.com/go v0.121.2 // indirect
4749
cloud.google.com/go/ai v0.12.0 // indirect
@@ -71,14 +73,14 @@ require (
7173
github.com/gordonklaus/ineffassign v0.1.0 // indirect
7274
github.com/jstemmer/go-junit-report/v2 v2.1.0 // indirect
7375
github.com/kisielk/errcheck v1.9.0 // indirect
76+
github.com/kylelemons/godebug v1.1.0 // indirect
7477
github.com/mdlayher/socket v0.5.1 // indirect
7578
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
7679
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
7780
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
7881
github.com/pierrec/lz4/v4 v4.1.22 // indirect
7982
github.com/pkg/errors v0.9.1 // indirect
8083
github.com/pmezard/go-difflib v1.0.0 // indirect
81-
github.com/prometheus/client_golang v1.22.0 // indirect
8284
github.com/prometheus/client_model v0.6.1 // indirect
8385
github.com/prometheus/common v0.62.0 // indirect
8486
github.com/prometheus/procfs v0.15.1 // indirect

go.sum

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ github.com/go-ping/ping v1.2.0 h1:vsJ8slZBZAXNCK4dPcI2PEE9eM9n9RbXbGouVQ/Y4yQ=
5858
github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
5959
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
6060
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
61-
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
6261
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
6362
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
6463
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -98,8 +97,6 @@ github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5
9897
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
9998
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
10099
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
101-
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
102-
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
103100
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f h1:dd33oobuIv9PcBVqvbEiCXEbNTomOHyj3WFuC5YiPRU=
104101
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f/go.mod h1:zhFlBeJssZ1YBCMZ5Lzu1pX4vhftDvU10WUVb1uXKtM=
105102
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -112,10 +109,14 @@ github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX
112109
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
113110
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
114111
github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
112+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
113+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
115114
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
116115
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
117116
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
118117
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
118+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
119+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
119120
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
120121
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
121122
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
@@ -131,8 +132,6 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
131132
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
132133
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
133134
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
134-
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
135-
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
136135
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
137136
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
138137
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -164,8 +163,6 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
164163
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
165164
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
166165
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
167-
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
168-
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
169166
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
170167
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
171168
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -185,8 +182,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
185182
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
186183
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
187184
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
188-
github.com/ti-mo/netfilter v0.5.2 h1:CTjOwFuNNeZ9QPdRXt1MZFLFUf84cKtiQutNauHWd40=
189-
github.com/ti-mo/netfilter v0.5.2/go.mod h1:Btx3AtFiOVdHReTDmP9AE+hlkOcvIy403u7BXXbWZKo=
190185
github.com/ti-mo/netfilter v0.5.3 h1:ikzduvnaUMwre5bhbNwWOd6bjqLMVb33vv0XXbK0xGQ=
191186
github.com/ti-mo/netfilter v0.5.3/go.mod h1:08SyBCg6hu1qyQk4s3DjjJKNrm3RTb32nm6AzyT972E=
192187
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -228,8 +223,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
228223
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
229224
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
230225
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
231-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
232-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
233226
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
234227
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
235228
golang.org/x/exp/typeparams v0.0.0-20250506013437-ce4c2cf36ca6 h1:UW7ILaA/QTIxnBKbgCV+72w0gRw97+MY93VyPjnGZJU=

internal/home/web.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"golang.org/x/net/http2"
2727
"golang.org/x/net/http2/h2c"
2828

29+
"github.com/AdguardTeam/AdGuardHome/internal/metrics"
2930
"github.com/AdguardTeam/AdGuardHome/internal/updater"
3031
)
3132

@@ -141,6 +142,9 @@ func newWebAPI(ctx context.Context, conf *webConfig) (w *webAPI) {
141142
metricsRegistry.MustRegister(collectors.NewGoCollector())
142143
metricsRegistry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
143144

145+
// Register DNS metrics
146+
metrics.RegisterDNSMetrics(metricsRegistry)
147+
144148
w = &webAPI{
145149
conf: conf,
146150
logger: conf.logger,

internal/metrics/dns.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package metrics
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
)
6+
7+
// DNS query result types matching internal/stats package
8+
const (
9+
ResultNotFiltered = "not_filtered"
10+
ResultFiltered = "filtered"
11+
ResultSafeBrowsing = "safe_browsing"
12+
ResultSafeSearch = "safe_search"
13+
ResultParental = "parental"
14+
)
15+
16+
// DNSQueriesByResult tracks DNS queries by their processing result
17+
var DNSQueriesByResult = prometheus.NewCounterVec(prometheus.CounterOpts{
18+
Name: "adguard_dns_queries_by_result_total",
19+
Help: "Total number of DNS queries by processing result",
20+
}, []string{"result"})
21+
22+
// RegisterDNSMetrics registers all DNS-related metrics with the provided registry
23+
func RegisterDNSMetrics(registry *prometheus.Registry) {
24+
registry.MustRegister(DNSQueriesByResult)
25+
}
26+
27+
// IncrementDNSQueryByResult increments counters for a specific query result type
28+
func IncrementDNSQueryByResult(result string) {
29+
DNSQueriesByResult.WithLabelValues(result).Inc()
30+
}

internal/metrics/dns_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package metrics
2+
3+
import (
4+
"testing"
5+
6+
"github.com/prometheus/client_golang/prometheus"
7+
"github.com/prometheus/client_golang/prometheus/testutil"
8+
)
9+
10+
func TestDNSMetrics(t *testing.T) {
11+
// Create a new counter for isolated testing
12+
testCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
13+
Name: "test_dns_queries_by_result_total",
14+
Help: "Test counter for DNS queries by processing result",
15+
}, []string{"result"})
16+
17+
// Test incrementing queries by result
18+
testCounter.WithLabelValues(ResultFiltered).Inc()
19+
testCounter.WithLabelValues(ResultNotFiltered).Inc()
20+
testCounter.WithLabelValues(ResultSafeBrowsing).Inc()
21+
testCounter.WithLabelValues(ResultNotFiltered).Inc() // Add another not filtered
22+
23+
// Verify result counters
24+
filteredValue := testutil.ToFloat64(testCounter.WithLabelValues(ResultFiltered))
25+
if filteredValue != 1 {
26+
t.Errorf("Expected filtered queries to be 1, got %f", filteredValue)
27+
}
28+
29+
notFilteredValue := testutil.ToFloat64(testCounter.WithLabelValues(ResultNotFiltered))
30+
if notFilteredValue != 2 {
31+
t.Errorf("Expected not filtered queries to be 2, got %f", notFilteredValue)
32+
}
33+
34+
safeBrowsingValue := testutil.ToFloat64(testCounter.WithLabelValues(ResultSafeBrowsing))
35+
if safeBrowsingValue != 1 {
36+
t.Errorf("Expected safe browsing queries to be 1, got %f", safeBrowsingValue)
37+
}
38+
}
39+
40+
func TestRegisterDNSMetrics(t *testing.T) {
41+
registry := prometheus.NewRegistry()
42+
43+
// This should not panic
44+
RegisterDNSMetrics(registry)
45+
46+
// Registering again should panic due to duplicate registration
47+
defer func() {
48+
if r := recover(); r == nil {
49+
t.Error("Expected panic when registering metrics twice")
50+
}
51+
}()
52+
RegisterDNSMetrics(registry)
53+
}

internal/stats/unit.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
13+
"github.com/AdguardTeam/AdGuardHome/internal/metrics"
1314
"github.com/AdguardTeam/dnsproxy/proxy"
1415
"github.com/AdguardTeam/golibs/errors"
1516
"github.com/AdguardTeam/golibs/logutil/slogutil"
@@ -328,6 +329,25 @@ func (u *unit) add(e *Entry) {
328329
u.timeSum += pt
329330
u.nTotal++
330331

332+
// Update Prometheus metrics
333+
// Map Result constants to metrics labels
334+
var resultLabel string
335+
switch e.Result {
336+
case RNotFiltered:
337+
resultLabel = metrics.ResultNotFiltered
338+
case RFiltered:
339+
resultLabel = metrics.ResultFiltered
340+
case RSafeBrowsing:
341+
resultLabel = metrics.ResultSafeBrowsing
342+
case RSafeSearch:
343+
resultLabel = metrics.ResultSafeSearch
344+
case RParental:
345+
resultLabel = metrics.ResultParental
346+
default:
347+
resultLabel = "unknown"
348+
}
349+
metrics.IncrementDNSQueryByResult(resultLabel)
350+
331351
for _, s := range e.UpstreamStats {
332352
if s.IsCached || s.Error != nil {
333353
continue

0 commit comments

Comments
 (0)