mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
This patch makes chasquid's monitoring server expose an OpenMetrics metrics endpoint. It adds a new package "expvarom" which implements an HTTP handler that exports expvar variables in the OpenMetrics text format. Then, the handler is registered by the monitoring server at /metrics (where most things expect it to be). The existing exported variables are also extended with descriptions, which is optional, but improves the readability of the metrics.
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"blitiri.com.ar/go/chasquid/internal/config"
|
|
"blitiri.com.ar/go/chasquid/internal/expvarom"
|
|
"blitiri.com.ar/go/log"
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
|
|
// To enable live profiling in the monitoring server.
|
|
_ "net/http/pprof"
|
|
)
|
|
|
|
func launchMonitoringServer(conf *config.Config) {
|
|
log.Infof("Monitoring HTTP server listening on %s", conf.MonitoringAddress)
|
|
|
|
osHostname, _ := os.Hostname()
|
|
|
|
indexData := struct {
|
|
Version string
|
|
SourceDate time.Time
|
|
StartTime time.Time
|
|
Config *config.Config
|
|
Hostname string
|
|
}{
|
|
Version: version,
|
|
SourceDate: sourceDate,
|
|
StartTime: time.Now(),
|
|
Config: conf,
|
|
Hostname: osHostname,
|
|
}
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
if err := monitoringHTMLIndex.Execute(w, indexData); err != nil {
|
|
log.Infof("monitoring handler error: %v", err)
|
|
}
|
|
})
|
|
|
|
http.HandleFunc("/metrics", expvarom.MetricsHandler)
|
|
http.HandleFunc("/debug/flags", debugFlagsHandler)
|
|
http.HandleFunc("/debug/config", debugConfigHandler(conf))
|
|
|
|
go http.ListenAndServe(conf.MonitoringAddress, nil)
|
|
}
|
|
|
|
// Functions available inside the templates.
|
|
var tmplFuncs = template.FuncMap{
|
|
"since": time.Since,
|
|
"roundDuration": roundDuration,
|
|
}
|
|
|
|
// Static index for the monitoring website.
|
|
var monitoringHTMLIndex = template.Must(
|
|
template.New("index").Funcs(tmplFuncs).Parse(
|
|
`<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{.Hostname}}: chasquid monitoring</title>
|
|
|
|
<style type="text/css">
|
|
body {
|
|
font-family: sans-serif;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<h1>chasquid @{{.Config.Hostname}}</h1>
|
|
|
|
chasquid {{.Version}}<br>
|
|
source date {{.SourceDate.Format "2006-01-02 15:04:05 -0700"}}<p>
|
|
|
|
started {{.StartTime.Format "Mon, 2006-01-02 15:04:05 -0700"}}<br>
|
|
up for {{.StartTime | since | roundDuration}}<br>
|
|
os hostname <i>{{.Hostname}}</i><p>
|
|
|
|
<ul>
|
|
<li><a href="/debug/queue">queue</a>
|
|
<li>monitoring
|
|
<ul>
|
|
<li><a href="/debug/requests?exp=1">requests (short-lived)</a>
|
|
<li><a href="/debug/events?exp=1">events (long-lived)</a>
|
|
<li><a href="https://blitiri.com.ar/p/chasquid/monitoring/#variables">
|
|
exported variables</a>:
|
|
<a href="/debug/vars">expvar</a>
|
|
<small><a href="https://golang.org/pkg/expvar/">(ref)</a></small>,
|
|
<a href="/metrics">openmetrics</a>
|
|
<small><a href="https://openmetrics.io/">(ref)</a></small>
|
|
</ul>
|
|
<li>execution
|
|
<ul>
|
|
<li><a href="/debug/flags">flags</a>
|
|
<li><a href="/debug/config">config</a>
|
|
<li><a href="/debug/pprof/cmdline">command line</a>
|
|
</ul>
|
|
<li><a href="/debug/pprof">pprof</a>
|
|
<small><a href="https://golang.org/pkg/net/http/pprof/">(ref)</a></small>
|
|
<ul>
|
|
</ul>
|
|
</ul>
|
|
</body>
|
|
|
|
</html>
|
|
`))
|
|
|
|
func debugFlagsHandler(w http.ResponseWriter, r *http.Request) {
|
|
visited := make(map[string]bool)
|
|
|
|
// Print set flags first, then the rest.
|
|
flag.Visit(func(f *flag.Flag) {
|
|
fmt.Fprintf(w, "-%s=%s\n", f.Name, f.Value.String())
|
|
visited[f.Name] = true
|
|
})
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
flag.VisitAll(func(f *flag.Flag) {
|
|
if !visited[f.Name] {
|
|
fmt.Fprintf(w, "-%s=%s\n", f.Name, f.Value.String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func debugConfigHandler(conf *config.Config) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
_, _ = w.Write([]byte(prototext.Format(conf)))
|
|
}
|
|
}
|
|
|
|
func roundDuration(d time.Duration) time.Duration {
|
|
return d.Round(time.Second)
|
|
}
|