From 1feb091b367bd3a70bad818a0c26088ede857471 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Fri, 21 Apr 2017 18:19:35 -0400 Subject: [PATCH] Initial XFS collector --- README.md | 1 + collector/fixtures/e2e-output.txt | 25 ++++ .../fixtures/sys/fs/xfs/sda1/stats/stats | 24 +++ collector/xfs_linux.go | 140 ++++++++++++++++++ end-to-end-test.sh | 1 + node_exporter.go | 2 +- .../github.com/prometheus/procfs/sysfs/doc.go | 16 ++ .../github.com/prometheus/procfs/sysfs/fs.go | 82 ++++++++++ .../github.com/prometheus/procfs/xfs/parse.go | 2 - .../github.com/prometheus/procfs/xfs/xfs.go | 5 + vendor/vendor.json | 16 +- 11 files changed, 306 insertions(+), 8 deletions(-) create mode 100644 collector/fixtures/sys/fs/xfs/sda1/stats/stats create mode 100644 collector/xfs_linux.go create mode 100644 vendor/github.com/prometheus/procfs/sysfs/doc.go create mode 100644 vendor/github.com/prometheus/procfs/sysfs/fs.go diff --git a/README.md b/README.md index 94a1a65f..918a8cc1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ time | Exposes the current system time. | _any_ uname | Exposes system information as provided by the uname system call. | Linux vmstat | Exposes statistics from `/proc/vmstat`. | Linux wifi | Exposes WiFi device and station statistics. | Linux +xfs | Exposes XFS runtime statistics. | Linux (kernel 4.4+) zfs | Exposes [ZFS](http://open-zfs.org/) performance statistics. | [Linux](http://zfsonlinux.org/) ### Disabled by default diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 68ef54b0..3e48a1c4 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2143,6 +2143,7 @@ node_scrape_collector_success{collector="sockstat"} 1 node_scrape_collector_success{collector="stat"} 1 node_scrape_collector_success{collector="textfile"} 1 node_scrape_collector_success{collector="wifi"} 1 +node_scrape_collector_success{collector="xfs"} 1 node_scrape_collector_success{collector="zfs"} 1 # HELP node_sockstat_FRAG_inuse Number of FRAG sockets in state inuse. # TYPE node_sockstat_FRAG_inuse gauge @@ -2222,6 +2223,30 @@ node_wifi_station_transmit_failed_total{device="wlan0"} 2 # HELP node_wifi_station_transmit_retries_total The total number of times a station has had to retry while sending a packet. # TYPE node_wifi_station_transmit_retries_total counter node_wifi_station_transmit_retries_total{device="wlan0"} 10 +# HELP node_xfs_allocation_btree_compares_total Number of allocation B-tree compares for a filesystem. +# TYPE node_xfs_allocation_btree_compares_total counter +node_xfs_allocation_btree_compares_total{device="sda1"} 0 +# HELP node_xfs_allocation_btree_lookups_total Number of allocation B-tree lookups for a filesystem. +# TYPE node_xfs_allocation_btree_lookups_total counter +node_xfs_allocation_btree_lookups_total{device="sda1"} 0 +# HELP node_xfs_allocation_btree_records_deleted_total Number of allocation B-tree records deleted for a filesystem. +# TYPE node_xfs_allocation_btree_records_deleted_total counter +node_xfs_allocation_btree_records_deleted_total{device="sda1"} 0 +# HELP node_xfs_allocation_btree_records_inserted_total Number of allocation B-tree records inserted for a filesystem. +# TYPE node_xfs_allocation_btree_records_inserted_total counter +node_xfs_allocation_btree_records_inserted_total{device="sda1"} 0 +# HELP node_xfs_extent_allocation_blocks_allocated_total Number of blocks allocated for a filesystem. +# TYPE node_xfs_extent_allocation_blocks_allocated_total counter +node_xfs_extent_allocation_blocks_allocated_total{device="sda1"} 872 +# HELP node_xfs_extent_allocation_blocks_freed_total Number of blocks freed for a filesystem. +# TYPE node_xfs_extent_allocation_blocks_freed_total counter +node_xfs_extent_allocation_blocks_freed_total{device="sda1"} 0 +# HELP node_xfs_extent_allocation_extents_allocated_total Number of extents allocated for a filesystem. +# TYPE node_xfs_extent_allocation_extents_allocated_total counter +node_xfs_extent_allocation_extents_allocated_total{device="sda1"} 1 +# HELP node_xfs_extent_allocation_extents_freed_total Number of extents freed for a filesystem. +# TYPE node_xfs_extent_allocation_extents_freed_total counter +node_xfs_extent_allocation_extents_freed_total{device="sda1"} 0 # HELP node_zfs_arc_anon_evictable_data kstat.zfs.misc.arcstats.anon_evictable_data # TYPE node_zfs_arc_anon_evictable_data untyped node_zfs_arc_anon_evictable_data 0 diff --git a/collector/fixtures/sys/fs/xfs/sda1/stats/stats b/collector/fixtures/sys/fs/xfs/sda1/stats/stats new file mode 100644 index 00000000..d5c04d9e --- /dev/null +++ b/collector/fixtures/sys/fs/xfs/sda1/stats/stats @@ -0,0 +1,24 @@ +extent_alloc 1 872 0 0 +abt 0 0 0 0 +blk_map 61 29 1 1 1 91 0 +bmbt 0 0 0 0 +dir 3 2 1 52 +trans 4 40 0 +ig 5 1 0 4 0 0 1 +log 8 21 0 5821 4 +push_ail 44 0 1102 15 0 2 0 2 0 2 +xstrat 1 0 +rw 28 0 +attr 0 0 0 0 +icluster 2 2 2 +vnodes 4 0 0 0 1 1 1 0 +buf 22 25 14 0 0 8 0 8 8 +abtb2 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 +abtc2 2 1 1 1 0 0 0 0 0 0 0 0 0 0 0 +bmbt2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +ibt2 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 +fibt2 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 +rmapbt 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +qm 0 0 0 0 0 0 0 0 +xpc 3571712 3568056 0 +debug 0 diff --git a/collector/xfs_linux.go b/collector/xfs_linux.go new file mode 100644 index 00000000..849cbffe --- /dev/null +++ b/collector/xfs_linux.go @@ -0,0 +1,140 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collector + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs/sysfs" + "github.com/prometheus/procfs/xfs" +) + +// An xfsCollector is a Collector which gathers metrics from XFS filesystems. +type xfsCollector struct { + fs sysfs.FS +} + +func init() { + Factories["xfs"] = NewXFSCollector +} + +// NewXFSCollector returns a new Collector exposing XFS statistics. +func NewXFSCollector() (Collector, error) { + fs, err := sysfs.NewFS(*sysPath) + if err != nil { + return nil, fmt.Errorf("failed to open sysfs: %v", err) + } + + return &xfsCollector{ + fs: fs, + }, nil +} + +// Update implements Collector. +func (c *xfsCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := c.fs.XFSStats() + if err != nil { + return fmt.Errorf("failed to retrieve XFS stats: %v", err) + } + + for _, s := range stats { + c.updateXFSStats(ch, s) + } + + return nil +} + +// updateXFSStats collects statistics for a single XFS filesystem. +func (c *xfsCollector) updateXFSStats(ch chan<- prometheus.Metric, s *xfs.Stats) { + const ( + subsystem = "xfs" + ) + + var ( + labels = []string{"device"} + ) + + // Metric names and descriptions are sourced from: + // http://xfs.org/index.php/Runtime_Stats. + // + // Each metric has a name that roughly follows the pattern of + // "node_xfs_category_value_total", using the categories and value names + // found on the XFS wiki. + // + // Note that statistics for more than one internal B-tree are measured, + // and as such, each one must be differentiated by name. + metrics := []struct { + name string + desc string + value float64 + }{ + { + name: "extent_allocation_extents_allocated_total", + desc: "Number of extents allocated for a filesystem.", + value: float64(s.ExtentAllocation.ExtentsAllocated), + }, + { + name: "extent_allocation_blocks_allocated_total", + desc: "Number of blocks allocated for a filesystem.", + value: float64(s.ExtentAllocation.BlocksAllocated), + }, + { + name: "extent_allocation_extents_freed_total", + desc: "Number of extents freed for a filesystem.", + value: float64(s.ExtentAllocation.ExtentsFreed), + }, + { + name: "extent_allocation_blocks_freed_total", + desc: "Number of blocks freed for a filesystem.", + value: float64(s.ExtentAllocation.BlocksFreed), + }, + { + name: "allocation_btree_lookups_total", + desc: "Number of allocation B-tree lookups for a filesystem.", + value: float64(s.AllocationBTree.Lookups), + }, + { + name: "allocation_btree_compares_total", + desc: "Number of allocation B-tree compares for a filesystem.", + value: float64(s.AllocationBTree.Compares), + }, + { + name: "allocation_btree_records_inserted_total", + desc: "Number of allocation B-tree records inserted for a filesystem.", + value: float64(s.AllocationBTree.RecordsInserted), + }, + { + name: "allocation_btree_records_deleted_total", + desc: "Number of allocation B-tree records deleted for a filesystem.", + value: float64(s.AllocationBTree.RecordsDeleted), + }, + } + + for _, m := range metrics { + desc := prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, m.name), + m.desc, + labels, + nil, + ) + + ch <- prometheus.MustNewConstMetric( + desc, + prometheus.CounterValue, + m.value, + s.Name, + ) + } +} diff --git a/end-to-end-test.sh b/end-to-end-test.sh index f47fbb50..c68d89cc 100755 --- a/end-to-end-test.sh +++ b/end-to-end-test.sh @@ -28,6 +28,7 @@ collectors=$(cat << COLLECTORS bonding megacli wifi + xfs zfs COLLECTORS ) diff --git a/node_exporter.go b/node_exporter.go index f6daa2d4..f5ef01e5 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -32,7 +32,7 @@ import ( ) const ( - defaultCollectors = "arp,conntrack,cpu,diskstats,entropy,edac,exec,filefd,filesystem,hwmon,infiniband,loadavg,mdadm,meminfo,netdev,netstat,sockstat,stat,textfile,time,uname,vmstat,wifi,zfs" + defaultCollectors = "arp,conntrack,cpu,diskstats,entropy,edac,exec,filefd,filesystem,hwmon,infiniband,loadavg,mdadm,meminfo,netdev,netstat,sockstat,stat,textfile,time,uname,vmstat,wifi,xfs,zfs" ) var ( diff --git a/vendor/github.com/prometheus/procfs/sysfs/doc.go b/vendor/github.com/prometheus/procfs/sysfs/doc.go new file mode 100644 index 00000000..9a6c244e --- /dev/null +++ b/vendor/github.com/prometheus/procfs/sysfs/doc.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sysfs provides functions to retrieve system and kernel metrics +// from the pseudo-filesystem sys. +package sysfs diff --git a/vendor/github.com/prometheus/procfs/sysfs/fs.go b/vendor/github.com/prometheus/procfs/sysfs/fs.go new file mode 100644 index 00000000..8e838008 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/sysfs/fs.go @@ -0,0 +1,82 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sysfs + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/prometheus/procfs/xfs" +) + +// FS represents the pseudo-filesystem sys, which provides an interface to +// kernel data structures. +type FS string + +// DefaultMountPoint is the common mount point of the sys filesystem. +const DefaultMountPoint = "/sys" + +// NewFS returns a new FS mounted under the given mountPoint. It will error +// if the mount point can't be read. +func NewFS(mountPoint string) (FS, error) { + info, err := os.Stat(mountPoint) + if err != nil { + return "", fmt.Errorf("could not read %s: %s", mountPoint, err) + } + if !info.IsDir() { + return "", fmt.Errorf("mount point %s is not a directory", mountPoint) + } + + return FS(mountPoint), nil +} + +// Path returns the path of the given subsystem relative to the sys root. +func (fs FS) Path(p ...string) string { + return filepath.Join(append([]string{string(fs)}, p...)...) +} + +// XFSStats retrieves XFS filesystem runtime statistics for each mounted XFS +// filesystem. Only available on kernel 4.4+. On older kernels, an empty +// slice of *xfs.Stats will be returned. +func (fs FS) XFSStats() ([]*xfs.Stats, error) { + matches, err := filepath.Glob(fs.Path("fs/xfs/*/stats/stats")) + if err != nil { + return nil, err + } + + stats := make([]*xfs.Stats, 0, len(matches)) + for _, m := range matches { + f, err := os.Open(m) + if err != nil { + return nil, err + } + + // "*" used in glob above indicates the name of the filesystem. + name := filepath.Base(filepath.Dir(filepath.Dir(m))) + + // File must be closed after parsing, regardless of success or + // failure. Defer is not used because of the loop. + s, err := xfs.ParseStats(f) + _ = f.Close() + if err != nil { + return nil, err + } + + s.Name = name + stats = append(stats, s) + } + + return stats, nil +} diff --git a/vendor/github.com/prometheus/procfs/xfs/parse.go b/vendor/github.com/prometheus/procfs/xfs/parse.go index d1285fa6..c8f6279f 100644 --- a/vendor/github.com/prometheus/procfs/xfs/parse.go +++ b/vendor/github.com/prometheus/procfs/xfs/parse.go @@ -17,7 +17,6 @@ import ( "bufio" "fmt" "io" - "log" "strconv" "strings" ) @@ -273,7 +272,6 @@ func vnodeStats(us []uint32) (VnodeStats, error) { // stats versions. Therefore, 7 or 8 elements may appear in // this slice. l := len(us) - log.Println(l) if l != 7 && l != 8 { return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l) } diff --git a/vendor/github.com/prometheus/procfs/xfs/xfs.go b/vendor/github.com/prometheus/procfs/xfs/xfs.go index ed77d907..d86794b7 100644 --- a/vendor/github.com/prometheus/procfs/xfs/xfs.go +++ b/vendor/github.com/prometheus/procfs/xfs/xfs.go @@ -22,6 +22,11 @@ package xfs // kernel source. Most counters are uint32s (same data types used in // xfs_stats.h), but some of the "extended precision stats" are uint64s. type Stats struct { + // The name of the filesystem used to source these statistics. + // If empty, this indicates aggregated statistics for all XFS + // filesystems on the host. + Name string + ExtentAllocation ExtentAllocationStats AllocationBTree BTreeStats BlockMapping BlockMappingStats diff --git a/vendor/vendor.json b/vendor/vendor.json index cce25976..1928d02f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -131,14 +131,20 @@ { "checksumSHA1": "cD4xn1qxbkiuXqUExpdnDroCTrY=", "path": "github.com/prometheus/procfs", - "revision": "a1dba9ce8baed984a2495b658c82687f8157b98f", - "revisionTime": "2017-02-16T22:32:56Z" + "revision": "332f6238064950a97bc3ed3f5421f6418537e707", + "revisionTime": "2017-04-21T21:58:51Z" }, { - "checksumSHA1": "kOWRcAHWFkId0aCIOSOyjzC0Zfc=", + "checksumSHA1": "eiBAd4edewJTOtTwxh/ubJdjd+I=", + "path": "github.com/prometheus/procfs/sysfs", + "revision": "332f6238064950a97bc3ed3f5421f6418537e707", + "revisionTime": "2017-04-21T21:58:51Z" + }, + { + "checksumSHA1": "xCiFAAwVTrjsfZT1BIJQ3DgeNCY=", "path": "github.com/prometheus/procfs/xfs", - "revision": "a1dba9ce8baed984a2495b658c82687f8157b98f", - "revisionTime": "2017-02-16T22:32:56Z" + "revision": "332f6238064950a97bc3ed3f5421f6418537e707", + "revisionTime": "2017-04-21T21:58:51Z" }, { "checksumSHA1": "uozMgPjB4AggpuuJkGq3FgAs4CA=",