sys-exporter/collector/meminfo/meminfo.go

114 lines
2.6 KiB
Go

package meminfo
import (
"context"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
"git.faercol.me/monitoring/sys-exporter/registry"
"git.faercol.me/monitoring/sys-exporter/utils"
"github.com/prometheus/client_golang/prometheus"
)
const meminfoFileLocation = "/proc/meminfo"
var meminfoRegex = regexp.MustCompile(`(?m)^(?P<key>\S+):\s+(?P<value>\d+(?:\s\S+$|$))`)
func valueInBytes(rawVal string) (int, error) {
split := strings.Split(rawVal, " ")
baseVal, err := strconv.ParseInt(split[0], 10, 64)
if err != nil {
return 0, fmt.Errorf("invalid base value %q: %w", split[0], err)
}
if len(split) == 1 { // no unit present in the value
return int(baseVal), nil
}
switch split[1] {
case "B":
return int(baseVal), nil
case "kB":
return 1000 * int(baseVal), nil
case "MB":
return 1_000_000 * int(baseVal), nil
case "GB":
return 1_000_000_000 * int(baseVal), nil
case "TB":
return 1_000_000_000_000 * int(baseVal), nil
default:
return 0, fmt.Errorf("invalid unit %q", split[1])
}
}
type MeminfoCollector struct {
meminfoFileLocation string
promExporter *prometheus.SummaryVec
updateFreq time.Duration
}
func (c *MeminfoCollector) Collect() interface{} {
return nil
}
func (c *MeminfoCollector) update() error {
fileContent, err := os.ReadFile(c.meminfoFileLocation)
if err != nil {
return fmt.Errorf("failed to read meminfo file: %w", err)
}
rawMatches, err := utils.HandleRegexp(string(fileContent), *meminfoRegex)
if err != nil {
return fmt.Errorf("failed to parse meminfo file: %w", err)
}
mappedVals := utils.HandleKeyValRegexRes(rawMatches)
for valueType, rawVal := range mappedVals {
val, err := valueInBytes(rawVal)
if err != nil {
return fmt.Errorf("invalid value in meminfo file: %w", err)
}
c.promExporter.WithLabelValues(valueType).Observe(float64(val))
}
return nil
}
func (c *MeminfoCollector) Run(ctx context.Context) {
if err := c.update(); err != nil {
fmt.Printf("Failed to init meminfo collector: %s\n", err)
}
for {
select {
case <-ctx.Done():
return
case <-time.After(c.updateFreq):
if err := c.update(); err != nil {
fmt.Printf("Failed to update meminfo collector: %s\n", err)
}
}
}
}
func (c *MeminfoCollector) PromCollector() prometheus.Collector {
return c.promExporter
}
func New() *MeminfoCollector {
return &MeminfoCollector{
updateFreq: 1 * time.Second,
meminfoFileLocation: meminfoFileLocation,
promExporter: prometheus.NewSummaryVec(prometheus.SummaryOpts{Namespace: "system", Name: "meminfo", Help: "System memory info"}, []string{"type"}),
}
}
func init() {
registry.R.MustRegisterCollector("system.meminfo", New())
}