Add systemd unit pid and cgroup matching to procstat (#3459)
This commit is contained in:
		
							parent
							
								
									ebd73b7279
								
							
						
					
					
						commit
						6ee6d55751
					
				|  | @ -6,11 +6,11 @@ The procstat plugin can be used to monitor system resource usage by an | ||||||
| individual process using their /proc data. | individual process using their /proc data. | ||||||
| 
 | 
 | ||||||
| Processes can be specified either by pid file, by executable name, by command | Processes can be specified either by pid file, by executable name, by command | ||||||
| line pattern matching, or by username (in this order or priority. Procstat | line pattern matching, by username, by systemd unit name, or by cgroup name/path | ||||||
| plugin will use `pgrep` when executable name is provided to obtain the pid. | (in this order or priority). Procstat plugin will use `pgrep` when executable | ||||||
| Procstat plugin will transmit IO, memory, cpu, file descriptor related | name is provided to obtain the pid. Procstat plugin will transmit IO, memory, | ||||||
| measurements for every process specified. A prefix can be set to isolate | cpu, file descriptor related measurements for every process specified. A prefix | ||||||
| individual process specific measurements. | can be set to isolate individual process specific measurements. | ||||||
| 
 | 
 | ||||||
| The plugin will tag processes according to how they are specified in the configuration. If a pid file is used, a "pidfile" tag will be generated. | The plugin will tag processes according to how they are specified in the configuration. If a pid file is used, a "pidfile" tag will be generated. | ||||||
| On the other hand, if an executable is used an "exe" tag will be generated. Possible tag names: | On the other hand, if an executable is used an "exe" tag will be generated. Possible tag names: | ||||||
|  | @ -19,6 +19,8 @@ On the other hand, if an executable is used an "exe" tag will be generated. Poss | ||||||
| * exe | * exe | ||||||
| * pattern | * pattern | ||||||
| * user | * user | ||||||
|  | * systemd_unit | ||||||
|  | * cgroup | ||||||
| 
 | 
 | ||||||
| Additionally the plugin will tag processes by their PID (pid_tag = true in the config) and their process name: | Additionally the plugin will tag processes by their PID (pid_tag = true in the config) and their process name: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| package procstat | package procstat | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os/exec" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -24,6 +27,8 @@ type Procstat struct { | ||||||
| 	Prefix      string | 	Prefix      string | ||||||
| 	ProcessName string | 	ProcessName string | ||||||
| 	User        string | 	User        string | ||||||
|  | 	SystemdUnit string | ||||||
|  | 	CGroup      string `toml:"cgroup"` | ||||||
| 	PidTag      bool | 	PidTag      bool | ||||||
| 
 | 
 | ||||||
| 	pidFinder       PIDFinder | 	pidFinder       PIDFinder | ||||||
|  | @ -42,6 +47,10 @@ var sampleConfig = ` | ||||||
|   # pattern = "nginx" |   # pattern = "nginx" | ||||||
|   ## user as argument for pgrep (ie, pgrep -u <user>) |   ## user as argument for pgrep (ie, pgrep -u <user>) | ||||||
|   # user = "nginx" |   # user = "nginx" | ||||||
|  |   ## Systemd unit name | ||||||
|  |   # systemd_unit = "nginx.service" | ||||||
|  | 	## CGroup name or path | ||||||
|  | 	# cgroup = "systemd/system.slice/nginx.service" | ||||||
| 
 | 
 | ||||||
|   ## override for process_name |   ## override for process_name | ||||||
|   ## This is optional; default is sourced from /proc/<pid>/status |   ## This is optional; default is sourced from /proc/<pid>/status | ||||||
|  | @ -275,6 +284,12 @@ func (p *Procstat) findPids() ([]PID, map[string]string, error) { | ||||||
| 	} else if p.User != "" { | 	} else if p.User != "" { | ||||||
| 		pids, err = f.Uid(p.User) | 		pids, err = f.Uid(p.User) | ||||||
| 		tags = map[string]string{"user": p.User} | 		tags = map[string]string{"user": p.User} | ||||||
|  | 	} else if p.SystemdUnit != "" { | ||||||
|  | 		pids, err = p.systemdUnitPIDs() | ||||||
|  | 		tags = map[string]string{"systemd_unit": p.SystemdUnit} | ||||||
|  | 	} else if p.CGroup != "" { | ||||||
|  | 		pids, err = p.cgroupPIDs() | ||||||
|  | 		tags = map[string]string{"cgroup": p.CGroup} | ||||||
| 	} else { | 	} else { | ||||||
| 		err = fmt.Errorf("Either exe, pid_file, user, or pattern has to be specified") | 		err = fmt.Errorf("Either exe, pid_file, user, or pattern has to be specified") | ||||||
| 	} | 	} | ||||||
|  | @ -282,6 +297,59 @@ func (p *Procstat) findPids() ([]PID, map[string]string, error) { | ||||||
| 	return pids, tags, err | 	return pids, tags, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *Procstat) systemdUnitPIDs() ([]PID, error) { | ||||||
|  | 	var pids []PID | ||||||
|  | 	cmd := exec.Command("systemctl", "show", p.SystemdUnit) | ||||||
|  | 	out, err := cmd.Output() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for _, line := range bytes.Split(out, []byte{'\n'}) { | ||||||
|  | 		kv := bytes.SplitN(line, []byte{'='}, 2) | ||||||
|  | 		if len(kv) != 2 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !bytes.Equal(kv[0], []byte("MainPID")) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if len(kv[1]) == 0 { | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  | 		pid, err := strconv.Atoi(string(kv[1])) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("invalid pid '%s'", kv[1]) | ||||||
|  | 		} | ||||||
|  | 		pids = append(pids, PID(pid)) | ||||||
|  | 	} | ||||||
|  | 	return pids, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Procstat) cgroupPIDs() ([]PID, error) { | ||||||
|  | 	var pids []PID | ||||||
|  | 
 | ||||||
|  | 	procsPath := p.CGroup | ||||||
|  | 	if procsPath[0] != '/' { | ||||||
|  | 		procsPath = "/sys/fs/cgroup/" + procsPath | ||||||
|  | 	} | ||||||
|  | 	procsPath = procsPath + "/cgroup.procs" | ||||||
|  | 	out, err := ioutil.ReadFile(procsPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for _, pidBS := range bytes.Split(out, []byte{'\n'}) { | ||||||
|  | 		if len(pidBS) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		pid, err := strconv.Atoi(string(pidBS)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("invalid pid '%s'", pidBS) | ||||||
|  | 		} | ||||||
|  | 		pids = append(pids, PID(pid)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return pids, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func init() { | func init() { | ||||||
| 	inputs.Add("procstat", func() telegraf.Input { | 	inputs.Add("procstat", func() telegraf.Input { | ||||||
| 		return &Procstat{} | 		return &Procstat{} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue