feat(inputs.procstat): Add option to select properties to collect (#15299)
This commit is contained in:
parent
2072deb34c
commit
a3b105e88e
|
|
@ -71,6 +71,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
## user -- username owning the process
|
||||
# tag_with = []
|
||||
|
||||
## Properties to collect
|
||||
## Available options are "cpu", "limits", "memory", "mmap"
|
||||
# properties = ["cpu", "limits", "memory", "mmap"]
|
||||
|
||||
## Method to use when finding process IDs. Can be one of 'pgrep', or
|
||||
## 'native'. The pgrep finder calls the pgrep executable in the PATH while
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type Process interface {
|
|||
Name() (string, error)
|
||||
SetTag(string, string)
|
||||
MemoryMaps(bool) (*[]process.MemoryMapsStat, error)
|
||||
Metric(prefix string, tagging map[string]bool, solarisMode bool) telegraf.Metric
|
||||
Metric(string, *collectionConfig) telegraf.Metric
|
||||
}
|
||||
|
||||
type PIDFinder interface {
|
||||
|
|
@ -66,7 +66,7 @@ func (p *Proc) percent(_ time.Duration) (float64, error) {
|
|||
}
|
||||
|
||||
// Add metrics a single Process
|
||||
func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool) telegraf.Metric {
|
||||
func (p *Proc) Metric(prefix string, cfg *collectionConfig) telegraf.Metric {
|
||||
if prefix != "" {
|
||||
prefix += "_"
|
||||
}
|
||||
|
|
@ -118,6 +118,7 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
fields[prefix+"created_at"] = createdAt * 1000000 // ms to ns
|
||||
}
|
||||
|
||||
if cfg.features["cpu"] {
|
||||
cpuTime, err := p.Times()
|
||||
if err == nil {
|
||||
fields[prefix+"cpu_time_user"] = cpuTime.User
|
||||
|
|
@ -127,27 +128,33 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
|
||||
cpuPerc, err := p.percent(time.Duration(0))
|
||||
if err == nil {
|
||||
if solarisMode {
|
||||
if cfg.solarisMode {
|
||||
fields[prefix+"cpu_usage"] = cpuPerc / float64(runtime.NumCPU())
|
||||
} else {
|
||||
fields[prefix+"cpu_usage"] = cpuPerc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This only returns values for RSS and VMS
|
||||
if cfg.features["memory"] {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.features["mmap"] {
|
||||
collectMemmap(p, prefix, fields)
|
||||
}
|
||||
|
||||
if cfg.features["limits"] {
|
||||
rlims, err := p.RlimitUsage(true)
|
||||
if err == nil {
|
||||
for _, rlim := range rlims {
|
||||
|
|
@ -186,18 +193,19 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the tags as requested by the user
|
||||
cmdline, err := p.Cmdline()
|
||||
if err == nil {
|
||||
if tagging["cmdline"] {
|
||||
if cfg.tagging["cmdline"] {
|
||||
p.tags["cmdline"] = cmdline
|
||||
} else {
|
||||
fields[prefix+"cmdline"] = cmdline
|
||||
}
|
||||
}
|
||||
|
||||
if tagging["pid"] {
|
||||
if cfg.tagging["pid"] {
|
||||
p.tags["pid"] = strconv.Itoa(int(p.Pid))
|
||||
} else {
|
||||
fields["pid"] = p.Pid
|
||||
|
|
@ -205,7 +213,7 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
|
||||
ppid, err := p.Ppid()
|
||||
if err == nil {
|
||||
if tagging["ppid"] {
|
||||
if cfg.tagging["ppid"] {
|
||||
p.tags["ppid"] = strconv.Itoa(int(ppid))
|
||||
} else {
|
||||
fields[prefix+"ppid"] = ppid
|
||||
|
|
@ -214,7 +222,7 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
|
||||
status, err := p.Status()
|
||||
if err == nil {
|
||||
if tagging["status"] {
|
||||
if cfg.tagging["status"] {
|
||||
p.tags["status"] = status[0]
|
||||
} else {
|
||||
fields[prefix+"status"] = status[0]
|
||||
|
|
@ -223,7 +231,7 @@ func (p *Proc) Metric(prefix string, tagging map[string]bool, solarisMode bool)
|
|||
|
||||
user, err := p.Username()
|
||||
if err == nil {
|
||||
if tagging["user"] {
|
||||
if cfg.tagging["user"] {
|
||||
p.tags["user"] = user
|
||||
} else {
|
||||
fields[prefix+"user"] = user
|
||||
|
|
|
|||
|
|
@ -26,10 +26,15 @@ var sampleConfig string
|
|||
|
||||
// execCommand is so tests can mock out exec.Command usage.
|
||||
var execCommand = exec.Command
|
||||
var availableTags = []string{"cmdline", "pid", "ppid", "status", "user"}
|
||||
|
||||
type PID int32
|
||||
|
||||
type collectionConfig struct {
|
||||
solarisMode bool
|
||||
tagging map[string]bool
|
||||
features map[string]bool
|
||||
}
|
||||
|
||||
type Procstat struct {
|
||||
PidFinder string `toml:"pid_finder"`
|
||||
PidFile string `toml:"pid_file"`
|
||||
|
|
@ -47,14 +52,14 @@ type Procstat struct {
|
|||
PidTag bool `toml:"pid_tag" deprecated:"1.29.0;use 'tag_with' instead"`
|
||||
WinService string `toml:"win_service"`
|
||||
Mode string `toml:"mode"`
|
||||
Properties []string `toml:"properties"`
|
||||
TagWith []string `toml:"tag_with"`
|
||||
Filter []Filter `toml:"filter"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
solarisMode bool
|
||||
finder PIDFinder
|
||||
processes map[PID]Process
|
||||
tagging map[string]bool
|
||||
cfg collectionConfig
|
||||
oldMode bool
|
||||
|
||||
createProcess func(PID) (Process, error)
|
||||
|
|
@ -75,9 +80,6 @@ func (*Procstat) SampleConfig() string {
|
|||
}
|
||||
|
||||
func (p *Procstat) Init() error {
|
||||
// Check solaris mode
|
||||
p.solarisMode = strings.EqualFold(p.Mode, "solaris")
|
||||
|
||||
// Keep the old settings for compatibility
|
||||
if p.PidTag && !choice.Contains("pid", p.TagWith) {
|
||||
p.TagWith = append(p.TagWith, "pid")
|
||||
|
|
@ -86,13 +88,29 @@ func (p *Procstat) Init() error {
|
|||
p.TagWith = append(p.TagWith, "cmdline")
|
||||
}
|
||||
|
||||
// Check tagging and setup LUT
|
||||
if err := choice.CheckSlice(p.TagWith, availableTags); err != nil {
|
||||
return fmt.Errorf("invalid tag_with setting: %w", err)
|
||||
}
|
||||
p.tagging = make(map[string]bool, len(p.TagWith))
|
||||
// Configure metric collection features
|
||||
p.cfg.solarisMode = strings.EqualFold(p.Mode, "solaris")
|
||||
|
||||
// Convert tagging settings
|
||||
p.cfg.tagging = make(map[string]bool, len(p.TagWith))
|
||||
for _, tag := range p.TagWith {
|
||||
p.tagging[tag] = true
|
||||
switch tag {
|
||||
case "cmdline", "pid", "ppid", "status", "user":
|
||||
default:
|
||||
return fmt.Errorf("invalid 'tag_with' setting %q", tag)
|
||||
}
|
||||
p.cfg.tagging[tag] = true
|
||||
}
|
||||
|
||||
// Convert collection properties
|
||||
p.cfg.features = make(map[string]bool, len(p.Properties))
|
||||
for _, prop := range p.Properties {
|
||||
switch prop {
|
||||
case "cpu", "limits", "memory", "mmap":
|
||||
default:
|
||||
return fmt.Errorf("invalid 'properties' setting %q", prop)
|
||||
}
|
||||
p.cfg.features[prop] = true
|
||||
}
|
||||
|
||||
// Check if we got any new-style configuration options and determine
|
||||
|
|
@ -234,7 +252,7 @@ func (p *Procstat) gatherOld(acc telegraf.Accumulator) error {
|
|||
p.processes[pid] = proc
|
||||
}
|
||||
running[pid] = true
|
||||
m := proc.Metric(p.Prefix, p.tagging, p.solarisMode)
|
||||
m := proc.Metric(p.Prefix, &p.cfg)
|
||||
m.SetTime(now)
|
||||
acc.AddMetric(m)
|
||||
}
|
||||
|
|
@ -333,7 +351,7 @@ func (p *Procstat) gatherNew(acc telegraf.Accumulator) error {
|
|||
p.processes[pid] = proc
|
||||
}
|
||||
running[pid] = true
|
||||
m := proc.Metric(p.Prefix, p.tagging, p.solarisMode)
|
||||
m := proc.Metric(p.Prefix, &p.cfg)
|
||||
m.SetTime(now)
|
||||
acc.AddMetric(m)
|
||||
}
|
||||
|
|
@ -624,6 +642,9 @@ func (p *Procstat) winServicePIDs() ([]PID, error) {
|
|||
|
||||
func init() {
|
||||
inputs.Add("procstat", func() telegraf.Input {
|
||||
return &Procstat{createProcess: newProc}
|
||||
return &Procstat{
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
createProcess: newProc,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ func (p *testProc) MemoryMaps(bool) (*[]process.MemoryMapsStat, error) {
|
|||
return &[]process.MemoryMapsStat{}, nil
|
||||
}
|
||||
|
||||
func (p *testProc) Metric(prefix string, tagging map[string]bool, _ bool) telegraf.Metric {
|
||||
func (p *testProc) Metric(prefix string, cfg *collectionConfig) telegraf.Metric {
|
||||
if prefix != "" {
|
||||
prefix += "_"
|
||||
}
|
||||
|
|
@ -161,13 +161,17 @@ func (p *testProc) Metric(prefix string, tagging map[string]bool, _ bool) telegr
|
|||
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),
|
||||
}
|
||||
if cfg.features["cpu"] {
|
||||
fields[prefix+"cpu_time_user"] = float64(0)
|
||||
fields[prefix+"cpu_time_system"] = float64(0)
|
||||
fields[prefix+"cpu_time_iowait"] = float64(0)
|
||||
fields[prefix+"cpu_usage"] = float64(0)
|
||||
}
|
||||
if cfg.features["memory"] {
|
||||
fields[prefix+"memory_rss"] = uint64(0)
|
||||
fields[prefix+"memory_vms"] = uint64(0)
|
||||
fields[prefix+"memory_usage"] = float32(0)
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
|
|
@ -178,31 +182,31 @@ func (p *testProc) Metric(prefix string, tagging map[string]bool, _ bool) telegr
|
|||
}
|
||||
|
||||
// Add the tags as requested by the user
|
||||
if tagging["cmdline"] {
|
||||
if cfg.tagging["cmdline"] {
|
||||
tags["cmdline"] = "test_proc"
|
||||
} else {
|
||||
fields[prefix+"cmdline"] = "test_proc"
|
||||
}
|
||||
|
||||
if tagging["pid"] {
|
||||
if cfg.tagging["pid"] {
|
||||
tags["pid"] = strconv.Itoa(int(p.pid))
|
||||
} else {
|
||||
fields["pid"] = int32(p.pid)
|
||||
}
|
||||
|
||||
if tagging["ppid"] {
|
||||
if cfg.tagging["ppid"] {
|
||||
tags["ppid"] = "0"
|
||||
} else {
|
||||
fields[prefix+"ppid"] = int32(0)
|
||||
}
|
||||
|
||||
if tagging["status"] {
|
||||
if cfg.tagging["status"] {
|
||||
tags["status"] = "running"
|
||||
} else {
|
||||
fields[prefix+"status"] = "running"
|
||||
}
|
||||
|
||||
if tagging["user"] {
|
||||
if cfg.tagging["user"] {
|
||||
tags["user"] = "testuser"
|
||||
} else {
|
||||
fields[prefix+"user"] = "testuser"
|
||||
|
|
@ -217,6 +221,7 @@ var exe = "foo"
|
|||
func TestInitInvalidFinder(t *testing.T) {
|
||||
plugin := Procstat{
|
||||
PidFinder: "foo",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
createProcess: newTestProc,
|
||||
}
|
||||
|
|
@ -232,6 +237,7 @@ func TestInitRequiresChildDarwin(t *testing.T) {
|
|||
Pattern: "somepattern",
|
||||
SupervisorUnits: []string{"a_unit"},
|
||||
PidFinder: "native",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
require.ErrorContains(t, p.Init(), "requires 'pgrep' finder")
|
||||
|
|
@ -239,6 +245,7 @@ func TestInitRequiresChildDarwin(t *testing.T) {
|
|||
|
||||
func TestInitMissingPidMethod(t *testing.T) {
|
||||
p := Procstat{
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
createProcess: newTestProc,
|
||||
}
|
||||
|
|
@ -267,6 +274,7 @@ func TestGather_CreateProcessErrorOk(t *testing.T) {
|
|||
p := Procstat{
|
||||
Exe: exe,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: func(PID) (Process, error) {
|
||||
|
|
@ -339,6 +347,7 @@ func TestGather_ProcessName(t *testing.T) {
|
|||
Exe: exe,
|
||||
ProcessName: "custom_name",
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -357,6 +366,7 @@ func TestGather_NoProcessNameUsesReal(t *testing.T) {
|
|||
p := Procstat{
|
||||
Exe: exe,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -373,6 +383,7 @@ func TestGather_NoPidTag(t *testing.T) {
|
|||
p := Procstat{
|
||||
Exe: exe,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -391,6 +402,7 @@ func TestGather_PidTag(t *testing.T) {
|
|||
Exe: exe,
|
||||
PidTag: true,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -409,6 +421,7 @@ func TestGather_Prefix(t *testing.T) {
|
|||
Exe: exe,
|
||||
Prefix: "custom_prefix",
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -425,6 +438,7 @@ func TestGather_Exe(t *testing.T) {
|
|||
p := Procstat{
|
||||
Exe: exe,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -443,6 +457,7 @@ func TestGather_User(t *testing.T) {
|
|||
p := Procstat{
|
||||
User: user,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -461,6 +476,7 @@ func TestGather_Pattern(t *testing.T) {
|
|||
p := Procstat{
|
||||
Pattern: pattern,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -479,6 +495,7 @@ func TestGather_PidFile(t *testing.T) {
|
|||
p := Procstat{
|
||||
PidFile: pidfile,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -498,6 +515,7 @@ func TestGather_PercentFirstPass(t *testing.T) {
|
|||
Pattern: "foo",
|
||||
PidTag: true,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newProc,
|
||||
|
|
@ -518,6 +536,7 @@ func TestGather_PercentSecondPass(t *testing.T) {
|
|||
Pattern: "foo",
|
||||
PidTag: true,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newProc,
|
||||
|
|
@ -536,6 +555,7 @@ func TestGather_systemdUnitPIDs(t *testing.T) {
|
|||
p := Procstat{
|
||||
SystemdUnit: "TestGather_systemdUnitPIDs",
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
}
|
||||
|
|
@ -562,6 +582,7 @@ func TestGather_cgroupPIDs(t *testing.T) {
|
|||
p := Procstat{
|
||||
CGroup: td,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
}
|
||||
|
|
@ -579,6 +600,7 @@ func TestProcstatLookupMetric(t *testing.T) {
|
|||
p := Procstat{
|
||||
Exe: "-Gsys",
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{543}),
|
||||
createProcess: newProc,
|
||||
|
|
@ -596,6 +618,7 @@ func TestGather_SameTimestamps(t *testing.T) {
|
|||
p := Procstat{
|
||||
PidFile: pidfile,
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
createProcess: newTestProc,
|
||||
|
|
@ -615,6 +638,7 @@ func TestGather_supervisorUnitPIDs(t *testing.T) {
|
|||
p := Procstat{
|
||||
SupervisorUnits: []string{"TestGather_supervisorUnitPIDs"},
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
}
|
||||
|
|
@ -632,6 +656,7 @@ func TestGather_MoresupervisorUnitPIDs(t *testing.T) {
|
|||
p := Procstat{
|
||||
SupervisorUnits: []string{"TestGather_STARTINGsupervisorUnitPIDs", "TestGather_FATALsupervisorUnitPIDs"},
|
||||
PidFinder: "test",
|
||||
Properties: []string{"cpu", "memory", "mmap"},
|
||||
Log: testutil.Logger{},
|
||||
finder: newTestFinder([]PID{pid}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
## user -- username owning the process
|
||||
# tag_with = []
|
||||
|
||||
## Properties to collect
|
||||
## Available options are "cpu", "limits", "memory", "mmap"
|
||||
# properties = ["cpu", "limits", "memory", "mmap"]
|
||||
|
||||
## Method to use when finding process IDs. Can be one of 'pgrep', or
|
||||
## 'native'. The pgrep finder calls the pgrep executable in the PATH while
|
||||
|
|
|
|||
Loading…
Reference in New Issue