ethtool: Prevent duplicate metric names (#2187)

Sanitizing the metric names can lead to duplicate metric names:

```
caller=level.go:63 level=error caller="error gathering metrics: [from Gatherer #2] collected metric \"node_ethtool_giant_hdr\" { label:<name:\"device\" value:\"ens192\" > untyped:<value:0" msg=" > } was collected before with the same name and label values"
```

Generate a map from the sanitized metric names to the metric names from
ethtool. In case of duplicate sanitized metric names drop both metrics,
because it is unknown which one to take.

Fixes: https://github.com/prometheus/node_exporter/issues/2185
Signed-off-by: Benjamin Drung <benjamin.drung@ionos.com>
This commit is contained in:
Benjamin Drung 2021-11-15 11:22:36 +01:00 committed by GitHub
parent 58ab0144af
commit d85cbaa17c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 5 deletions

View file

@ -385,19 +385,39 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
continue continue
} }
// Sanitizing the metric names can lead to duplicate metric names. Therefore check for clashes beforehand.
metricFQNames := make(map[string]string)
for metric := range stats {
if !c.metricsPattern.MatchString(metric) {
continue
}
metricFQName := buildEthtoolFQName(metric)
existingMetric, exists := metricFQNames[metricFQName]
if exists {
level.Debug(c.logger).Log("msg", "dropping duplicate metric name", "device", device,
"metricFQName", metricFQName, "metric1", existingMetric, "metric2", metric)
// Keep the metric as "deleted" in the dict in case there are 3 duplicates.
metricFQNames[metricFQName] = ""
} else {
metricFQNames[metricFQName] = metric
}
}
// Sort metric names so that the test fixtures will match up // Sort metric names so that the test fixtures will match up
keys := make([]string, 0, len(stats)) keys := make([]string, 0, len(metricFQNames))
for k := range stats { for k := range metricFQNames {
keys = append(keys, k) keys = append(keys, k)
} }
sort.Strings(keys) sort.Strings(keys)
for _, metric := range keys { for _, metricFQName := range keys {
if !c.metricsPattern.MatchString(metric) { metric := metricFQNames[metricFQName]
if metric == "" {
// Skip the "deleted" duplicate metrics
continue continue
} }
val := stats[metric] val := stats[metric]
metricFQName := buildEthtoolFQName(metric)
// Check to see if this metric exists; if not then create it and store it in c.entries. // Check to see if this metric exists; if not then create it and store it in c.entries.
entry, exists := c.entries[metric] entry, exists := c.entries[metric]

View file

@ -13,3 +13,5 @@ NIC statistics:
rx_multicast: 23973 rx_multicast: 23973
tx_aborted: 0 tx_aborted: 0
tx_underrun: 0 tx_underrun: 0
duplicate metric: 1
duplicate_metric: 2