master
go 174 lines 4.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package supervisord
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
10 )
11
12 func (c *Collector) collect() (map[string]int64, error) {
13 info, err := c.client.getAllProcessInfo()
14 if err != nil {
15 return nil, err
16 }
17
18 ms := make(map[string]int64)
19 c.collectAllProcessInfo(ms, info)
20
21 return ms, nil
22 }
23
24 func (c *Collector) collectAllProcessInfo(ms map[string]int64, info []processStatus) {
25 c.resetCache()
26 ms["running_processes"] = 0
27 ms["non_running_processes"] = 0
28 for _, p := range info {
29 if _, ok := c.cache[p.group]; !ok {
30 c.cache[p.group] = make(map[string]bool)
31 c.addProcessGroupCharts(p)
32 }
33 if _, ok := c.cache[p.group][p.name]; !ok {
34 c.addProcessToCharts(p)
35 }
36 c.cache[p.group][p.name] = true
37
38 ms["group_"+p.group+"_running_processes"] += 0
39 ms["group_"+p.group+"_non_running_processes"] += 0
40 if isProcRunning(p) {
41 ms["running_processes"] += 1
42 ms["group_"+p.group+"_running_processes"] += 1
43 } else {
44 ms["non_running_processes"] += 1
45 ms["group_"+p.group+"_non_running_processes"] += 1
46 }
47 id := procID(p)
48 ms[id+"_state_code"] = int64(p.state)
49 ms[id+"_exit_status"] = int64(p.exitStatus)
50 ms[id+"_uptime"] = calcProcessUptime(p)
51 ms[id+"_downtime"] = calcProcessDowntime(p)
52 }
53 c.cleanupCache()
54 }
55
56 func (c *Collector) resetCache() {
57 for _, procs := range c.cache {
58 for name := range procs {
59 procs[name] = false
60 }
61 }
62 }
63
64 func (c *Collector) cleanupCache() {
65 for group, procs := range c.cache {
66 for name, ok := range procs {
67 if !ok {
68 c.removeProcessFromCharts(group, name)
69 delete(c.cache[group], name)
70 }
71 }
72 if len(c.cache[group]) == 0 {
73 c.removeProcessGroupCharts(group)
74 delete(c.cache, group)
75 }
76 }
77 }
78
79 func calcProcessUptime(p processStatus) int64 {
80 if !isProcRunning(p) {
81 return 0
82 }
83 return int64(p.now - p.start)
84 }
85
86 func calcProcessDowntime(p processStatus) int64 {
87 if isProcRunning(p) || p.stop == 0 {
88 return 0
89 }
90 return int64(p.now - p.stop)
91 }
92
93 func (c *Collector) addProcessGroupCharts(p processStatus) {
94 charts := newProcGroupCharts(p.group)
95 if err := c.Charts().Add(*charts...); err != nil {
96 c.Warning(err)
97 }
98 }
99
100 func (c *Collector) addProcessToCharts(p processStatus) {
101 id := procID(p)
102 for _, chart := range *c.Charts() {
103 var dimID string
104 switch chart.ID {
105 case fmt.Sprintf(groupProcessesStateCodeChartTmpl.ID, p.group):
106 dimID = id + "_state_code"
107 case fmt.Sprintf(groupProcessesExitStatusChartTmpl.ID, p.group):
108 dimID = id + "_exit_status"
109 case fmt.Sprintf(groupProcessesUptimeChartTmpl.ID, p.group):
110 dimID = id + "_uptime"
111 case fmt.Sprintf(groupProcessesDowntimeChartTmpl.ID, p.group):
112 dimID = id + "_downtime"
113 default:
114 continue
115 }
116 dim := &collectorapi.Dim{ID: dimID, Name: p.name}
117 if err := chart.AddDim(dim); err != nil {
118 c.Warning(err)
119 return
120 }
121 chart.MarkNotCreated()
122 }
123 }
124
125 func (c *Collector) removeProcessGroupCharts(group string) {
126 prefix := "group_" + group
127 for _, c := range *c.Charts() {
128 if strings.HasPrefix(c.ID, prefix) {
129 c.MarkRemove()
130 c.MarkNotCreated()
131 }
132 }
133 }
134
135 func (c *Collector) removeProcessFromCharts(group, name string) {
136 id := procID(processStatus{name: name, group: group})
137 for _, chart := range *c.Charts() {
138 var dimID string
139 switch chart.ID {
140 case fmt.Sprintf(groupProcessesStateCodeChartTmpl.ID, group):
141 dimID = id + "_state_code"
142 case fmt.Sprintf(groupProcessesExitStatusChartTmpl.ID, group):
143 dimID = id + "_exit_status"
144 case fmt.Sprintf(groupProcessesUptimeChartTmpl.ID, group):
145 dimID = id + "_uptime"
146 case fmt.Sprintf(groupProcessesDowntimeChartTmpl.ID, group):
147 dimID = id + "_downtime"
148 default:
149 continue
150 }
151 if err := chart.MarkDimRemove(dimID, true); err != nil {
152 c.Warning(err)
153 return
154 }
155 chart.MarkNotCreated()
156 }
157 }
158
159 func procID(p processStatus) string {
160 return fmt.Sprintf("group_%s_process_%s", p.group, p.name)
161 }
162
163 func isProcRunning(p processStatus) bool {
164 // http://supervisord.org/subprocess.html#process-states
165 // STOPPED (0)
166 // STARTING (10)
167 // RUNNING (20)
168 // BACKOFF (30)
169 // STOPPING (40)
170 // EXITED (100)
171 // FATAL (200)
172 // UNKNOWN (1000)
173 return p.state == 20
174 }