Adds cpu busy time and percentages

This commit is contained in:
Josh Palay 2015-08-12 11:09:20 -07:00 committed by Cameron Sparr
parent ba1e4917d1
commit 1e742aec04
2 changed files with 176 additions and 17 deletions

View File

@ -4,15 +4,27 @@ import (
"fmt" "fmt"
"github.com/influxdb/telegraf/plugins" "github.com/influxdb/telegraf/plugins"
"github.com/influxdb/telegraf/plugins/system/ps/cpu"
) )
type CPUStats struct { type CPUStats struct {
ps PS ps PS
lastStats []cpu.CPUTimesStat
PerCPU bool `toml:"percpu"` PerCPU bool `toml:"percpu"`
TotalCPU bool `toml:"totalcpu"` TotalCPU bool `toml:"totalcpu"`
} }
func NewCPUStats(ps PS) *CPUStats {
times, _ := ps.CPUTimes()
stats := CPUStats{
ps: ps,
lastStats: times,
}
return &stats
}
func (_ *CPUStats) Description() string { func (_ *CPUStats) Description() string {
return "Read metrics about cpu usage" return "Read metrics about cpu usage"
} }
@ -33,11 +45,14 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error {
return fmt.Errorf("error getting CPU info: %s", err) return fmt.Errorf("error getting CPU info: %s", err)
} }
for _, cts := range times { for i, cts := range times {
tags := map[string]string{ tags := map[string]string{
"cpu": cts.CPU, "cpu": cts.CPU,
} }
busy, total := busyAndTotalCpuTime(cts)
// Add total cpu numbers
add(acc, "user", cts.User, tags) add(acc, "user", cts.User, tags)
add(acc, "system", cts.System, tags) add(acc, "system", cts.System, tags)
add(acc, "idle", cts.Idle, tags) add(acc, "idle", cts.Idle, tags)
@ -49,13 +64,53 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error {
add(acc, "guest", cts.Guest, tags) add(acc, "guest", cts.Guest, tags)
add(acc, "guestNice", cts.GuestNice, tags) add(acc, "guestNice", cts.GuestNice, tags)
add(acc, "stolen", cts.Stolen, tags) add(acc, "stolen", cts.Stolen, tags)
add(acc, "busy", busy, tags)
// Add in percentage
lastCts := s.lastStats[i]
lastBusy, lastTotal := busyAndTotalCpuTime(lastCts)
busyDelta := busy - lastBusy
totalDelta := total - lastTotal
if totalDelta < 0 {
return fmt.Errorf("Error: current total CPU time is less than previous total CPU time")
} }
if totalDelta == 0 {
return nil
}
add(acc, "percentageUser", 100*(cts.User-lastCts.User)/totalDelta, tags)
add(acc, "percentageSystem", 100*(cts.System-lastCts.System)/totalDelta, tags)
add(acc, "percentageIdle", 100*(cts.Idle-lastCts.Idle)/totalDelta, tags)
add(acc, "percentageNice", 100*(cts.Nice-lastCts.Nice)/totalDelta, tags)
add(acc, "percentageIowait", 100*(cts.Iowait-lastCts.Iowait)/totalDelta, tags)
add(acc, "percentageIrq", 100*(cts.Irq-lastCts.Irq)/totalDelta, tags)
add(acc, "percentageSoftirq", 100*(cts.Softirq-lastCts.Softirq)/totalDelta, tags)
add(acc, "percentageSteal", 100*(cts.Steal-lastCts.Steal)/totalDelta, tags)
add(acc, "percentageGuest", 100*(cts.Guest-lastCts.Guest)/totalDelta, tags)
add(acc, "percentageGuestNice", 100*(cts.GuestNice-lastCts.GuestNice)/totalDelta, tags)
add(acc, "percentageStolen", 100*(cts.Stolen-lastCts.Stolen)/totalDelta, tags)
add(acc, "percentageBusy", 100*busyDelta/totalDelta, tags)
}
s.lastStats = times
return nil return nil
} }
func busyAndTotalCpuTime(t cpu.CPUTimesStat) (float64, float64) {
busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal +
t.Guest + t.GuestNice + t.Stolen
return busy, busy + t.Idle
}
func init() { func init() {
plugins.Add("cpu", func() plugins.Plugin { plugins.Add("cpu", func() plugins.Plugin {
return &CPUStats{ps: &systemPS{}} realPS := &systemPS{}
return NewCPUStats(realPS)
}) })
} }

View File

@ -1,6 +1,8 @@
package system package system
import ( import (
"fmt"
"reflect"
"testing" "testing"
"github.com/influxdb/telegraf/plugins/system/ps/cpu" "github.com/influxdb/telegraf/plugins/system/ps/cpu"
@ -44,6 +46,21 @@ func TestSystemStats_GenerateStats(t *testing.T) {
Stolen: 0.051, Stolen: 0.051,
} }
cts2 := cpu.CPUTimesStat{
CPU: "cpu0",
User: 11.4, // increased by 8.3
System: 10.9, // increased by 2.7
Idle: 158.8699, // increased by 78.7699 (for total increase of 100)
Nice: 2.5, // increased by 1.2
Iowait: 0.7, // increased by 0.5
Irq: 1.2, // increased by 1.1
Softirq: 0.31, // increased by 0.2
Steal: 0.0002, // increased by 0.0001
Guest: 12.9, // increased by 4.8
GuestNice: 2.524, // increased by 2.2
Stolen: 0.281, // increased by 0.23
}
mps.On("CPUTimes").Return([]cpu.CPUTimesStat{cts}, nil) mps.On("CPUTimes").Return([]cpu.CPUTimesStat{cts}, nil)
du := &disk.DiskUsageStat{ du := &disk.DiskUsageStat{
@ -171,26 +188,72 @@ func TestSystemStats_GenerateStats(t *testing.T) {
assert.True(t, acc.CheckValue("load5", 1.5)) assert.True(t, acc.CheckValue("load5", 1.5))
assert.True(t, acc.CheckValue("load15", 0.8)) assert.True(t, acc.CheckValue("load15", 0.8))
cs := &CPUStats{ps: &mps} cs := NewCPUStats(&mps)
cputags := map[string]string{ cputags := map[string]string{
"cpu": "cpu0", "cpu": "cpu0",
} }
preCPUPoints := len(acc.Points)
err = cs.Gather(&acc)
require.NoError(t, err)
numCPUPoints := len(acc.Points) - preCPUPoints
expectedCPUPoints := 12
assert.Equal(t, numCPUPoints, expectedCPUPoints)
// Computed values are checked with delta > 0 becasue of floating point arithmatic
// imprecision
assertContainsTaggedFloat(t, acc, "user", 3.1, 0, cputags)
assertContainsTaggedFloat(t, acc, "system", 8.2, 0, cputags)
assertContainsTaggedFloat(t, acc, "idle", 80.1, 0, cputags)
assertContainsTaggedFloat(t, acc, "nice", 1.3, 0, cputags)
assertContainsTaggedFloat(t, acc, "iowait", 0.2, 0, cputags)
assertContainsTaggedFloat(t, acc, "irq", 0.1, 0, cputags)
assertContainsTaggedFloat(t, acc, "softirq", 0.11, 0, cputags)
assertContainsTaggedFloat(t, acc, "steal", 0.0001, 0, cputags)
assertContainsTaggedFloat(t, acc, "guest", 8.1, 0, cputags)
assertContainsTaggedFloat(t, acc, "guestNice", 0.324, 0, cputags)
assertContainsTaggedFloat(t, acc, "stolen", 0.051, 0, cputags)
assertContainsTaggedFloat(t, acc, "busy", 21.4851, 0.0005, cputags)
mps2 := MockPS{}
mps2.On("CPUTimes").Return([]cpu.CPUTimesStat{cts2}, nil)
cs.ps = &mps2
// Should have added cpu percentages too
err = cs.Gather(&acc) err = cs.Gather(&acc)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, acc.CheckTaggedValue("user", 3.1, cputags)) numCPUPoints = len(acc.Points) - (preCPUPoints + numCPUPoints)
assert.True(t, acc.CheckTaggedValue("system", 8.2, cputags)) expectedCPUPoints = 24
assert.True(t, acc.CheckTaggedValue("idle", 80.1, cputags)) assert.Equal(t, numCPUPoints, expectedCPUPoints)
assert.True(t, acc.CheckTaggedValue("nice", 1.3, cputags))
assert.True(t, acc.CheckTaggedValue("iowait", 0.2, cputags)) assertContainsTaggedFloat(t, acc, "user", 11.4, 0, cputags)
assert.True(t, acc.CheckTaggedValue("irq", 0.1, cputags)) assertContainsTaggedFloat(t, acc, "system", 10.9, 0, cputags)
assert.True(t, acc.CheckTaggedValue("softirq", 0.11, cputags)) assertContainsTaggedFloat(t, acc, "idle", 158.8699, 0, cputags)
assert.True(t, acc.CheckTaggedValue("steal", 0.0001, cputags)) assertContainsTaggedFloat(t, acc, "nice", 2.5, 0, cputags)
assert.True(t, acc.CheckTaggedValue("guest", 8.1, cputags)) assertContainsTaggedFloat(t, acc, "iowait", 0.7, 0, cputags)
assert.True(t, acc.CheckTaggedValue("guestNice", 0.324, cputags)) assertContainsTaggedFloat(t, acc, "irq", 1.2, 0, cputags)
assert.True(t, acc.CheckTaggedValue("stolen", 0.051, cputags)) assertContainsTaggedFloat(t, acc, "softirq", 0.31, 0, cputags)
assertContainsTaggedFloat(t, acc, "steal", 0.0002, 0, cputags)
assertContainsTaggedFloat(t, acc, "guest", 12.9, 0, cputags)
assertContainsTaggedFloat(t, acc, "guestNice", 2.524, 0, cputags)
assertContainsTaggedFloat(t, acc, "stolen", 0.281, 0, cputags)
assertContainsTaggedFloat(t, acc, "busy", 42.7152, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageUser", 8.3, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageSystem", 2.7, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageIdle", 78.7699, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageNice", 1.2, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageIowait", 0.5, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageIrq", 1.1, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageSoftirq", 0.2, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageSteal", 0.0001, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageGuest", 4.8, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageGuestNice", 2.2, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageStolen", 0.23, 0.0005, cputags)
assertContainsTaggedFloat(t, acc, "percentageBusy", 21.2301, 0.0005, cputags)
err = (&DiskStats{&mps}).Gather(&acc) err = (&DiskStats{&mps}).Gather(&acc)
require.NoError(t, err) require.NoError(t, err)
@ -319,3 +382,44 @@ func TestSystemStats_GenerateStats(t *testing.T) {
assert.True(t, acc.CheckTaggedValue("total_active_file", uint64(26), dockertags)) assert.True(t, acc.CheckTaggedValue("total_active_file", uint64(26), dockertags))
assert.True(t, acc.CheckTaggedValue("total_unevictable", uint64(27), dockertags)) assert.True(t, acc.CheckTaggedValue("total_unevictable", uint64(27), dockertags))
} }
// Asserts that a given accumulator contains a measurment of type float64 with
// specific tags within a certain distance of a given expected value. Asserts a failure
// if the measurement is of the wrong type, or if no matching measurements are found
//
// Paramaters:
// t *testing.T : Testing object to use
// acc testutil.Accumulator: Accumulator to examine
// measurement string : Name of the measurement to examine
// expectedValue float64 : Value to search for within the measurement
// delta float64 : Maximum acceptable distance of an accumulated value
// from the expectedValue parameter. Useful when
// floating-point arithmatic imprecision makes looking
// for an exact match impractical
// tags map[string]string : Tag set the found measurement must have. Set to nil to
// ignore the tag set.
func assertContainsTaggedFloat(
t *testing.T,
acc testutil.Accumulator,
measurement string,
expectedValue float64,
delta float64,
tags map[string]string,
) {
for _, pt := range acc.Points {
if pt.Measurement == measurement {
if (tags == nil) || reflect.DeepEqual(pt.Tags, tags) {
if value, ok := pt.Values["value"].(float64); ok {
if (value >= expectedValue-delta) && (value <= expectedValue+delta) {
// Found the point, return without failing
return
}
} else {
assert.Fail(t, fmt.Sprintf("Measurement \"%s\" does not have type float64", measurement))
}
}
}
}
assert.Fail(t, fmt.Sprintf("Could not find measurement \"%s\" with requested tags within %f of %f", measurement, delta, expectedValue))
}