Compare commits
2 commits
a99d624cdd
...
9e5450475e
Author | SHA1 | Date | |
---|---|---|---|
9e5450475e | |||
c48f1e0f66 |
11 changed files with 718 additions and 1 deletions
|
@ -1,7 +1,33 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/registry"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
_ "git.faercol.me/monitoring/sys-exporter/collector/loadavg"
|
||||
_ "git.faercol.me/monitoring/sys-exporter/collector/meminfo"
|
||||
_ "git.faercol.me/monitoring/sys-exporter/collector/pacman"
|
||||
_ "git.faercol.me/monitoring/sys-exporter/collector/systemd"
|
||||
_ "git.faercol.me/monitoring/sys-exporter/collector/uptime"
|
||||
)
|
||||
|
||||
// type gatherer struct {
|
||||
// reg *prometheus.Registry
|
||||
// }
|
||||
|
||||
// func (g *gatherer) Gather() (pcm.MetricFamily, error) {
|
||||
// return g.reg.
|
||||
// }
|
||||
|
||||
func main() {
|
||||
fmt.Println("starting server")
|
||||
|
||||
registry.R.Run(context.Background())
|
||||
|
||||
http.Handle("/metrics", promhttp.HandlerFor(registry.R.PromRegistry(), promhttp.HandlerOpts{}))
|
||||
http.ListenAndServe(":2112", nil)
|
||||
}
|
||||
|
|
13
collector/collector.go
Normal file
13
collector/collector.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type Collector interface {
|
||||
Collect() interface{}
|
||||
PromCollector() prometheus.Collector
|
||||
Run(ctx context.Context)
|
||||
}
|
86
collector/loadavg/loadavg.go
Normal file
86
collector/loadavg/loadavg.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package loadavg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/registry"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const loadAvgFileLocation = "/proc/loadavg"
|
||||
|
||||
type LoadAvgCollector struct {
|
||||
loadAvgFileLocation string
|
||||
promExporter *prometheus.SummaryVec
|
||||
updateFreq time.Duration
|
||||
}
|
||||
|
||||
func (c *LoadAvgCollector) Collect() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *LoadAvgCollector) update() error {
|
||||
fileContent, err := os.ReadFile(c.loadAvgFileLocation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read loadavg file: %w", err)
|
||||
}
|
||||
|
||||
vals := strings.Split(string(fileContent), " ")
|
||||
if len(vals) != 5 {
|
||||
return fmt.Errorf("invalid format %q for loadavg file", string(fileContent))
|
||||
}
|
||||
|
||||
var load1Min, load5Min, load15Min float64
|
||||
if load1Min, err = strconv.ParseFloat(vals[0], 64); err != nil {
|
||||
return fmt.Errorf("invalid value %q for load 1min: %w", vals[0], err)
|
||||
}
|
||||
if load5Min, err = strconv.ParseFloat(vals[1], 64); err != nil {
|
||||
return fmt.Errorf("invalid value %q for load 5mins: %w", vals[1], err)
|
||||
}
|
||||
if load15Min, err = strconv.ParseFloat(vals[2], 64); err != nil {
|
||||
return fmt.Errorf("invalid value %q for load 15min: %w", vals[2], err)
|
||||
}
|
||||
|
||||
c.promExporter.WithLabelValues("1min").Observe(load1Min)
|
||||
c.promExporter.WithLabelValues("5mins").Observe(load5Min)
|
||||
c.promExporter.WithLabelValues("15mins").Observe(load15Min)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *LoadAvgCollector) Run(ctx context.Context) {
|
||||
if err := c.update(); err != nil {
|
||||
fmt.Printf("Failed to init loadavg 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 loadavg collector: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LoadAvgCollector) PromCollector() prometheus.Collector {
|
||||
return c.promExporter
|
||||
}
|
||||
|
||||
func New() *LoadAvgCollector {
|
||||
return &LoadAvgCollector{
|
||||
updateFreq: 1 * time.Second,
|
||||
loadAvgFileLocation: loadAvgFileLocation,
|
||||
promExporter: prometheus.NewSummaryVec(prometheus.SummaryOpts{Namespace: "system", Name: "loadavg", Help: "Load average of the system"}, []string{"period"}),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.R.MustRegisterCollector("system.loadavg", New())
|
||||
}
|
114
collector/meminfo/meminfo.go
Normal file
114
collector/meminfo/meminfo.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
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())
|
||||
}
|
111
collector/pacman/pacman.go
Normal file
111
collector/pacman/pacman.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package pacman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/registry"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
totalLabel = "total"
|
||||
updatesLabel = "updates"
|
||||
)
|
||||
|
||||
func listCommandLines(command string, args ...string) (int, error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if string(out) == "" { // pacman can return a returncode 1 when the list is empty
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("failed to run command: %w (%s)", err, string(out))
|
||||
}
|
||||
|
||||
return len(strings.Split(string(out), "\n")), nil
|
||||
}
|
||||
|
||||
type PacmanCollector struct {
|
||||
installedPackages int
|
||||
promPackages *prometheus.GaugeVec
|
||||
pendingUpdates int
|
||||
updateFreq time.Duration
|
||||
}
|
||||
|
||||
func (c *PacmanCollector) Collect() interface{} {
|
||||
return map[string]int{
|
||||
"installed_packages": c.installedPackages,
|
||||
"pending_updates": c.pendingUpdates,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PacmanCollector) updateInstalledPackages() error {
|
||||
count, err := listCommandLines("/sbin/pacman", "-Q")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to count installed packages: %w", err)
|
||||
}
|
||||
|
||||
c.installedPackages = count
|
||||
c.promPackages.WithLabelValues(totalLabel).Set(float64(count))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PacmanCollector) updatePendingUpdates() error {
|
||||
updateCmd := exec.Command("/sbin/pacman", "-Sy")
|
||||
if out, err := updateCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to update pacman cache: %w (%s)", err, string(out))
|
||||
}
|
||||
|
||||
count, err := listCommandLines("/sbin/pacman", "-Qu")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to count updated packages: %w", err)
|
||||
}
|
||||
|
||||
c.pendingUpdates = count
|
||||
c.promPackages.WithLabelValues(updatesLabel).Set(float64(count))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PacmanCollector) Run(ctx context.Context) {
|
||||
if err := c.updateInstalledPackages(); err != nil {
|
||||
fmt.Printf("Failed to init count of installed packages: %s\n", err)
|
||||
}
|
||||
if err := c.updatePendingUpdates(); err != nil {
|
||||
fmt.Printf("Failed to init count of updates: %s\n", err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(c.updateFreq):
|
||||
if err := c.updateInstalledPackages(); err != nil {
|
||||
fmt.Printf("Failed to update count of installed packages: %s\n", err)
|
||||
}
|
||||
if err := c.updatePendingUpdates(); err != nil {
|
||||
fmt.Printf("Failed to update count of updates: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PacmanCollector) PromCollector() prometheus.Collector {
|
||||
return c.promPackages
|
||||
}
|
||||
|
||||
func New() *PacmanCollector {
|
||||
c := PacmanCollector{
|
||||
updateFreq: 5 * time.Minute,
|
||||
promPackages: prometheus.NewGaugeVec(prometheus.GaugeOpts{Namespace: "packages", Subsystem: "pacman", Name: "packages_count", Help: "Count of pacman packages"}, []string{"status"}),
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.R.MustRegisterCollector("packages.pacman", New())
|
||||
}
|
130
collector/systemd/systemd.go
Normal file
130
collector/systemd/systemd.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package systemd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/registry"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type serviceStatus struct {
|
||||
Unit string `json:"unit"`
|
||||
Load string `json:"load"`
|
||||
Active string `json:"active"`
|
||||
Sub string `json:"sub"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func (s serviceStatus) unitType() string {
|
||||
split := strings.Split(s.Unit, ".")
|
||||
if len(split) < 2 {
|
||||
return ""
|
||||
}
|
||||
return split[len(split)-1]
|
||||
}
|
||||
|
||||
type SystemdCollector struct {
|
||||
promExporter *prometheus.GaugeVec
|
||||
updateFreq time.Duration
|
||||
}
|
||||
|
||||
func (c *SystemdCollector) Collect() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SystemdCollector) getServicesStatus() ([]serviceStatus, error) {
|
||||
out, err := exec.Command("/sbin/systemctl", "list-units", "--output", "json").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run systemd command: %w", err)
|
||||
}
|
||||
|
||||
res := []serviceStatus{}
|
||||
if err := json.Unmarshal(out, &res); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse systemd output: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *SystemdCollector) update() error {
|
||||
systemd_output, err := c.getServicesStatus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get services status: %w", err)
|
||||
}
|
||||
|
||||
mappingPerType := map[string][]serviceStatus{}
|
||||
for _, s := range systemd_output {
|
||||
mappingPerType[s.unitType()] = append(mappingPerType[s.unitType()], s)
|
||||
}
|
||||
|
||||
totalUnits := 0
|
||||
runningUnits := 0
|
||||
failedUnits := 0
|
||||
|
||||
for unitType, units := range mappingPerType {
|
||||
|
||||
unitsForType := 0
|
||||
healthyForType := 0
|
||||
failedForType := 0
|
||||
|
||||
for _, u := range units {
|
||||
unitsForType++
|
||||
totalUnits++
|
||||
if u.Active == "active" {
|
||||
runningUnits++
|
||||
healthyForType++
|
||||
}
|
||||
if u.Active == "failed" {
|
||||
failedUnits++
|
||||
failedForType++
|
||||
}
|
||||
}
|
||||
|
||||
c.promExporter.WithLabelValues(unitType, "total").Set(float64(unitsForType))
|
||||
c.promExporter.WithLabelValues(unitType, "running").Set(float64(healthyForType))
|
||||
c.promExporter.WithLabelValues(unitType, "failed").Set(float64(failedForType))
|
||||
}
|
||||
|
||||
c.promExporter.WithLabelValues("total", "total").Set(float64(totalUnits))
|
||||
c.promExporter.WithLabelValues("total", "running").Set(float64(runningUnits))
|
||||
c.promExporter.WithLabelValues("total", "failed").Set(float64(failedUnits))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SystemdCollector) Run(ctx context.Context) {
|
||||
if err := c.update(); err != nil {
|
||||
fmt.Printf("Failed to init systemd status: %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 systemd status: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SystemdCollector) PromCollector() prometheus.Collector {
|
||||
return c.promExporter
|
||||
}
|
||||
|
||||
func New() *SystemdCollector {
|
||||
return &SystemdCollector{
|
||||
updateFreq: 30 * time.Second,
|
||||
promExporter: prometheus.NewGaugeVec(prometheus.GaugeOpts{Namespace: "services", Subsystem: "systemd", Name: "running_units", Help: "Count of running services for systemd"}, []string{"type", "status"}),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.R.MustRegisterCollector("services.systemd", New())
|
||||
}
|
84
collector/uptime/uptime.go
Normal file
84
collector/uptime/uptime.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package uptime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/registry"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const procUptimeLocation = "/proc/uptime"
|
||||
|
||||
type UptimeCollector struct {
|
||||
ctx context.Context
|
||||
procFileLocation string
|
||||
uptimeFreq time.Duration
|
||||
uptime time.Duration
|
||||
uptimeProm prometheus.Gauge
|
||||
}
|
||||
|
||||
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) {
|
||||
if err := c.update(); err != nil {
|
||||
fmt.Printf("Failed to init collector: %s\n", err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(c.uptimeFreq):
|
||||
if err := c.update(); err != nil {
|
||||
fmt.Printf("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())
|
||||
}
|
14
go.mod
14
go.mod
|
@ -1,3 +1,17 @@
|
|||
module git.faercol.me/monitoring/sys-exporter
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require github.com/prometheus/client_golang v1.20.5
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
)
|
||||
|
|
24
go.sum
Normal file
24
go.sum
Normal file
|
@ -0,0 +1,24 @@
|
|||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
42
registry/registry.go
Normal file
42
registry/registry.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.faercol.me/monitoring/sys-exporter/collector"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var R CollectorRegistry
|
||||
|
||||
type CollectorRegistry struct {
|
||||
promRegistry *prometheus.Registry
|
||||
collectors map[string]collector.Collector
|
||||
}
|
||||
|
||||
func (r *CollectorRegistry) RegisterCollector(name string, c collector.Collector) error {
|
||||
r.collectors[name] = c
|
||||
return r.promRegistry.Register(c.PromCollector())
|
||||
}
|
||||
|
||||
func (r *CollectorRegistry) MustRegisterCollector(name string, c collector.Collector) {
|
||||
r.collectors[name] = c
|
||||
r.promRegistry.MustRegister(c.PromCollector())
|
||||
}
|
||||
|
||||
func (r *CollectorRegistry) PromRegistry() *prometheus.Registry {
|
||||
return r.promRegistry
|
||||
}
|
||||
|
||||
func (r *CollectorRegistry) Run(ctx context.Context) {
|
||||
for _, c := range r.collectors {
|
||||
go c.Run(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
R = CollectorRegistry{
|
||||
promRegistry: prometheus.NewRegistry(),
|
||||
collectors: make(map[string]collector.Collector),
|
||||
}
|
||||
}
|
73
utils/utils.go
Normal file
73
utils/utils.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// HandleRegexp handles a regex with named capture groups.
|
||||
// If the given regexp does not have any named capture group, or if no match is found, an error is returned
|
||||
// Matches are returned as a map which keys are the capture groups names
|
||||
func HandleRegexp(dat string, pattern regexp.Regexp) (res []map[string]string, err error) {
|
||||
namedGroups := 0
|
||||
for _, n := range pattern.SubexpNames() {
|
||||
if n != "" {
|
||||
namedGroups++
|
||||
}
|
||||
}
|
||||
if namedGroups == 0 {
|
||||
return nil, errors.New("no named subgroup in pattern")
|
||||
}
|
||||
|
||||
matches := pattern.FindAllStringSubmatch(dat, -1)
|
||||
if matches == nil {
|
||||
return nil, errors.New("no match found")
|
||||
}
|
||||
res = make([]map[string]string, 0)
|
||||
|
||||
for _, line := range matches {
|
||||
subMatchMap := make(map[string]string)
|
||||
for i, name := range pattern.SubexpNames() {
|
||||
if i != 0 {
|
||||
subMatchMap[name] = string(line[i])
|
||||
}
|
||||
}
|
||||
res = append(res, subMatchMap)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HandleKeyValRegexRes merges a list of maps containing result as a key/value format
|
||||
//
|
||||
// Input maps all have the following format:
|
||||
//
|
||||
// {
|
||||
// "key": "some_key",
|
||||
// "value": "some_val",
|
||||
// }
|
||||
//
|
||||
// Final result will have the format
|
||||
//
|
||||
// {
|
||||
// "some_key": "some_val",
|
||||
// }
|
||||
func HandleKeyValRegexRes(rawMatches []map[string]string) map[string]string {
|
||||
matchedMap := make(map[string]string)
|
||||
for _, m := range rawMatches {
|
||||
currKey := ""
|
||||
currVal := ""
|
||||
for k, v := range m {
|
||||
if k == "key" {
|
||||
currKey = v
|
||||
} else if k == "value" {
|
||||
currVal = v
|
||||
}
|
||||
// All other keys are ignored
|
||||
// Should not happen given the given regexp
|
||||
}
|
||||
if currKey != "" && currVal != "" {
|
||||
matchedMap[currKey] = currVal
|
||||
}
|
||||
}
|
||||
return matchedMap
|
||||
}
|
Loading…
Add table
Reference in a new issue