Release v1.9.1 (#3285)

* Avoid memory leak by using value rather than reference. (#3277)

Signed-off-by: Rolf Klemenz <tickez@madnet.ch>

* pressure: Fix missing IRQ on older kernels (#3263)

Fix "no data" error on kernels that support some PSI status, but don't
yet have IRQ presure metrics. Only report "no data" error if `pressure`
is enabled and no PSI metrics were found.

Fixes: https://github.com/prometheus/node_exporter/issues/3259

Signed-off-by: Ben Kochie <superq@gmail.com>

* Release v1.9.1

* [BUGFIX] pressure: Fix missing IRQ on older kernels #3263
* [BUGFIX] Fix Darwin memory leak #3277

Signed-off-by: Ben Kochie <superq@gmail.com>

---------

Signed-off-by: Rolf Klemenz <tickez@madnet.ch>
Signed-off-by: Ben Kochie <superq@gmail.com>
Co-authored-by: Rolf Klemenz <tickez@madnet.ch>
This commit is contained in:
Ben Kochie 2025-04-01 17:14:14 +02:00 committed by GitHub
parent 02afa5c53c
commit 4e93d3f034
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 65 additions and 43 deletions

View file

@ -5,6 +5,11 @@
* [ENHANCEMENT] * [ENHANCEMENT]
* [BUGFIX] * [BUGFIX]
## 1.9.1 / 2025-04-01
* [BUGFIX] pressure: Fix missing IRQ on older kernels #3263
* [BUGFIX] Fix Darwin memory leak #3277
## 1.9.0 / 2025-02-17 ## 1.9.0 / 2025-02-17
* [CHANGE] meminfo: Convert linux implementation to use procfs lib #3049 * [CHANGE] meminfo: Convert linux implementation to use procfs lib #3049

View file

@ -1 +1 @@
1.9.0 1.9.1

View file

@ -89,7 +89,7 @@ type filesystemStats struct {
labels filesystemLabels labels filesystemLabels
size, free, avail float64 size, free, avail float64
files, filesFree float64 files, filesFree float64
purgeable *float64 purgeable float64
ro, deviceError float64 ro, deviceError float64
} }
@ -232,11 +232,10 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
c.mountInfoDesc, prometheus.GaugeValue, c.mountInfoDesc, prometheus.GaugeValue,
1.0, s.labels.device, s.labels.major, s.labels.minor, s.labels.mountPoint, 1.0, s.labels.device, s.labels.major, s.labels.minor, s.labels.mountPoint,
) )
if s.purgeable != nil { if s.purgeable >= 0 {
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.purgeableDesc, prometheus.GaugeValue, c.purgeableDesc, prometheus.GaugeValue,
*s.purgeable, s.labels.device, s.labels.mountPoint, s.purgeable, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.labels.fsType, s.labels.deviceError,
) )
} }
} }

View file

@ -20,23 +20,22 @@ package collector
#cgo CFLAGS: -x objective-c #cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation #cgo LDFLAGS: -framework Foundation
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
Float64 *purgeable(char *path) { Float64 purgeable(char *path) {
CFNumberRef tmp; CFNumberRef tmp;
Float64 *value;
NSError *error = nil; NSError *error = nil;
NSString *str = [NSString stringWithUTF8String:path]; NSString *str = [NSString stringWithUTF8String:path];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:str]; NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:str];
NSDictionary *results = [fileURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error]; NSDictionary *results = [fileURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error];
if (results) { if (results) {
if ((tmp = CFDictionaryGetValue((CFDictionaryRef)results, NSURLVolumeAvailableCapacityForImportantUsageKey)) == NULL) if ((tmp = CFDictionaryGetValue((CFDictionaryRef)results, NSURLVolumeAvailableCapacityForImportantUsageKey)) == NULL) {
return NULL; return -1.0f;
value = (Float64 *)malloc(sizeof(Float64)); }
if (CFNumberGetValue(tmp, kCFNumberFloat64Type, value)) { Float64 value;
if (CFNumberGetValue(tmp, kCFNumberFloat64Type, &value)) {
return value; return value;
} }
} }
free(value); return -1.0f;
return NULL;
} }
*/ */
import "C" import "C"
@ -100,7 +99,7 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) {
avail: float64(mnt[i].f_bavail) * float64(mnt[i].f_bsize), avail: float64(mnt[i].f_bavail) * float64(mnt[i].f_bsize),
files: float64(mnt[i].f_files), files: float64(mnt[i].f_files),
filesFree: float64(mnt[i].f_ffree), filesFree: float64(mnt[i].f_ffree),
purgeable: (*float64)(C.purgeable(C.CString(mountpoint))), purgeable: float64(C.purgeable(C.CString(mountpoint))),
ro: ro, ro: ro,
}) })
} }

View file

@ -27,8 +27,15 @@ import (
"github.com/prometheus/procfs" "github.com/prometheus/procfs"
) )
const (
psiResourceCPU = "cpu"
psiResourceIO = "io"
psiResourceMemory = "memory"
psiResourceIRQ = "irq"
)
var ( var (
psiResources = []string{"cpu", "io", "memory", "irq"} psiResources = []string{psiResourceCPU, psiResourceIO, psiResourceMemory, psiResourceIRQ}
) )
type pressureStatsCollector struct { type pressureStatsCollector struct {
@ -93,13 +100,18 @@ func NewPressureStatsCollector(logger *slog.Logger) (Collector, error) {
// Update calls procfs.NewPSIStatsForResource for the different resources and updates the values // Update calls procfs.NewPSIStatsForResource for the different resources and updates the values
func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error {
foundResources := 0
for _, res := range psiResources { for _, res := range psiResources {
c.logger.Debug("collecting statistics for resource", "resource", res) c.logger.Debug("collecting statistics for resource", "resource", res)
vals, err := c.fs.PSIStatsForResource(res) vals, err := c.fs.PSIStatsForResource(res)
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) && res != psiResourceIRQ {
c.logger.Debug("pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel") c.logger.Debug("pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel", "resource", res)
return ErrNoData continue
}
if errors.Is(err, os.ErrNotExist) && res == psiResourceIRQ {
c.logger.Debug("IRQ pressure information is unavailable, you need a Linux kernel >= 6.1 and/or CONFIG_PSI enabled for your kernel", "resource", res)
continue
} }
if errors.Is(err, syscall.ENOTSUP) { if errors.Is(err, syscall.ENOTSUP) {
c.logger.Debug("pressure information is disabled, add psi=1 kernel command line to enable it") c.logger.Debug("pressure information is disabled, add psi=1 kernel command line to enable it")
@ -109,28 +121,35 @@ func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error {
} }
// IRQ pressure does not have 'some' data. // IRQ pressure does not have 'some' data.
// See https://github.com/torvalds/linux/blob/v6.9/include/linux/psi_types.h#L65 // See https://github.com/torvalds/linux/blob/v6.9/include/linux/psi_types.h#L65
if vals.Some == nil && res != "irq" { if vals.Some == nil && res != psiResourceIRQ {
c.logger.Debug("pressure information returned no 'some' data") c.logger.Debug("pressure information returned no 'some' data")
return ErrNoData return ErrNoData
} }
if vals.Full == nil && res != "cpu" { if vals.Full == nil && res != psiResourceCPU {
c.logger.Debug("pressure information returned no 'full' data") c.logger.Debug("pressure information returned no 'full' data")
return ErrNoData return ErrNoData
} }
switch res { switch res {
case "cpu": case psiResourceCPU:
ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0)
case "io": case psiResourceIO:
ch <- prometheus.MustNewConstMetric(c.io, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.io, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0)
ch <- prometheus.MustNewConstMetric(c.ioFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.ioFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0)
case "memory": case psiResourceMemory:
ch <- prometheus.MustNewConstMetric(c.mem, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.mem, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0)
ch <- prometheus.MustNewConstMetric(c.memFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.memFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0)
case "irq": case psiResourceIRQ:
ch <- prometheus.MustNewConstMetric(c.irqFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.irqFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0)
default: default:
c.logger.Debug("did not account for resource", "resource", res) c.logger.Debug("did not account for resource", "resource", res)
continue
} }
foundResources++
}
if foundResources == 0 {
c.logger.Debug("pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel")
return ErrNoData
} }
return nil return nil

14
go.mod
View file

@ -1,6 +1,6 @@
module github.com/prometheus/node_exporter module github.com/prometheus/node_exporter
go 1.22.0 go 1.23.0
require ( require (
github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/kingpin/v2 v2.4.0
@ -29,7 +29,7 @@ require (
github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b // == v0.15.1 + https://github.com/prometheus/procfs/commit/1754b780536bb81082baa913e04cc4fff4d2baea github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b // == v0.15.1 + https://github.com/prometheus/procfs/commit/1754b780536bb81082baa913e04cc4fff4d2baea
github.com/safchain/ethtool v0.5.10 github.com/safchain/ethtool v0.5.10
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/sys v0.30.0 golang.org/x/sys v0.31.0
howett.net/plist v1.0.1 howett.net/plist v1.0.1
) )
@ -51,11 +51,11 @@ require (
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.32.0 // indirect golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.33.0 // indirect golang.org/x/net v0.37.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/oauth2 v0.28.0 // indirect
golang.org/x/sync v0.10.0 // indirect golang.org/x/sync v0.12.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.1 // indirect google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

24
go.sum
View file

@ -102,23 +102,23 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=