diff --git a/README.md b/README.md index fe53ad3..91efd89 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,9 @@ Template variables available: will only contain a single process. - `{{.StartTime}}` contains the start time of the process. This can be useful in conjunction with PID because PIDs get reused over time. +- `{{.Cgroups}}` contains (if supported) the cgroups of the process + (`/proc/self/cgroup`). This is particularly useful for identifying to which container + a process belongs. Using `PID` or `StartTime` is discouraged: this is almost never what you want, and is likely to result in high cardinality metrics which Prometheus will have diff --git a/common.go b/common.go index e818cd1..eca19f6 100644 --- a/common.go +++ b/common.go @@ -9,6 +9,7 @@ type ( ProcAttributes struct { Name string Cmdline []string + Cgroups []string Username string PID int StartTime time.Time diff --git a/config/config.go b/config/config.go index ec8f104..ebb23a5 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,7 @@ type ( } templateParams struct { + Cgroups []string Comm string ExeBase string ExeFull string @@ -116,6 +117,7 @@ func (m *matchNamer) MatchAndName(nacl common.ProcAttributes) (bool, string) { var buf bytes.Buffer m.template.Execute(&buf, &templateParams{ Comm: nacl.Name, + Cgroups: nacl.Cgroups, ExeBase: exebase, ExeFull: exefull, Matches: matches, diff --git a/fixtures/14804/cgroup b/fixtures/14804/cgroup new file mode 100644 index 0000000..27413f6 --- /dev/null +++ b/fixtures/14804/cgroup @@ -0,0 +1 @@ +0::/system.slice/docker-8dde0b0d6e919baef8d635cd9399b22639ed1e400eaec1b1cb94ff3b216cf3c3.scope diff --git a/go.mod b/go.mod index 0c94154..666db4f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.26.0 - github.com/prometheus/procfs v0.6.0 + github.com/prometheus/procfs v0.7.1 github.com/rogpeppe/go-internal v1.8.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index dfe5c86..5060f77 100644 --- a/go.sum +++ b/go.sum @@ -259,6 +259,8 @@ github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULU github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.1 h1:TlEtJq5GvGqMykEwWzbZWjjztF86swFhsPix1i0bkgA= +github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/proc/base_test.go b/proc/base_test.go index 976f9a1..7302165 100644 --- a/proc/base_test.go +++ b/proc/base_test.go @@ -67,7 +67,7 @@ func (n namer) MatchAndName(nacl common.ProcAttributes) (bool, string) { func newProcIDStatic(pid, ppid int, startTime uint64, name string, cmdline []string) (ID, Static) { return ID{pid, startTime}, - Static{name, cmdline, ppid, time.Unix(int64(startTime), 0).UTC(), 1000} + Static{name, cmdline, []string{}, ppid, time.Unix(int64(startTime), 0).UTC(), 1000} } func newProc(pid int, name string, m Metrics) IDInfo { diff --git a/proc/read.go b/proc/read.go index 2b004bd..c1606ed 100644 --- a/proc/read.go +++ b/proc/read.go @@ -30,6 +30,7 @@ type ( Static struct { Name string Cmdline []string + Cgroups []string ParentPid int StartTime time.Time EffectiveUID int @@ -135,6 +136,7 @@ type ( stat *procfs.ProcStat status *procfs.ProcStatus cmdline []string + cgroups []procfs.Cgroup io *procfs.ProcIO fs *FS wchan *string @@ -299,6 +301,18 @@ func (p *proccache) getStatus() (procfs.ProcStatus, error) { return *p.status, nil } +func (p *proccache) getCgroups() ([]procfs.Cgroup, error) { + if p.cgroups == nil { + cgroups, err := p.Proc.Cgroups() + if err != nil { + return nil, err + } + p.cgroups = cgroups + } + + return p.cgroups, nil +} + // GetProcID implements Proc. func (p *proccache) GetProcID() (ID, error) { if p.procid == nil { @@ -367,6 +381,19 @@ func (p *proccache) GetStatic() (Static, error) { return Static{}, err } + // /proc//cgroup(s) is normally world-readable. + // However cgroups aren't always supported -> return an empty array in that + // case. + cgroups, err := p.getCgroups() + var cgroupsStr []string + if err != nil { + cgroupsStr = []string{} + } else { + for _, c := range cgroups { + cgroupsStr = append(cgroupsStr, c.Path) + } + } + effectiveUID, err := strconv.ParseInt(status.UIDs[1], 10, 64) if err != nil { return Static{}, err @@ -375,6 +402,7 @@ func (p *proccache) GetStatic() (Static, error) { return Static{ Name: stat.Comm, Cmdline: cmdline, + Cgroups: cgroupsStr, ParentPid: stat.PPID, StartTime: startTime, EffectiveUID: int(effectiveUID), diff --git a/proc/read_test.go b/proc/read_test.go index b2bb9ed..bddda5d 100644 --- a/proc/read_test.go +++ b/proc/read_test.go @@ -63,6 +63,7 @@ func TestReadFixture(t *testing.T) { wantstatic := Static{ Name: "process-exporte", Cmdline: []string{"./process-exporter", "-procnames", "bash"}, + Cgroups: []string{"/system.slice/docker-8dde0b0d6e919baef8d635cd9399b22639ed1e400eaec1b1cb94ff3b216cf3c3.scope"}, ParentPid: 10884, StartTime: stime, EffectiveUID: 1000, diff --git a/proc/tracker.go b/proc/tracker.go index f08901c..7b41a87 100644 --- a/proc/tracker.go +++ b/proc/tracker.go @@ -419,6 +419,7 @@ func (t *Tracker) Update(iter Iter) (CollectErrors, []Update, error) { nacl := common.ProcAttributes{ Name: idinfo.Name, Cmdline: idinfo.Cmdline, + Cgroups: idinfo.Cgroups, Username: t.lookupUid(idinfo.EffectiveUID), PID: idinfo.Pid, StartTime: idinfo.StartTime,