Skip to content

Commit 8abcfe5

Browse files
committed
Migrate from standard library flags package to kingpin.v2
1 parent 6bd07d1 commit 8abcfe5

File tree

8 files changed

+171
-833
lines changed

8 files changed

+171
-833
lines changed

cmd/mqtt2prometheus.go

Lines changed: 91 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,86 +3,81 @@ package main
33
import (
44
"crypto/tls"
55
"crypto/x509"
6-
"encoding/json"
7-
"flag"
86
"fmt"
97
"io/ioutil"
108
"net/http"
9+
_ "net/http/pprof"
1110
"os"
11+
"os/user"
12+
"runtime"
1213
"time"
1314

14-
"go.uber.org/zap"
15-
"go.uber.org/zap/zapcore"
15+
"github.com/prometheus/common/promlog"
16+
"github.com/prometheus/common/promlog/flag"
1617

18+
"github.com/alecthomas/kingpin/v2"
1719
mqtt "github.com/eclipse/paho.mqtt.golang"
18-
"github.com/go-kit/kit/log"
19-
kitzap "github.com/go-kit/kit/log/zap"
20+
"github.com/go-kit/log/level"
2021
"github.com/hikhvar/mqtt2prometheus/pkg/config"
2122
"github.com/hikhvar/mqtt2prometheus/pkg/metrics"
2223
"github.com/hikhvar/mqtt2prometheus/pkg/mqttclient"
2324
"github.com/prometheus/client_golang/prometheus"
2425
"github.com/prometheus/client_golang/prometheus/promhttp"
26+
"github.com/prometheus/common/version"
2527
"github.com/prometheus/exporter-toolkit/web"
28+
"github.com/prometheus/exporter-toolkit/web/kingpinflag"
2629
)
2730

28-
// These variables are set by goreleaser at linking time.
29-
var (
30-
version string
31-
commit string
32-
date string
33-
)
34-
35-
var (
36-
configFlag = flag.String(
37-
"config",
38-
"config.yaml",
39-
"config file",
40-
)
41-
portFlag = flag.String(
42-
"listen-port",
43-
"9641",
44-
"HTTP port used to expose metrics",
45-
)
46-
addressFlag = flag.String(
47-
"listen-address",
48-
"0.0.0.0",
49-
"listen address for HTTP server used to expose metrics",
50-
)
51-
versionFlag = flag.Bool(
52-
"version",
53-
false,
54-
"show the builds version, date and commit",
55-
)
56-
logLevelFlag = zap.LevelFlag("log-level", zap.InfoLevel, "sets the default loglevel (default: \"info\")")
57-
logEncodingFlag = flag.String(
58-
"log-format",
59-
"console",
60-
"set the desired log output format. Valid values are 'console' and 'json'",
61-
)
62-
webConfigFlag = flag.String(
63-
"web-config-file",
64-
"",
65-
"[EXPERIMENTAL] Path to configuration file that can enable TLS or authentication for metric scraping.",
66-
)
67-
usePasswordFromFile = flag.Bool(
68-
"treat-mqtt-password-as-file-name",
69-
false,
70-
"treat MQTT2PROM_MQTT_PASSWORD as a secret file path e.g. /var/run/secrets/mqtt-credential",
31+
func main() {
32+
var (
33+
metricsPath = kingpin.Flag(
34+
"web.telemetry-path",
35+
"Path under which to expose metrics.",
36+
).Default("/metrics").String()
37+
configFlag = kingpin.Flag(
38+
"config",
39+
"config file",
40+
).Default("config.yaml").String()
41+
/*
42+
maxRequests = kingpin.Flag(
43+
"web.max-requests",
44+
"Maximum number of parallel scrape requests. Use 0 to disable.",
45+
).Default("40").Int()
46+
*/
47+
usePasswordFromFile = kingpin.Flag(
48+
"treat-mqtt-password-as-file-name",
49+
"treat MQTT2PROM_MQTT_PASSWORD as a secret file path e.g. /var/run/secrets/mqtt-credential",
50+
).Default("false").Bool()
51+
maxProcs = kingpin.Flag(
52+
"runtime.gomaxprocs", "The target number of CPUs Go will run on (GOMAXPROCS)",
53+
).Envar("GOMAXPROCS").Default("1").Int()
54+
toolkitFlags = kingpinflag.AddFlags(kingpin.CommandLine, ":9641")
7155
)
72-
)
7356

74-
func main() {
75-
flag.Parse()
76-
if *versionFlag {
77-
mustShowVersion()
78-
os.Exit(0)
57+
promlogConfig := &promlog.Config{}
58+
flag.AddFlags(kingpin.CommandLine, promlogConfig)
59+
kingpin.Version(version.Print("mqtt2prometheus_exporter"))
60+
kingpin.CommandLine.UsageWriter(os.Stdout)
61+
kingpin.HelpFlag.Short('h')
62+
kingpin.Parse()
63+
logger := promlog.New(promlogConfig)
64+
65+
level.Info(logger).Log("msg", "Starting mqtt2prometheus_exporter", "version", version.Info())
66+
level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext())
67+
if user, err := user.Current(); err == nil && user.Uid == "0" {
68+
level.Warn(logger).Log("msg", "MQTT2Prometheus Exporter is running as root user. This exporter is designed to run as unprivileged user, root is not required.")
7969
}
80-
logger := mustSetupLogger()
81-
defer logger.Sync() //nolint:errcheck
70+
runtime.GOMAXPROCS(*maxProcs)
71+
level.Debug(logger).Log("msg", "Go MAXPROCS", "procs", runtime.GOMAXPROCS(0))
72+
73+
prometheus.MustRegister(
74+
version.NewCollector("mqtt2prometheus_exporter"),
75+
)
8276
c := make(chan os.Signal, 1)
8377
cfg, err := config.LoadConfig(*configFlag, logger)
8478
if err != nil {
85-
logger.Fatal("Could not load config", zap.Error(err))
79+
level.Error(logger).Log("msg", "Could not load config", "err", err)
80+
os.Exit(1)
8681
}
8782

8883
mqtt_user := os.Getenv("MQTT2PROM_MQTT_USER")
@@ -93,11 +88,13 @@ func main() {
9388
mqtt_password := os.Getenv("MQTT2PROM_MQTT_PASSWORD")
9489
if *usePasswordFromFile {
9590
if mqtt_password == "" {
96-
logger.Fatal("MQTT2PROM_MQTT_PASSWORD is required")
91+
level.Error(logger).Log("msg", "MQTT2PROM_MQTT_PASSWORD is required")
92+
os.Exit(1)
9793
}
9894
secret, err := ioutil.ReadFile(mqtt_password)
9995
if err != nil {
100-
logger.Fatal("unable to read mqtt password from secret file", zap.Error(err))
96+
level.Error(logger).Log("msg", "unable to read mqtt password from secret file", "err", err)
97+
os.Exit(1)
10198
}
10299
cfg.MQTT.Password = string(secret)
103100
} else {
@@ -121,17 +118,19 @@ func main() {
121118
if cfg.MQTT.ClientCert != "" || cfg.MQTT.ClientKey != "" {
122119
tlsconfig, err := newTLSConfig(cfg)
123120
if err != nil {
124-
logger.Fatal("Invalid tls certificate settings", zap.Error(err))
121+
level.Error(logger).Log("msg", "Invalid tls certificate settings", "err", err)
122+
os.Exit(1)
125123
}
126124
mqttClientOptions.SetTLSConfig(tlsconfig)
127125
}
128126

129127
collector := metrics.NewCollector(cfg.Cache.Timeout, cfg.Metrics, logger)
130128
extractor, err := setupExtractor(cfg)
131129
if err != nil {
132-
logger.Fatal("could not setup a metric extractor", zap.Error(err))
130+
level.Error(logger).Log("msg", "could not setup a metric extractor", "err", err)
131+
os.Exit(1)
133132
}
134-
ingest := metrics.NewIngest(collector, extractor, cfg.MQTT.DeviceIDRegex)
133+
ingest := metrics.NewIngest(collector, extractor, cfg.MQTT.DeviceIDRegex, logger)
135134
mqttClientOptions.SetOnConnectHandler(ingest.OnConnectHandler)
136135
mqttClientOptions.SetConnectionLostHandler(ingest.ConnectionLostHandler)
137136
errorChan := make(chan error, 1)
@@ -147,7 +146,7 @@ func main() {
147146
// connected, break loop
148147
break
149148
}
150-
logger.Warn("could not connect to mqtt broker, sleep 10 second", zap.Error(err))
149+
level.Warn(logger).Log("msg", "could not connect to mqtt broker, sleep 10 second", "err", err)
151150
time.Sleep(10 * time.Second)
152151
}
153152

@@ -160,50 +159,48 @@ func main() {
160159
reg.MustRegister(collector)
161160
gatherer = reg
162161
}
163-
http.Handle("/metrics", promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}))
164-
s := &http.Server{
165-
Addr: getListenAddress(),
166-
Handler: http.DefaultServeMux,
162+
163+
http.Handle(*metricsPath, promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}))
164+
if *metricsPath != "/" {
165+
landingConfig := web.LandingConfig{
166+
Name: "MQTT2Prometheus Exporter",
167+
Description: "Prometheus MQTT2Prometheus Exporter",
168+
Version: version.Info(),
169+
Links: []web.LandingLinks{
170+
{
171+
Address: *metricsPath,
172+
Text: "Metrics",
173+
},
174+
},
175+
}
176+
landingPage, err := web.NewLandingPage(landingConfig)
177+
if err != nil {
178+
level.Error(logger).Log("err", err)
179+
os.Exit(1)
180+
}
181+
http.Handle("/", landingPage)
167182
}
183+
184+
server := &http.Server{}
185+
168186
go func() {
169-
err = web.ListenAndServe(s, *webConfigFlag, setupGoKitLogger(logger))
170-
if err != nil {
171-
logger.Fatal("Error while serving http", zap.Error(err))
187+
if err := web.ListenAndServe(server, toolkitFlags, logger); err != nil {
188+
level.Error(logger).Log("err", err)
189+
os.Exit(1)
172190
}
173191
}()
174192

175193
for {
176194
select {
177195
case <-c:
178-
logger.Info("Terminated via Signal. Stop.")
196+
level.Info(logger).Log("msg", "Terminated via Signal. Stop.")
179197
os.Exit(0)
180198
case err = <-errorChan:
181-
logger.Error("Error while processing message", zap.Error(err))
199+
level.Error(logger).Log("msg", "Error while processing message", "err", err)
182200
}
183201
}
184202
}
185203

186-
func getListenAddress() string {
187-
return fmt.Sprintf("%s:%s", *addressFlag, *portFlag)
188-
}
189-
190-
func mustShowVersion() {
191-
versionInfo := struct {
192-
Version string
193-
Commit string
194-
Date string
195-
}{
196-
Version: version,
197-
Commit: commit,
198-
Date: date,
199-
}
200-
201-
err := json.NewEncoder(os.Stdout).Encode(versionInfo)
202-
if err != nil {
203-
panic(err)
204-
}
205-
}
206-
207204
func mustMQTTClientID() string {
208205
host, err := os.Hostname()
209206
if err != nil {
@@ -213,26 +210,6 @@ func mustMQTTClientID() string {
213210
return fmt.Sprintf("%s-%d", host, pid)
214211
}
215212

216-
func mustSetupLogger() *zap.Logger {
217-
cfg := zap.NewProductionConfig()
218-
cfg.Level = zap.NewAtomicLevelAt(*logLevelFlag)
219-
cfg.Encoding = *logEncodingFlag
220-
if cfg.Encoding == "console" {
221-
cfg.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder
222-
}
223-
logger, err := cfg.Build()
224-
if err != nil {
225-
panic(fmt.Sprintf("failed to build logger: %v", err))
226-
}
227-
228-
config.SetProcessContext(logger)
229-
return logger
230-
}
231-
232-
func setupGoKitLogger(l *zap.Logger) log.Logger {
233-
return kitzap.NewZapSugarLogger(l, zap.NewAtomicLevelAt(*logLevelFlag).Level())
234-
}
235-
236213
func setupExtractor(cfg config.Config) (metrics.Extractor, error) {
237214
parser := metrics.NewParser(cfg.Metrics, cfg.JsonParsing.Separator, cfg.Cache.StateDir)
238215
if cfg.MQTT.ObjectPerTopicConfig != nil {

go.mod

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,38 @@ module github.com/hikhvar/mqtt2prometheus
33
go 1.18
44

55
require (
6+
github.com/alecthomas/kingpin/v2 v2.4.0
67
github.com/eclipse/paho.mqtt.golang v1.3.5
78
github.com/expr-lang/expr v1.16.9
8-
github.com/go-kit/kit v0.10.0
9+
github.com/go-kit/log v0.2.1
910
github.com/patrickmn/go-cache v2.1.0+incompatible
10-
github.com/prometheus/client_golang v1.11.1
11-
github.com/prometheus/exporter-toolkit v0.7.3
11+
github.com/prometheus/client_golang v1.17.0
12+
github.com/prometheus/common v0.45.0
13+
github.com/prometheus/exporter-toolkit v0.11.0
1214
github.com/thedevsaddam/gojsonq/v2 v2.5.2
13-
go.uber.org/zap v1.16.0
1415
gopkg.in/yaml.v2 v2.4.0
1516
)
1617

1718
require (
19+
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
1820
github.com/beorn7/perks v1.0.1 // indirect
19-
github.com/cespare/xxhash/v2 v2.1.1 // indirect
20-
github.com/go-kit/log v0.1.0 // indirect
21-
github.com/go-logfmt/logfmt v0.5.0 // indirect
22-
github.com/golang/protobuf v1.5.0 // indirect
21+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
22+
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
23+
github.com/go-logfmt/logfmt v0.5.1 // indirect
24+
github.com/golang/protobuf v1.5.3 // indirect
2325
github.com/gorilla/websocket v1.4.2 // indirect
2426
github.com/jpillora/backoff v1.0.0 // indirect
25-
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
27+
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
2628
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
27-
github.com/pkg/errors v0.9.1 // indirect
28-
github.com/prometheus/client_model v0.2.0 // indirect
29-
github.com/prometheus/common v0.29.0 // indirect
30-
github.com/prometheus/procfs v0.6.0 // indirect
31-
go.uber.org/atomic v1.6.0 // indirect
32-
go.uber.org/multierr v1.5.0 // indirect
29+
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
30+
github.com/prometheus/procfs v0.11.1 // indirect
31+
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
3332
golang.org/x/crypto v0.31.0 // indirect
3433
golang.org/x/net v0.21.0 // indirect
35-
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
34+
golang.org/x/oauth2 v0.12.0 // indirect
35+
golang.org/x/sync v0.10.0 // indirect
3636
golang.org/x/sys v0.28.0 // indirect
3737
golang.org/x/text v0.21.0 // indirect
38-
google.golang.org/appengine v1.6.6 // indirect
38+
google.golang.org/appengine v1.6.7 // indirect
3939
google.golang.org/protobuf v1.33.0 // indirect
4040
)

0 commit comments

Comments
 (0)