feat(init): initial working implementation

This commit is contained in:
2023-12-15 23:39:40 +00:00
parent 1d458c84c4
commit 204938f5b1
7 changed files with 806 additions and 0 deletions

130
battery/battery.go Normal file
View File

@@ -0,0 +1,130 @@
//go:build darwin
package battery
import (
"math"
"time"
)
type Battery struct {
// BatteryCellDisconnectCount is the number of times the battery cells have
// been disconnected.
BatteryCellDisconnectCount int
// BuiltIn indicates if the battery is built-in or not.
BuiltIn bool
// ChargeRateAmps is the current charge rate in mAh. Negative values indicate
// discharge, positive values indicate charging.
ChargeRateAmps int64
// ChargeRateWatts is the current charge rate in mWh. Negative values indicate
// discharge, positive values indicate charging.
ChargeRateWatts float64
// CurrentCapacityAmps is the current battery capacity in mAh.
CurrentCapacityAmps int
// CurrentCapacityWatts is the current battery capacity in mWh.
CurrentCapacityWatts float64
// CurrentPercentage is the current battery capacity as a percentage.
CurrentPercentage int
// CycleCount is the current cycle count.
CycleCount int
// DesignCapacityAmps is the design capacity in mAh.
DesignCapacityAmps int
// DesignCapacityWatts is the design capacity in mWh.
DesignCapacityWatts float64
// DeviceName is the battery device name.
DeviceName string
// FullyCharged indicates if the battery is fully charged.
FullyCharged bool
// Health is the battery health as a percentage (0-100%).
Health int
// IsCharging indicates if the battery is currently charging.
IsCharging bool
// MaxCapacityAmps is the maximum capacity in mAh.
MaxCapacityAmps int
// MaxCapacityWatts is the maximum capacity in mWh.
MaxCapacityWatts float64
// Serial is the battery serial number.
Serial string
// Temperature is the current temperature in °C.
Temperature float64
// TimeRemaining is the estimated time remaining until the battery is
// fully charged or discharged.
TimeRemaining time.Duration
// Voltage is the current voltage in mV.
Voltage int64
}
func newBattery(b *batteryRaw) *Battery {
volts := float64(b.Voltage) / 1000
return &Battery{
BatteryCellDisconnectCount: b.BatteryCellDisconnectCount,
BuiltIn: b.BuiltIn,
ChargeRateAmps: b.Amperage,
ChargeRateWatts: roundTo(float64(b.Amperage)*volts, 3),
CurrentCapacityAmps: b.CurrentCapacity,
CurrentCapacityWatts: roundTo(float64(b.CurrentCapacity)*volts, 3),
CurrentPercentage: b.CurrentPercentage,
CycleCount: b.CycleCount,
DesignCapacityAmps: b.DesignCapacity,
DesignCapacityWatts: roundTo(float64(b.DesignCapacity)*volts, 3),
DeviceName: b.DeviceName,
FullyCharged: b.FullyCharged,
Health: b.Health,
IsCharging: b.IsCharging,
MaxCapacityAmps: b.MaxCapacity,
MaxCapacityWatts: roundTo(float64(b.MaxCapacity)*volts, 3),
Serial: b.Serial,
Temperature: float64(b.Temperature) / 100,
TimeRemaining: time.Duration(b.TimeRemaining) * time.Minute,
Voltage: b.Voltage,
}
}
func Get() (*Battery, error) {
batteriesRaw, err := getAllRaw()
if err != nil {
return nil, err
}
return newBattery(batteriesRaw[0]), nil
}
func GetAll() ([]*Battery, error) {
batteriesRaw, err := getAllRaw()
if err != nil {
return nil, err
}
batteries := []*Battery{}
for _, b := range batteriesRaw {
batteries = append(batteries, newBattery(b))
}
return batteries, nil
}
// roundTo rounds a float64 to 'places' decimal places
func roundTo(value float64, places int) float64 {
shift := math.Pow(10, float64(places))
return math.Round(value*shift) / shift
}

48
battery/ioreg.go Normal file
View File

@@ -0,0 +1,48 @@
//go:build darwin
package battery
import (
"os/exec"
"howett.net/plist"
)
// batteryRaw is the raw data structure returned by ioreg.
type batteryRaw struct {
Amperage int64 `plist:"Amperage"`
AvgTimeToEmpty int `plist:"AvgTimeToEmpty"`
AvgTimeToFull int `plist:"AvgTimeToFull"`
BatteryCellDisconnectCount int `plist:"BatteryCellDisconnectCount"`
BuiltIn bool `plist:"built-in"`
CurrentCapacity int `plist:"AppleRawCurrentCapacity"`
CurrentPercentage int `plist:"CurrentCapacity"`
CycleCount int `plist:"CycleCount"`
DesignCapacity int `plist:"DesignCapacity"`
DeviceName string `plist:"DeviceName"`
DesignCycleCount int `plist:"DesignCycleCount9C"`
ExternalConnected bool `plist:"ExternalConnected"`
FullyCharged bool `plist:"FullyCharged"`
Health int `plist:"MaxCapacity"`
IsCharging bool `plist:"IsCharging"`
MaxCapacity int `plist:"AppleRawMaxCapacity"`
Serial string `plist:"Serial"`
Temperature int `plist:"Temperature"`
TimeRemaining int `plist:"TimeRemaining"`
Voltage int64 `plist:"Voltage"`
}
func getAllRaw() ([]*batteryRaw, error) {
b, err := exec.Command("ioreg", "-ra", "-c", "AppleSmartBattery").Output()
if err != nil {
return nil, err
}
batteries := []*batteryRaw{}
_, err = plist.Unmarshal(b, &batteries)
if err != nil {
return nil, err
}
return batteries, nil
}

19
go.mod Normal file
View File

@@ -0,0 +1,19 @@
module github.com/jimeh/macos-battery-exporter
go 1.21.4
require (
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/common v0.45.0
howett.net/plist v1.0.1
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
golang.org/x/sys v0.15.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

31
go.sum Normal file
View File

@@ -0,0 +1,31 @@
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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=

117
main.go Normal file
View File

@@ -0,0 +1,117 @@
//go:build darwin
package main
import (
"flag"
"fmt"
"log"
"log/slog"
"os"
"strings"
"github.com/jimeh/macos-battery-exporter/prom"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/expfmt"
)
var (
outputFlag = flag.String(
"o", "", "Output file to write to in Prometheus format",
)
serverFlag = flag.Bool("s", false, "Run as a Prometheus metrics server")
bindFlag = flag.String(
"b", "127.0.0.1", "Bind address to run server on",
)
portFlag = flag.Int("p", 9108, "Port to run server on")
namespaceFlag = flag.String("n", "node", "Namespace for metrics")
logLevelFlag = flag.String("l", "info", "Log level")
)
func main() {
if err := mainE(); err != nil {
log.Fatal(err)
}
}
func mainE() error {
flag.Parse()
err := setupSLog(*logLevelFlag)
if err != nil {
return err
}
if *serverFlag {
opts := prom.ServerOptions{
Bind: *bindFlag,
Port: *portFlag,
}
return prom.RunServer(
*namespaceFlag,
prometheus.DefaultRegisterer.(*prometheus.Registry),
opts,
)
} else {
registry := prometheus.NewRegistry()
err := registry.Register(prom.NewCollector(*namespaceFlag))
if err != nil {
return err
}
gatherers := prometheus.Gatherers{registry}
metricFamilies, err := gatherers.Gather()
if err != nil {
return err
}
var sb strings.Builder
for _, mf := range metricFamilies {
_, err := expfmt.MetricFamilyToText(&sb, mf)
if err != nil {
return err
}
}
if *outputFlag != "" {
return writeToFile(sb.String(), *outputFlag)
} else {
fmt.Print(sb.String())
}
}
return nil
}
func setupSLog(levelStr string) error {
var level slog.Level
err := level.UnmarshalText([]byte(levelStr))
if err != nil {
return err
}
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
logger := slog.New(handler)
slog.SetDefault(logger)
return nil
}
func writeToFile(data, outputFile string) error {
file, err := os.Create(outputFile)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(data)
if err != nil {
return err
}
return nil
}

350
prom/collector.go Normal file
View File

@@ -0,0 +1,350 @@
//go:build darwin
package prom
import (
"log/slog"
"github.com/jimeh/macos-battery-exporter/battery"
"github.com/prometheus/client_golang/prometheus"
)
const (
serialLabel = "serial"
deviceNameLabel = "device_name"
builtInLabel = "built_in"
)
type Collector struct {
descInfo *prometheus.Desc
descBatteryCellDisconnectCount *prometheus.Desc
descChargeRateAmps *prometheus.Desc
descChargeRateWatts *prometheus.Desc
descCurrentCapacityAmps *prometheus.Desc
descCurrentCapacityWatts *prometheus.Desc
descCurrentPercentage *prometheus.Desc
descCycleCount *prometheus.Desc
descDesignCapacityAmps *prometheus.Desc
descDesignCapacityWatts *prometheus.Desc
descFullyCharged *prometheus.Desc
descHealth *prometheus.Desc
descIsCharging *prometheus.Desc
descMaxCapacityAmps *prometheus.Desc
descMaxCapacityWatts *prometheus.Desc
descTemperature *prometheus.Desc
descTimeRemaining *prometheus.Desc
descVoltage *prometheus.Desc
}
var _ prometheus.Collector = &Collector{}
func NewCollector(namespace string) *Collector {
c := &Collector{
descInfo: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "battery", "info"),
"Basic details about the battery.",
[]string{serialLabel, deviceNameLabel, builtInLabel},
nil,
),
descBatteryCellDisconnectCount: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "cell_disconnect_count",
),
"Total number of times a battery cell has been disconnected.",
[]string{serialLabel},
nil,
),
descChargeRateAmps: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "charge_rate_amps",
),
"Current charge rate in Ah.",
[]string{serialLabel},
nil,
),
descChargeRateWatts: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "charge_rate_watts",
),
"Current charge rate in Wh.",
[]string{serialLabel},
nil,
),
descCurrentCapacityAmps: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "current_capacity_amps",
),
"Current charge capacity in Ah.",
[]string{serialLabel},
nil,
),
descCurrentCapacityWatts: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "current_capacity_watts",
),
"Current charge capacity in Wh.",
[]string{serialLabel},
nil,
),
descCurrentPercentage: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "current_percentage",
),
"Current battery charge percentage.",
[]string{serialLabel},
nil,
),
descCycleCount: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "cycle_count",
),
"Current battery cycle count.",
[]string{serialLabel},
nil,
),
descDesignCapacityAmps: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "design_capacity_amps",
),
"Design capacity in Ah.",
[]string{serialLabel},
nil,
),
descDesignCapacityWatts: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "design_capacity_watts",
),
"Design capacity in Wh.",
[]string{serialLabel},
nil,
),
descFullyCharged: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "fully_charged",
),
"Indicates if the battery is fully charged.",
[]string{serialLabel},
nil,
),
descHealth: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "health",
),
"Battery health as a percentage (0-100%).",
[]string{serialLabel},
nil,
),
descIsCharging: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "is_charging",
),
"Indicates if the battery is currently charging.",
[]string{serialLabel},
nil,
),
descMaxCapacityAmps: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "max_capacity_amps",
),
"Design capacity in Ah.",
[]string{serialLabel},
nil,
),
descMaxCapacityWatts: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "max_capacity_watts",
),
"Design capacity in Wh.",
[]string{serialLabel},
nil,
),
descTemperature: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "temperature_celsius",
),
"Current battery temperature in °C.",
[]string{serialLabel},
nil,
),
descTimeRemaining: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "time_remaining_seconds",
),
"Estimated time remaining until battery is fully "+
"charged or discharged.",
[]string{serialLabel},
nil,
),
descVoltage: prometheus.NewDesc(
prometheus.BuildFQName(
namespace, "battery", "voltage_volts",
),
"Current battery voltage in V.",
[]string{serialLabel},
nil,
),
}
return c
}
func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.descInfo
ch <- c.descBatteryCellDisconnectCount
ch <- c.descChargeRateAmps
ch <- c.descChargeRateWatts
ch <- c.descCurrentCapacityAmps
ch <- c.descCurrentCapacityWatts
ch <- c.descCurrentPercentage
ch <- c.descCycleCount
ch <- c.descDesignCapacityAmps
ch <- c.descDesignCapacityWatts
ch <- c.descFullyCharged
ch <- c.descHealth
ch <- c.descIsCharging
ch <- c.descMaxCapacityAmps
ch <- c.descMaxCapacityWatts
ch <- c.descTemperature
ch <- c.descTimeRemaining
}
func (c *Collector) Collect(ch chan<- prometheus.Metric) {
slog.Debug("collecting battery metrics")
batteries, err := battery.GetAll()
if err != nil {
slog.Error(
"failed to get battery details",
slog.String("error", err.Error()),
)
return
}
for _, battery := range batteries {
labels := []string{battery.Serial}
ch <- prometheus.MustNewConstMetric(
c.descInfo,
prometheus.GaugeValue,
1,
battery.Serial, battery.DeviceName, boolToString(battery.BuiltIn),
)
ch <- prometheus.MustNewConstMetric(
c.descBatteryCellDisconnectCount,
prometheus.GaugeValue,
float64(battery.BatteryCellDisconnectCount),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descChargeRateAmps,
prometheus.GaugeValue,
float64(battery.ChargeRateAmps)/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descChargeRateWatts,
prometheus.GaugeValue,
battery.ChargeRateWatts/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descCurrentCapacityAmps,
prometheus.GaugeValue,
float64(battery.CurrentCapacityAmps)/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descCurrentCapacityWatts,
prometheus.GaugeValue,
battery.CurrentCapacityWatts/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descCurrentPercentage,
prometheus.GaugeValue,
float64(battery.CurrentPercentage),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descCycleCount,
prometheus.CounterValue,
float64(battery.CycleCount),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descDesignCapacityAmps,
prometheus.GaugeValue,
float64(battery.DesignCapacityAmps)/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descDesignCapacityWatts,
prometheus.GaugeValue,
battery.DesignCapacityWatts/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descFullyCharged,
prometheus.GaugeValue,
boolToFloat64(battery.FullyCharged),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descHealth,
prometheus.GaugeValue,
float64(battery.Health),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descIsCharging,
prometheus.GaugeValue,
boolToFloat64(battery.IsCharging),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descMaxCapacityAmps,
prometheus.GaugeValue,
float64(battery.MaxCapacityAmps)/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descMaxCapacityWatts,
prometheus.GaugeValue,
battery.MaxCapacityWatts/1000,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descTemperature,
prometheus.GaugeValue,
battery.Temperature,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descTimeRemaining,
prometheus.GaugeValue,
battery.TimeRemaining.Seconds(),
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.descVoltage,
prometheus.GaugeValue,
float64(battery.Voltage)/1000,
labels...,
)
}
}
func boolToString(b bool) string {
if b {
return "true"
}
return "false"
}
func boolToFloat64(b bool) float64 {
if b {
return 1
}
return 0
}

111
prom/server.go Normal file
View File

@@ -0,0 +1,111 @@
//go:build darwin
package prom
import (
"fmt"
"log/slog"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type Registry interface {
prometheus.Registerer
prometheus.Gatherer
}
type ServerOptions struct {
Bind string
Port int
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
Logger *slog.Logger
}
type Server struct {
*http.Server
registry Registry
mux *http.ServeMux
}
func NewServer(registry *prometheus.Registry, options ServerOptions) *Server {
if options.Bind == "" {
options.Bind = "127.0.0.1"
}
if options.Port == 0 {
options.Port = 9108
}
if options.ReadTimeout == 0 {
options.ReadTimeout = 5 * time.Second
}
if options.WriteTimeout == 0 {
options.WriteTimeout = 10 * time.Second
}
if options.IdleTimeout == 0 {
options.IdleTimeout = 30 * time.Second
}
mux := http.NewServeMux()
mux.Handle(
"/metrics",
promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
)
return &Server{
mux: mux,
registry: registry,
Server: &http.Server{
Addr: fmt.Sprintf("%s:%d", options.Bind, options.Port),
ReadTimeout: options.ReadTimeout,
WriteTimeout: options.WriteTimeout,
IdleTimeout: options.IdleTimeout,
Handler: mux,
},
}
}
func (s *Server) Register(
collectors ...prometheus.Collector,
) error {
for _, c := range collectors {
err := s.registry.Register(c)
if err != nil {
return err
}
}
return nil
}
func (s *Server) ListenAndServe() error {
slog.Info(
"starting prometheus server",
slog.String("addr", s.Addr),
)
return s.Server.ListenAndServe()
}
func RunServer(
namespace string,
registry *prometheus.Registry,
options ServerOptions,
) error {
if namespace == "" {
namespace = "node"
}
s := NewServer(registry, options)
collector := NewCollector(namespace)
err := s.Register(collector)
if err != nil {
return err
}
return s.ListenAndServe()
}