1717package collector
1818
1919import (
20+ "errors"
2021 "fmt"
2122 "os"
2223 "path/filepath"
@@ -29,13 +30,14 @@ import (
2930 "github.com/go-kit/log"
3031 "github.com/go-kit/log/level"
3132 "github.com/prometheus/client_golang/prometheus"
32- "github.com/prometheus /procfs"
33- "github.com/prometheus /procfs/sysfs"
33+ "github.com/rexagod /procfs"
34+ "github.com/rexagod /procfs/sysfs"
3435 "golang.org/x/exp/maps"
3536)
3637
3738type cpuCollector struct {
38- fs procfs.FS
39+ procfs procfs.FS
40+ sysfs sysfs.FS
3941 cpu * prometheus.Desc
4042 cpuInfo * prometheus.Desc
4143 cpuFrequencyHz * prometheus.Desc
@@ -45,6 +47,7 @@ type cpuCollector struct {
4547 cpuCoreThrottle * prometheus.Desc
4648 cpuPackageThrottle * prometheus.Desc
4749 cpuIsolated * prometheus.Desc
50+ cpuOnline * prometheus.Desc
4851 logger log.Logger
4952 cpuStats map [int64 ]procfs.CPUStat
5053 cpuStatsMutex sync.Mutex
@@ -71,17 +74,17 @@ func init() {
7174
7275// NewCPUCollector returns a new Collector exposing kernel/system statistics.
7376func NewCPUCollector (logger log.Logger ) (Collector , error ) {
74- fs , err := procfs .NewFS (* procPath )
77+ pfs , err := procfs .NewFS (* procPath )
7578 if err != nil {
7679 return nil , fmt .Errorf ("failed to open procfs: %w" , err )
7780 }
7881
79- sysfs , err := sysfs .NewFS (* sysPath )
82+ sfs , err := sysfs .NewFS (* sysPath )
8083 if err != nil {
8184 return nil , fmt .Errorf ("failed to open sysfs: %w" , err )
8285 }
8386
84- isolcpus , err := sysfs .IsolatedCPUs ()
87+ isolcpus , err := sfs .IsolatedCPUs ()
8588 if err != nil {
8689 if ! os .IsNotExist (err ) {
8790 return nil , fmt .Errorf ("Unable to get isolated cpus: %w" , err )
@@ -90,8 +93,9 @@ func NewCPUCollector(logger log.Logger) (Collector, error) {
9093 }
9194
9295 c := & cpuCollector {
93- fs : fs ,
94- cpu : nodeCPUSecondsDesc ,
96+ procfs : pfs ,
97+ sysfs : sfs ,
98+ cpu : nodeCPUSecondsDesc ,
9599 cpuInfo : prometheus .NewDesc (
96100 prometheus .BuildFQName (namespace , cpuCollectorSubsystem , "info" ),
97101 "CPU information from /proc/cpuinfo." ,
@@ -132,6 +136,11 @@ func NewCPUCollector(logger log.Logger) (Collector, error) {
132136 "Whether each core is isolated, information from /sys/devices/system/cpu/isolated." ,
133137 []string {"cpu" }, nil ,
134138 ),
139+ cpuOnline : prometheus .NewDesc (
140+ prometheus .BuildFQName (namespace , cpuCollectorSubsystem , "online" ),
141+ "CPUs that are online and being scheduled." ,
142+ []string {"cpu" }, nil ,
143+ ),
135144 logger : logger ,
136145 isolatedCpus : isolcpus ,
137146 cpuStats : make (map [int64 ]procfs.CPUStat ),
@@ -178,12 +187,21 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error {
178187 if c .isolatedCpus != nil {
179188 c .updateIsolated (ch )
180189 }
181- return c .updateThermalThrottle (ch )
190+ err := c .updateThermalThrottle (ch )
191+ if err != nil {
192+ return err
193+ }
194+ err = c .updateOnline (ch )
195+ if err != nil {
196+ return err
197+ }
198+
199+ return nil
182200}
183201
184202// updateInfo reads /proc/cpuinfo
185203func (c * cpuCollector ) updateInfo (ch chan <- prometheus.Metric ) error {
186- info , err := c .fs .CPUInfo ()
204+ info , err := c .procfs .CPUInfo ()
187205 if err != nil {
188206 return err
189207 }
@@ -334,9 +352,31 @@ func (c *cpuCollector) updateIsolated(ch chan<- prometheus.Metric) {
334352 }
335353}
336354
355+ // updateOnline reads /sys/devices/system/cpu/cpu*/online through sysfs and exports online status metrics.
356+ func (c * cpuCollector ) updateOnline (ch chan <- prometheus.Metric ) error {
357+ cpus , err := c .sysfs .CPUs ()
358+ if err != nil {
359+ return err
360+ }
361+ // No-op if the system does not support CPU online stats.
362+ cpu0 := cpus [0 ]
363+ if _ , err := cpu0 .Online (); err != nil && errors .Is (err , os .ErrNotExist ) {
364+ return nil
365+ }
366+ for _ , cpu := range cpus {
367+ setOnline := float64 (0 )
368+ if online , _ := cpu .Online (); online {
369+ setOnline = 1
370+ }
371+ ch <- prometheus .MustNewConstMetric (c .cpuOnline , prometheus .GaugeValue , setOnline , cpu .Number ())
372+ }
373+
374+ return nil
375+ }
376+
337377// updateStat reads /proc/stat through procfs and exports CPU-related metrics.
338378func (c * cpuCollector ) updateStat (ch chan <- prometheus.Metric ) error {
339- stats , err := c .fs .Stat ()
379+ stats , err := c .procfs .Stat ()
340380 if err != nil {
341381 return err
342382 }
0 commit comments