diff --git a/plugins/inputs/diskio/README.md b/plugins/inputs/diskio/README.md index de550f32eae41..52b6b6849376a 100644 --- a/plugins/inputs/diskio/README.md +++ b/plugins/inputs/diskio/README.md @@ -73,6 +73,9 @@ docker run --privileged -v /:/hostfs:ro -v /run/udev:/run/udev:ro -e HOST_PROC=/ - iops_in_progress (integer, gauge) - merged_reads (integer, counter) - merged_writes (integer, counter) + - io_util (float64, gauge, percent) + - io_await (float64, gauge, milliseconds) + - io_svctm (float64, gauge, milliseconds) On linux these values correspond to the values in [`/proc/diskstats`][1] and [`/sys/block//stat`][2]. @@ -123,6 +126,18 @@ efficiency. Thus two 4K reads may become one 8K read before it is ultimately handed to the disk, and so it will be counted (and queued) as only one I/O. These fields lets you know how often this was done. +### `io_await` + +The average time per I/O operation (ms) + +### `io_svctm` + +The service time per I/O operation, excluding wait time (ms) + +### `io_util` + +The percentage of time the disk was active (%) + ## Sample Queries ### Calculate percent IO utilization per disk and host @@ -147,3 +162,9 @@ diskio,name=sda1 merged_reads=0i,reads=2353i,writes=10i,write_bytes=2117632i,wri diskio,name=centos/var_log reads=1063077i,writes=591025i,read_bytes=139325491712i,write_bytes=144233131520i,read_time=650221i,write_time=24368817i,io_time=852490i,weighted_io_time=25037394i,iops_in_progress=1i,merged_reads=0i,merged_writes=0i 1578326400000000000 diskio,name=sda write_time=49i,io_time=1317i,weighted_io_time=1404i,reads=2495i,read_time=1357i,write_bytes=2117632i,iops_in_progress=0i,merged_reads=0i,merged_writes=0i,writes=10i,read_bytes=38956544i 1578326400000000000 ``` + +```text +diskio,name=sda io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000 +diskio,name=sda1 io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000 +diskio,name=sda2 io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000 +``` diff --git a/plugins/inputs/diskio/diskio.go b/plugins/inputs/diskio/diskio.go index 009a0d39e9d47..daf97ad1de31b 100644 --- a/plugins/inputs/diskio/diskio.go +++ b/plugins/inputs/diskio/diskio.go @@ -6,6 +6,9 @@ import ( "fmt" "regexp" "strings" + "time" + + "github.com/shirou/gopsutil/v3/disk" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/filter" @@ -32,11 +35,13 @@ type DiskIO struct { SkipSerialNumber bool `toml:"skip_serial_number"` Log telegraf.Logger `toml:"-"` - ps system.PS - infoCache map[string]diskInfoCache - deviceFilter filter.Filter - warnDiskName map[string]bool - warnDiskTags map[string]bool + ps system.PS + infoCache map[string]diskInfoCache + deviceFilter filter.Filter + warnDiskName map[string]bool + warnDiskTags map[string]bool + lastIOCounterStat map[string]disk.IOCountersStat + lastCollectTime time.Time } func (*DiskIO) SampleConfig() string { @@ -57,6 +62,7 @@ func (d *DiskIO) Init() error { d.infoCache = make(map[string]diskInfoCache) d.warnDiskName = make(map[string]bool) d.warnDiskTags = make(map[string]bool) + d.lastIOCounterStat = make(map[string]disk.IOCountersStat) return nil } @@ -73,8 +79,8 @@ func (d *DiskIO) Gather(acc telegraf.Accumulator) error { if err != nil { return fmt.Errorf("error getting disk io info: %w", err) } - - for _, io := range diskio { + collectTime := time.Now() + for k, io := range diskio { match := false if d.deviceFilter != nil && d.deviceFilter.Match(io.Name) { match = true @@ -125,9 +131,23 @@ func (d *DiskIO) Gather(acc telegraf.Accumulator) error { "merged_reads": io.MergedReadCount, "merged_writes": io.MergedWriteCount, } + if lastValue, exists := d.lastIOCounterStat[k]; exists { + deltaRWCount := float64(io.ReadCount + io.WriteCount - lastValue.ReadCount - lastValue.WriteCount) + deltaRWTime := float64(io.ReadTime + io.WriteTime - lastValue.ReadTime - lastValue.WriteTime) + deltaIOTime := float64(io.IoTime - lastValue.IoTime) + if deltaRWCount > 0 { + fields["io_await"] = deltaRWTime / deltaRWCount + fields["io_svctm"] = deltaIOTime / deltaRWCount + } + itv := float64(collectTime.Sub(d.lastCollectTime).Milliseconds()) + if itv > 0 { + fields["io_util"] = 100 * deltaIOTime / itv + } + } acc.AddCounter("diskio", fields, tags) } - + d.lastCollectTime = collectTime + d.lastIOCounterStat = diskio return nil } diff --git a/plugins/inputs/diskio/diskio_test.go b/plugins/inputs/diskio/diskio_test.go index 9b2a8355734a8..17dce11367223 100644 --- a/plugins/inputs/diskio/diskio_test.go +++ b/plugins/inputs/diskio/diskio_test.go @@ -2,6 +2,7 @@ package diskio import ( "testing" + "time" "github.com/shirou/gopsutil/v3/disk" "github.com/stretchr/testify/require" @@ -127,3 +128,65 @@ func TestDiskIO(t *testing.T) { }) } } + +func TestDiskIOUtil(t *testing.T) { + cts := map[string]disk.IOCountersStat{ + "sda": { + ReadCount: 888, + WriteCount: 5341, + ReadBytes: 100000, + WriteBytes: 200000, + ReadTime: 7123, + WriteTime: 9087, + MergedReadCount: 11, + MergedWriteCount: 12, + Name: "sda", + IoTime: 123552, + SerialNumber: "ab-123-ad", + }, + } + + cts2 := map[string]disk.IOCountersStat{ + "sda": { + ReadCount: 1000, + WriteCount: 6000, + ReadBytes: 200000, + WriteBytes: 300000, + ReadTime: 8123, + WriteTime: 9187, + MergedReadCount: 16, + MergedWriteCount: 30, + Name: "sda", + IoTime: 163552, + SerialNumber: "ab-123-ad", + }, + } + + var acc testutil.Accumulator + var mps system.MockPS + mps.On("DiskIO").Return(cts, nil) + diskio := &DiskIO{ + Log: testutil.Logger{}, + Devices: []string{"sd*"}, + ps: &mps, + } + require.NoError(t, diskio.Init()) + // gather + require.NoError(t, diskio.Gather(&acc)) + // sleep + time.Sleep(1 * time.Second) + // gather twice + mps2 := system.MockPS{} + mps2.On("DiskIO").Return(cts2, nil) + diskio.ps = &mps2 + + err := diskio.Gather(&acc) + require.NoError(t, err) + require.True(t, acc.HasField("diskio", "io_util"), "miss io util") + require.True(t, acc.HasField("diskio", "io_svctm"), "miss io_svctm") + require.True(t, acc.HasField("diskio", "io_await"), "miss io_await") + + require.True(t, acc.HasFloatField("diskio", "io_util"), "io_util not have value") + require.True(t, acc.HasFloatField("diskio", "io_svctm"), "io_svctm not have value") + require.True(t, acc.HasFloatField("diskio", "io_await"), "io_await not have value") +}