Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/procstat adds new procstat metric about pgrep search #4307

Merged
merged 15 commits into from
Jun 19, 2018
Merged
13 changes: 13 additions & 0 deletions internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"strconv"
"strings"
"syscall"
"time"
"unicode"
)
Expand Down Expand Up @@ -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
}
11 changes: 10 additions & 1 deletion plugins/inputs/procstat/pgrep.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"os/exec"
"strconv"
"strings"

"github.com/influxdata/telegraf/internal"
)

// Implemention of PIDGatherer that execs pgrep to find processes
Expand Down Expand Up @@ -62,8 +64,15 @@ 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, no processes found
if i, _ := internal.ExitStatus(err); i == 1 {
return "", nil
}

if err != nil {
return "", fmt.Errorf("Error running %s: %s", path, err)
//return "", fmt.Errorf("Error running %s: %s", path, err)
return "", fmt.Errorf("Error executing command: %v %v, err: %s", path, args, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep only the old error message.

}
return string(out), err
}
Expand Down
23 changes: 23 additions & 0 deletions plugins/inputs/procstat/procstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,29 @@ func (p *Procstat) Gather(acc telegraf.Accumulator) error {
}
p.procs = procs

//adds a metric with info on the pgrep query
fields := make(map[string]interface{})
tags := make(map[string]string)
fields["pid_count"] = len(procs)
tags["pid_finder"] = p.PidFinder

//specific tags based on config
if p.PidFile != "" {
tags["pidfile"] = p.PidFile
} else if p.Exe != "" {
tags["exe"] = p.Exe
} else if p.Pattern != "" {
tags["pattern"] = p.Pattern
} else if p.User != "" {
tags["user"] = p.User
} else if p.SystemdUnit != "" {
tags["systemd_unit"] = p.SystemdUnit
} else if p.CGroup != "" {
tags["cgroup"] = p.CGroup
}

acc.AddFields("procstat_lookup", fields, tags)

for _, proc := range p.procs {
p.addMetrics(proc, acc)
}
Expand Down
37 changes: 37 additions & 0 deletions plugins/inputs/procstat/procstat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"
"time"

"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/testutil"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/process"
Expand Down Expand Up @@ -369,3 +370,39 @@ func TestGather_cgroupPIDs(t *testing.T) {
assert.Equal(t, []PID{1234, 5678}, pids)
assert.Equal(t, td, tags["cgroup"])
}

func TestExitStatus(t *testing.T) {
out, err := exec.Command("pgrep", "-G", "sys").Output()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command could return different results on different systems, so it could create random failures, perhaps you can make the needed error by hand. Also, it tests a function in internal/internal.go so it should be placed in internal/internal_test.go

t.Logf("cmd out: %v", out)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove any logging in the test cases, use assertions to document results.

exitNum, _ := internal.ExitStatus(err)
t.Logf("error status: %v", exitNum)
require.Equal(t, 1, exitNum)
}

func TestNoPgrepResults(t *testing.T) {
p := Procstat{
PidFinder: "pgrep",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above for this and the next test. You should be able to use testPgrep in this same file to fake the results.

//executing this command should result exit status 1
Exe: "-Gsys",
}

var acc testutil.Accumulator
err := acc.GatherError(p.Gather)

t.Logf("gather err: %v", err)
require.NoError(t, err)
}

func TestProcstatMetrics(t *testing.T) {
p := Procstat{
PidFinder: "pgrep",
//executing this command should result exit status 1
Exe: "-Gsys",
}
var acc testutil.Accumulator
err := acc.GatherError(p.Gather)
require.NoError(t, err)
t.Logf("num of pids: %v", len(p.procs))
t.Logf("num of metrics: %v", len(acc.Metrics))
require.Equal(t, len(p.procs)+1, len(acc.Metrics))
}