Fix export of stale device error metrics for unmounted filesystems

Instead of maintaining a counter metric for device errors in memory,
this change exports a gauge and uses const metrics to avoid leaking
metrics for unmounted filesystems.
This commit is contained in:
Tobias Schmidt 2017-03-22 21:48:18 -03:00
parent 7b93b52010
commit d290ea94b8
2 changed files with 34 additions and 19 deletions

View file

@ -46,9 +46,9 @@ var (
type filesystemCollector struct { type filesystemCollector struct {
ignoredMountPointsPattern *regexp.Regexp ignoredMountPointsPattern *regexp.Regexp
ignoredFSTypesPattern *regexp.Regexp ignoredFSTypesPattern *regexp.Regexp
sizeDesc, freeDesc, availDesc, sizeDesc, freeDesc, availDesc *prometheus.Desc
filesDesc, filesFreeDesc, roDesc *prometheus.Desc filesDesc, filesFreeDesc *prometheus.Desc
devErrors *prometheus.CounterVec roDesc, deviceErrorDesc *prometheus.Desc
} }
type filesystemLabels struct { type filesystemLabels struct {
@ -57,7 +57,9 @@ type filesystemLabels struct {
type filesystemStats struct { type filesystemStats struct {
labels filesystemLabels labels filesystemLabels
size, free, avail, files, filesFree, ro float64 size, free, avail float64
files, filesFree float64
ro, deviceError float64
} }
func init() { func init() {
@ -106,10 +108,11 @@ func NewFilesystemCollector() (Collector, error) {
filesystemLabelNames, nil, filesystemLabelNames, nil,
) )
devErrors := prometheus.NewCounterVec(prometheus.CounterOpts{ deviceErrorDesc := prometheus.NewDesc(
Name: prometheus.BuildFQName(Namespace, subsystem, "device_errors_total"), prometheus.BuildFQName(Namespace, subsystem, "device_error"),
Help: "Total number of errors occurred when getting stats for device", "Whether an error occured while getting statistics for the given device.",
}, filesystemLabelNames) filesystemLabelNames, nil,
)
return &filesystemCollector{ return &filesystemCollector{
ignoredMountPointsPattern: mountPointPattern, ignoredMountPointsPattern: mountPointPattern,
@ -120,7 +123,7 @@ func NewFilesystemCollector() (Collector, error) {
filesDesc: filesDesc, filesDesc: filesDesc,
filesFreeDesc: filesFreeDesc, filesFreeDesc: filesFreeDesc,
roDesc: roDesc, roDesc: roDesc,
devErrors: devErrors, deviceErrorDesc: deviceErrorDesc,
}, nil }, nil
} }
@ -137,6 +140,14 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
} }
seen[s.labels] = true seen[s.labels] = true
ch <- prometheus.MustNewConstMetric(
c.deviceErrorDesc, prometheus.GaugeValue,
s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
if s.deviceError > 0 {
continue
}
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.sizeDesc, prometheus.GaugeValue, c.sizeDesc, prometheus.GaugeValue,
s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType,
@ -162,6 +173,5 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType,
) )
} }
c.devErrors.Collect(ch)
return nil return nil
} }

View file

@ -46,13 +46,15 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) {
log.Debugf("Ignoring fs type: %s", labels.fsType) log.Debugf("Ignoring fs type: %s", labels.fsType)
continue continue
} }
labelValues := []string{labels.device, labels.mountPoint, labels.fsType}
buf := new(syscall.Statfs_t) buf := new(syscall.Statfs_t)
err := syscall.Statfs(labels.mountPoint, buf) err := syscall.Statfs(labels.mountPoint, buf)
if err != nil { if err != nil {
c.devErrors.WithLabelValues(labelValues...).Inc() stats = append(stats, filesystemStats{
log.Debugf("Statfs on %s returned %s", labels: labels,
labels.mountPoint, err) deviceError: 1,
})
log.Errorf("Error on statfs() system call for %q: %s", labels.mountPoint, err)
continue continue
} }
@ -82,11 +84,14 @@ func mountPointDetails() ([]filesystemLabels, error) {
defer file.Close() defer file.Close()
filesystems := []filesystemLabels{} filesystems := []filesystemLabels{}
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
parts := strings.Fields(scanner.Text()) parts := strings.Fields(scanner.Text())
filesystems = append(filesystems, filesystemLabels{parts[0], parts[1], parts[2]}) filesystems = append(filesystems, filesystemLabels{
device: parts[0],
mountPoint: parts[1],
fsType: parts[2],
})
} }
return filesystems, scanner.Err() return filesystems, scanner.Err()
} }