1) Breaking changes! Updated collection code for proper Prometheus support - Now metrics names of all subcollectors have prefixes: nsq_topic_, nsq_channel_, nsq_client_ As a result metrics registration code now passing Prometheus' client validation, also it fixes scrape warnings - "type" label in exported metrics removed as obsolete 2) Add reset of gauges before scrape (like in haproxy-exporter) 3) Refactor: subcollectors logic is simplified, multiple collectors support removed We always use 'stats' collector so it removes unnecessary flexibility and complexity.
106 lines
2.6 KiB
Go
106 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/lovoo/nsq_exporter/collector"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// Version of nsq_exporter. Set at build time.
|
|
const Version = "0.0.0.dev"
|
|
|
|
var (
|
|
listenAddress = flag.String("web.listen", ":9117", "Address on which to expose metrics and web interface.")
|
|
metricsPath = flag.String("web.path", "/metrics", "Path under which to expose metrics.")
|
|
nsqdURL = flag.String("nsqd.addr", "http://localhost:4151/stats", "Address of the nsqd node.")
|
|
enabledCollectors = flag.String("collect", "stats.topics,stats.channels", "Comma-separated list of collectors to use.")
|
|
namespace = flag.String("namespace", "nsq", "Namespace for the NSQ metrics.")
|
|
|
|
statsRegistry = map[string]func(namespace string) collector.StatsCollector{
|
|
"topics": collector.TopicStats,
|
|
"channels": collector.ChannelStats,
|
|
"clients": collector.ClientStats,
|
|
}
|
|
)
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
ex, err := createNsqExecutor()
|
|
if err != nil {
|
|
log.Fatalf("error creating nsq executor: %v", err)
|
|
}
|
|
prometheus.MustRegister(ex)
|
|
|
|
http.Handle(*metricsPath, prometheus.Handler())
|
|
if *metricsPath != "" && *metricsPath != "/" {
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`<html>
|
|
<head><title>NSQ Exporter</title></head>
|
|
<body>
|
|
<h1>NSQ Exporter</h1>
|
|
<p><a href="` + *metricsPath + `">Metrics</a></p>
|
|
</body>
|
|
</html>`))
|
|
})
|
|
}
|
|
|
|
log.Print("listening to ", *listenAddress)
|
|
err = http.ListenAndServe(*listenAddress, nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func createNsqExecutor() (*collector.NsqExecutor, error) {
|
|
|
|
nsqdURL, err := normalizeURL(*nsqdURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ex := collector.NewNsqExecutor(*namespace, nsqdURL)
|
|
for _, param := range strings.Split(*enabledCollectors, ",") {
|
|
param = strings.TrimSpace(param)
|
|
parts := strings.SplitN(param, ".", 2)
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("invalid collector name: %s", param)
|
|
}
|
|
if parts[0] != "stats" {
|
|
return nil, fmt.Errorf("invalid collector prefix: %s", parts[0])
|
|
}
|
|
|
|
name := parts[1]
|
|
c, has := statsRegistry[name]
|
|
if !has {
|
|
return nil, fmt.Errorf("unknown stats collector: %s", name)
|
|
}
|
|
ex.Use(c(*namespace))
|
|
}
|
|
return ex, nil
|
|
}
|
|
|
|
func normalizeURL(ustr string) (string, error) {
|
|
ustr = strings.ToLower(ustr)
|
|
if !strings.HasPrefix(ustr, "https://") && !strings.HasPrefix(ustr, "http://") {
|
|
ustr = "http://" + ustr
|
|
}
|
|
|
|
u, err := url.Parse(ustr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if u.Path == "" {
|
|
u.Path = "/stats"
|
|
}
|
|
u.RawQuery = "format=json"
|
|
return u.String(), nil
|
|
}
|