mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-17 18:26:36 +00:00
dns: add enhanced metrics (#1999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
committed by
GitHub
parent
85455bd04d
commit
f0d5267874
@@ -3,14 +3,19 @@
|
|||||||
The dns collector exposes metrics about the DNS server
|
The dns collector exposes metrics about the DNS server
|
||||||
|
|
||||||
|||
|
|||
|
||||||
-|-
|
-|-|-
|
||||||
Metric name prefix | `dns`
|
Metric name prefix | `dns` |
|
||||||
Classes | [`Win32_PerfRawData_DNS_DNS`](https://technet.microsoft.com/en-us/library/cc977686.aspx)
|
Classes | [`Win32_PerfRawData_DNS_DNS`](https://technet.microsoft.com/en-us/library/cc977686.aspx) |
|
||||||
Enabled by default? | No
|
Enabled by default | Yes |
|
||||||
|
Metric name prefix (error stats) | `windows_dns` |
|
||||||
|
Classes | [`MicrosoftDNS_Statistic`](https://learn.microsoft.com/en-us/windows/win32/dns/dns-wmi-provider-overview) |
|
||||||
|
Enabled by default (error stats)? | Yes |
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
None
|
Name | Description
|
||||||
|
-----|------------
|
||||||
|
`collector.dns.enabled` | Comma-separated list of collectors to use. Available collectors: `metrics`, `error_stats`. Defaults to all collectors if not specified.
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
@@ -38,12 +43,56 @@ Name | Description | Type | Labels
|
|||||||
`windows_dns_wins_queries_total` | _Not yet documented_ | counter | `direction`
|
`windows_dns_wins_queries_total` | _Not yet documented_ | counter | `direction`
|
||||||
`windows_dns_wins_responses_total` | _Not yet documented_ | counter | `direction`
|
`windows_dns_wins_responses_total` | _Not yet documented_ | counter | `direction`
|
||||||
`windows_dns_unmatched_responses_total` | _Not yet documented_ | counter | None
|
`windows_dns_unmatched_responses_total` | _Not yet documented_ | counter | None
|
||||||
|
`windows_dns_error_stats_total` | DNS error statistics from MicrosoftDNS_Statistic | counter | `name`, `collection_name`, `dns_server`
|
||||||
|
|
||||||
|
### Sub-collectors
|
||||||
|
|
||||||
|
The DNS collector is split into two sub-collectors:
|
||||||
|
|
||||||
|
1. `metrics` - Collects standard DNS performance metrics using PDH (Performance Data Helper)
|
||||||
|
2. `wmi_stats` - Collects DNS error statistics from the MicrosoftDNS_Statistic WMI class
|
||||||
|
|
||||||
|
By default, both sub-collectors are enabled. You can enable specific sub-collectors using the `collector.dns.enabled` flag.
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
To enable only DNS error statistics collection:
|
||||||
|
```powershell
|
||||||
|
windows_exporter.exe --collector.dns.enabled=wmi_stats
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable only standard DNS metrics:
|
||||||
|
```powershell
|
||||||
|
windows_exporter.exe --collector.dns.enabled=metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable both (default behavior):
|
||||||
|
```powershell
|
||||||
|
windows_exporter.exe --collector.dns.enabled=metrics,wmi_stats
|
||||||
|
```
|
||||||
|
|
||||||
### Example metric
|
### Example metric
|
||||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
```
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="BadKey"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="BadSig"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="BadTime"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="FormError"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="Max"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NoError"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NotAuth"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NotImpl"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NotZone"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NxDomain"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="NxRRSet"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="Refused"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="ServFail"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="UnknownError"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="YxDomain"} 0
|
||||||
|
windows_dns_wmi_stats_total{collection_name="Error Stats",dns_server="EC2AMAZ-5NNM8M1",name="YxRRSet"} 0
|
||||||
|
```
|
||||||
|
|
||||||
## Useful queries
|
## Useful queries
|
||||||
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
|
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
|
||||||
|
|
||||||
## Alerting examples
|
## Alerting examples
|
||||||
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
|
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
|
||||||
@@ -18,8 +18,11 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"github.com/alecthomas/kingpin/v2"
|
||||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||||
@@ -28,12 +31,23 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Name = "dns"
|
const (
|
||||||
|
Name = "dns"
|
||||||
|
subCollectorMetrics = "metrics"
|
||||||
|
subCollectorWMIStats = "wmi_stats"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct{}
|
type Config struct {
|
||||||
|
CollectorsEnabled []string `yaml:"collectors_enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var ConfigDefaults = Config{}
|
var ConfigDefaults = Config{
|
||||||
|
CollectorsEnabled: []string{
|
||||||
|
subCollectorMetrics,
|
||||||
|
subCollectorWMIStats,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_DNS_DNS metrics.
|
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_DNS_DNS metrics.
|
||||||
type Collector struct {
|
type Collector struct {
|
||||||
@@ -42,6 +56,9 @@ type Collector struct {
|
|||||||
perfDataCollector *pdh.Collector
|
perfDataCollector *pdh.Collector
|
||||||
perfDataObject []perfDataCounterValues
|
perfDataObject []perfDataCounterValues
|
||||||
|
|
||||||
|
miSession *mi.Session
|
||||||
|
miQuery mi.Query
|
||||||
|
|
||||||
dynamicUpdatesFailures *prometheus.Desc
|
dynamicUpdatesFailures *prometheus.Desc
|
||||||
dynamicUpdatesQueued *prometheus.Desc
|
dynamicUpdatesQueued *prometheus.Desc
|
||||||
dynamicUpdatesReceived *prometheus.Desc
|
dynamicUpdatesReceived *prometheus.Desc
|
||||||
@@ -64,6 +81,7 @@ type Collector struct {
|
|||||||
zoneTransferResponsesReceived *prometheus.Desc
|
zoneTransferResponsesReceived *prometheus.Desc
|
||||||
zoneTransferSuccessReceived *prometheus.Desc
|
zoneTransferSuccessReceived *prometheus.Desc
|
||||||
zoneTransferSuccessSent *prometheus.Desc
|
zoneTransferSuccessSent *prometheus.Desc
|
||||||
|
dnsWMIStats *prometheus.Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *Config) *Collector {
|
func New(config *Config) *Collector {
|
||||||
@@ -71,6 +89,10 @@ func New(config *Config) *Collector {
|
|||||||
config = &ConfigDefaults
|
config = &ConfigDefaults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.CollectorsEnabled == nil {
|
||||||
|
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
|
||||||
|
}
|
||||||
|
|
||||||
c := &Collector{
|
c := &Collector{
|
||||||
config: *config,
|
config: *config,
|
||||||
}
|
}
|
||||||
@@ -78,8 +100,26 @@ func New(config *Config) *Collector {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithFlags(_ *kingpin.Application) *Collector {
|
func NewWithFlags(app *kingpin.Application) *Collector {
|
||||||
return &Collector{}
|
c := &Collector{
|
||||||
|
config: ConfigDefaults,
|
||||||
|
}
|
||||||
|
c.config.CollectorsEnabled = make([]string, 0)
|
||||||
|
|
||||||
|
var collectorsEnabled string
|
||||||
|
|
||||||
|
app.Flag(
|
||||||
|
"collector.dns.enabled",
|
||||||
|
"Comma-separated list of collectors to use. Defaults to all, if not specified.",
|
||||||
|
).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
|
||||||
|
|
||||||
|
app.Action(func(*kingpin.ParseContext) error {
|
||||||
|
c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) GetName() string {
|
func (c *Collector) GetName() string {
|
||||||
@@ -92,7 +132,31 @@ func (c *Collector) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
|
||||||
|
for _, collector := range c.config.CollectorsEnabled {
|
||||||
|
if !slices.Contains([]string{subCollectorMetrics, subCollectorWMIStats}, collector) {
|
||||||
|
return fmt.Errorf("unknown sub collector: %s. Possible values: %s", collector,
|
||||||
|
strings.Join([]string{subCollectorMetrics, subCollectorWMIStats}, ", "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(c.config.CollectorsEnabled, subCollectorMetrics) {
|
||||||
|
if err := c.buildMetricsCollector(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(c.config.CollectorsEnabled, subCollectorWMIStats) {
|
||||||
|
if err := c.buildErrorStatsCollector(miSession); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) buildMetricsCollector() error {
|
||||||
c.zoneTransferRequestsReceived = prometheus.NewDesc(
|
c.zoneTransferRequestsReceived = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(types.Namespace, Name, "zone_transfer_requests_received_total"),
|
prometheus.BuildFQName(types.Namespace, Name, "zone_transfer_requests_received_total"),
|
||||||
"Number of zone transfer requests (AXFR/IXFR) received by the master DNS server",
|
"Number of zone transfer requests (AXFR/IXFR) received by the master DNS server",
|
||||||
@@ -226,6 +290,13 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
c.dnsWMIStats = prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(types.Namespace, Name, "wmi_stats_total"),
|
||||||
|
"DNS WMI statistics from MicrosoftDNS_Statistic",
|
||||||
|
[]string{"name", "collection_name", "dns_server"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DNS", pdh.InstancesAll)
|
c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DNS", pdh.InstancesAll)
|
||||||
@@ -236,9 +307,43 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Collector) buildErrorStatsCollector(miSession *mi.Session) error {
|
||||||
|
if miSession == nil {
|
||||||
|
return errors.New("miSession is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := mi.NewQuery("SELECT Name, CollectionName, Value, DnsServerName FROM MicrosoftDNS_Statistic WHERE CollectionName = 'Error Stats'")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create query: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.miSession = miSession
|
||||||
|
c.miQuery = query
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||||
|
errs := make([]error, 0)
|
||||||
|
|
||||||
|
if slices.Contains(c.config.CollectorsEnabled, subCollectorMetrics) {
|
||||||
|
if err := c.collectMetrics(ch); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("failed collecting metrics: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(c.config.CollectorsEnabled, subCollectorWMIStats) {
|
||||||
|
if err := c.collectErrorStats(ch); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("failed collecting WMI statistics: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collector) collectMetrics(ch chan<- prometheus.Metric) error {
|
||||||
err := c.perfDataCollector.Collect(&c.perfDataObject)
|
err := c.perfDataCollector.Collect(&c.perfDataObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to collect DNS metrics: %w", err)
|
return fmt.Errorf("failed to collect DNS metrics: %w", err)
|
||||||
@@ -495,3 +600,24 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Collector) collectErrorStats(ch chan<- prometheus.Metric) error {
|
||||||
|
var stats []Statistic
|
||||||
|
if err := c.miSession.Query(&stats, mi.NamespaceRootMicrosoftDNS, c.miQuery); err != nil {
|
||||||
|
return fmt.Errorf("failed to query DNS statistics: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect DNS error statistics
|
||||||
|
for _, stat := range stats {
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.dnsWMIStats,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(stat.Value),
|
||||||
|
stat.Name,
|
||||||
|
stat.CollectionName,
|
||||||
|
stat.DnsServerName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -107,3 +107,11 @@ type perfDataCounterValues struct {
|
|||||||
_ float64 `perfdata:"Zone Transfer SOA Request Sent"`
|
_ float64 `perfdata:"Zone Transfer SOA Request Sent"`
|
||||||
_ float64 `perfdata:"Zone Transfer Success"`
|
_ float64 `perfdata:"Zone Transfer Success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Statistic represents the structure for DNS error statistics
|
||||||
|
type Statistic struct {
|
||||||
|
Name string `mi:"Name"`
|
||||||
|
CollectionName string `mi:"CollectionName"`
|
||||||
|
Value uint64 `mi:"Value"`
|
||||||
|
DnsServerName string `mi:"DnsServerName"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ var (
|
|||||||
NamespaceRootWindowsFSRM = utils.Must(NewNamespace("root/microsoft/windows/fsrm"))
|
NamespaceRootWindowsFSRM = utils.Must(NewNamespace("root/microsoft/windows/fsrm"))
|
||||||
NamespaceRootWebAdministration = utils.Must(NewNamespace("root/WebAdministration"))
|
NamespaceRootWebAdministration = utils.Must(NewNamespace("root/WebAdministration"))
|
||||||
NamespaceRootMSCluster = utils.Must(NewNamespace("root/MSCluster"))
|
NamespaceRootMSCluster = utils.Must(NewNamespace("root/MSCluster"))
|
||||||
|
NamespaceRootMicrosoftDNS = utils.Must(NewNamespace("root/MicrosoftDNS"))
|
||||||
)
|
)
|
||||||
|
|
||||||
type Query *uint16
|
type Query *uint16
|
||||||
|
|||||||
Reference in New Issue
Block a user