blocky/resolver/metrics_resolver.go

116 lines
3.2 KiB
Go

package resolver
import (
"fmt"
"strings"
"time"
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/metrics"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
)
// MetricsResolver resolver that records metrics about requests/response
type MetricsResolver struct {
NextResolver
cfg config.PrometheusConfig
totalQueries *prometheus.CounterVec
totalResponse *prometheus.CounterVec
totalErrors prometheus.Counter
durationHistogram *prometheus.HistogramVec
}
// Resolve resolves the passed request
func (m *MetricsResolver) Resolve(request *Request) (*Response, error) {
response, err := m.next.Resolve(request)
if m.cfg.Enable {
m.totalQueries.With(prometheus.Labels{
"client": strings.Join(request.ClientNames, ","),
"type": dns.TypeToString[request.Req.Question[0].Qtype]}).Inc()
if err != nil {
m.totalErrors.Inc()
} else {
m.totalResponse.With(prometheus.Labels{
"reason": response.Reason,
"response_code": dns.RcodeToString[response.Res.Rcode],
"response_type": response.RType.String()}).Inc()
reqDurationMs := float64(time.Since(request.RequestTS).Milliseconds())
m.durationHistogram.WithLabelValues(response.RType.String()).Observe(reqDurationMs)
}
}
return response, err
}
// Configuration gets the config of this resolver in a string slice
func (m *MetricsResolver) Configuration() (result []string) {
result = append(result, "metrics:")
result = append(result, fmt.Sprintf(" Enable = %t", m.cfg.Enable))
result = append(result, fmt.Sprintf(" Path = %s", m.cfg.Path))
return
}
// NewMetricsResolver creates a new intance of the MetricsResolver type
func NewMetricsResolver(cfg config.PrometheusConfig) ChainedResolver {
durationHistogram := durationHistogram()
totalQueries := totalQueriesMetric()
totalResponse := totalResponseMetric()
totalErrors := totalErrorMetric()
metrics.RegisterMetric(durationHistogram)
metrics.RegisterMetric(totalQueries)
metrics.RegisterMetric(totalResponse)
metrics.RegisterMetric(totalErrors)
return &MetricsResolver{
cfg: cfg,
durationHistogram: durationHistogram,
totalQueries: totalQueries,
totalResponse: totalResponse,
totalErrors: totalErrors,
}
}
func totalQueriesMetric() *prometheus.CounterVec {
return prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "blocky_query_total",
Help: "Number of total queries",
}, []string{"client", "type"},
)
}
func totalErrorMetric() prometheus.Counter {
return prometheus.NewCounter(
prometheus.CounterOpts{
Name: "blocky_error_total",
Help: "Number of total errors",
},
)
}
func durationHistogram() *prometheus.HistogramVec {
return prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "blocky_request_duration_ms",
Help: "Request duration distribution",
Buckets: []float64{5, 10, 20, 30, 50, 75, 100, 200, 500, 1000, 2000},
},
[]string{"response_type"},
)
}
func totalResponseMetric() *prometheus.CounterVec {
return prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "blocky_response_total",
Help: "Number of total responses",
}, []string{"reason", "response_code", "response_type"},
)
}