Add sysinfo collector
All checks were successful
/ go-test (push) Successful in 9m48s

This commit is contained in:
Melora Hugues 2024-12-07 17:19:05 +01:00
parent f84419de0d
commit 5df32a9b98
Signed by: faercol
SSH key fingerprint: SHA256:lzUKsKDIw1w0bcLnoBu84oYJOnLmi7SeKetszpyDCFY
4 changed files with 198 additions and 0 deletions

View file

@ -14,6 +14,7 @@ import (
_ "git.faercol.me/monitoring/sys-exporter/collector/loadavg" _ "git.faercol.me/monitoring/sys-exporter/collector/loadavg"
_ "git.faercol.me/monitoring/sys-exporter/collector/meminfo" _ "git.faercol.me/monitoring/sys-exporter/collector/meminfo"
_ "git.faercol.me/monitoring/sys-exporter/collector/pacman" _ "git.faercol.me/monitoring/sys-exporter/collector/pacman"
_ "git.faercol.me/monitoring/sys-exporter/collector/sysinfo"
_ "git.faercol.me/monitoring/sys-exporter/collector/systemd" _ "git.faercol.me/monitoring/sys-exporter/collector/systemd"
_ "git.faercol.me/monitoring/sys-exporter/collector/uptime" _ "git.faercol.me/monitoring/sys-exporter/collector/uptime"
) )

View file

@ -0,0 +1,167 @@
package sysinfo
import (
"context"
"errors"
"fmt"
"os"
"regexp"
"strings"
"time"
"git.faercol.me/monitoring/sys-exporter/registry"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)
var versionRegexp = regexp.MustCompile(`(?m)^(\w+) version ([^ ]+) .*$`)
const (
hosnameFile = "/etc/hostname"
osRealeaseFile = "/etc/os-release"
versionFile = "/proc/version"
)
var labels = []string{
"hostname",
"kernel",
"os",
"distro",
"distro_version",
}
type SysinfoCollector struct {
hostname string
kernelVersion string
os string
distro string
distroVersion string
hostnameFile string
osReleaseFile string
versionFile string
promExporter *prometheus.GaugeVec
updateFreq time.Duration
l *zap.SugaredLogger
}
func (c *SysinfoCollector) Collect() interface{} {
return nil
}
func (c *SysinfoCollector) labels() prometheus.Labels {
return prometheus.Labels{
"hostname": c.hostname,
"kernel": c.kernelVersion,
"os": c.os,
"distro": c.distro,
"distro_version": c.distroVersion,
}
}
func (c *SysinfoCollector) updateHostname() error {
rawHostname, err := os.ReadFile(c.hostnameFile)
if err != nil {
return err
}
c.hostname = strings.TrimSpace(string(rawHostname))
return nil
}
func (c *SysinfoCollector) updateKernelVersion() error {
rawVersion, err := os.ReadFile(c.versionFile)
if err != nil {
return err
}
if matches := versionRegexp.FindAllStringSubmatch(string(rawVersion), -1); matches == nil {
return errors.New("failed to match /proc/version")
} else {
c.os = matches[0][1]
c.kernelVersion = matches[0][2]
}
return nil
}
func (c *SysinfoCollector) updateDistro() error {
rawContent, err := os.ReadFile(c.osReleaseFile)
if err != nil {
return err
}
releaseData, err := parseOSRelease(rawContent)
if err != nil {
return fmt.Errorf("failed to parse /etc/os-release file: %w", err)
}
switch releaseData["ID"] {
case "arch":
c.distro = "Arch Linux"
c.distroVersion = releaseData["BUILD_ID"]
case "debian":
c.distro = "Debian GNU/Linux"
c.distroVersion = releaseData["VERSION"]
default:
c.distro = releaseData["NAME"]
}
return nil
}
func (c *SysinfoCollector) update() error {
if err := c.updateKernelVersion(); err != nil {
return fmt.Errorf("failed to get kernel version: %w", err)
}
if err := c.updateHostname(); err != nil {
return fmt.Errorf("failed to get hostname: %w", err)
}
if err := c.updateDistro(); err != nil {
return fmt.Errorf("failed to get distro info: %w", err)
}
c.promExporter.With(c.labels()).Set(1)
return nil
}
func (c *SysinfoCollector) 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.updateFreq):
c.l.Debug("Updating collector")
if err := c.update(); err != nil {
c.l.Errorf("Failed to update collector: %s\n", err)
}
}
}
}
func (c *SysinfoCollector) PromCollector() prometheus.Collector {
return c.promExporter
}
func New() *SysinfoCollector {
return &SysinfoCollector{
updateFreq: 30 * time.Minute,
hostnameFile: hosnameFile,
osReleaseFile: osRealeaseFile,
versionFile: versionFile,
promExporter: prometheus.NewGaugeVec(prometheus.GaugeOpts{Namespace: "system", Name: "info", Help: "System global information"}, labels),
}
}
func init() {
registry.R.MustRegisterCollector("system.info", New())
}

View file

@ -0,0 +1,29 @@
package sysinfo
import (
"fmt"
"strings"
)
func parseOSRelease(content []byte) (map[string]string, error) {
res := make(map[string]string)
for _, line := range strings.Split(string(content), "\n") {
if line == "" {
continue
}
lineVals := strings.SplitN(line, "=", 2)
if len(lineVals) != 2 {
return nil, fmt.Errorf("impossible to split line %q", line)
}
if _, ok := res[lineVals[0]]; ok {
return nil, fmt.Errorf("duplicate key %q", lineVals[0])
}
res[lineVals[0]] = lineVals[1]
}
return res, nil
}

View file

@ -5,3 +5,4 @@ collectors:
- system.meminfo - system.meminfo
- services.systemd - services.systemd
- system.uptime - system.uptime
- system.info