node_exporter/collector/meminfo.go
Brian Brazil 1c17481a42 Collect at every scrape, rather than at regular intervals.
Switch to Update using the Collecter Collect interface, due to not knowing all
metricnames in all modules beforehand we can't use Describe and thus the full
Collecter interface.

Remove 'updates', it's meaning varies by module and doesn't add much.
2014-10-29 17:00:36 +00:00

100 lines
2.1 KiB
Go

// +build !nomeminfo
package collector
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
)
const (
procMemInfo = "/proc/meminfo"
memInfoSubsystem = "memory"
)
var (
memInfoMetrics = map[string]prometheus.Gauge{}
)
type meminfoCollector struct {
config Config
}
func init() {
Factories["meminfo"] = NewMeminfoCollector
}
// Takes a config struct and prometheus registry and returns a new Collector exposing
// memory stats.
func NewMeminfoCollector(config Config) (Collector, error) {
c := meminfoCollector{
config: config,
}
return &c, nil
}
func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) (err error) {
memInfo, err := getMemInfo()
if err != nil {
return fmt.Errorf("Couldn't get meminfo: %s", err)
}
glog.V(1).Infof("Set node_mem: %#v", memInfo)
for k, v := range memInfo {
if _, ok := memInfoMetrics[k]; !ok {
memInfoMetrics[k] = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Subsystem: memInfoSubsystem,
Name: k,
Help: k + " from /proc/meminfo.",
})
}
memInfoMetrics[k].Set(v)
memInfoMetrics[k].Collect(ch)
}
return err
}
func getMemInfo() (map[string]float64, error) {
file, err := os.Open(procMemInfo)
if err != nil {
return nil, err
}
return parseMemInfo(file)
}
func parseMemInfo(r io.ReadCloser) (map[string]float64, error) {
defer r.Close()
memInfo := map[string]float64{}
scanner := bufio.NewScanner(r)
re := regexp.MustCompile("\\((.*)\\)")
for scanner.Scan() {
line := scanner.Text()
parts := strings.Fields(string(line))
fv, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
return nil, fmt.Errorf("Invalid value in meminfo: %s", err)
}
switch len(parts) {
case 2: // no unit
case 3: // has unit, we presume kB
fv *= 1024
default:
return nil, fmt.Errorf("Invalid line in %s: %s", procMemInfo, line)
}
key := parts[0][:len(parts[0])-1] // remove trailing : from key
// Active(anon) -> Active_anon
key = re.ReplaceAllString(key, "_${1}")
memInfo[key] = fv
}
return memInfo, nil
}