1package metrics
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "os"
8 "sync"
9 "time"
10
11 "tangled.sh/seiso.moe/alky/pkg/dns"
12)
13
14var (
15 instanceID string
16 initOnce sync.Once
17 version string
18)
19
20func initializeInstanceID() {
21 hostname, err := os.Hostname()
22 if err != nil {
23 hostname = "unknown-host"
24 }
25
26 if version != "" {
27 instanceID = fmt.Sprintf("%s-%s", hostname, version)
28 } else {
29 instanceID = fmt.Sprintf("%s-dev", hostname)
30 }
31}
32
33func MetricsMiddleware(metrics *ClickHouseMetrics) func(dns.Handler) dns.Handler {
34 if metrics == nil {
35 slog := slog.Default()
36 slog.Warn("Metrics client is nil, metrics middleware will be a no-op")
37 return func(next dns.Handler) dns.Handler {
38 return next
39 }
40 }
41
42 instanceID := GetInstanceID()
43
44 return func(next dns.Handler) dns.Handler {
45 return dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Request) {
46 if r.Context == nil {
47 r.Context = context.Background()
48 }
49
50 start := time.Now()
51 next.ServeDNS(w, r)
52 duration := time.Since(start)
53
54 if r.Message != nil && len(r.Message.Question) > 0 {
55 question := r.Message.Question[0]
56
57 metrics.RecordQuery(QueryMetric{
58 Timestamp: start,
59 InstanceID: instanceID,
60 QueryName: question.QName,
61 QueryType: question.QType.String(),
62 QueryClass: question.QClass.String(),
63 RemoteAddr: r.RemoteAddr.String(),
64 ResponseCode: r.Message.Header.RCode.String(),
65 Duration: duration.Nanoseconds(),
66 })
67 } else {
68 slog := slog.Default()
69 slog.Warn("Metrics middleware received request with missing message or question", "remote_addr", r.RemoteAddr)
70 }
71 })
72 }
73}
74
75func GetInstanceID() string {
76 initOnce.Do(initializeInstanceID)
77 return instanceID
78}