11package app
22
33import (
4+ "errors"
5+ "fmt"
6+ "io"
47 "net/http"
58 "os"
9+ "strings"
610
7- "github.com/Luzilla/dnsbl_exporter/collector"
811 "github.com/Luzilla/dnsbl_exporter/config"
12+ "github.com/Luzilla/dnsbl_exporter/internal/index"
13+ "github.com/Luzilla/dnsbl_exporter/internal/metrics"
14+ "github.com/Luzilla/dnsbl_exporter/internal/prober"
15+ "github.com/Luzilla/dnsbl_exporter/internal/setup"
916 "github.com/prometheus/client_golang/prometheus"
10- "github.com/prometheus/client_golang/prometheus/promhttp"
11- "github.com/urfave/cli"
12-
13- log "github.com/sirupsen/logrus"
17+ "github.com/urfave/cli/v2"
18+ "golang.org/x/exp/slog"
1419)
1520
1621type DNSBLApp struct {
1722 App * cli.App
1823}
1924
25+ var (
26+ appName , appVersion , appPath string
27+ resolver string
28+ )
29+
2030// NewApp ...
2131func NewApp (name string , version string ) DNSBLApp {
22-
23- cli .VersionFlag = cli.BoolFlag {
24- Name : "version, V" ,
25- Usage : "Print the version information." ,
26- }
27-
28- app := cli .NewApp ()
29- app .Name = name
30- app .Version = version
31- app .Flags = []cli.Flag {
32- cli.StringFlag {
33- Name : "config.dns-resolver" ,
34- Value : "127.0.0.1:53" ,
35- Usage : "IP address[:port] of the resolver to use." ,
36- EnvVar : "DNSBL_EXP_RESOLVER" ,
32+ appName = name
33+ appVersion = version
34+
35+ a := cli .NewApp ()
36+ a .Name = appName
37+ a .Version = appVersion
38+ a .Flags = []cli.Flag {
39+ & cli.StringFlag {
40+ Name : "config.dns-resolver" ,
41+ Value : "127.0.0.1:53" ,
42+ Usage : "IP address[:port] of the resolver to use." ,
43+ EnvVars : []string {"DNSBL_EXP_RESOLVER" },
44+ Destination : & resolver ,
3745 },
38- cli.StringFlag {
39- Name : "config.rbls" ,
40- Value : "./rbls.ini" ,
41- Usage : "Configuration file which contains RBLs" ,
42- EnvVar : "DNSBL_EXP_RBLS" ,
46+ & cli.StringFlag {
47+ Name : "config.rbls" ,
48+ Value : "./rbls.ini" ,
49+ Usage : "Configuration file which contains RBLs" ,
50+ EnvVars : [] string { "DNSBL_EXP_RBLS" } ,
4351 },
44- cli.StringFlag {
45- Name : "config.targets" ,
46- Value : "./targets.ini" ,
47- Usage : "Configuration file which contains the targets to check." ,
48- EnvVar : "DNSBL_EXP_TARGETS" ,
52+ & cli.StringFlag {
53+ Name : "config.targets" ,
54+ Value : "./targets.ini" ,
55+ Usage : "Configuration file which contains the targets to check." ,
56+ EnvVars : [] string { "DNSBL_EXP_TARGETS" } ,
4957 },
50- cli.StringFlag {
51- Name : "web.listen-address" ,
52- Value : ":9211" ,
53- Usage : "Address to listen on for web interface and telemetry." ,
54- EnvVar : "DNSBL_EXP_LISTEN" ,
58+ & cli.StringFlag {
59+ Name : "web.listen-address" ,
60+ Value : ":9211" ,
61+ Usage : "Address to listen on for web interface and telemetry." ,
62+ EnvVars : [] string { "DNSBL_EXP_LISTEN" } ,
5563 },
56- cli.StringFlag {
57- Name : "web.telemetry-path" ,
58- Value : "/metrics" ,
59- Usage : "Path under which to expose metrics." ,
64+ & cli.StringFlag {
65+ Name : "web.telemetry-path" ,
66+ Value : "/metrics" ,
67+ Usage : "Path under which to expose metrics." ,
68+ Destination : & appPath ,
69+ Action : func (cCtx * cli.Context , v string ) error {
70+ if ! strings .HasPrefix (v , "/" ) {
71+ return cli .Exit ("Missing / to prefix the path: --web.telemetry-path" , 2 )
72+ }
73+ return nil
74+ },
6075 },
61- cli.BoolFlag {
76+ & cli.BoolFlag {
6277 Name : "web.include-exporter-metrics" ,
6378 Usage : "Include metrics about the exporter itself (promhttp_*, process_*, go_*)." ,
79+ Value : false ,
6480 },
65- cli.BoolFlag {
81+ & cli.BoolFlag {
6682 Name : "log.debug" ,
6783 Usage : "Enable more output in the logs, otherwise INFO." ,
84+ Value : false ,
6885 },
69- cli.StringFlag {
86+ & cli.StringFlag {
7087 Name : "log.output" ,
7188 Value : "stdout" ,
7289 Usage : "Destination of our logs: stdout, stderr" ,
90+ Action : func (cCtx * cli.Context , v string ) error {
91+ if v != "stdout" && v != "stderr" {
92+ return cli .Exit ("We currently support only stdout and stderr: --log.output" , 2 )
93+ }
94+ return nil
95+ },
7396 },
7497 }
7598
7699 return DNSBLApp {
77- App : app ,
100+ App : a ,
78101 }
79102}
80103
81- func (app * DNSBLApp ) Bootstrap () {
82- app .App .Action = func (ctx * cli.Context ) error {
104+ func (a * DNSBLApp ) Bootstrap () {
105+ a .App .Action = func (ctx * cli.Context ) error {
83106 // setup logging
107+ fmt .Println ("VERSION: " + appVersion )
108+ handler := & slog.HandlerOptions {}
109+ var writer io.Writer
110+
111+ if ctx .Bool ("log.debug" ) {
112+ handler .Level = slog .LevelDebug
113+ }
114+
84115 switch ctx .String ("log.output" ) {
85116 case "stdout" :
86- log . SetOutput ( os .Stdout )
117+ writer = os .Stdout
87118 case "stderr" :
88- log .SetOutput (os .Stderr )
89- default :
90- cli .ShowAppHelp (ctx )
91- return cli .NewExitError ("We currently support only stdout and stderr: --log.output" , 2 )
119+ writer = os .Stderr
92120 }
93- if ctx .Bool ("log.debug" ) {
94- log .SetLevel (log .DebugLevel )
121+
122+ log := slog .New (handler .NewTextHandler (writer ))
123+
124+ c := config.Config {
125+ Logger : log .With ("area" , "config" ),
95126 }
96127
97- cfgRbls , err := config .LoadFile (ctx .String ("config.rbls" ), "rbl" )
128+ cfgRbls , err := c .LoadFile (ctx .String ("config.rbls" ))
98129 if err != nil {
99130 return err
100131 }
101132
102- cfgTargets , err := config .LoadFile (ctx .String ("config.targets" ), "targets" )
133+ err = c .ValidateConfig (cfgRbls , "rbl" )
134+ if err != nil {
135+ return fmt .Errorf ("unable to load the rbls from the config: %w" , err )
136+ }
137+
138+ cfgTargets , err := c .LoadFile (ctx .String ("config.targets" ))
103139 if err != nil {
104140 return err
105141 }
106142
107- http .HandleFunc ("/" , func (w http.ResponseWriter , r * http.Request ) {
108- w .Write ([]byte (`<html>
109- <head><title>` + app .App .Name + `</title></head>
110- <body>
111- <h1>` + app .App .Name + ` @ ` + app .App .Version + `</h1>
112- <p><a href="` + ctx .String ("web.telemetry-path" ) + `">Metrics</a></p>
113- <p><a href="https://github.com/Luzilla/dnsbl_exporter">Code on Github</a></p>
114- </body>
115- </html>` ))
116- })
143+ err = c .ValidateConfig (cfgTargets , "targets" )
144+ if err != nil {
145+ if ! errors .Is (err , config .ErrNoServerEntries ) && ! errors .Is (err , config .ErrNoSuchSection ) {
146+ return err
147+ }
148+ log .Info ("starting exporter without targets — check the /prober endpoint or correct the .ini file" )
149+ }
150+
151+ iHandler := index.IndexHandler {
152+ Name : appName ,
153+ Version : appVersion ,
154+ Path : appPath ,
155+ }
156+
157+ http .HandleFunc ("/" , iHandler .Handler )
117158
118- rbls := config .GetRbls (cfgRbls )
119- targets := config .GetTargets (cfgTargets )
159+ rbls := c .GetRbls (cfgRbls )
160+ targets := c .GetTargets (cfgTargets )
120161
121- registry := createRegistry ()
162+ registry := setup . CreateRegistry ()
122163
123- collector := createCollector (rbls , targets , ctx . String ( "config.dns-resolver " ))
124- registry .MustRegister (collector )
164+ rblCollector := setup . CreateCollector (rbls , targets , resolver , log . With ( "area" , "metrics " ))
165+ registry .MustRegister (rblCollector )
125166
126- registryExporter := createRegistry ()
167+ registryExporter := setup . CreateRegistry ()
127168
128169 if ctx .Bool ("web.include-exporter-metrics" ) {
129- log .Infoln ("Exposing exporter metrics" )
170+ log .Info ("Exposing exporter metrics" )
130171
131172 registryExporter .MustRegister (
132173 prometheus .NewProcessCollector (prometheus.ProcessCollectorOpts {}),
133174 prometheus .NewGoCollector (),
134175 )
135176 }
136177
137- handler := promhttp .HandlerFor (
138- prometheus.Gatherers {
139- registry ,
140- registryExporter ,
141- },
142- promhttp.HandlerOpts {
143- ErrorHandling : promhttp .ContinueOnError ,
144- Registry : registry ,
145- },
146- )
178+ mHandler := metrics.MetricsHandler {
179+ Registry : registry ,
180+ RegistryExporter : registryExporter ,
181+ }
147182
148- http .Handle (ctx .String ("web.telemetry-path" ), handler )
183+ http .Handle (ctx .String ("web.telemetry-path" ), mHandler .Handler ())
184+
185+ pHandler := prober.ProberHandler {
186+ Resolver : resolver ,
187+ Rbls : rbls ,
188+ Logger : log .With ("area" , "prober" ),
189+ }
190+ http .Handle ("/prober" , pHandler )
149191
150- log .Infoln ("Starting on: " , ctx .String ("web.listen-address" ))
192+ log .Info ("Starting on: " + ctx .String ("web.listen-address" ))
151193 err = http .ListenAndServe (ctx .String ("web.listen-address" ), nil )
152194 if err != nil {
153195 return err
@@ -157,14 +199,6 @@ func (app *DNSBLApp) Bootstrap() {
157199 }
158200}
159201
160- func (app * DNSBLApp ) Run (args []string ) error {
161- return app .App .Run (args )
162- }
163-
164- func createCollector (rbls []string , targets []string , resolver string ) * collector.RblCollector {
165- return collector .NewRblCollector (rbls , targets , resolver )
166- }
167-
168- func createRegistry () * prometheus.Registry {
169- return prometheus .NewRegistry ()
202+ func (a * DNSBLApp ) Run (args []string ) error {
203+ return a .App .Run (args )
170204}
0 commit comments