-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# Supervisor Exporter | ||
|
||
The Supervisor Exporter is a simple Go application that collects process status information from the Supervisor process control system and exposes it as Prometheus metrics. This allows you to monitor the state of processes managed by Supervisor. | ||
|
||
## Table of Contents | ||
|
||
- [Features](#features) | ||
- [Getting Started](#getting-started) | ||
- [Prerequisites](#prerequisites) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Configuration](#configuration) | ||
- [Prometheus Metrics](#prometheus-metrics) | ||
- [License](#license) | ||
|
||
## Features | ||
|
||
- Collects process status information from Supervisor. | ||
- Exposes process state, exit status, and more as Prometheus metrics. | ||
- Configurable via command line parameters. | ||
- Provides a simple HTTP server for Prometheus to scrape metrics. | ||
- Handles unreachable Supervisord XML-RPC endpoints gracefully. | ||
|
||
## Getting Started | ||
|
||
### Prerequisites | ||
|
||
Before running the Supervisor Exporter, make sure you have the following prerequisites: | ||
|
||
- Go (Golang) installed on your system. | ||
|
||
### Installation | ||
|
||
1. Clone the repository: | ||
|
||
```shell | ||
git clone https://github.com/salimd/supervisord_exporter.git | ||
``` | ||
|
||
2. Build the application: | ||
```shell | ||
go build | ||
``` | ||
|
||
### Usage | ||
|
||
To start the Supervisord Exporter, run the following command: | ||
|
||
```shell | ||
./supervisord_exporter | ||
``` | ||
|
||
By default, the exporter will listen on port 9876 and use the Supervisor XML-RPC interface at `http://localhost:9001/RPC2`. You can change the defaults using command line parameters (see [Configuration](#configuration) section). | ||
|
||
### Configuration | ||
|
||
The Supervisord Exporter can be configured using command line parameters. Here are the available parameters: | ||
|
||
* `-supervisord-url`: The URL of the Supervisord XML-RPC interface. Default is `http://localhost:9001/RPC2` | ||
* `-web.listen-address`: The address and port where the exporter will listen for HTTP requests. Default is `:9876` | ||
* `-web.telemetry-path`: Path under which to expose metrics. Default is `/metrics` | ||
* `-version`: Print the version information and exit. | ||
|
||
Example of custom configuration: | ||
|
||
```shell | ||
./supervisord_exporter -supervisord-url="http://example.com:9001/RPC2" -web.listen-address=":8080" -web.telemetry-path="/metrics" | ||
``` | ||
|
||
### Prometheus Metrics | ||
|
||
The Supervisord Exporter exposes the following Prometheus metric: | ||
|
||
* `supervisor_process_info`: Gauge vector with labels for `name`, `group`, `state`, `start`, and `exit_status`. The `state` label will be `RUNNING` if the process is running, and `exit_status` will be `0` for a running process. | ||
* `supervisord_up`: Gauge metric indicating the status of the connection to Supervisord (1 if up, 0 if down). If the Supervisord XML-RPC endpoint is unreachable, this metric will be set to 0, and there will be no supervisor_process_info metrics in the output. | ||
|
||
|
||
Sample metric: | ||
``` | ||
supervisor_process_info{exit_status="0",group="apache2",name="apache2",state="RUNNING"} 1 | ||
supervisord_up{} 1 | ||
``` | ||
|
||
### License | ||
|
||
This project is licensed under the MIT License | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module supervisord_exporter | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/beorn7/perks v1.0.1 // indirect | ||
github.com/cespare/xxhash/v2 v2.2.0 // indirect | ||
github.com/golang/protobuf v1.5.3 // indirect | ||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect | ||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect | ||
github.com/prometheus/client_golang v1.17.0 // indirect | ||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect | ||
github.com/prometheus/common v0.44.0 // indirect | ||
github.com/prometheus/procfs v0.11.1 // indirect | ||
golang.org/x/sys v0.11.0 // indirect | ||
google.golang.org/protobuf v1.31.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | ||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | ||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= | ||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | ||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= | ||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= | ||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= | ||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= | ||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= | ||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= | ||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= | ||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= | ||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= | ||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= | ||
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= | ||
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= | ||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= | ||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= | ||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/kolo/xmlrpc" | ||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/prometheus/client_golang/prometheus/promhttp" | ||
) | ||
|
||
var ( | ||
supervisordURL string | ||
listenAddress string | ||
metricsPath string | ||
version bool | ||
appVersion float32 = 0.1 | ||
|
||
processesMetric = prometheus.NewGaugeVec( | ||
prometheus.GaugeOpts{ | ||
Name: "supervisor_process_info", | ||
Help: "Supervisor process information", | ||
}, | ||
[]string{"name", "group", "state", "start", "exit_status"}, | ||
) | ||
supervisordUp = prometheus.NewGauge( | ||
prometheus.GaugeOpts{ | ||
Name: "supervisord_up", | ||
Help: "Supervisord XML-RPC connection status (1 if up, 0 if down)", | ||
}, | ||
) | ||
) | ||
|
||
func init() { | ||
flag.StringVar(&supervisordURL, "supervisord-url", "http://localhost:9001/RPC2", "Supervisord XML-RPC URL") | ||
flag.StringVar(&listenAddress, "web.listen-address", ":9876", "Address to listen for HTTP requests") | ||
flag.StringVar(&metricsPath, "web.telemetry-path", "/metrics", "Path under which to expose metrics") | ||
flag.BoolVar(&version, "version", false, "Displays application version") | ||
|
||
flag.Parse() | ||
} | ||
|
||
func fetchSupervisorProcessInfo() { | ||
client, err := xmlrpc.NewClient(supervisordURL, nil) | ||
if err != nil { | ||
log.Printf("Error creating Supervisor XML-RPC client: %v", err) | ||
supervisordUp.Set(0) | ||
processesMetric.Reset() | ||
return | ||
} | ||
defer client.Close() | ||
|
||
result := []map[string]interface{}{} | ||
if err := client.Call("supervisor.getAllProcessInfo", nil, &result); err != nil { | ||
log.Printf("Error calling Supervisor XML-RPC method: %v", err) | ||
supervisordUp.Set(0) | ||
processesMetric.Reset() | ||
return | ||
} | ||
|
||
supervisordUp.Set(1) | ||
|
||
// Create a map to store the latest process information for each unique combination of name and group | ||
latestInfo := make(map[string]map[string]interface{}) | ||
|
||
for _, data := range result { | ||
name, _ := data["name"].(string) | ||
group, _ := data["group"].(string) | ||
|
||
// Generate a unique key for the combination of name and group | ||
key := name + group | ||
|
||
// Check if the latest information for this combination already exists | ||
if existing, ok := latestInfo[key]; ok { | ||
// Compare timestamps to determine which information is more recent | ||
existingStartTime, _ := existing["start"].(int64) | ||
newStartTime, _ := data["start"].(int64) | ||
|
||
// If the new information is more recent, update the latestInfo map | ||
if newStartTime > existingStartTime { | ||
latestInfo[key] = data | ||
} | ||
} else { | ||
// If no previous information exists for this combination, add it to the map | ||
latestInfo[key] = data | ||
} | ||
} | ||
|
||
// Clear the previous metric values | ||
processesMetric.Reset() | ||
|
||
for _, data := range latestInfo { | ||
name, _ := data["name"].(string) | ||
group, _ := data["group"].(string) | ||
state, _ := data["statename"].(string) | ||
start, _ := data["start"].(int64) | ||
exitStatus, _ := data["exitstatus"].(int) | ||
|
||
value := 0 | ||
if state == "RUNNING" { | ||
value = 1 | ||
} | ||
|
||
processesMetric.WithLabelValues(name, group, state, fmt.Sprintf("%d", start), fmt.Sprintf("%d", exitStatus)).Set(float64(value)) | ||
} | ||
} | ||
|
||
func metricsHandler(w http.ResponseWriter, r *http.Request) { | ||
fetchSupervisorProcessInfo() | ||
promhttp.Handler().ServeHTTP(w, r) | ||
} | ||
|
||
func main() { | ||
if version { | ||
fmt.Printf("Supervisor Exporter v%v\n", appVersion) | ||
os.Exit(0) | ||
} | ||
|
||
prometheus.MustRegister(processesMetric) | ||
prometheus.MustRegister(supervisordUp) | ||
|
||
http.HandleFunc(metricsPath, metricsHandler) | ||
|
||
fmt.Printf("Listening on %s\n", listenAddress) | ||
if err := http.ListenAndServe(listenAddress, nil); err != nil { | ||
fmt.Printf("Error: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} |