diff --git a/internal/internal.go b/internal/internal.go index d86b32d26..adc4df820 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -11,6 +11,7 @@ import ( "os/exec" "strconv" "strings" + "syscall" "time" "unicode" ) @@ -193,3 +194,15 @@ func RandomSleep(max time.Duration, shutdown chan struct{}) { return } } + +// Exit status takes the error from exec.Command +// and returns the exit status and true +// if error is not exit status, will return 0 and false +func ExitStatus(err error) (int, bool) { + if exiterr, ok := err.(*exec.ExitError); ok { + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return status.ExitStatus(), true + } + } + return 0, false +} diff --git a/plugins/inputs/procstat/pgrep.go b/plugins/inputs/procstat/pgrep.go index cf0754e6d..703febaa9 100644 --- a/plugins/inputs/procstat/pgrep.go +++ b/plugins/inputs/procstat/pgrep.go @@ -6,6 +6,8 @@ import ( "os/exec" "strconv" "strings" + + "github.com/influxdata/telegraf/internal" ) // Implemention of PIDGatherer that execs pgrep to find processes @@ -62,6 +64,12 @@ func find(path string, args []string) ([]PID, error) { func run(path string, args []string) (string, error) { out, err := exec.Command(path, args...).Output() + + //if exit code 1, ie no processes found, do not return error + if i, _ := internal.ExitStatus(err); i == 1 { + return "", nil + } + if err != nil { return "", fmt.Errorf("Error running %s: %s", path, err) } diff --git a/plugins/inputs/procstat/procstat.go b/plugins/inputs/procstat/procstat.go index 51f71f6a1..01505bdd0 100644 --- a/plugins/inputs/procstat/procstat.go +++ b/plugins/inputs/procstat/procstat.go @@ -97,7 +97,7 @@ func (p *Procstat) Gather(acc telegraf.Accumulator) error { p.createProcess = defaultProcess } - procs, err := p.updateProcesses(p.procs) + procs, err := p.updateProcesses(acc, p.procs) if err != nil { acc.AddError(fmt.Errorf("E! Error: procstat getting process, exe: [%s] pidfile: [%s] pattern: [%s] user: [%s] %s", p.Exe, p.PidFile, p.Pattern, p.User, err.Error())) @@ -230,8 +230,8 @@ func (p *Procstat) addMetrics(proc Process, acc telegraf.Accumulator) { } // Update monitored Processes -func (p *Procstat) updateProcesses(prevInfo map[PID]Process) (map[PID]Process, error) { - pids, tags, err := p.findPids() +func (p *Procstat) updateProcesses(acc telegraf.Accumulator, prevInfo map[PID]Process) (map[PID]Process, error) { + pids, tags, err := p.findPids(acc) if err != nil { return nil, err } @@ -281,9 +281,9 @@ func (p *Procstat) getPIDFinder() (PIDFinder, error) { } // Get matching PIDs and their initial tags -func (p *Procstat) findPids() ([]PID, map[string]string, error) { +func (p *Procstat) findPids(acc telegraf.Accumulator) ([]PID, map[string]string, error) { var pids []PID - var tags map[string]string + tags := make(map[string]string) var err error f, err := p.getPIDFinder() @@ -313,7 +313,18 @@ func (p *Procstat) findPids() ([]PID, map[string]string, error) { err = fmt.Errorf("Either exe, pid_file, user, pattern, systemd_unit, or cgroup must be specified") } - return pids, tags, err + rTags := make(map[string]string) + for k, v := range tags { + rTags[k] = v + } + + //adds a metric with info on the pgrep query + fields := make(map[string]interface{}) + tags["pid_finder"] = p.PidFinder + fields["pid_count"] = len(pids) + acc.AddFields("procstat_lookup", fields, tags) + + return pids, rTags, err } // execCommand is so tests can mock out exec.Command usage. diff --git a/plugins/inputs/procstat/procstat_test.go b/plugins/inputs/procstat/procstat_test.go index d77391fc2..ff4b70060 100644 --- a/plugins/inputs/procstat/procstat_test.go +++ b/plugins/inputs/procstat/procstat_test.go @@ -343,7 +343,8 @@ func TestGather_systemdUnitPIDs(t *testing.T) { createPIDFinder: pidFinder([]PID{}, nil), SystemdUnit: "TestGather_systemdUnitPIDs", } - pids, tags, err := p.findPids() + var acc testutil.Accumulator + pids, tags, err := p.findPids(&acc) require.NoError(t, err) assert.Equal(t, []PID{11408}, pids) assert.Equal(t, "TestGather_systemdUnitPIDs", tags["systemd_unit"]) @@ -364,8 +365,20 @@ func TestGather_cgroupPIDs(t *testing.T) { createPIDFinder: pidFinder([]PID{}, nil), CGroup: td, } - pids, tags, err := p.findPids() + var acc testutil.Accumulator + pids, tags, err := p.findPids(&acc) require.NoError(t, err) assert.Equal(t, []PID{1234, 5678}, pids) assert.Equal(t, td, tags["cgroup"]) } + +func TestProcstatLookupMetric(t *testing.T) { + p := Procstat{ + createPIDFinder: pidFinder([]PID{543}, nil), + Exe: "-Gsys", + } + var acc testutil.Accumulator + err := acc.GatherError(p.Gather) + require.NoError(t, err) + require.Equal(t, len(p.procs)+1, len(acc.Metrics)) +}