Include CPU usage percent with procstat data

closes #484
This commit is contained in:
Cameron Sparr 2016-01-19 17:28:02 -07:00
parent d3a5cca1bc
commit d3925fe578
4 changed files with 39 additions and 33 deletions

View File

@ -216,7 +216,7 @@ func (a *Agent) Test() error {
// Special instructions for some inputs. cpu, for example, needs to be // Special instructions for some inputs. cpu, for example, needs to be
// run twice in order to return cpu usage percentages. // run twice in order to return cpu usage percentages.
switch input.Name { switch input.Name {
case "cpu", "mongodb": case "cpu", "mongodb", "procstat":
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
fmt.Printf("* Plugin: %s, Collection 2\n", input.Name) fmt.Printf("* Plugin: %s, Collection 2\n", input.Name)
if err := input.Input.Gather(acc); err != nil { if err := input.Input.Gather(acc); err != nil {

View File

@ -18,10 +18,14 @@ type Procstat struct {
Exe string Exe string
Pattern string Pattern string
Prefix string Prefix string
pidmap map[int32]*process.Process
} }
func NewProcstat() *Procstat { func NewProcstat() *Procstat {
return &Procstat{} return &Procstat{
pidmap: make(map[int32]*process.Process),
}
} }
var sampleConfig = ` var sampleConfig = `
@ -46,12 +50,12 @@ func (_ *Procstat) Description() string {
} }
func (p *Procstat) Gather(acc inputs.Accumulator) error { func (p *Procstat) Gather(acc inputs.Accumulator) error {
procs, err := p.createProcesses() err := p.createProcesses()
if err != nil { if err != nil {
log.Printf("Error: procstat getting process, exe: [%s] pidfile: [%s] pattern: [%s] %s", log.Printf("Error: procstat getting process, exe: [%s] pidfile: [%s] pattern: [%s] %s",
p.Exe, p.PidFile, p.Pattern, err.Error()) p.Exe, p.PidFile, p.Pattern, err.Error())
} else { } else {
for _, proc := range procs { for _, proc := range p.pidmap {
p := NewSpecProcessor(p.Prefix, acc, proc) p := NewSpecProcessor(p.Prefix, acc, proc)
p.pushMetrics() p.pushMetrics()
} }
@ -60,8 +64,7 @@ func (p *Procstat) Gather(acc inputs.Accumulator) error {
return nil return nil
} }
func (p *Procstat) createProcesses() ([]*process.Process, error) { func (p *Procstat) createProcesses() error {
var out []*process.Process
var errstring string var errstring string
var outerr error var outerr error
@ -71,11 +74,14 @@ func (p *Procstat) createProcesses() ([]*process.Process, error) {
} }
for _, pid := range pids { for _, pid := range pids {
p, err := process.NewProcess(int32(pid)) _, ok := p.pidmap[pid]
if err == nil { if !ok {
out = append(out, p) proc, err := process.NewProcess(pid)
} else { if err == nil {
errstring += err.Error() + " " p.pidmap[pid] = proc
} else {
errstring += err.Error() + " "
}
} }
} }
@ -83,7 +89,7 @@ func (p *Procstat) createProcesses() ([]*process.Process, error) {
outerr = fmt.Errorf("%s", errstring) outerr = fmt.Errorf("%s", errstring)
} }
return out, outerr return outerr
} }
func (p *Procstat) getAllPids() ([]int32, error) { func (p *Procstat) getAllPids() ([]int32, error) {

View File

@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/shirou/gopsutil/process"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -23,6 +24,7 @@ func TestGather(t *testing.T) {
p := Procstat{ p := Procstat{
PidFile: file.Name(), PidFile: file.Name(),
Prefix: "foo", Prefix: "foo",
pidmap: make(map[int32]*process.Process),
} }
p.Gather(&acc) p.Gather(&acc)
assert.True(t, acc.HasFloatField("procstat", "foo_cpu_time_user")) assert.True(t, acc.HasFloatField("procstat", "foo_cpu_time_user"))

View File

@ -2,7 +2,7 @@ package procstat
import ( import (
"fmt" "fmt"
"log" "time"
"github.com/shirou/gopsutil/process" "github.com/shirou/gopsutil/process"
@ -40,7 +40,7 @@ func NewSpecProcessor(
tags := make(map[string]string) tags := make(map[string]string)
tags["pid"] = fmt.Sprintf("%v", p.Pid) tags["pid"] = fmt.Sprintf("%v", p.Pid)
if name, err := p.Name(); err == nil { if name, err := p.Name(); err == nil {
tags["name"] = name tags["process_name"] = name
} }
return &SpecProcessor{ return &SpecProcessor{
Prefix: prefix, Prefix: prefix,
@ -52,21 +52,11 @@ func NewSpecProcessor(
} }
func (p *SpecProcessor) pushMetrics() { func (p *SpecProcessor) pushMetrics() {
if err := p.pushFDStats(); err != nil { p.pushFDStats()
log.Printf("procstat, fd stats not available: %s", err.Error()) p.pushCtxStats()
} p.pushIOStats()
if err := p.pushCtxStats(); err != nil { p.pushCPUStats()
log.Printf("procstat, ctx stats not available: %s", err.Error()) p.pushMemoryStats()
}
if err := p.pushIOStats(); err != nil {
log.Printf("procstat, io stats not available: %s", err.Error())
}
if err := p.pushCPUStats(); err != nil {
log.Printf("procstat, cpu stats not available: %s", err.Error())
}
if err := p.pushMemoryStats(); err != nil {
log.Printf("procstat, mem stats not available: %s", err.Error())
}
p.flush() p.flush()
} }
@ -113,10 +103,18 @@ func (p *SpecProcessor) pushCPUStats() error {
p.add("cpu_time_iowait", cpu_time.Iowait) p.add("cpu_time_iowait", cpu_time.Iowait)
p.add("cpu_time_irq", cpu_time.Irq) p.add("cpu_time_irq", cpu_time.Irq)
p.add("cpu_time_soft_irq", cpu_time.Softirq) p.add("cpu_time_soft_irq", cpu_time.Softirq)
p.add("cpu_time_soft_steal", cpu_time.Steal) p.add("cpu_time_steal", cpu_time.Steal)
p.add("cpu_time_soft_stolen", cpu_time.Stolen) p.add("cpu_time_stolen", cpu_time.Stolen)
p.add("cpu_time_soft_guest", cpu_time.Guest) p.add("cpu_time_guest", cpu_time.Guest)
p.add("cpu_time_soft_guest_nice", cpu_time.GuestNice) p.add("cpu_time_guest_nice", cpu_time.GuestNice)
cpu_perc, err := p.proc.CPUPercent(time.Duration(0))
if err != nil {
return err
} else if cpu_perc == 0 {
return nil
}
p.add("cpu_usage", cpu_perc)
return nil return nil
} }