Add flag to change the location of the procfs.

Remove all hardcoded references to `/proc`. For all collectors that do
not use `github.com/prometheus/procfs` yet, provide a wrapper to
generate the full paths.

Reformulate help strings, errors and comments to remove absolute
references to `/proc`.

This is a breaking change: the `-collector.ipvs.procfs` flag is removed
in favor of the general flag. Since it only affected that collector it
was only useful for development, so this should not cause many issues.
This commit is contained in:
Matthias Rampke 2015-09-26 14:53:46 +02:00
parent cf3aa37f1a
commit 20b551ab2b
15 changed files with 57 additions and 67 deletions

View file

@ -17,7 +17,6 @@ import (
) )
const ( const (
procDiskStats = "/proc/diskstats"
diskSubsystem = "disk" diskSubsystem = "disk"
) )
@ -147,6 +146,7 @@ func NewDiskstatsCollector() (Collector, error) {
} }
func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) (err error) {
procDiskStats := procFilePath("diskstats")
diskStats, err := getDiskStats() diskStats, err := getDiskStats()
if err != nil { if err != nil {
return fmt.Errorf("couldn't get diskstats: %s", err) return fmt.Errorf("couldn't get diskstats: %s", err)
@ -184,7 +184,7 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) (err error) {
} }
func getDiskStats() (map[string]map[int]string, error) { func getDiskStats() (map[string]map[int]string, error) {
file, err := os.Open(procDiskStats) file, err := os.Open(procFilePath("diskstats"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -202,7 +202,7 @@ func parseDiskStats(r io.Reader) (map[string]map[int]string, error) {
for scanner.Scan() { for scanner.Scan() {
parts := strings.Fields(string(scanner.Text())) parts := strings.Fields(string(scanner.Text()))
if len(parts) < 4 { // we strip major, minor and dev if len(parts) < 4 { // we strip major, minor and dev
return nil, fmt.Errorf("invalid line in %s: %s", procDiskStats, scanner.Text()) return nil, fmt.Errorf("invalid line in %s: %s", procFilePath("diskstats"), scanner.Text())
} }
dev := parts[2] dev := parts[2]
diskStats[dev] = map[int]string{} diskStats[dev] = map[int]string{}

View file

@ -13,7 +13,6 @@ import (
) )
const ( const (
procFileFDStat = "/proc/sys/fs/file-nr"
fileFDStatSubsystem = "filefd" fileFDStatSubsystem = "filefd"
) )
@ -33,7 +32,7 @@ func NewFileFDStatCollector() (Collector, error) {
} }
func (c *fileFDStatCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *fileFDStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
fileFDStat, err := getFileFDStats(procFileFDStat) fileFDStat, err := getFileFDStats(procFilePath("sys/fs/file-nr"))
if err != nil { if err != nil {
return fmt.Errorf("couldn't get file-nr: %s", err) return fmt.Errorf("couldn't get file-nr: %s", err)
} }
@ -44,7 +43,7 @@ func (c *fileFDStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
Namespace: Namespace, Namespace: Namespace,
Subsystem: fileFDStatSubsystem, Subsystem: fileFDStatSubsystem,
Name: name, Name: name,
Help: fmt.Sprintf("filefd %s from %s.", name, procFileFDStat), Help: fmt.Sprintf("File descriptor statistics: %s.", name),
}, },
) )
v, err := strconv.ParseFloat(value, 64) v, err := strconv.ParseFloat(value, 64)

View file

@ -14,7 +14,6 @@ import (
const ( const (
defIgnoredMountPoints = "^/(sys|proc|dev)($|/)" defIgnoredMountPoints = "^/(sys|proc|dev)($|/)"
procMounts = "/proc/mounts"
) )
var ( var (
@ -60,7 +59,7 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) {
} }
func mountPointDetails() ([]filesystemDetails, error) { func mountPointDetails() ([]filesystemDetails, error) {
file, err := os.Open(procMounts) file, err := os.Open(procFilePath("mounts"))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,6 +4,7 @@ package collector
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -13,10 +14,6 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
const (
procInterrupts = "/proc/interrupts"
)
type interruptsCollector struct { type interruptsCollector struct {
metric *prometheus.CounterVec metric *prometheus.CounterVec
} }
@ -33,7 +30,7 @@ func NewInterruptsCollector() (Collector, error) {
prometheus.CounterOpts{ prometheus.CounterOpts{
Namespace: Namespace, Namespace: Namespace,
Name: "interrupts", Name: "interrupts",
Help: "Interrupt details from /proc/interrupts.", Help: "Interrupt details.",
}, },
[]string{"CPU", "type", "info", "devices"}, []string{"CPU", "type", "info", "devices"},
), ),
@ -71,7 +68,7 @@ type interrupt struct {
} }
func getInterrupts() (map[string]interrupt, error) { func getInterrupts() (map[string]interrupt, error) {
file, err := os.Open(procInterrupts) file, err := os.Open(procFilePath("interrupts"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -87,7 +84,7 @@ func parseInterrupts(r io.Reader) (map[string]interrupt, error) {
) )
if !scanner.Scan() { if !scanner.Scan() {
return nil, fmt.Errorf("%s empty", procInterrupts) return nil, errors.New("interrupts empty")
} }
cpuNum := len(strings.Fields(string(scanner.Text()))) // one header per cpu cpuNum := len(strings.Fields(string(scanner.Text()))) // one header per cpu

View file

@ -3,7 +3,6 @@
package collector package collector
import ( import (
"flag"
"fmt" "fmt"
"strconv" "strconv"
@ -11,10 +10,6 @@ import (
"github.com/prometheus/procfs" "github.com/prometheus/procfs"
) )
var (
ipvsProcfsMountPoint = flag.String("collector.ipvs.procfs", procfs.DefaultMountPoint, "procfs mountpoint.")
)
type ipvsCollector struct { type ipvsCollector struct {
Collector Collector
fs procfs.FS fs procfs.FS
@ -46,7 +41,7 @@ func newIPVSCollector() (*ipvsCollector, error) {
subsystem = "ipvs" subsystem = "ipvs"
) )
c.fs, err = procfs.NewFS(*ipvsProcfsMountPoint) c.fs, err = procfs.NewFS(*procPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -107,7 +107,7 @@ var (
) )
func TestIPVSCollector(t *testing.T) { func TestIPVSCollector(t *testing.T) {
if err := flag.Set("collector.ipvs.procfs", "fixtures"); err != nil { if err := flag.Set("collector.procfs", "fixtures"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
collector, err := newIPVSCollector() collector, err := newIPVSCollector()
@ -169,7 +169,7 @@ func (c miniCollector) Describe(ch chan<- *prometheus.Desc) {
} }
func TestIPVSCollectorResponse(t *testing.T) { func TestIPVSCollectorResponse(t *testing.T) {
if err := flag.Set("collector.ipvs.procfs", "fixtures"); err != nil { if err := flag.Set("collector.procfs", "fixtures"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
collector, err := NewIPVSCollector() collector, err := NewIPVSCollector()

View file

@ -12,10 +12,6 @@ import (
"github.com/prometheus/log" "github.com/prometheus/log"
) )
const (
procLoad = "/proc/loadavg"
)
type loadavgCollector struct { type loadavgCollector struct {
metric prometheus.Gauge metric prometheus.Gauge
} }
@ -48,7 +44,7 @@ func (c *loadavgCollector) Update(ch chan<- prometheus.Metric) (err error) {
} }
func getLoad1() (float64, error) { func getLoad1() (float64, error) {
data, err := ioutil.ReadFile(procLoad) data, err := ioutil.ReadFile(procFilePath("loadavg"))
if err != nil { if err != nil {
return 0, err return 0, err
} }

View file

@ -15,7 +15,6 @@ import (
) )
var ( var (
statusfile = "/proc/mdstat"
statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`) statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`) buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
) )
@ -90,7 +89,7 @@ func evalBuildline(buildline string) (int64, error) {
func parseMdstat(mdStatusFilePath string) ([]mdStatus, error) { func parseMdstat(mdStatusFilePath string) ([]mdStatus, error) {
content, err := ioutil.ReadFile(mdStatusFilePath) content, err := ioutil.ReadFile(mdStatusFilePath)
if err != nil { if err != nil {
return []mdStatus{}, fmt.Errorf("error parsing %s: %s", statusfile, err) return []mdStatus{}, fmt.Errorf("error parsing mdstatus: %s", err)
} }
mdStatusFile := string(content) mdStatusFile := string(content)
@ -128,13 +127,13 @@ func parseMdstat(mdStatusFilePath string) ([]mdStatus, error) {
isActive := (mainLine[2] == "active") // activity status of said md-device isActive := (mainLine[2] == "active") // activity status of said md-device
if len(lines) <= i+3 { if len(lines) <= i+3 {
return mdStates, fmt.Errorf("error parsing %s: entry for %s has fewer lines than expected", statusfile, currentMD) return mdStates, fmt.Errorf("error parsing mdstatus: entry for %s has fewer lines than expected", currentMD)
} }
active, total, size, err := evalStatusline(lines[i+1]) // parse statusline, always present active, total, size, err := evalStatusline(lines[i+1]) // parse statusline, always present
if err != nil { if err != nil {
return mdStates, fmt.Errorf("error parsing %s: %s", statusfile, err) return mdStates, fmt.Errorf("error parsing mdstatus: %s", err)
} }
// Now get the number of synced blocks. // Now get the number of synced blocks.
@ -153,7 +152,7 @@ func parseMdstat(mdStatusFilePath string) ([]mdStatus, error) {
if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") { if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
syncedBlocks, err = evalBuildline(lines[j]) syncedBlocks, err = evalBuildline(lines[j])
if err != nil { if err != nil {
return mdStates, fmt.Errorf("error parsing %s: %s", statusfile, err) return mdStates, fmt.Errorf("error parsing mdstatus: %s", err)
} }
} else { } else {
syncedBlocks = size syncedBlocks = size
@ -209,6 +208,7 @@ var (
) )
func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) (err error) {
statusfile := procFilePath("mdstatus")
// take care we don't crash on non-existent statusfiles // take care we don't crash on non-existent statusfiles
_, err = os.Stat(statusfile) _, err = os.Stat(statusfile)
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -223,7 +223,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) (err error) {
// First parse mdstat-file... // First parse mdstat-file...
mdstate, err := parseMdstat(statusfile) mdstate, err := parseMdstat(statusfile)
if err != nil { if err != nil {
return fmt.Errorf("error parsing %s: %s", statusfile, err) return fmt.Errorf("error parsing mdstatus: %s", err)
} }
// ... and then plug the result into the metrics to be exported. // ... and then plug the result into the metrics to be exported.

View file

@ -16,7 +16,6 @@ import (
) )
const ( const (
procMemInfo = "/proc/meminfo"
memInfoSubsystem = "memory" memInfoSubsystem = "memory"
) )
@ -48,7 +47,7 @@ func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) (err error) {
Namespace: Namespace, Namespace: Namespace,
Subsystem: memInfoSubsystem, Subsystem: memInfoSubsystem,
Name: k, Name: k,
Help: k + " from /proc/meminfo.", Help: fmt.Sprintf("Memory information field %s.", k),
}) })
} }
c.metrics[k].Set(v) c.metrics[k].Set(v)
@ -58,7 +57,7 @@ func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) (err error) {
} }
func getMemInfo() (map[string]float64, error) { func getMemInfo() (map[string]float64, error) {
file, err := os.Open(procMemInfo) file, err := os.Open(procFilePath("meminfo"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -86,7 +85,7 @@ func parseMemInfo(r io.Reader) (map[string]float64, error) {
case 3: // has unit, we presume kB case 3: // has unit, we presume kB
fv *= 1024 fv *= 1024
default: default:
return nil, fmt.Errorf("Invalid line in %s: %s", procMemInfo, line) return nil, fmt.Errorf("Invalid line in meminfo: %s", line)
} }
key := parts[0][:len(parts[0])-1] // remove trailing : from key key := parts[0][:len(parts[0])-1] // remove trailing : from key
// Active(anon) -> Active_anon // Active(anon) -> Active_anon

View file

@ -16,10 +16,6 @@ import (
"github.com/prometheus/log" "github.com/prometheus/log"
) )
const (
procNetDev = "/proc/net/dev"
)
var ( var (
procNetDevFieldSep = regexp.MustCompile("[ :] *") procNetDevFieldSep = regexp.MustCompile("[ :] *")
netdevIgnoredDevices = flag.String( netdevIgnoredDevices = flag.String(
@ -59,7 +55,7 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) {
if !ok { if !ok {
desc = prometheus.NewDesc( desc = prometheus.NewDesc(
prometheus.BuildFQName(Namespace, c.subsystem, key), prometheus.BuildFQName(Namespace, c.subsystem, key),
fmt.Sprintf("%s from /proc/net/dev.", key), fmt.Sprintf("Network device statistic %s.", key),
[]string{"device"}, []string{"device"},
nil, nil,
) )
@ -76,7 +72,7 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) {
} }
func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) {
file, err := os.Open(procNetDev) file, err := os.Open(procFilePath("net/dev"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -91,8 +87,8 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string
scanner.Scan() scanner.Scan()
parts := strings.Split(string(scanner.Text()), "|") parts := strings.Split(string(scanner.Text()), "|")
if len(parts) != 3 { // interface + receive + transmit if len(parts) != 3 { // interface + receive + transmit
return nil, fmt.Errorf("invalid header line in %s: %s", return nil, fmt.Errorf("invalid header line in net/dev: %s",
procNetDev, scanner.Text()) scanner.Text())
} }
header := strings.Fields(parts[1]) header := strings.Fields(parts[1])
@ -101,7 +97,7 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string
line := strings.TrimLeft(string(scanner.Text()), " ") line := strings.TrimLeft(string(scanner.Text()), " ")
parts := procNetDevFieldSep.Split(line, -1) parts := procNetDevFieldSep.Split(line, -1)
if len(parts) != 2*len(header)+1 { if len(parts) != 2*len(header)+1 {
return nil, fmt.Errorf("invalid line in %s: %s", procNetDev, scanner.Text()) return nil, fmt.Errorf("invalid line in net/dev: %s", scanner.Text())
} }
dev := parts[0][:len(parts[0])] dev := parts[0][:len(parts[0])]

View file

@ -14,8 +14,6 @@ import (
) )
const ( const (
procNetStat = "/proc/net/netstat"
procSNMPStat = "/proc/net/snmp"
netStatsSubsystem = "netstat" netStatsSubsystem = "netstat"
) )
@ -36,11 +34,11 @@ func NewNetStatCollector() (Collector, error) {
} }
func (c *netStatCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *netStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
netStats, err := getNetStats(procNetStat) netStats, err := getNetStats(procFilePath("net/netstat"))
if err != nil { if err != nil {
return fmt.Errorf("couldn't get netstats: %s", err) return fmt.Errorf("couldn't get netstats: %s", err)
} }
snmpStats, err := getNetStats(procSNMPStat) snmpStats, err := getNetStats(procFilePath("net/snmp"))
if err != nil { if err != nil {
return fmt.Errorf("couldn't get SNMP stats: %s", err) return fmt.Errorf("couldn't get SNMP stats: %s", err)
} }
@ -58,7 +56,7 @@ func (c *netStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
Namespace: Namespace, Namespace: Namespace,
Subsystem: netStatsSubsystem, Subsystem: netStatsSubsystem,
Name: key, Name: key,
Help: fmt.Sprintf("%s %s from /proc/net/{netstat,snmp}.", protocol, name), Help: fmt.Sprintf("Protocol %s statistic %s.", protocol, name),
}, },
) )
} }

17
collector/procpath.go Normal file
View file

@ -0,0 +1,17 @@
package collector
import (
"flag"
"path"
"github.com/prometheus/procfs"
)
var (
// The path of the proc filesystem.
procPath = flag.String("collector.procfs", procfs.DefaultMountPoint, "procfs mountpoint.")
)
func procFilePath(name string) string {
return path.Join(*procPath, name)
}

View file

@ -13,7 +13,6 @@ import (
) )
const ( const (
procSockStat = "/proc/net/sockstat"
sockStatSubsystem = "sockstat" sockStatSubsystem = "sockstat"
) )
@ -36,7 +35,7 @@ func NewSockStatCollector() (Collector, error) {
} }
func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
sockStats, err := getSockStats(procSockStat) sockStats, err := getSockStats(procFilePath("net/sockstat"))
if err != nil { if err != nil {
return fmt.Errorf("couldn't get sockstats: %s", err) return fmt.Errorf("couldn't get sockstats: %s", err)
} }
@ -49,7 +48,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
Namespace: Namespace, Namespace: Namespace,
Subsystem: sockStatSubsystem, Subsystem: sockStatSubsystem,
Name: key, Name: key,
Help: fmt.Sprintf("%s %s from /proc/net/sockstat.", protocol, name), Help: fmt.Sprintf("Number of %s sockets in state %s.", protocol, name),
}, },
) )
} }

View file

@ -12,7 +12,6 @@ import (
) )
const ( const (
procStat = "/proc/stat"
userHz = 100 userHz = 100
) )
@ -75,9 +74,9 @@ func NewStatCollector() (Collector, error) {
}, nil }, nil
} }
// Expose a variety of stats from /proc/stats. // Expose kernel and system statistics.
func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) {
file, err := os.Open(procStat) file, err := os.Open(procFilePath("stat"))
if err != nil { if err != nil {
return err return err
} }

View file

@ -13,11 +13,6 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
const (
procTCPStat = "/proc/net/tcp"
procTCP6Stat = "/proc/net/tcp6"
)
type TCPConnectionState int type TCPConnectionState int
const ( const (
@ -58,14 +53,15 @@ func NewTCPStatCollector() (Collector, error) {
} }
func (c *tcpStatCollector) Update(ch chan<- prometheus.Metric) (err error) { func (c *tcpStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
tcpStats, err := getTCPStats(procTCPStat) tcpStats, err := getTCPStats(procFilePath("net/tcp"))
if err != nil { if err != nil {
return fmt.Errorf("couldn't get tcpstats: %s", err) return fmt.Errorf("couldn't get tcpstats: %s", err)
} }
// if enabled ipv6 system // if enabled ipv6 system
if _, hasIPv6 := os.Stat(procTCP6Stat); hasIPv6 == nil { tcp6File := procFilePath("net/tcp6")
tcp6Stats, err := getTCPStats(procTCP6Stat) if _, hasIPv6 := os.Stat(tcp6File); hasIPv6 == nil {
tcp6Stats, err := getTCPStats(tcp6File)
if err != nil { if err != nil {
return fmt.Errorf("couldn't get tcp6stats: %s", err) return fmt.Errorf("couldn't get tcp6stats: %s", err)
} }