Created
June 26, 2025 09:30
-
-
Save jyxjjj/6ccf795cab21e34e035d0f13daee8d0d to your computer and use it in GitHub Desktop.
Supervisor Monitor
This file contains hidden or 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
package main | |
import ( | |
"bytes" | |
"encoding/xml" | |
"fmt" | |
"io" | |
"net" | |
"net/http" | |
"strings" | |
"github.com/gin-gonic/gin" | |
) | |
// supervisor XML-RPC request body | |
const xmlrpcRequest = `<?xml version="1.0"?> | |
<methodCall> | |
<methodName>supervisor.getAllProcessInfo</methodName> | |
</methodCall>` | |
type ProcessInfo struct { | |
Name string `xml:"name"` | |
Group string `xml:"group"` | |
Description string `xml:"description"` | |
Start int64 `xml:"start"` | |
Stop int64 `xml:"stop"` | |
Now int64 `xml:"now"` | |
State int `xml:"state"` | |
Statename string `xml:"statename"` | |
Spawnerr string `xml:"spawnerr"` | |
Exitstatus int `xml:"exitstatus"` | |
Logfile string `xml:"logfile"` | |
StdoutLogfile string `xml:"stdout_logfile"` | |
StderrLogfile string `xml:"stderr_logfile"` | |
Pid int `xml:"pid"` | |
} | |
func getSupervisorMetrics() (string, error) { | |
// 通过 unix socket 连接 supervisor | |
dial := func(proto, addr string) (net.Conn, error) { | |
return net.Dial("unix", "/var/run/supervisor.sock") | |
} | |
client := &http.Client{ | |
Transport: &http.Transport{ | |
Dial: dial, | |
}, | |
} | |
// supervisor 的 XML-RPC API 路径 | |
url := "http://unix/RPC2" | |
resp, err := client.Post(url, "text/xml", strings.NewReader(xmlrpcRequest)) | |
if err != nil { | |
return "", err | |
} | |
defer resp.Body.Close() | |
body, err := io.ReadAll(resp.Body) | |
if err != nil { | |
return "", err | |
} | |
// 解析 XML-RPC 响应 | |
var result struct { | |
XMLName xml.Name `xml:"methodResponse"` | |
Params struct { | |
Param struct { | |
Value struct { | |
Array struct { | |
Data struct { | |
Values []struct { | |
Struct struct { | |
Members []struct { | |
Name string `xml:"name"` | |
Value struct { | |
String string `xml:"string"` | |
Int int `xml:"int"` | |
I4 int `xml:"i4"` | |
} `xml:",any"` | |
} `xml:"member"` | |
} `xml:"struct"` | |
} `xml:"value"` | |
} `xml:"data"` | |
} `xml:"array"` | |
} `xml:"value"` | |
} `xml:"param"` | |
} `xml:"params"` | |
} | |
if err := xml.Unmarshal(body, &result); err != nil { | |
return "", err | |
} | |
// 组装 Prometheus 格式 | |
var buf bytes.Buffer | |
buf.WriteString("# HELP supervisor_process_status Supervisor process status (1=RUNNING, 0=other)\n") | |
buf.WriteString("# TYPE supervisor_process_status gauge\n") | |
for _, v := range result.Params.Param.Value.Array.Data.Values { | |
var name, statename string | |
for _, m := range v.Struct.Members { | |
if m.Name == "name" { | |
name = m.Value.String | |
} | |
if m.Name == "statename" { | |
statename = m.Value.String | |
} | |
} | |
value := 0 | |
if statename == "RUNNING" { | |
value = 1 | |
} | |
buf.WriteString(fmt.Sprintf("supervisor_process_status{name=\"%s\"} %d\n", name, value)) | |
} | |
return buf.String(), nil | |
} | |
func main() { | |
gin.SetMode(gin.ReleaseMode) | |
r := gin.Default() | |
r.GET("/metrics", func(c *gin.Context) { | |
metrics, err := getSupervisorMetrics() | |
if err != nil { | |
c.String(http.StatusOK, "# supervisor not available\n") | |
return | |
} | |
c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(metrics)) | |
}) | |
r.Run(":9093") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment