sys-exporter/collector/uptime/uptime.go

92 lines
2.1 KiB
Go

package uptime
import (
"context"
"fmt"
"os"
"strconv"
"strings"
"time"
"git.faercol.me/monitoring/sys-exporter/registry"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)
const procUptimeLocation = "/proc/uptime"
type UptimeCollector struct {
ctx context.Context
procFileLocation string
uptimeFreq time.Duration
uptime time.Duration
uptimeProm prometheus.Gauge
l *zap.SugaredLogger
}
func (c *UptimeCollector) Collect() interface{} {
return c.uptime
}
func (c *UptimeCollector) update() error {
fileContent, err := os.ReadFile(c.procFileLocation)
if err != nil {
return fmt.Errorf("failed to read uptime file: %w", err)
}
uptimeVals := strings.Split(string(fileContent), " ")
if len(uptimeVals) != 2 {
return fmt.Errorf("invalid format for /proc/uptime: %q", string(fileContent))
}
uptimeFloat, err := strconv.ParseFloat(uptimeVals[0], 64)
if err != nil {
return fmt.Errorf("invalid uptime format for float %s: %w", uptimeVals[0], err)
}
c.uptime = time.Duration(int(uptimeFloat)) * time.Second
c.uptimeProm.Set(uptimeFloat)
return nil
}
func (c *UptimeCollector) PromCollector() prometheus.Collector {
return c.uptimeProm
}
func (c *UptimeCollector) Run(ctx context.Context, l *zap.SugaredLogger) {
c.l = l
c.l.Debug("Initializing collector")
if err := c.update(); err != nil {
c.l.Errorf("Failed to init collector: %s\n", err)
}
for {
select {
case <-ctx.Done():
c.l.Debug("Stopping collector")
return
case <-time.After(c.uptimeFreq):
c.l.Debug("Updating collector")
if err := c.update(); err != nil {
c.l.Errorf("Failed to update collector: %s\n", err)
}
}
}
}
func New() *UptimeCollector {
return &UptimeCollector{
ctx: context.TODO(),
procFileLocation: procUptimeLocation,
uptimeFreq: 1 * time.Second,
uptime: 0,
uptimeProm: prometheus.NewGauge(prometheus.GaugeOpts{Namespace: "system", Name: "uptime_nanosec", Help: "System uptime in nanoseconds"}),
}
}
func init() {
registry.R.MustRegisterCollector("system.uptime", New())
}