2015-09-26 17:36:40 +02:00
// Copyright 2015 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.
2015-05-12 13:06:41 +02:00
// +build !nodiskstats
2014-06-04 13:12:34 +02:00
package collector
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"github.com/prometheus/client_golang/prometheus"
2015-10-30 21:20:06 +01:00
"github.com/prometheus/common/log"
2017-08-12 15:07:24 +02:00
"gopkg.in/alecthomas/kingpin.v2"
2014-06-04 13:12:34 +02:00
)
const (
2018-10-15 17:24:28 +02:00
diskSubsystem = "disk"
diskSectorSize = 512
diskstatsFilename = "diskstats"
2014-06-04 13:12:34 +02:00
)
var (
2017-08-12 15:07:24 +02:00
ignoredDevices = kingpin . Flag ( "collector.diskstats.ignored-devices" , "Regexp of devices to ignore for diskstats." ) . Default ( "^(ram|loop|fd|(h|s|v|xv)d[a-z]|nvme\\d+n\\d+p)\\d+$" ) . String ( )
2014-06-04 13:12:34 +02:00
)
2018-01-17 17:55:55 +01:00
type typedFactorDesc struct {
desc * prometheus . Desc
valueType prometheus . ValueType
factor float64
}
func ( d * typedFactorDesc ) mustNewConstMetric ( value float64 , labels ... string ) prometheus . Metric {
if d . factor != 0 {
value *= d . factor
}
return prometheus . MustNewConstMetric ( d . desc , d . valueType , value , labels ... )
}
2014-06-04 13:12:34 +02:00
type diskstatsCollector struct {
ignoredDevicesPattern * regexp . Regexp
2018-01-17 17:55:55 +01:00
descs [ ] typedFactorDesc
2014-06-04 13:12:34 +02:00
}
func init ( ) {
2017-09-28 15:06:26 +02:00
registerCollector ( "diskstats" , defaultEnabled , NewDiskstatsCollector )
2014-06-04 13:12:34 +02:00
}
2017-02-28 17:44:53 +01:00
// NewDiskstatsCollector returns a new Collector exposing disk device stats.
2018-10-15 17:24:28 +02:00
// Docs from https://www.kernel.org/doc/Documentation/iostats.txt
2015-05-20 20:04:49 +02:00
func NewDiskstatsCollector ( ) ( Collector , error ) {
2014-11-25 03:00:17 +01:00
var diskLabelNames = [ ] string { "device" }
return & diskstatsCollector {
2014-06-04 13:12:34 +02:00
ignoredDevicesPattern : regexp . MustCompile ( * ignoredDevices ) ,
2018-01-17 17:55:55 +01:00
descs : [ ] typedFactorDesc {
2016-12-28 15:21:31 +01:00
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "reads_completed_total" ) ,
2016-12-28 15:21:31 +01:00
"The total number of reads completed successfully." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "reads_merged_total" ) ,
2018-10-15 17:24:28 +02:00
"The total number of reads merged." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "read_bytes_total" ) ,
"The total number of bytes read successfully." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : diskSectorSize ,
2016-12-28 15:21:31 +01:00
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "read_time_seconds_total" ) ,
2018-09-02 09:46:45 +02:00
"The total number of seconds spent by all reads." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : .001 ,
2016-12-28 15:21:31 +01:00
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "writes_completed_total" ) ,
2016-12-28 15:21:31 +01:00
"The total number of writes completed successfully." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "writes_merged_total" ) ,
2018-10-15 17:24:28 +02:00
"The number of writes merged." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "written_bytes_total" ) ,
"The total number of bytes written successfully." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : diskSectorSize ,
2016-12-28 15:21:31 +01:00
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "write_time_seconds_total" ) ,
"This is the total number of seconds spent by all writes." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : .001 ,
2016-12-28 15:21:31 +01:00
} ,
{
desc : prometheus . NewDesc (
2017-09-28 15:06:26 +02:00
prometheus . BuildFQName ( namespace , diskSubsystem , "io_now" ) ,
2016-12-28 15:21:31 +01:00
"The number of I/Os currently in progress." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . GaugeValue ,
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "io_time_seconds_total" ) ,
"Total seconds spent doing I/Os." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : .001 ,
2016-12-28 15:21:31 +01:00
} ,
{
desc : prometheus . NewDesc (
2018-01-17 17:55:55 +01:00
prometheus . BuildFQName ( namespace , diskSubsystem , "io_time_weighted_seconds_total" ) ,
2018-10-15 17:24:28 +02:00
"The weighted # of seconds spent doing I/Os." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
factor : .001 ,
} ,
{
desc : prometheus . NewDesc (
prometheus . BuildFQName ( namespace , diskSubsystem , "discards_completed_total" ) ,
"The total number of discards completed successfully." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
prometheus . BuildFQName ( namespace , diskSubsystem , "discards_merged_total" ) ,
"The total number of discards merged." ,
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
prometheus . BuildFQName ( namespace , diskSubsystem , "discarded_sectors_total" ) ,
2018-10-30 18:45:00 +01:00
"The total number of sectors discarded successfully." ,
2018-10-15 17:24:28 +02:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
} ,
{
desc : prometheus . NewDesc (
prometheus . BuildFQName ( namespace , diskSubsystem , "discard_time_seconds_total" ) ,
"This is the total number of seconds spent by all discards." ,
2016-12-28 15:21:31 +01:00
diskLabelNames ,
nil ,
) , valueType : prometheus . CounterValue ,
2018-01-17 17:55:55 +01:00
factor : .001 ,
2016-12-28 15:21:31 +01:00
} ,
2014-11-25 03:00:17 +01:00
} ,
} , nil
2014-06-04 13:12:34 +02:00
}
2016-12-28 15:21:31 +01:00
func ( c * diskstatsCollector ) Update ( ch chan <- prometheus . Metric ) error {
2014-06-04 13:12:34 +02:00
diskStats , err := getDiskStats ( )
if err != nil {
2014-11-25 03:00:17 +01:00
return fmt . Errorf ( "couldn't get diskstats: %s" , err )
2014-06-04 13:12:34 +02:00
}
2014-11-25 03:00:17 +01:00
2014-06-04 13:12:34 +02:00
for dev , stats := range diskStats {
if c . ignoredDevicesPattern . MatchString ( dev ) {
2015-05-28 21:21:44 +02:00
log . Debugf ( "Ignoring device: %s" , dev )
2014-06-04 13:12:34 +02:00
continue
}
2014-11-25 03:00:17 +01:00
2016-12-28 15:21:31 +01:00
for i , value := range stats {
2018-10-30 18:45:00 +01:00
// ignore unrecognized additional stats
if i >= len ( c . descs ) {
break
}
2014-06-04 13:12:34 +02:00
v , err := strconv . ParseFloat ( value , 64 )
if err != nil {
2014-11-25 03:00:17 +01:00
return fmt . Errorf ( "invalid value %s in diskstats: %s" , value , err )
2014-06-04 13:12:34 +02:00
}
2016-12-28 15:21:31 +01:00
ch <- c . descs [ i ] . mustNewConstMetric ( v , dev )
2014-06-04 13:12:34 +02:00
}
}
2016-12-28 15:21:31 +01:00
return nil
2014-06-04 13:12:34 +02:00
}
2018-10-15 17:24:28 +02:00
func getDiskStats ( ) ( map [ string ] [ ] string , error ) {
file , err := os . Open ( procFilePath ( diskstatsFilename ) )
2014-06-04 13:12:34 +02:00
if err != nil {
return nil , err
}
2014-11-25 00:30:07 +01:00
defer file . Close ( )
2014-06-04 13:12:34 +02:00
return parseDiskStats ( file )
}
2018-10-15 17:24:28 +02:00
func parseDiskStats ( r io . Reader ) ( map [ string ] [ ] string , error ) {
2014-11-25 00:30:07 +01:00
var (
2018-10-15 17:24:28 +02:00
diskStats = map [ string ] [ ] string { }
2014-11-25 00:30:07 +01:00
scanner = bufio . NewScanner ( r )
)
2014-06-04 13:12:34 +02:00
for scanner . Scan ( ) {
2017-02-28 18:42:43 +01:00
parts := strings . Fields ( scanner . Text ( ) )
2014-11-25 03:00:17 +01:00
if len ( parts ) < 4 { // we strip major, minor and dev
2018-10-15 17:24:28 +02:00
return nil , fmt . Errorf ( "invalid line in %s: %s" , procFilePath ( diskstatsFilename ) , scanner . Text ( ) )
2014-06-04 13:12:34 +02:00
}
dev := parts [ 2 ]
2018-10-15 17:24:28 +02:00
diskStats [ dev ] = parts [ 3 : ]
2014-06-04 13:12:34 +02:00
}
2014-11-25 00:30:07 +01:00
2017-02-28 19:31:35 +01:00
return diskStats , scanner . Err ( )
2014-06-04 13:12:34 +02:00
}