| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | // Copyright 2015 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 04:35:24 -07:00
										 |  |  | //go:build !nosupervisord
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | // +build !nosupervisord
 | 
					
						
							| 
									
										
										
										
											2015-10-16 12:17:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | package collector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-01-27 23:50:23 -08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2020-01-27 23:50:23 -08:00
										 |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 00:25:05 -08:00
										 |  |  | 	"github.com/alecthomas/kingpin/v2" | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 	"github.com/mattn/go-xmlrpc" | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2021-01-27 10:04:52 -08:00
										 |  |  | 	supervisordURL = kingpin.Flag("collector.supervisord.url", "XML RPC endpoint.").Default("http://localhost:9001/RPC2").Envar("SUPERVISORD_URL").String() | 
					
						
							| 
									
										
										
										
											2020-01-27 23:50:23 -08:00
										 |  |  | 	xrpc           *xmlrpc.Client | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type supervisordCollector struct { | 
					
						
							|  |  |  | 	upDesc         *prometheus.Desc | 
					
						
							|  |  |  | 	stateDesc      *prometheus.Desc | 
					
						
							|  |  |  | 	exitStatusDesc *prometheus.Desc | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 	startTimeDesc  *prometheus.Desc | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	logger         *slog.Logger | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 	registerCollector("supervisord", defaultDisabled, NewSupervisordCollector) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 08:44:53 -08:00
										 |  |  | // NewSupervisordCollector returns a new Collector exposing supervisord statistics.
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | func NewSupervisordCollector(logger *slog.Logger) (Collector, error) { | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		subsystem  = "supervisord" | 
					
						
							|  |  |  | 		labelNames = []string{"name", "group"} | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2020-01-27 23:50:23 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if u, err := url.Parse(*supervisordURL); err == nil && u.Scheme == "unix" { | 
					
						
							|  |  |  | 		// Fake the URI scheme as http, since net/http.*Transport.roundTrip will complain
 | 
					
						
							|  |  |  | 		// about a non-http(s) transport.
 | 
					
						
							|  |  |  | 		xrpc = xmlrpc.NewClient("http://unix/RPC2") | 
					
						
							|  |  |  | 		xrpc.HttpClient.Transport = &http.Transport{ | 
					
						
							|  |  |  | 			DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { | 
					
						
							|  |  |  | 				d := net.Dialer{Timeout: 10 * time.Second} | 
					
						
							|  |  |  | 				return d.DialContext(ctx, "unix", u.Path) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		xrpc = xmlrpc.NewClient(*supervisordURL) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	logger.Warn("This collector is deprecated and will be removed in the next major version release.") | 
					
						
							| 
									
										
										
										
											2023-05-10 22:50:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	return &supervisordCollector{ | 
					
						
							|  |  |  | 		upDesc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 			prometheus.BuildFQName(namespace, subsystem, "up"), | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 			"Process Up", | 
					
						
							|  |  |  | 			labelNames, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		), | 
					
						
							|  |  |  | 		stateDesc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 			prometheus.BuildFQName(namespace, subsystem, "state"), | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 			"Process State", | 
					
						
							|  |  |  | 			labelNames, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		), | 
					
						
							|  |  |  | 		exitStatusDesc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 			prometheus.BuildFQName(namespace, subsystem, "exit_status"), | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 			"Process Exit Status", | 
					
						
							|  |  |  | 			labelNames, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		), | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 		startTimeDesc: prometheus.NewDesc( | 
					
						
							|  |  |  | 			prometheus.BuildFQName(namespace, subsystem, "start_time_seconds"), | 
					
						
							|  |  |  | 			"Process start time", | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 			labelNames, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		), | 
					
						
							| 
									
										
										
										
											2019-12-31 08:19:37 -08:00
										 |  |  | 		logger: logger, | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *supervisordCollector) isRunning(state int) bool { | 
					
						
							|  |  |  | 	// http://supervisord.org/subprocess.html#process-states
 | 
					
						
							|  |  |  | 	const ( | 
					
						
							| 
									
										
										
										
											2019-01-04 07:58:53 -08:00
										 |  |  | 		// STOPPED  = 0
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 		STARTING = 10 | 
					
						
							|  |  |  | 		RUNNING  = 20 | 
					
						
							| 
									
										
										
										
											2019-01-04 07:58:53 -08:00
										 |  |  | 		// BACKOFF  = 30
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 		STOPPING = 40 | 
					
						
							| 
									
										
										
										
											2019-01-04 07:58:53 -08:00
										 |  |  | 		// EXITED   = 100
 | 
					
						
							|  |  |  | 		// FATAL    = 200
 | 
					
						
							|  |  |  | 		// UNKNOWN  = 1000
 | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	switch state { | 
					
						
							|  |  |  | 	case STARTING, RUNNING, STOPPING: | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error { | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 	var info struct { | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 		Name          string `xmlrpc:"name"` | 
					
						
							|  |  |  | 		Group         string `xmlrpc:"group"` | 
					
						
							|  |  |  | 		Start         int    `xmlrpc:"start"` | 
					
						
							|  |  |  | 		Stop          int    `xmlrpc:"stop"` | 
					
						
							|  |  |  | 		Now           int    `xmlrpc:"now"` | 
					
						
							|  |  |  | 		State         int    `xmlrpc:"state"` | 
					
						
							|  |  |  | 		StateName     string `xmlrpc:"statename"` | 
					
						
							|  |  |  | 		SpawnErr      string `xmlrpc:"spanerr"` | 
					
						
							|  |  |  | 		ExitStatus    int    `xmlrpc:"exitstatus"` | 
					
						
							|  |  |  | 		StdoutLogfile string `xmlrcp:"stdout_logfile"` | 
					
						
							|  |  |  | 		StderrLogfile string `xmlrcp:"stderr_logfile"` | 
					
						
							|  |  |  | 		PID           int    `xmlrpc:"pid"` | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 23:50:23 -08:00
										 |  |  | 	res, err := xrpc.Call("supervisor.getAllProcessInfo") | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-15 13:27:14 -07:00
										 |  |  | 		return fmt.Errorf("unable to call supervisord: %w", err) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, p := range res.(xmlrpc.Array) { | 
					
						
							|  |  |  | 		for k, v := range p.(xmlrpc.Struct) { | 
					
						
							|  |  |  | 			switch k { | 
					
						
							|  |  |  | 			case "name": | 
					
						
							|  |  |  | 				info.Name = v.(string) | 
					
						
							|  |  |  | 			case "group": | 
					
						
							|  |  |  | 				info.Group = v.(string) | 
					
						
							|  |  |  | 			case "start": | 
					
						
							|  |  |  | 				info.Start = v.(int) | 
					
						
							|  |  |  | 			case "stop": | 
					
						
							|  |  |  | 				info.Stop = v.(int) | 
					
						
							|  |  |  | 			case "now": | 
					
						
							|  |  |  | 				info.Now = v.(int) | 
					
						
							|  |  |  | 			case "state": | 
					
						
							|  |  |  | 				info.State = v.(int) | 
					
						
							|  |  |  | 			case "statename": | 
					
						
							|  |  |  | 				info.StateName = v.(string) | 
					
						
							|  |  |  | 			case "exitstatus": | 
					
						
							|  |  |  | 				info.ExitStatus = v.(int) | 
					
						
							|  |  |  | 			case "pid": | 
					
						
							|  |  |  | 				info.PID = v.(int) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-09 08:27:30 -07:00
										 |  |  | 		labels := []string{info.Name, info.Group} | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-09 08:27:30 -07:00
										 |  |  | 		ch <- prometheus.MustNewConstMetric(c.stateDesc, prometheus.GaugeValue, float64(info.State), labels...) | 
					
						
							|  |  |  | 		ch <- prometheus.MustNewConstMetric(c.exitStatusDesc, prometheus.GaugeValue, float64(info.ExitStatus), labels...) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if c.isRunning(info.State) { | 
					
						
							| 
									
										
										
										
											2018-04-09 08:27:30 -07:00
										 |  |  | 			ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 1, labels...) | 
					
						
							| 
									
										
										
										
											2018-08-06 07:54:46 -07:00
										 |  |  | 			ch <- prometheus.MustNewConstMetric(c.startTimeDesc, prometheus.CounterValue, float64(info.Start), labels...) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-04-09 08:27:30 -07:00
										 |  |  | 			ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 0, labels...) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 		c.logger.Debug("process info", "group", info.Group, "name", info.Name, "state", info.StateName, "pid", info.PID) | 
					
						
							| 
									
										
										
										
											2015-10-05 09:11:09 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |