Skip to content

Commit

Permalink
Merge pull request #6 from roclark/parse-stats-file
Browse files Browse the repository at this point in the history
Parse metrics from stats file
  • Loading branch information
Joe Handzik authored Apr 3, 2017
2 parents 95f2127 + 41f29aa commit b5fd943
Showing 1 changed file with 104 additions and 1 deletion.
105 changes: 104 additions & 1 deletion sources/procfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/prometheus/client_golang/prometheus"
)

const (
samplesHelp string = "Total number of times the given metric has been collected."
maximumHelp string = "The maximum value retrieved for the given metric."
minimumHelp string = "The minimum value retrieved for the given metric."
totalHelp string = "The sum of all values collected for the given metric."
)

type lustreProcMetric struct {
subsystem string
name string
Expand Down Expand Up @@ -70,6 +78,7 @@ func (s *lustreSource) generateOSSMetricTemplates() error {
"recovery_time_hard": "Maximum timeout 'recover_time_soft' can increment to for a single server",
"recovery_time_soft": "Duration in seconds for a client to attempt to reconnect after a crash (automatically incremented if servers are still in an error state)",
"soft_sync_limit": "Number of RPCs necessary before triggering a sync",
"stats": "A collection of statistics specific to Lustre",
"sync_journal": "Binary indicator as to whether or not the journal is set for asynchronous commits",
"tot_dirty": "Total number of exports that have been marked dirty",
"tot_granted": "Total number of exports that have been marked granted",
Expand Down Expand Up @@ -116,6 +125,8 @@ func NewLustreSource() (LustreSource, error) {
}

func (s *lustreSource) Update(ch chan<- prometheus.Metric) (err error) {
metricType := "single"

for _, metric := range s.lustreProcMetrics {
paths, err := filepath.Glob(filepath.Join(s.basePath, metric.path, metric.name))
if err != nil {
Expand All @@ -125,8 +136,14 @@ func (s *lustreSource) Update(ch chan<- prometheus.Metric) (err error) {
continue
}
for _, path := range paths {
switch metric.name {
case "stats":
metricType = "stats"
default:
metricType = "single"
}

err = s.parseFile(metric.source, "single", path, metric.helpText, func(nodeType string, nodeName string, name string, helpText string, value uint64) {
err = s.parseFile(metric.source, metricType, path, metric.helpText, func(nodeType string, nodeName string, name string, helpText string, value uint64) {
ch <- s.constMetric(nodeType, nodeName, name, helpText, value)
})
if err != nil {
Expand All @@ -137,6 +154,78 @@ func (s *lustreSource) Update(ch chan<- prometheus.Metric) (err error) {
return nil
}

func parseReadWriteBytes(regexString string, statsFile string) (metricMap map[string]map[string]uint64, err error) {
bytesRegex, err := regexp.Compile(regexString)
if err != nil {
return nil, err
}

bytesString := bytesRegex.FindString(statsFile)
if len(bytesString) == 0 {
return nil, nil
}

r, err := regexp.Compile(" +")
if err != nil {
return nil, err
}

bytesSplit := r.Split(bytesString, -1)
// bytesSplit is in the following format:
// bytesString: {name} {number of samples} 'samples' [{units}] {minimum} {maximum} {sum}
// bytesSplit: [0] [1] [2] [3] [4] [5] [6]
samples, err := strconv.ParseUint(bytesSplit[1], 10, 64)
if err != nil {
return nil, err
}
minimum, err := strconv.ParseUint(bytesSplit[4], 10, 64)
if err != nil {
return nil, err
}
maximum, err := strconv.ParseUint(bytesSplit[5], 10, 64)
if err != nil {
return nil, err
}
total, err := strconv.ParseUint(bytesSplit[6], 10, 64)
if err != nil {
return nil, err
}

metricMap = make(map[string]map[string]uint64)

metricMap["samples_total"] = map[string]uint64{samplesHelp: samples}
metricMap["minimum_size_bytes"] = map[string]uint64{minimumHelp: minimum}
metricMap["maximum_size_bytes"] = map[string]uint64{maximumHelp: maximum}
metricMap["total_bytes"] = map[string]uint64{totalHelp: total}

return metricMap, nil
}

func parseStatsFile(path string) (metricMap map[string]map[string]map[string]uint64, err error) {
statsFileBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

statsFile := string(statsFileBytes[:])

readStatsMap, err := parseReadWriteBytes("read_bytes .*", statsFile)
if err != nil {
return nil, err
}

writeStatsMap, err := parseReadWriteBytes("write_bytes .*", statsFile)
if err != nil {
return nil, err
}

metricMap = make(map[string]map[string]map[string]uint64)
metricMap["read"] = readStatsMap
metricMap["write"] = writeStatsMap

return metricMap, nil
}

func (s *lustreSource) parseFile(nodeType string, metricType string, path string, helpText string, handler func(string, string, string, string, uint64)) (err error) {
pathElements := strings.Split(path, "/")
pathLen := len(pathElements)
Expand All @@ -156,6 +245,20 @@ func (s *lustreSource) parseFile(nodeType string, metricType string, path string
return err
}
handler(nodeType, nodeName, name, helpText, convertedValue)
case "stats":
metricMap, err := parseStatsFile(path)
if err != nil {
return err
}

for statType, statMap := range metricMap {
for key, metricMap := range statMap {
metricName := statType + "_" + key
for detailedHelp, value := range metricMap {
handler(nodeType, nodeName, metricName, detailedHelp, value)
}
}
}
}
return nil
}
Expand Down

0 comments on commit b5fd943

Please sign in to comment.