chore(inputs.procstat): Cleanup code (#14326)
This commit is contained in:
parent
1c2c03d778
commit
f2cc928178
|
|
@ -1,9 +0,0 @@
|
||||||
[agent]
|
|
||||||
interval="1s"
|
|
||||||
flush_interval="1s"
|
|
||||||
|
|
||||||
[[inputs.procstat]]
|
|
||||||
exe = "telegraf"
|
|
||||||
|
|
||||||
[[outputs.file]]
|
|
||||||
files = ["stdout"]
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build !linux
|
|
||||||
|
|
||||||
package procstat
|
|
||||||
|
|
||||||
func collectMemmap(Process, string, map[string]any) {}
|
|
||||||
|
|
@ -11,13 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NativeFinder uses gopsutil to find processes
|
// NativeFinder uses gopsutil to find processes
|
||||||
type NativeFinder struct {
|
type NativeFinder struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// NewNativeFinder ...
|
|
||||||
func NewNativeFinder() (PIDFinder, error) {
|
|
||||||
return &NativeFinder{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uid will return all pids for the given user
|
// Uid will return all pids for the given user
|
||||||
func (pg *NativeFinder) UID(user string) ([]PID, error) {
|
func (pg *NativeFinder) UID(user string) ([]PID, error) {
|
||||||
|
|
@ -80,38 +74,24 @@ func (pg *NativeFinder) FullPattern(pattern string) ([]PID, error) {
|
||||||
return pids, err
|
return pids, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChildPattern matches children pids on the command line when the process was executed
|
// Children matches children pids on the command line when the process was executed
|
||||||
func (pg *NativeFinder) ChildPattern(pattern string) ([]PID, error) {
|
func (pg *NativeFinder) Children(pid PID) ([]PID, error) {
|
||||||
regxPattern, err := regexp.Compile(pattern)
|
// Get all running processes
|
||||||
|
p, err := process.NewProcess(int32(pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("compiling regexp failed: %w", err)
|
return nil, fmt.Errorf("getting process %d failed: %w", pid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
procs, err := process.Processes()
|
// Get all children of the current process
|
||||||
|
children, err := p.Children()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getting processes failed: %w", err)
|
return nil, fmt.Errorf("unable to get children of process %d: %w", p.Pid, err)
|
||||||
|
}
|
||||||
|
pids := make([]PID, 0, len(children))
|
||||||
|
for _, child := range children {
|
||||||
|
pids = append(pids, PID(child.Pid))
|
||||||
}
|
}
|
||||||
|
|
||||||
var pids []PID
|
|
||||||
for _, p := range procs {
|
|
||||||
cmd, err := p.Cmdline()
|
|
||||||
if err != nil || !regxPattern.MatchString(cmd) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
parent, err := process.NewProcess(p.Pid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get process %d: %w", p.Pid, err)
|
|
||||||
}
|
|
||||||
children, err := parent.Children()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get children of process %d: %w", p.Pid, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range children {
|
|
||||||
pids = append(pids, PID(child.Pid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pids, err
|
return pids, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,3 +107,28 @@ func (pg *NativeFinder) FastProcessList() ([]*process.Process, error) {
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pattern matches on the process name
|
||||||
|
func (pg *NativeFinder) Pattern(pattern string) ([]PID, error) {
|
||||||
|
var pids []PID
|
||||||
|
regxPattern, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return pids, err
|
||||||
|
}
|
||||||
|
procs, err := pg.FastProcessList()
|
||||||
|
if err != nil {
|
||||||
|
return pids, err
|
||||||
|
}
|
||||||
|
for _, p := range procs {
|
||||||
|
name, err := processName(p)
|
||||||
|
if err != nil {
|
||||||
|
//skip, this can be caused by the pid no longer existing
|
||||||
|
//or you having no permissions to access it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if regxPattern.MatchString(name) {
|
||||||
|
pids = append(pids, PID(p.Pid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pids, err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
//go:build !windows
|
|
||||||
|
|
||||||
package procstat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pattern matches on the process name
|
|
||||||
func (pg *NativeFinder) Pattern(pattern string) ([]PID, error) {
|
|
||||||
var pids []PID
|
|
||||||
regxPattern, err := regexp.Compile(pattern)
|
|
||||||
if err != nil {
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
procs, err := pg.FastProcessList()
|
|
||||||
if err != nil {
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
for _, p := range procs {
|
|
||||||
name, err := p.Exe()
|
|
||||||
if err != nil {
|
|
||||||
//skip, this can be caused by the pid no longer existing
|
|
||||||
//or you having no permissions to access it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if regxPattern.MatchString(name) {
|
|
||||||
pids = append(pids, PID(p.Pid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -11,17 +12,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkPattern(b *testing.B) {
|
func BenchmarkPattern(b *testing.B) {
|
||||||
finder, err := NewNativeFinder()
|
finder := &NativeFinder{}
|
||||||
require.NoError(b, err)
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
_, err = finder.Pattern(".*")
|
_, err := finder.Pattern(".*")
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFullPattern(b *testing.B) {
|
func BenchmarkFullPattern(b *testing.B) {
|
||||||
finder, err := NewNativeFinder()
|
finder := &NativeFinder{}
|
||||||
require.NoError(b, err)
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
_, err := finder.FullPattern(".*")
|
_, err := finder.FullPattern(".*")
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
@ -53,10 +52,47 @@ func TestChildPattern(t *testing.T) {
|
||||||
expected = append(expected, PID(cmd2.Process.Pid))
|
expected = append(expected, PID(cmd2.Process.Pid))
|
||||||
|
|
||||||
// Use the plugin to find the children
|
// Use the plugin to find the children
|
||||||
finder, err := NewNativeFinder()
|
finder := &NativeFinder{}
|
||||||
|
parent, err := finder.Pattern(parentName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Len(t, parent, 1)
|
||||||
childs, err := finder.ChildPattern(parentName)
|
childs, err := finder.Children(parent[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, expected, childs)
|
require.ElementsMatch(t, expected, childs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGather_RealPatternIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
pg := &NativeFinder{}
|
||||||
|
pids, err := pg.Pattern(`procstat`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, pids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGather_RealFullPatternIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
t.Skip("Skipping integration test on Non-Windows OS")
|
||||||
|
}
|
||||||
|
pg := &NativeFinder{}
|
||||||
|
pids, err := pg.FullPattern(`%procstat%`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, pids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGather_RealUserIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pg := &NativeFinder{}
|
||||||
|
pids, err := pg.UID(currentUser.Username)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, pids)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package procstat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pattern matches on the process name
|
|
||||||
func (pg *NativeFinder) Pattern(pattern string) ([]PID, error) {
|
|
||||||
var pids []PID
|
|
||||||
regxPattern, err := regexp.Compile(pattern)
|
|
||||||
if err != nil {
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
procs, err := pg.FastProcessList()
|
|
||||||
if err != nil {
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
for _, p := range procs {
|
|
||||||
name, err := p.Name()
|
|
||||||
if err != nil {
|
|
||||||
//skip, this can be caused by the pid no longer existing
|
|
||||||
//or you having no permissions to access it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if regxPattern.MatchString(name) {
|
|
||||||
pids = append(pids, PID(p.Pid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pids, err
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package procstat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/user"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGather_RealPatternIntegration(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("Skipping integration test in short mode")
|
|
||||||
}
|
|
||||||
pg, err := NewNativeFinder()
|
|
||||||
require.NoError(t, err)
|
|
||||||
pids, err := pg.Pattern(`procstat`)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEmpty(t, pids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGather_RealFullPatternIntegration(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("Skipping integration test in short mode")
|
|
||||||
}
|
|
||||||
pg, err := NewNativeFinder()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
pids, err := pg.FullPattern(`%procstat%`)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEmpty(t, pids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGather_RealUserIntegration(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("Skipping integration test in short mode")
|
|
||||||
}
|
|
||||||
currentUser, err := user.Current()
|
|
||||||
require.NoError(t, err)
|
|
||||||
pg, err := NewNativeFinder()
|
|
||||||
require.NoError(t, err)
|
|
||||||
pids, err := pg.UID(currentUser.Username)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEmpty(t, pids)
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,20 @@
|
||||||
|
|
||||||
package procstat
|
package procstat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processName(p *process.Process) (string, error) {
|
||||||
|
return p.Exe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryPidWithWinServiceName(_ string) (uint32, error) {
|
||||||
|
return 0, errors.New("os not supporting win_service option")
|
||||||
|
}
|
||||||
|
|
||||||
func collectMemmap(proc Process, prefix string, fields map[string]any) {
|
func collectMemmap(proc Process, prefix string, fields map[string]any) {
|
||||||
memMapStats, err := proc.MemoryMaps(true)
|
memMapStats, err := proc.MemoryMaps(true)
|
||||||
if err == nil && len(*memMapStats) == 1 {
|
if err == nil && len(*memMapStats) == 1 {
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
//go:build !linux && !windows
|
||||||
|
|
||||||
|
package procstat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processName(p *process.Process) (string, error) {
|
||||||
|
return p.Exe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryPidWithWinServiceName(_ string) (uint32, error) {
|
||||||
|
return 0, errors.New("os not supporting win_service option")
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectMemmap(Process, string, map[string]any) {}
|
||||||
|
|
@ -6,10 +6,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/svc/mgr"
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func processName(p *process.Process) (string, error) {
|
||||||
|
return p.Name()
|
||||||
|
}
|
||||||
|
|
||||||
func getService(name string) (*mgr.Service, error) {
|
func getService(name string) (*mgr.Service, error) {
|
||||||
m, err := mgr.Connect()
|
m, err := mgr.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -48,3 +53,5 @@ func queryPidWithWinServiceName(winServiceName string) (uint32, error) {
|
||||||
|
|
||||||
return p.ProcessId, nil
|
return p.ProcessId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectMemmap(Process, string, map[string]any) {}
|
||||||
|
|
@ -15,7 +15,7 @@ type Pgrep struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPgrep() (PIDFinder, error) {
|
func newPgrepFinder() (PIDFinder, error) {
|
||||||
path, err := exec.LookPath("pgrep")
|
path, err := exec.LookPath("pgrep")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find pgrep binary: %w", err)
|
return nil, fmt.Errorf("could not find pgrep binary: %w", err)
|
||||||
|
|
@ -40,69 +40,38 @@ func (pg *Pgrep) PidFile(path string) ([]PID, error) {
|
||||||
|
|
||||||
func (pg *Pgrep) Pattern(pattern string) ([]PID, error) {
|
func (pg *Pgrep) Pattern(pattern string) ([]PID, error) {
|
||||||
args := []string{pattern}
|
args := []string{pattern}
|
||||||
return find(pg.path, args)
|
return pg.find(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pg *Pgrep) UID(user string) ([]PID, error) {
|
func (pg *Pgrep) UID(user string) ([]PID, error) {
|
||||||
args := []string{"-u", user}
|
args := []string{"-u", user}
|
||||||
return find(pg.path, args)
|
return pg.find(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pg *Pgrep) FullPattern(pattern string) ([]PID, error) {
|
func (pg *Pgrep) FullPattern(pattern string) ([]PID, error) {
|
||||||
args := []string{"-f", pattern}
|
args := []string{"-f", pattern}
|
||||||
return find(pg.path, args)
|
return pg.find(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pg *Pgrep) ChildPattern(pattern string) ([]PID, error) {
|
func (pg *Pgrep) Children(pid PID) ([]PID, error) {
|
||||||
args := []string{"-P", pattern}
|
args := []string{"-P", strconv.FormatInt(int64(pid), 10)}
|
||||||
out, err := run(pg.path, args)
|
return pg.find(args)
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pids := []PID{}
|
func (pg *Pgrep) find(args []string) ([]PID, error) {
|
||||||
pid, err := strconv.ParseInt(pattern, 10, 32)
|
// Execute pgrep with the given arguments
|
||||||
|
buf, err := exec.Command(pg.path, args...).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// Exit code 1 means "no processes found" so we should not return
|
||||||
}
|
// an error in this case.
|
||||||
pids = append(pids, PID(pid))
|
if status, _ := internal.ExitStatus(err); status == 1 {
|
||||||
|
return nil, nil
|
||||||
fields := strings.Fields(out)
|
|
||||||
for _, field := range fields {
|
|
||||||
pid, err := strconv.ParseInt(field, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return pids, err
|
|
||||||
}
|
}
|
||||||
pids = append(pids, PID(pid))
|
return nil, fmt.Errorf("error running %q: %w", pg.path, err)
|
||||||
}
|
}
|
||||||
|
out := string(buf)
|
||||||
|
|
||||||
return pids, nil
|
// Parse the command output to extract the PIDs
|
||||||
}
|
|
||||||
|
|
||||||
func find(path string, args []string) ([]PID, error) {
|
|
||||||
out, err := run(path, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseOutput(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 %q: %w", path, err)
|
|
||||||
}
|
|
||||||
return string(out), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOutput(out string) ([]PID, error) {
|
|
||||||
pids := []PID{}
|
pids := []PID{}
|
||||||
fields := strings.Fields(out)
|
fields := strings.Fields(out)
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,20 @@ package procstat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/shirou/gopsutil/v3/process"
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:interfacebloat // conditionally allow to contain more methods
|
|
||||||
type Process interface {
|
type Process interface {
|
||||||
PID() PID
|
PID() PID
|
||||||
Tags() map[string]string
|
|
||||||
|
|
||||||
PageFaults() (*process.PageFaultsStat, error)
|
|
||||||
IOCounters() (*process.IOCountersStat, error)
|
|
||||||
MemoryInfo() (*process.MemoryInfoStat, error)
|
|
||||||
MemoryMaps(bool) (*[]process.MemoryMapsStat, error)
|
|
||||||
Name() (string, error)
|
Name() (string, error)
|
||||||
Cmdline() (string, error)
|
SetTag(string, string)
|
||||||
NumCtxSwitches() (*process.NumCtxSwitchesStat, error)
|
MemoryMaps(bool) (*[]process.MemoryMapsStat, error)
|
||||||
NumFDs() (int32, error)
|
Metric(prefix string, cmdLineTag, solarisMode bool) telegraf.Metric
|
||||||
NumThreads() (int32, error)
|
|
||||||
Percent(interval time.Duration) (float64, error)
|
|
||||||
MemoryPercent() (float32, error)
|
|
||||||
Times() (*cpu.TimesStat, error)
|
|
||||||
RlimitUsage(bool) ([]process.RlimitStat, error)
|
|
||||||
Username() (string, error)
|
|
||||||
CreateTime() (int64, error)
|
|
||||||
Ppid() (int32, error)
|
|
||||||
Status() ([]string, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PIDFinder interface {
|
type PIDFinder interface {
|
||||||
|
|
@ -37,7 +23,7 @@ type PIDFinder interface {
|
||||||
Pattern(pattern string) ([]PID, error)
|
Pattern(pattern string) ([]PID, error)
|
||||||
UID(user string) ([]PID, error)
|
UID(user string) ([]PID, error)
|
||||||
FullPattern(path string) ([]PID, error)
|
FullPattern(path string) ([]PID, error)
|
||||||
ChildPattern(path string) ([]PID, error)
|
Children(pid PID) ([]PID, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Proc struct {
|
type Proc struct {
|
||||||
|
|
@ -46,7 +32,7 @@ type Proc struct {
|
||||||
*process.Process
|
*process.Process
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProc(pid PID) (Process, error) {
|
func newProc(pid PID) (Process, error) {
|
||||||
p, err := process.NewProcess(int32(pid))
|
p, err := process.NewProcess(int32(pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -60,19 +46,15 @@ func NewProc(pid PID) (Process, error) {
|
||||||
return proc, nil
|
return proc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proc) Tags() map[string]string {
|
|
||||||
return p.tags
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proc) PID() PID {
|
func (p *Proc) PID() PID {
|
||||||
return PID(p.Process.Pid)
|
return PID(p.Process.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proc) Username() (string, error) {
|
func (p *Proc) SetTag(k, v string) {
|
||||||
return p.Process.Username()
|
p.tags[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proc) Percent(_ time.Duration) (float64, error) {
|
func (p *Proc) percent(_ time.Duration) (float64, error) {
|
||||||
cpuPerc, err := p.Process.Percent(time.Duration(0))
|
cpuPerc, err := p.Process.Percent(time.Duration(0))
|
||||||
if !p.hasCPUTimes && err == nil {
|
if !p.hasCPUTimes && err == nil {
|
||||||
p.hasCPUTimes = true
|
p.hasCPUTimes = true
|
||||||
|
|
@ -80,3 +62,159 @@ func (p *Proc) Percent(_ time.Duration) (float64, error) {
|
||||||
}
|
}
|
||||||
return cpuPerc, err
|
return cpuPerc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add metrics a single Process
|
||||||
|
func (p *Proc) Metric(prefix string, cmdLineTag, solarisMode bool) telegraf.Metric {
|
||||||
|
if prefix != "" {
|
||||||
|
prefix += "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make(map[string]interface{})
|
||||||
|
|
||||||
|
if _, nameInTags := p.tags["process_name"]; !nameInTags {
|
||||||
|
name, err := p.Name()
|
||||||
|
if err == nil {
|
||||||
|
p.tags["process_name"] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := p.tags["user"]; !ok {
|
||||||
|
user, err := p.Username()
|
||||||
|
if err == nil {
|
||||||
|
p.tags["user"] = user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pid is not present as a tag, include it as a field.
|
||||||
|
if _, pidInTags := p.tags["pid"]; !pidInTags {
|
||||||
|
fields["pid"] = p.Pid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the command line as a tag if the option is set
|
||||||
|
if cmdLineTag {
|
||||||
|
if _, ok := p.tags["cmdline"]; !ok {
|
||||||
|
cmdline, err := p.Cmdline()
|
||||||
|
if err == nil {
|
||||||
|
p.tags["cmdline"] = cmdline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numThreads, err := p.NumThreads()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"num_threads"] = numThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
fds, err := p.NumFDs()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"num_fds"] = fds
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := p.NumCtxSwitches()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"voluntary_context_switches"] = ctx.Voluntary
|
||||||
|
fields[prefix+"involuntary_context_switches"] = ctx.Involuntary
|
||||||
|
}
|
||||||
|
|
||||||
|
faults, err := p.PageFaults()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"minor_faults"] = faults.MinorFaults
|
||||||
|
fields[prefix+"major_faults"] = faults.MajorFaults
|
||||||
|
fields[prefix+"child_minor_faults"] = faults.ChildMinorFaults
|
||||||
|
fields[prefix+"child_major_faults"] = faults.ChildMajorFaults
|
||||||
|
}
|
||||||
|
|
||||||
|
io, err := p.IOCounters()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"read_count"] = io.ReadCount
|
||||||
|
fields[prefix+"write_count"] = io.WriteCount
|
||||||
|
fields[prefix+"read_bytes"] = io.ReadBytes
|
||||||
|
fields[prefix+"write_bytes"] = io.WriteBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
createdAt, err := p.CreateTime() // returns epoch in ms
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"created_at"] = createdAt * 1000000 // ms to ns
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuTime, err := p.Times()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"cpu_time_user"] = cpuTime.User
|
||||||
|
fields[prefix+"cpu_time_system"] = cpuTime.System
|
||||||
|
fields[prefix+"cpu_time_iowait"] = cpuTime.Iowait // only reported on Linux
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuPerc, err := p.percent(time.Duration(0))
|
||||||
|
if err == nil {
|
||||||
|
if solarisMode {
|
||||||
|
fields[prefix+"cpu_usage"] = cpuPerc / float64(runtime.NumCPU())
|
||||||
|
} else {
|
||||||
|
fields[prefix+"cpu_usage"] = cpuPerc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This only returns values for RSS and VMS
|
||||||
|
mem, err := p.MemoryInfo()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"memory_rss"] = mem.RSS
|
||||||
|
fields[prefix+"memory_vms"] = mem.VMS
|
||||||
|
}
|
||||||
|
|
||||||
|
collectMemmap(p, prefix, fields)
|
||||||
|
|
||||||
|
memPerc, err := p.MemoryPercent()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"memory_usage"] = memPerc
|
||||||
|
}
|
||||||
|
|
||||||
|
rlims, err := p.RlimitUsage(true)
|
||||||
|
if err == nil {
|
||||||
|
for _, rlim := range rlims {
|
||||||
|
var name string
|
||||||
|
switch rlim.Resource {
|
||||||
|
case process.RLIMIT_CPU:
|
||||||
|
name = "cpu_time"
|
||||||
|
case process.RLIMIT_DATA:
|
||||||
|
name = "memory_data"
|
||||||
|
case process.RLIMIT_STACK:
|
||||||
|
name = "memory_stack"
|
||||||
|
case process.RLIMIT_RSS:
|
||||||
|
name = "memory_rss"
|
||||||
|
case process.RLIMIT_NOFILE:
|
||||||
|
name = "num_fds"
|
||||||
|
case process.RLIMIT_MEMLOCK:
|
||||||
|
name = "memory_locked"
|
||||||
|
case process.RLIMIT_AS:
|
||||||
|
name = "memory_vms"
|
||||||
|
case process.RLIMIT_LOCKS:
|
||||||
|
name = "file_locks"
|
||||||
|
case process.RLIMIT_SIGPENDING:
|
||||||
|
name = "signals_pending"
|
||||||
|
case process.RLIMIT_NICE:
|
||||||
|
name = "nice_priority"
|
||||||
|
case process.RLIMIT_RTPRIO:
|
||||||
|
name = "realtime_priority"
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields[prefix+"rlimit_"+name+"_soft"] = rlim.Soft
|
||||||
|
fields[prefix+"rlimit_"+name+"_hard"] = rlim.Hard
|
||||||
|
if name != "file_locks" { // gopsutil doesn't currently track the used file locks count
|
||||||
|
fields[prefix+name] = rlim.Used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ppid, err := p.Ppid()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"ppid"] = ppid
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := p.Status()
|
||||||
|
if err == nil {
|
||||||
|
fields[prefix+"status"] = status[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return metric.New("procstat", p.tags, fields, time.Time{})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/process"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
@ -23,38 +21,39 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
|
// execCommand is so tests can mock out exec.Command usage.
|
||||||
|
var execCommand = exec.Command
|
||||||
|
|
||||||
type PID int32
|
type PID int32
|
||||||
|
|
||||||
type Procstat struct {
|
type Procstat struct {
|
||||||
PidFinder string `toml:"pid_finder"`
|
PidFinder string `toml:"pid_finder"`
|
||||||
PidFile string `toml:"pid_file"`
|
PidFile string `toml:"pid_file"`
|
||||||
Exe string
|
Exe string `toml:"exe"`
|
||||||
Pattern string
|
Pattern string `toml:"pattern"`
|
||||||
Prefix string
|
Prefix string `toml:"prefix"`
|
||||||
CmdLineTag bool `toml:"cmdline_tag"`
|
CmdLineTag bool `toml:"cmdline_tag"`
|
||||||
ProcessName string
|
ProcessName string `toml:"process_name"`
|
||||||
User string
|
User string `toml:"user"`
|
||||||
SystemdUnits string `toml:"systemd_units"`
|
SystemdUnits string `toml:"systemd_units"`
|
||||||
SupervisorUnit []string `toml:"supervisor_unit"`
|
SupervisorUnit []string `toml:"supervisor_unit"`
|
||||||
IncludeSystemdChildren bool `toml:"include_systemd_children"`
|
IncludeSystemdChildren bool `toml:"include_systemd_children"`
|
||||||
CGroup string `toml:"cgroup"`
|
CGroup string `toml:"cgroup"`
|
||||||
PidTag bool
|
PidTag bool `toml:"pid_tag"`
|
||||||
WinService string `toml:"win_service"`
|
WinService string `toml:"win_service"`
|
||||||
Mode string
|
Mode string `toml:"mode"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
solarisMode bool
|
solarisMode bool
|
||||||
|
finder PIDFinder
|
||||||
|
processes map[PID]Process
|
||||||
|
|
||||||
finder PIDFinder
|
createProcess func(PID) (Process, error)
|
||||||
|
|
||||||
createPIDFinder func() (PIDFinder, error)
|
|
||||||
procs map[PID]Process
|
|
||||||
createProcess func(PID) (Process, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PidsTags struct {
|
type PidsTags struct {
|
||||||
PIDS []PID
|
PIDs []PID
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
Err error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Procstat) SampleConfig() string {
|
func (*Procstat) SampleConfig() string {
|
||||||
|
|
@ -62,76 +61,128 @@ func (*Procstat) SampleConfig() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) Init() error {
|
func (p *Procstat) Init() error {
|
||||||
if strings.ToLower(p.Mode) == "solaris" {
|
// Check solaris mode
|
||||||
p.solarisMode = true
|
p.solarisMode = strings.ToLower(p.Mode) == "solaris"
|
||||||
|
|
||||||
|
// Check filtering
|
||||||
|
switch {
|
||||||
|
case len(p.SupervisorUnit) > 0, p.SystemdUnits != "", p.WinService != "",
|
||||||
|
p.CGroup != "", p.PidFile != "", p.Exe != "", p.Pattern != "",
|
||||||
|
p.User != "":
|
||||||
|
// Do nothing as those are valid settings
|
||||||
|
default:
|
||||||
|
return errors.New("require filter option but none set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instantiate the finder
|
||||||
switch p.PidFinder {
|
switch p.PidFinder {
|
||||||
case "":
|
case "", "pgrep":
|
||||||
p.PidFinder = "pgrep"
|
p.PidFinder = "pgrep"
|
||||||
p.createPIDFinder = NewPgrep
|
finder, err := newPgrepFinder()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating pgrep finder failed: %w", err)
|
||||||
|
}
|
||||||
|
p.finder = finder
|
||||||
case "native":
|
case "native":
|
||||||
p.createPIDFinder = NewNativeFinder
|
// gopsutil relies on pgrep when looking up children on darwin
|
||||||
case "pgrep":
|
// see https://github.com/shirou/gopsutil/blob/v3.23.10/process/process_darwin.go#L235
|
||||||
p.createPIDFinder = NewPgrep
|
requiresChildren := len(p.SupervisorUnit) > 0 && p.Pattern != ""
|
||||||
|
if requiresChildren && runtime.GOOS == "darwin" {
|
||||||
|
return errors.New("configuration requires the 'pgrep' finder on you OS")
|
||||||
|
}
|
||||||
|
p.finder = &NativeFinder{}
|
||||||
|
case "test":
|
||||||
|
p.Log.Warn("running in test mode")
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown pid_finder %q", p.PidFinder)
|
return fmt.Errorf("unknown pid_finder %q", p.PidFinder)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gopsutil relies on pgrep when looking up children on darwin
|
// Initialize the running process cache
|
||||||
// see https://github.com/shirou/gopsutil/blob/v3.23.10/process/process_darwin.go#L235
|
p.processes = make(map[PID]Process)
|
||||||
requiresChildren := len(p.SupervisorUnit) > 0 && p.Pattern != ""
|
|
||||||
if requiresChildren && p.PidFinder == "native" && runtime.GOOS == "darwin" {
|
|
||||||
return errors.New("configuration requires the 'pgrep' finder on you OS")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) Gather(acc telegraf.Accumulator) error {
|
func (p *Procstat) Gather(acc telegraf.Accumulator) error {
|
||||||
pidCount := 0
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
newProcs := make(map[PID]Process, len(p.procs))
|
results, err := p.findPids()
|
||||||
tags := make(map[string]string)
|
if err != nil {
|
||||||
pidTags := p.findPids()
|
// Add lookup error-metric
|
||||||
for _, pidTag := range pidTags {
|
fields := map[string]interface{}{
|
||||||
if len(pidTag.PIDS) < 1 && len(p.SupervisorUnit) > 0 {
|
"pid_count": 0,
|
||||||
|
"running": 0,
|
||||||
|
"result_code": 1,
|
||||||
|
}
|
||||||
|
tags := map[string]string{
|
||||||
|
"pid_finder": p.PidFinder,
|
||||||
|
"result": "lookup_error",
|
||||||
|
}
|
||||||
|
acc.AddFields("procstat_lookup", fields, tags, now)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int
|
||||||
|
running := make(map[PID]bool)
|
||||||
|
for _, r := range results {
|
||||||
|
if len(r.PIDs) < 1 && len(p.SupervisorUnit) > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pids := pidTag.PIDS
|
count += len(r.PIDs)
|
||||||
err := pidTag.Err
|
for _, pid := range r.PIDs {
|
||||||
pidCount += len(pids)
|
// Use the cached processes as we need the existing instances
|
||||||
for key, value := range pidTag.Tags {
|
// to compute delta-metrics (e.g. cpu-usage).
|
||||||
tags[key] = value
|
proc, found := p.processes[pid]
|
||||||
}
|
if !found {
|
||||||
if err != nil {
|
// We've found a process that was not recorded before so add it
|
||||||
fields := map[string]interface{}{
|
// to the list of processes
|
||||||
"pid_count": 0,
|
proc, err = p.createProcess(pid)
|
||||||
"running": 0,
|
if err != nil {
|
||||||
"result_code": 1,
|
// No problem; process may have ended after we found it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Assumption: if a process has no name, it probably does not exist
|
||||||
|
if name, _ := proc.Name(); name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add initial tags
|
||||||
|
for k, v := range r.Tags {
|
||||||
|
proc.SetTag(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pid tag if needed
|
||||||
|
if p.PidTag {
|
||||||
|
proc.SetTag("pid", strconv.Itoa(int(pid)))
|
||||||
|
}
|
||||||
|
if p.ProcessName != "" {
|
||||||
|
proc.SetTag("process_name", p.ProcessName)
|
||||||
|
}
|
||||||
|
p.processes[pid] = proc
|
||||||
}
|
}
|
||||||
tags["pid_finder"] = p.PidFinder
|
running[pid] = true
|
||||||
tags["result"] = "lookup_error"
|
m := proc.Metric(p.Prefix, p.CmdLineTag, p.solarisMode)
|
||||||
acc.AddFields("procstat_lookup", fields, tags, now)
|
m.SetTime(now)
|
||||||
return err
|
acc.AddMetric(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.updateProcesses(pids, pidTag.Tags, p.procs, newProcs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.procs = newProcs
|
// Cleanup processes that are not running anymore
|
||||||
for _, proc := range p.procs {
|
for pid := range p.processes {
|
||||||
p.addMetric(proc, acc, now)
|
if !running[pid] {
|
||||||
|
delete(p.processes, pid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add lookup statistics-metric
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"pid_count": pidCount,
|
"pid_count": count,
|
||||||
"running": len(p.procs),
|
"running": len(running),
|
||||||
"result_code": 0,
|
"result_code": 0,
|
||||||
}
|
}
|
||||||
|
tags := map[string]string{
|
||||||
tags["pid_finder"] = p.PidFinder
|
"pid_finder": p.PidFinder,
|
||||||
tags["result"] = "success"
|
"result": "success",
|
||||||
|
}
|
||||||
if len(p.SupervisorUnit) > 0 {
|
if len(p.SupervisorUnit) > 0 {
|
||||||
tags["supervisor_unit"] = strings.Join(p.SupervisorUnit, ";")
|
tags["supervisor_unit"] = strings.Join(p.SupervisorUnit, ";")
|
||||||
}
|
}
|
||||||
|
|
@ -140,315 +191,99 @@ func (p *Procstat) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add metrics a single Process
|
// Get matching PIDs and their initial tags
|
||||||
func (p *Procstat) addMetric(proc Process, acc telegraf.Accumulator, t time.Time) {
|
func (p *Procstat) findPids() ([]PidsTags, error) {
|
||||||
var prefix string
|
switch {
|
||||||
if p.Prefix != "" {
|
case len(p.SupervisorUnit) > 0:
|
||||||
prefix = p.Prefix + "_"
|
return p.findSupervisorUnits()
|
||||||
}
|
case p.SystemdUnits != "":
|
||||||
|
return p.systemdUnitPIDs()
|
||||||
fields := map[string]interface{}{}
|
case p.WinService != "":
|
||||||
|
pids, err := p.winServicePIDs()
|
||||||
//If process_name tag is not already set, set to actual name
|
|
||||||
if _, nameInTags := proc.Tags()["process_name"]; !nameInTags {
|
|
||||||
name, err := proc.Name()
|
|
||||||
if err == nil {
|
|
||||||
proc.Tags()["process_name"] = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If user tag is not already set, set to actual name
|
|
||||||
if _, ok := proc.Tags()["user"]; !ok {
|
|
||||||
user, err := proc.Username()
|
|
||||||
if err == nil {
|
|
||||||
proc.Tags()["user"] = user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If pid is not present as a tag, include it as a field.
|
|
||||||
if _, pidInTags := proc.Tags()["pid"]; !pidInTags {
|
|
||||||
fields["pid"] = int32(proc.PID())
|
|
||||||
}
|
|
||||||
|
|
||||||
//If cmd_line tag is true and it is not already set add cmdline as a tag
|
|
||||||
if p.CmdLineTag {
|
|
||||||
if _, ok := proc.Tags()["cmdline"]; !ok {
|
|
||||||
cmdline, err := proc.Cmdline()
|
|
||||||
if err == nil {
|
|
||||||
proc.Tags()["cmdline"] = cmdline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
numThreads, err := proc.NumThreads()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"num_threads"] = numThreads
|
|
||||||
}
|
|
||||||
|
|
||||||
fds, err := proc.NumFDs()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"num_fds"] = fds
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, err := proc.NumCtxSwitches()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"voluntary_context_switches"] = ctx.Voluntary
|
|
||||||
fields[prefix+"involuntary_context_switches"] = ctx.Involuntary
|
|
||||||
}
|
|
||||||
|
|
||||||
faults, err := proc.PageFaults()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"minor_faults"] = faults.MinorFaults
|
|
||||||
fields[prefix+"major_faults"] = faults.MajorFaults
|
|
||||||
fields[prefix+"child_minor_faults"] = faults.ChildMinorFaults
|
|
||||||
fields[prefix+"child_major_faults"] = faults.ChildMajorFaults
|
|
||||||
}
|
|
||||||
|
|
||||||
io, err := proc.IOCounters()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"read_count"] = io.ReadCount
|
|
||||||
fields[prefix+"write_count"] = io.WriteCount
|
|
||||||
fields[prefix+"read_bytes"] = io.ReadBytes
|
|
||||||
fields[prefix+"write_bytes"] = io.WriteBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
createdAt, err := proc.CreateTime() // returns epoch in ms
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"created_at"] = createdAt * 1000000 // ms to ns
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuTime, err := proc.Times()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"cpu_time_user"] = cpuTime.User
|
|
||||||
fields[prefix+"cpu_time_system"] = cpuTime.System
|
|
||||||
fields[prefix+"cpu_time_iowait"] = cpuTime.Iowait // only reported on Linux
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuPerc, err := proc.Percent(time.Duration(0))
|
|
||||||
if err == nil {
|
|
||||||
if p.solarisMode {
|
|
||||||
fields[prefix+"cpu_usage"] = cpuPerc / float64(runtime.NumCPU())
|
|
||||||
} else {
|
|
||||||
fields[prefix+"cpu_usage"] = cpuPerc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This only returns values for RSS and VMS
|
|
||||||
mem, err := proc.MemoryInfo()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"memory_rss"] = mem.RSS
|
|
||||||
fields[prefix+"memory_vms"] = mem.VMS
|
|
||||||
}
|
|
||||||
|
|
||||||
collectMemmap(proc, prefix, fields)
|
|
||||||
|
|
||||||
memPerc, err := proc.MemoryPercent()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"memory_usage"] = memPerc
|
|
||||||
}
|
|
||||||
|
|
||||||
rlims, err := proc.RlimitUsage(true)
|
|
||||||
if err == nil {
|
|
||||||
for _, rlim := range rlims {
|
|
||||||
var name string
|
|
||||||
switch rlim.Resource {
|
|
||||||
case process.RLIMIT_CPU:
|
|
||||||
name = "cpu_time"
|
|
||||||
case process.RLIMIT_DATA:
|
|
||||||
name = "memory_data"
|
|
||||||
case process.RLIMIT_STACK:
|
|
||||||
name = "memory_stack"
|
|
||||||
case process.RLIMIT_RSS:
|
|
||||||
name = "memory_rss"
|
|
||||||
case process.RLIMIT_NOFILE:
|
|
||||||
name = "num_fds"
|
|
||||||
case process.RLIMIT_MEMLOCK:
|
|
||||||
name = "memory_locked"
|
|
||||||
case process.RLIMIT_AS:
|
|
||||||
name = "memory_vms"
|
|
||||||
case process.RLIMIT_LOCKS:
|
|
||||||
name = "file_locks"
|
|
||||||
case process.RLIMIT_SIGPENDING:
|
|
||||||
name = "signals_pending"
|
|
||||||
case process.RLIMIT_NICE:
|
|
||||||
name = "nice_priority"
|
|
||||||
case process.RLIMIT_RTPRIO:
|
|
||||||
name = "realtime_priority"
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fields[prefix+"rlimit_"+name+"_soft"] = rlim.Soft
|
|
||||||
fields[prefix+"rlimit_"+name+"_hard"] = rlim.Hard
|
|
||||||
if name != "file_locks" { // gopsutil doesn't currently track the used file locks count
|
|
||||||
fields[prefix+name] = rlim.Used
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ppid, err := proc.Ppid()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"ppid"] = ppid
|
|
||||||
}
|
|
||||||
|
|
||||||
status, err := proc.Status()
|
|
||||||
if err == nil {
|
|
||||||
fields[prefix+"status"] = status[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AddFields("procstat", fields, proc.Tags(), t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update monitored Processes
|
|
||||||
func (p *Procstat) updateProcesses(pids []PID, tags map[string]string, prevInfo map[PID]Process, procs map[PID]Process) {
|
|
||||||
for _, pid := range pids {
|
|
||||||
info, ok := prevInfo[pid]
|
|
||||||
if ok {
|
|
||||||
// Assumption: if a process has no name, it probably does not exist
|
|
||||||
if name, _ := info.Name(); name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procs[pid] = info
|
|
||||||
} else {
|
|
||||||
proc, err := p.createProcess(pid)
|
|
||||||
if err != nil {
|
|
||||||
// No problem; process may have ended after we found it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Assumption: if a process has no name, it probably does not exist
|
|
||||||
if name, _ := proc.Name(); name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procs[pid] = proc
|
|
||||||
|
|
||||||
// Add initial tags
|
|
||||||
for k, v := range tags {
|
|
||||||
proc.Tags()[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add pid tag if needed
|
|
||||||
if p.PidTag {
|
|
||||||
proc.Tags()["pid"] = strconv.Itoa(int(pid))
|
|
||||||
}
|
|
||||||
if p.ProcessName != "" {
|
|
||||||
proc.Tags()["process_name"] = p.ProcessName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and return PIDGatherer lazily
|
|
||||||
func (p *Procstat) getPIDFinder() (PIDFinder, error) {
|
|
||||||
if p.finder == nil {
|
|
||||||
f, err := p.createPIDFinder()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
p.finder = f
|
tags := map[string]string{"win_service": p.WinService}
|
||||||
}
|
return []PidsTags{{pids, tags}}, nil
|
||||||
return p.finder, nil
|
case p.CGroup != "":
|
||||||
}
|
return p.cgroupPIDs()
|
||||||
|
case p.PidFile != "":
|
||||||
// Get matching PIDs and their initial tags
|
pids, err := p.finder.PidFile(p.PidFile)
|
||||||
func (p *Procstat) findPids() []PidsTags {
|
|
||||||
var pidTags []PidsTags
|
|
||||||
|
|
||||||
if len(p.SupervisorUnit) > 0 {
|
|
||||||
groups, groupsTags, err := p.supervisorPIDs()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pidTags = append(pidTags, PidsTags{nil, nil, err})
|
return nil, err
|
||||||
return pidTags
|
|
||||||
}
|
}
|
||||||
// According to the PID, find the system process number and use pgrep to filter to get the number of child processes
|
tags := map[string]string{"pidfile": p.PidFile}
|
||||||
for _, group := range groups {
|
return []PidsTags{{pids, tags}}, nil
|
||||||
f, err := p.getPIDFinder()
|
case p.Exe != "":
|
||||||
if err != nil {
|
pids, err := p.finder.Pattern(p.Exe)
|
||||||
pidTags = append(pidTags, PidsTags{nil, nil, err})
|
if err != nil {
|
||||||
return pidTags
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
p.Pattern = groupsTags[group]["pid"]
|
|
||||||
if p.Pattern == "" {
|
|
||||||
pidTags = append(pidTags, PidsTags{nil, groupsTags[group], err})
|
|
||||||
return pidTags
|
|
||||||
}
|
|
||||||
|
|
||||||
pids, tags, err := p.SimpleFindPids(f)
|
|
||||||
if err != nil {
|
|
||||||
pidTags = append(pidTags, PidsTags{nil, nil, err})
|
|
||||||
return pidTags
|
|
||||||
}
|
|
||||||
// Handle situations where the PID does not exist
|
|
||||||
if len(pids) == 0 {
|
|
||||||
pidTags = append(pidTags, PidsTags{nil, groupsTags[group], err})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
stats := groupsTags[group]
|
|
||||||
// Merge tags map
|
|
||||||
for k, v := range stats {
|
|
||||||
_, ok := tags[k]
|
|
||||||
if !ok {
|
|
||||||
tags[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Remove duplicate pid tags
|
|
||||||
delete(tags, "pid")
|
|
||||||
pidTags = append(pidTags, PidsTags{pids, tags, err})
|
|
||||||
}
|
}
|
||||||
|
tags := map[string]string{"exe": p.Exe}
|
||||||
return pidTags
|
return []PidsTags{{pids, tags}}, nil
|
||||||
} else if p.SystemdUnits != "" {
|
case p.Pattern != "":
|
||||||
groups := p.systemdUnitPIDs()
|
pids, err := p.finder.FullPattern(p.Pattern)
|
||||||
return groups
|
if err != nil {
|
||||||
} else if p.CGroup != "" {
|
return nil, err
|
||||||
groups := p.cgroupPIDs()
|
}
|
||||||
return groups
|
tags := map[string]string{"pattern": p.Pattern}
|
||||||
|
return []PidsTags{{pids, tags}}, nil
|
||||||
|
case p.User != "":
|
||||||
|
pids, err := p.finder.UID(p.User)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tags := map[string]string{"user": p.User}
|
||||||
|
return []PidsTags{{pids, tags}}, nil
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("no filter option set")
|
||||||
|
}
|
||||||
|
|
||||||
f, err := p.getPIDFinder()
|
func (p *Procstat) findSupervisorUnits() ([]PidsTags, error) {
|
||||||
|
groups, groupsTags, err := p.supervisorPIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pidTags = append(pidTags, PidsTags{nil, nil, err})
|
return nil, fmt.Errorf("getting supervisor PIDs failed: %w", err)
|
||||||
return pidTags
|
|
||||||
}
|
|
||||||
pids, tags, err := p.SimpleFindPids(f)
|
|
||||||
pidTags = append(pidTags, PidsTags{pids, tags, err})
|
|
||||||
|
|
||||||
return pidTags
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get matching PIDs and their initial tags
|
|
||||||
func (p *Procstat) SimpleFindPids(f PIDFinder) ([]PID, map[string]string, error) {
|
|
||||||
var pids []PID
|
|
||||||
tags := make(map[string]string)
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if p.PidFile != "" {
|
|
||||||
pids, err = f.PidFile(p.PidFile)
|
|
||||||
tags = map[string]string{"pidfile": p.PidFile}
|
|
||||||
} else if p.Exe != "" {
|
|
||||||
pids, err = f.Pattern(p.Exe)
|
|
||||||
tags = map[string]string{"exe": p.Exe}
|
|
||||||
} else if len(p.SupervisorUnit) > 0 && p.Pattern != "" {
|
|
||||||
pids, err = f.ChildPattern(p.Pattern)
|
|
||||||
tags = map[string]string{"pattern": p.Pattern, "parent_pid": p.Pattern}
|
|
||||||
} else if p.Pattern != "" {
|
|
||||||
pids, err = f.FullPattern(p.Pattern)
|
|
||||||
tags = map[string]string{"pattern": p.Pattern}
|
|
||||||
} else if p.User != "" {
|
|
||||||
pids, err = f.UID(p.User)
|
|
||||||
tags = map[string]string{"user": p.User}
|
|
||||||
} else if p.WinService != "" {
|
|
||||||
pids, err = p.winServicePIDs()
|
|
||||||
tags = map[string]string{"win_service": p.WinService}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("either exe, pid_file, user, pattern, systemd_unit, cgroup, or win_service must be specified")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pids, tags, err
|
// According to the PID, find the system process number and get the child processes
|
||||||
}
|
pidTags := make([]PidsTags, 0, len(groups))
|
||||||
|
for _, group := range groups {
|
||||||
|
grppid := groupsTags[group]["pid"]
|
||||||
|
if grppid == "" {
|
||||||
|
pidTags = append(pidTags, PidsTags{nil, groupsTags[group]})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// execCommand is so tests can mock out exec.Command usage.
|
pid, err := strconv.ParseInt(grppid, 10, 32)
|
||||||
var execCommand = exec.Command
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("converting PID %q failed: %w", grppid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all children of the supervisor unit
|
||||||
|
pids, err := p.finder.Children(PID(pid))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting children for %d failed: %w", pid, err)
|
||||||
|
}
|
||||||
|
tags := map[string]string{"pattern": p.Pattern, "parent_pid": p.Pattern}
|
||||||
|
|
||||||
|
// Handle situations where the PID does not exist
|
||||||
|
if len(pids) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge tags map
|
||||||
|
for k, v := range groupsTags[group] {
|
||||||
|
_, ok := tags[k]
|
||||||
|
if !ok {
|
||||||
|
tags[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove duplicate pid tags
|
||||||
|
delete(tags, "pid")
|
||||||
|
pidTags = append(pidTags, PidsTags{pids, tags})
|
||||||
|
}
|
||||||
|
return pidTags, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Procstat) supervisorPIDs() ([]string, map[string]map[string]string, error) {
|
func (p *Procstat) supervisorPIDs() ([]string, map[string]map[string]string, error) {
|
||||||
out, err := execCommand("supervisorctl", "status", strings.Join(p.SupervisorUnit, " ")).Output()
|
out, err := execCommand("supervisorctl", "status", strings.Join(p.SupervisorUnit, " ")).Output()
|
||||||
|
|
@ -494,7 +329,7 @@ func (p *Procstat) supervisorPIDs() ([]string, map[string]map[string]string, err
|
||||||
return p.SupervisorUnit, mainPids, nil
|
return p.SupervisorUnit, mainPids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) systemdUnitPIDs() []PidsTags {
|
func (p *Procstat) systemdUnitPIDs() ([]PidsTags, error) {
|
||||||
if p.IncludeSystemdChildren {
|
if p.IncludeSystemdChildren {
|
||||||
p.CGroup = fmt.Sprintf("systemd/system.slice/%s", p.SystemdUnits)
|
p.CGroup = fmt.Sprintf("systemd/system.slice/%s", p.SystemdUnits)
|
||||||
return p.cgroupPIDs()
|
return p.cgroupPIDs()
|
||||||
|
|
@ -502,9 +337,12 @@ func (p *Procstat) systemdUnitPIDs() []PidsTags {
|
||||||
|
|
||||||
var pidTags []PidsTags
|
var pidTags []PidsTags
|
||||||
pids, err := p.simpleSystemdUnitPIDs()
|
pids, err := p.simpleSystemdUnitPIDs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
tags := map[string]string{"systemd_unit": p.SystemdUnits}
|
tags := map[string]string{"systemd_unit": p.SystemdUnits}
|
||||||
pidTags = append(pidTags, PidsTags{pids, tags, err})
|
pidTags = append(pidTags, PidsTags{pids, tags})
|
||||||
return pidTags
|
return pidTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) simpleSystemdUnitPIDs() ([]PID, error) {
|
func (p *Procstat) simpleSystemdUnitPIDs() ([]PID, error) {
|
||||||
|
|
@ -536,7 +374,7 @@ func (p *Procstat) simpleSystemdUnitPIDs() ([]PID, error) {
|
||||||
return pids, nil
|
return pids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) cgroupPIDs() []PidsTags {
|
func (p *Procstat) cgroupPIDs() ([]PidsTags, error) {
|
||||||
procsPath := p.CGroup
|
procsPath := p.CGroup
|
||||||
if procsPath[0] != '/' {
|
if procsPath[0] != '/' {
|
||||||
procsPath = "/sys/fs/cgroup/" + procsPath
|
procsPath = "/sys/fs/cgroup/" + procsPath
|
||||||
|
|
@ -544,17 +382,20 @@ func (p *Procstat) cgroupPIDs() []PidsTags {
|
||||||
|
|
||||||
items, err := filepath.Glob(procsPath)
|
items, err := filepath.Glob(procsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []PidsTags{{nil, nil, fmt.Errorf("glob failed: %w", err)}}
|
return nil, fmt.Errorf("glob failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pidTags := make([]PidsTags, 0, len(items))
|
pidTags := make([]PidsTags, 0, len(items))
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
pids, err := p.singleCgroupPIDs(item)
|
pids, err := p.singleCgroupPIDs(item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
tags := map[string]string{"cgroup": p.CGroup, "cgroup_full": item}
|
tags := map[string]string{"cgroup": p.CGroup, "cgroup_full": item}
|
||||||
pidTags = append(pidTags, PidsTags{pids, tags, err})
|
pidTags = append(pidTags, PidsTags{pids, tags})
|
||||||
}
|
}
|
||||||
|
|
||||||
return pidTags
|
return pidTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Procstat) singleCgroupPIDs(path string) ([]PID, error) {
|
func (p *Procstat) singleCgroupPIDs(path string) ([]PID, error) {
|
||||||
|
|
@ -610,6 +451,6 @@ func (p *Procstat) winServicePIDs() ([]PID, error) {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("procstat", func() telegraf.Input {
|
inputs.Add("procstat", func() telegraf.Input {
|
||||||
return &Procstat{createProcess: NewProc}
|
return &Procstat{createProcess: newProc}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,11 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
|
||||||
"github.com/shirou/gopsutil/v3/process"
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -78,12 +79,10 @@ type testPgrep struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func pidFinder(pids []PID) func() (PIDFinder, error) {
|
func newTestFinder(pids []PID) PIDFinder {
|
||||||
return func() (PIDFinder, error) {
|
return &testPgrep{
|
||||||
return &testPgrep{
|
pids: pids,
|
||||||
pids: pids,
|
err: nil,
|
||||||
err: nil,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +106,7 @@ func (pg *testPgrep) FullPattern(_ string) ([]PID, error) {
|
||||||
return pg.pids, pg.err
|
return pg.pids, pg.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pg *testPgrep) ChildPattern(_ string) ([]PID, error) {
|
func (pg *testPgrep) Children(_ PID) ([]PID, error) {
|
||||||
pids := []PID{7311, 8111, 8112}
|
pids := []PID{7311, 8111, 8112}
|
||||||
return pids, pg.err
|
return pids, pg.err
|
||||||
}
|
}
|
||||||
|
|
@ -128,77 +127,75 @@ func (p *testProc) PID() PID {
|
||||||
return p.pid
|
return p.pid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testProc) Username() (string, error) {
|
func (p *testProc) Name() (string, error) {
|
||||||
return "testuser", nil
|
return "test_proc", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testProc) Tags() map[string]string {
|
func (p *testProc) SetTag(k, v string) {
|
||||||
return p.tags
|
p.tags[k] = v
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) PageFaults() (*process.PageFaultsStat, error) {
|
|
||||||
return &process.PageFaultsStat{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) IOCounters() (*process.IOCountersStat, error) {
|
|
||||||
return &process.IOCountersStat{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) MemoryInfo() (*process.MemoryInfoStat, error) {
|
|
||||||
return &process.MemoryInfoStat{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testProc) MemoryMaps(bool) (*[]process.MemoryMapsStat, error) {
|
func (p *testProc) MemoryMaps(bool) (*[]process.MemoryMapsStat, error) {
|
||||||
return &[]process.MemoryMapsStat{}, nil
|
return &[]process.MemoryMapsStat{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testProc) Name() (string, error) {
|
func (p *testProc) Metric(prefix string, cmdLineTag, _ bool) telegraf.Metric {
|
||||||
return "test_proc", nil
|
if prefix != "" {
|
||||||
}
|
prefix += "_"
|
||||||
|
}
|
||||||
|
|
||||||
func (p *testProc) NumCtxSwitches() (*process.NumCtxSwitchesStat, error) {
|
fields := map[string]interface{}{
|
||||||
return &process.NumCtxSwitchesStat{}, nil
|
"pid": int32(p.pid),
|
||||||
}
|
"ppid": int32(0),
|
||||||
|
prefix + "num_fds": int32(0),
|
||||||
|
prefix + "num_threads": int32(0),
|
||||||
|
prefix + "voluntary_context_switches": int64(0),
|
||||||
|
prefix + "involuntary_context_switches": int64(0),
|
||||||
|
prefix + "minor_faults": uint64(0),
|
||||||
|
prefix + "major_faults": uint64(0),
|
||||||
|
prefix + "child_major_faults": uint64(0),
|
||||||
|
prefix + "child_minor_faults": uint64(0),
|
||||||
|
prefix + "read_bytes": uint64(0),
|
||||||
|
prefix + "read_count": uint64(0),
|
||||||
|
prefix + "write_bytes": uint64(0),
|
||||||
|
prefix + "write_count": uint64(0),
|
||||||
|
prefix + "created_at": int64(0),
|
||||||
|
prefix + "cpu_time_user": float64(0),
|
||||||
|
prefix + "cpu_time_system": float64(0),
|
||||||
|
prefix + "cpu_time_iowait": float64(0),
|
||||||
|
prefix + "cpu_usage": float64(0),
|
||||||
|
prefix + "memory_rss": uint64(0),
|
||||||
|
prefix + "memory_vms": uint64(0),
|
||||||
|
prefix + "memory_usage": float32(0),
|
||||||
|
prefix + "status": "running",
|
||||||
|
}
|
||||||
|
|
||||||
func (p *testProc) NumFDs() (int32, error) {
|
tags := map[string]string{
|
||||||
return 0, nil
|
"process_name": "test_proc",
|
||||||
}
|
"user": "testuser",
|
||||||
|
}
|
||||||
|
for k, v := range p.tags {
|
||||||
|
tags[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
func (p *testProc) NumThreads() (int32, error) {
|
if cmdLineTag {
|
||||||
return 0, nil
|
tags["cmdline"] = "test_proc"
|
||||||
}
|
}
|
||||||
|
return metric.New("procstat", tags, fields, time.Time{})
|
||||||
func (p *testProc) Percent(_ time.Duration) (float64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) MemoryPercent() (float32, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) CreateTime() (int64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) Times() (*cpu.TimesStat, error) {
|
|
||||||
return &cpu.TimesStat{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) RlimitUsage(_ bool) ([]process.RlimitStat, error) {
|
|
||||||
return []process.RlimitStat{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) Ppid() (int32, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *testProc) Status() ([]string, error) {
|
|
||||||
return []string{"running"}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pid = PID(42)
|
var pid = PID(42)
|
||||||
var exe = "foo"
|
var exe = "foo"
|
||||||
|
|
||||||
|
func TestInitInvalidFinder(t *testing.T) {
|
||||||
|
plugin := Procstat{
|
||||||
|
PidFinder: "foo",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
createProcess: newTestProc,
|
||||||
|
}
|
||||||
|
require.Error(t, plugin.Init())
|
||||||
|
}
|
||||||
|
|
||||||
func TestInitRequiresChildDarwin(t *testing.T) {
|
func TestInitRequiresChildDarwin(t *testing.T) {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
t.Skip("Skipping test on non-darwin platform")
|
t.Skip("Skipping test on non-darwin platform")
|
||||||
|
|
@ -208,196 +205,228 @@ func TestInitRequiresChildDarwin(t *testing.T) {
|
||||||
Pattern: "somepattern",
|
Pattern: "somepattern",
|
||||||
SupervisorUnit: []string{"a_unit"},
|
SupervisorUnit: []string{"a_unit"},
|
||||||
PidFinder: "native",
|
PidFinder: "native",
|
||||||
|
Log: testutil.Logger{},
|
||||||
}
|
}
|
||||||
require.ErrorContains(t, p.Init(), "requires the 'pgrep' finder")
|
require.ErrorContains(t, p.Init(), "requires the 'pgrep' finder")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_CreateProcessErrorOk(t *testing.T) {
|
func TestInitMissingPidMethod(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Log: testutil.Logger{},
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
createProcess: newTestProc,
|
||||||
|
}
|
||||||
|
require.ErrorContains(t, p.Init(), "require filter option but none set")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGather_CreateProcessErrorOk(t *testing.T) {
|
||||||
|
p := Procstat{
|
||||||
|
Exe: exe,
|
||||||
|
PidFinder: "test",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
createProcess: func(PID) (Process, error) {
|
createProcess: func(PID) (Process, error) {
|
||||||
return nil, fmt.Errorf("createProcess error")
|
return nil, fmt.Errorf("createProcess error")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
}
|
|
||||||
|
|
||||||
func TestGather_CreatePIDFinderError(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
p := Procstat{
|
|
||||||
createPIDFinder: func() (PIDFinder, error) {
|
|
||||||
return nil, fmt.Errorf("createPIDFinder error")
|
|
||||||
},
|
|
||||||
createProcess: newTestProc,
|
|
||||||
}
|
|
||||||
require.Error(t, acc.GatherError(p.Gather))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_ProcessName(t *testing.T) {
|
func TestGather_ProcessName(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
ProcessName: "custom_name",
|
ProcessName: "custom_name",
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, "custom_name", acc.TagValue("procstat", "process_name"))
|
require.Equal(t, "custom_name", acc.TagValue("procstat", "process_name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_NoProcessNameUsesReal(t *testing.T) {
|
func TestGather_NoProcessNameUsesReal(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pid := PID(os.Getpid())
|
pid := PID(os.Getpid())
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.True(t, acc.HasTag("procstat", "process_name"))
|
require.True(t, acc.HasTag("procstat", "process_name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_NoPidTag(t *testing.T) {
|
func TestGather_NoPidTag(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
require.True(t, acc.HasInt32Field("procstat", "pid"))
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
|
require.True(t, acc.HasInt64Field("procstat", "pid"))
|
||||||
require.False(t, acc.HasTag("procstat", "pid"))
|
require.False(t, acc.HasTag("procstat", "pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_PidTag(t *testing.T) {
|
func TestGather_PidTag(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
PidTag: true,
|
PidTag: true,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, "42", acc.TagValue("procstat", "pid"))
|
require.Equal(t, "42", acc.TagValue("procstat", "pid"))
|
||||||
require.False(t, acc.HasInt32Field("procstat", "pid"))
|
require.False(t, acc.HasInt32Field("procstat", "pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_Prefix(t *testing.T) {
|
func TestGather_Prefix(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
Prefix: "custom_prefix",
|
Prefix: "custom_prefix",
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
require.True(t, acc.HasInt32Field("procstat", "custom_prefix_num_fds"))
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
|
require.True(t, acc.HasInt64Field("procstat", "custom_prefix_num_fds"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_Exe(t *testing.T) {
|
func TestGather_Exe(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Exe: exe,
|
Exe: exe,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, exe, acc.TagValue("procstat", "exe"))
|
require.Equal(t, exe, acc.TagValue("procstat", "exe"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_User(t *testing.T) {
|
func TestGather_User(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
user := "ada"
|
user := "ada"
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
User: user,
|
User: user,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, user, acc.TagValue("procstat", "user"))
|
require.Equal(t, user, acc.TagValue("procstat", "user"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_Pattern(t *testing.T) {
|
func TestGather_Pattern(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pattern := "foo"
|
pattern := "foo"
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Pattern: pattern,
|
Pattern: pattern,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, pattern, acc.TagValue("procstat", "pattern"))
|
require.Equal(t, pattern, acc.TagValue("procstat", "pattern"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_MissingPidMethod(t *testing.T) {
|
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
p := Procstat{
|
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
|
||||||
createProcess: newTestProc,
|
|
||||||
}
|
|
||||||
require.Error(t, acc.GatherError(p.Gather))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGather_PidFile(t *testing.T) {
|
func TestGather_PidFile(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pidfile := "/path/to/pidfile"
|
pidfile := "/path/to/pidfile"
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
PidFile: pidfile,
|
PidFile: pidfile,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.Equal(t, pidfile, acc.TagValue("procstat", "pidfile"))
|
require.Equal(t, pidfile, acc.TagValue("procstat", "pidfile"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_PercentFirstPass(t *testing.T) {
|
func TestGather_PercentFirstPass(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pid := PID(os.Getpid())
|
pid := PID(os.Getpid())
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Pattern: "foo",
|
Pattern: "foo",
|
||||||
PidTag: true,
|
PidTag: true,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: NewProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
|
require.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
|
||||||
require.False(t, acc.HasFloatField("procstat", "cpu_usage"))
|
require.False(t, acc.HasFloatField("procstat", "cpu_usage"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_PercentSecondPass(t *testing.T) {
|
func TestGather_PercentSecondPass(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pid := PID(os.Getpid())
|
pid := PID(os.Getpid())
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
Pattern: "foo",
|
Pattern: "foo",
|
||||||
PidTag: true,
|
PidTag: true,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: NewProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
require.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
|
require.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
|
||||||
require.True(t, acc.HasFloatField("procstat", "cpu_usage"))
|
require.True(t, acc.HasFloatField("procstat", "cpu_usage"))
|
||||||
|
|
@ -405,17 +434,19 @@ func TestGather_PercentSecondPass(t *testing.T) {
|
||||||
|
|
||||||
func TestGather_systemdUnitPIDs(t *testing.T) {
|
func TestGather_systemdUnitPIDs(t *testing.T) {
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
createPIDFinder: pidFinder([]PID{}),
|
SystemdUnits: "TestGather_systemdUnitPIDs",
|
||||||
SystemdUnits: "TestGather_systemdUnitPIDs",
|
PidFinder: "test",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
}
|
}
|
||||||
pidsTags := p.findPids()
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
pidsTags, err := p.findPids()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, pidsTag := range pidsTags {
|
for _, pidsTag := range pidsTags {
|
||||||
pids := pidsTag.PIDS
|
require.Equal(t, []PID{11408}, pidsTag.PIDs)
|
||||||
tags := pidsTag.Tags
|
require.Equal(t, "TestGather_systemdUnitPIDs", pidsTag.Tags["systemd_unit"])
|
||||||
err := pidsTag.Err
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, []PID{11408}, pids)
|
|
||||||
require.Equal(t, "TestGather_systemdUnitPIDs", tags["systemd_unit"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -429,41 +460,50 @@ func TestGather_cgroupPIDs(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
createPIDFinder: pidFinder([]PID{}),
|
CGroup: td,
|
||||||
CGroup: td,
|
PidFinder: "test",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
}
|
}
|
||||||
pidsTags := p.findPids()
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
pidsTags, err := p.findPids()
|
||||||
|
require.NoError(t, err)
|
||||||
for _, pidsTag := range pidsTags {
|
for _, pidsTag := range pidsTags {
|
||||||
pids := pidsTag.PIDS
|
require.Equal(t, []PID{1234, 5678}, pidsTag.PIDs)
|
||||||
tags := pidsTag.Tags
|
require.Equal(t, td, pidsTag.Tags["cgroup"])
|
||||||
err := pidsTag.Err
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, []PID{1234, 5678}, pids)
|
|
||||||
require.Equal(t, td, tags["cgroup"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcstatLookupMetric(t *testing.T) {
|
func TestProcstatLookupMetric(t *testing.T) {
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
createPIDFinder: pidFinder([]PID{543}),
|
Exe: "-Gsys",
|
||||||
createProcess: NewProc,
|
PidFinder: "test",
|
||||||
Exe: "-Gsys",
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{543}),
|
||||||
|
createProcess: newProc,
|
||||||
}
|
}
|
||||||
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Gather(&acc))
|
||||||
require.Len(t, acc.Metrics, len(p.procs)+1)
|
require.Len(t, acc.GetTelegrafMetrics(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_SameTimestamps(t *testing.T) {
|
func TestGather_SameTimestamps(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
|
||||||
pidfile := "/path/to/pidfile"
|
pidfile := "/path/to/pidfile"
|
||||||
|
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
PidFile: pidfile,
|
PidFile: pidfile,
|
||||||
createPIDFinder: pidFinder([]PID{pid}),
|
PidFinder: "test",
|
||||||
createProcess: newTestProc,
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
|
createProcess: newTestProc,
|
||||||
}
|
}
|
||||||
require.NoError(t, acc.GatherError(p.Gather))
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
procstat, _ := acc.Get("procstat")
|
procstat, _ := acc.Get("procstat")
|
||||||
procstatLookup, _ := acc.Get("procstat_lookup")
|
procstatLookup, _ := acc.Get("procstat_lookup")
|
||||||
|
|
@ -473,40 +513,42 @@ func TestGather_SameTimestamps(t *testing.T) {
|
||||||
|
|
||||||
func TestGather_supervisorUnitPIDs(t *testing.T) {
|
func TestGather_supervisorUnitPIDs(t *testing.T) {
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
createPIDFinder: pidFinder([]PID{}),
|
SupervisorUnit: []string{"TestGather_supervisorUnitPIDs"},
|
||||||
SupervisorUnit: []string{"TestGather_supervisorUnitPIDs"},
|
PidFinder: "test",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
}
|
}
|
||||||
pidsTags := p.findPids()
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
pidsTags, err := p.findPids()
|
||||||
|
require.NoError(t, err)
|
||||||
for _, pidsTag := range pidsTags {
|
for _, pidsTag := range pidsTags {
|
||||||
pids := pidsTag.PIDS
|
require.Equal(t, []PID{7311, 8111, 8112}, pidsTag.PIDs)
|
||||||
tags := pidsTag.Tags
|
require.Equal(t, "TestGather_supervisorUnitPIDs", pidsTag.Tags["supervisor_unit"])
|
||||||
err := pidsTag.Err
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, []PID{7311, 8111, 8112}, pids)
|
|
||||||
require.Equal(t, "TestGather_supervisorUnitPIDs", tags["supervisor_unit"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather_MoresupervisorUnitPIDs(t *testing.T) {
|
func TestGather_MoresupervisorUnitPIDs(t *testing.T) {
|
||||||
p := Procstat{
|
p := Procstat{
|
||||||
createPIDFinder: pidFinder([]PID{}),
|
SupervisorUnit: []string{"TestGather_STARTINGsupervisorUnitPIDs", "TestGather_FATALsupervisorUnitPIDs"},
|
||||||
Pattern: "7311",
|
PidFinder: "test",
|
||||||
SupervisorUnit: []string{"TestGather_STARTINGsupervisorUnitPIDs", "TestGather_FATALsupervisorUnitPIDs"},
|
Log: testutil.Logger{},
|
||||||
|
finder: newTestFinder([]PID{pid}),
|
||||||
}
|
}
|
||||||
pidsTags := p.findPids()
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
pidsTags, err := p.findPids()
|
||||||
|
require.NoError(t, err)
|
||||||
for _, pidsTag := range pidsTags {
|
for _, pidsTag := range pidsTags {
|
||||||
pids := pidsTag.PIDS
|
require.Empty(t, pidsTag.PIDs)
|
||||||
tags := pidsTag.Tags
|
switch pidsTag.Tags["supervisor_unit"] {
|
||||||
err := pidsTag.Err
|
case "TestGather_STARTINGsupervisorUnitPIDs":
|
||||||
require.Empty(t, pids)
|
require.Equal(t, "STARTING", pidsTag.Tags["status"])
|
||||||
require.Contains(t, []string{"TestGather_STARTINGsupervisorUnitPIDs", "TestGather_FATALsupervisorUnitPIDs"}, tags["supervisor_unit"])
|
case "TestGather_FATALsupervisorUnitPIDs":
|
||||||
if tags["supervisor_unit"] == "TestGather_STARTINGsupervisorUnitPIDs" {
|
require.Equal(t, "FATAL", pidsTag.Tags["status"])
|
||||||
require.Equal(t, "STARTING", tags["status"])
|
require.Equal(t, "Exited too quickly (process log may have details)", pidsTag.Tags["error"])
|
||||||
require.NoError(t, err)
|
default:
|
||||||
} else if tags["supervisor_unit"] == "TestGather_FATALsupervisorUnitPIDs" {
|
t.Fatalf("unexpected value for tag 'supervisor_unit': %q", pidsTag.Tags["supervisor_unit"])
|
||||||
require.Equal(t, "FATAL", tags["status"])
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "Exited too quickly (process log may have details)", tags["error"])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
//go:build !windows
|
|
||||||
|
|
||||||
package procstat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func queryPidWithWinServiceName(_ string) (uint32, error) {
|
|
||||||
return 0, fmt.Errorf("os not support win_service option")
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue