2024-12-01 16:00:52 +00:00
|
|
|
package uptime
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"git.faercol.me/monitoring/sys-exporter/registry"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2024-12-02 17:37:19 +00:00
|
|
|
"go.uber.org/zap"
|
2024-12-01 16:00:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const procUptimeLocation = "/proc/uptime"
|
|
|
|
|
|
|
|
type UptimeCollector struct {
|
|
|
|
ctx context.Context
|
|
|
|
procFileLocation string
|
|
|
|
uptimeFreq time.Duration
|
|
|
|
uptime time.Duration
|
|
|
|
uptimeProm prometheus.Gauge
|
2024-12-02 17:37:19 +00:00
|
|
|
|
|
|
|
l *zap.SugaredLogger
|
2024-12-01 16:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-12-02 17:37:19 +00:00
|
|
|
func (c *UptimeCollector) Run(ctx context.Context, l *zap.SugaredLogger) {
|
|
|
|
c.l = l
|
|
|
|
|
|
|
|
c.l.Debug("Initializing collector")
|
2024-12-01 16:00:52 +00:00
|
|
|
if err := c.update(); err != nil {
|
2024-12-02 17:37:19 +00:00
|
|
|
c.l.Errorf("Failed to init collector: %s\n", err)
|
2024-12-01 16:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2024-12-02 17:37:19 +00:00
|
|
|
c.l.Debug("Stopping collector")
|
2024-12-01 16:00:52 +00:00
|
|
|
return
|
|
|
|
case <-time.After(c.uptimeFreq):
|
2024-12-02 17:37:19 +00:00
|
|
|
c.l.Debug("Updating collector")
|
2024-12-01 16:00:52 +00:00
|
|
|
if err := c.update(); err != nil {
|
2024-12-02 17:37:19 +00:00
|
|
|
c.l.Errorf("Failed to update collector: %s\n", err)
|
2024-12-01 16:00:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func New() *UptimeCollector {
|
|
|
|
return &UptimeCollector{
|
|
|
|
ctx: context.TODO(),
|
|
|
|
procFileLocation: procUptimeLocation,
|
|
|
|
uptimeFreq: 1 * time.Second,
|
|
|
|
uptime: 0,
|
2024-12-07 16:22:49 +00:00
|
|
|
uptimeProm: prometheus.NewGauge(prometheus.GaugeOpts{Namespace: "system", Name: "uptime_sec", Help: "System uptime in seconds"}),
|
2024-12-01 16:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
registry.R.MustRegisterCollector("system.uptime", New())
|
|
|
|
}
|