@@ -5,10 +5,12 @@ import (
55 "encoding/json"
66 "flag"
77 "fmt"
8+ "io/ioutil"
89 "log"
910 "net/http"
1011 "os"
1112 "os/signal"
13+ "path"
1214 "strconv"
1315 "strings"
1416 "syscall"
@@ -72,6 +74,34 @@ func main() {
7274 }
7375}
7476
77+ type ProcessStats struct {
78+ FdCount int `json:"fd_count"`
79+ SocketCount int `json:"socket_count"`
80+ }
81+
82+ func processStats (pid int ) * ProcessStats {
83+ dir := fmt .Sprintf ("/proc/%d/fd" , pid )
84+ fds , err := ioutil .ReadDir (dir )
85+ if err != nil {
86+ return nil
87+ }
88+ var socketCount int
89+ for _ , fd := range fds {
90+ fdPath := path .Join (dir , fd .Name ())
91+ linkName , err := os .Readlink (fdPath )
92+ if err != nil {
93+ continue
94+ }
95+ if strings .HasPrefix (linkName , "socket" ) {
96+ socketCount ++
97+ }
98+ }
99+ return & ProcessStats {
100+ FdCount : len (fds ),
101+ SocketCount : socketCount ,
102+ }
103+ }
104+
75105func subsystem () ([]cgroups.Subsystem , error ) {
76106 root := "/sys/fs/cgroup"
77107 s := []cgroups.Subsystem {
@@ -82,13 +112,18 @@ func subsystem() ([]cgroups.Subsystem, error) {
82112 return s , nil
83113}
84114
85- func statsCgroups (ctx context.Context , system cgroups.Cgroup ) (map [string ]* cgroups.Metrics , error ) {
115+ type cgroupMetrics struct {
116+ * cgroups.Metrics
117+ Process * ProcessStats `json:"process_stats"`
118+ }
119+
120+ func statsCgroups (ctx context.Context , system cgroups.Cgroup ) (map [string ]* cgroupMetrics , error ) {
86121 processes , err := system .Processes (cgroups .Devices , true )
87122 if err != nil {
88123 return nil , fmt .Errorf ("cgroups load: %s" , err )
89124 }
90125
91- groups := make (map [string ]* cgroups. Metrics , len (processes ))
126+ groups := make (map [string ]* cgroupMetrics , len (processes ))
92127 for _ , p := range processes {
93128 name := strings .TrimPrefix (p .Path , "/sys/fs/cgroup/devices" )
94129 name = strings .TrimSuffix (name , "/" )
@@ -108,15 +143,20 @@ func statsCgroups(ctx context.Context, system cgroups.Cgroup) (map[string]*cgrou
108143 log .Printf ("control stat: %s" , err )
109144 continue
110145 }
111- groups [name ] = stats
146+ ps := processStats (p .Pid )
147+ groups [name ] = & cgroupMetrics {
148+ Metrics : stats ,
149+ Process : ps ,
150+ }
112151 }
113152 return groups , nil
114153}
115154
116155type dockerStats struct {
117- CPU types.CPUStats `json:"cpu_stats,omitempty"`
118- PreCPU types.CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
119- Memory types.MemoryStats `json:"memory_stats,omitempty"`
156+ CPU types.CPUStats `json:"cpu_stats,omitempty"`
157+ PreCPU types.CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
158+ Memory types.MemoryStats `json:"memory_stats,omitempty"`
159+ Process * ProcessStats `json:"process_stats"`
120160}
121161
122162func statsDockerContainers (ctx context.Context , dockerClient * client.Client ) (map [string ]dockerStats , error ) {
@@ -145,6 +185,12 @@ func statsDockerContainers(ctx context.Context, dockerClient *client.Client) (ma
145185 }
146186 res .Body .Close ()
147187 name := fmt .Sprintf ("/docker%s" , strings .Join (container .Names , "/" ))
188+ inspect , err := dockerClient .ContainerInspect (ctx , container .ID )
189+ if err == nil {
190+ if inspect .State != nil {
191+ stats .Process = processStats (inspect .State .Pid )
192+ }
193+ }
148194 dockerContainers [name ] = stats
149195 }
150196
@@ -200,6 +246,49 @@ func exportMetrics(system cgroups.Cgroup, dockerClient *client.Client) func(w ht
200246 fmt .Fprintln (w )
201247 }
202248
249+ fmt .Fprintln (w , `# HELP container_open_fds Number of open file descriptors
250+ # TYPE container_open_fds gauge` )
251+ for name , stats := range groups {
252+ if stats .Process == nil {
253+ continue
254+ }
255+ fmt .Fprintf (w , `container_open_fds{id=%s} %d` , strconv .Quote (name ), stats .Process .FdCount )
256+ fmt .Fprintln (w )
257+ }
258+ for name , stats := range dockerContainers {
259+ if stats .Process == nil {
260+ continue
261+ }
262+ fmt .Fprintf (w , `container_open_fds{id=%s} %d` , strconv .Quote (name ), stats .Process .FdCount )
263+ fmt .Fprintln (w )
264+ }
265+
266+ fmt .Fprintln (w , `# HELP container_open_sockets Number of open sockets
267+ # TYPE container_open_sockets gauge` )
268+ for name , stats := range groups {
269+ if stats .Process == nil {
270+ continue
271+ }
272+ fmt .Fprintf (w , `container_open_sockets{id=%s} %d` , strconv .Quote (name ), stats .Process .SocketCount )
273+ fmt .Fprintln (w )
274+ }
275+ for name , stats := range dockerContainers {
276+ if stats .Process == nil {
277+ continue
278+ }
279+ fmt .Fprintf (w , `container_open_sockets{id=%s} %d` , strconv .Quote (name ), stats .Process .SocketCount )
280+ fmt .Fprintln (w )
281+ }
282+
283+ processStats := processStats (os .Getpid ())
284+ if processStats != nil {
285+ fmt .Fprintln (w , `# HELP process_open_fds Number of open file descriptors
286+ # TYPE process_open_fds gauge` )
287+ fmt .Fprintf (w , `process_open_fds %d` , processStats .FdCount )
288+ fmt .Fprintln (w )
289+ }
290+
203291 return
204292 }
293+
205294}
0 commit comments