77 "net/http"
88 "os"
99 "os/signal"
10+ "strings"
1011 "syscall"
1112 "time"
1213
@@ -18,48 +19,97 @@ import (
1819)
1920
2021var (
21- cfg * config.Config
22+ version = "dev"
23+ commit = "unknown"
24+ date = "unknown"
2225)
2326
24- var rootCmd = & cobra.Command {
25- Use : "crowdsec-exporter" ,
26- Short : "Prometheus exporter for CrowdSec decisions" ,
27- Long : `A Prometheus exporter that exposes CrowdSec decisions with rich geographical
28- and ASN information as metrics, compatible with Grafana dashboards.` ,
29- RunE : runExporter ,
27+ func main () {
28+ if err := newRootCmd ().Execute (); err != nil {
29+ slog .Error ("Command execution failed" , "error" , err )
30+ os .Exit (1 )
31+ }
3032}
3133
32- func init () {
33- // Global flags
34- rootCmd .PersistentFlags ().String ("crowdsec-url" , "http://localhost:8080" , "CrowdSec Local API URL" )
35- rootCmd .PersistentFlags ().String ("crowdsec-login" , "" , "CrowdSec machine login" )
36- rootCmd .PersistentFlags ().String ("crowdsec-password" , "" , "CrowdSec machine password" )
37- rootCmd .PersistentFlags ().String ("crowdsec-registration-token" , "" , "CrowdSec auto-registration token" )
38- rootCmd .PersistentFlags ().String ("crowdsec-machine-name" , "" , "Machine name for auto-registration (defaults to hostname)" )
39- rootCmd .PersistentFlags ().String ("listen-address" , ":9090" , "Address to listen on for web interface and metrics" )
40- rootCmd .PersistentFlags ().String ("metrics-path" , "/metrics" , "Path under which to expose metrics" )
41- rootCmd .PersistentFlags ().String ("instance-name" , "crowdsec" , "Instance name to use in metrics labels" )
42- rootCmd .PersistentFlags ().String ("log-level" , "info" , "Log level (debug, info, warn, error)" )
34+ func newRootCmd () * cobra.Command {
35+ cmd := & cobra.Command {
36+ Use : "crowdsec-exporter" ,
37+ Short : "Prometheus exporter for CrowdSec decisions" ,
38+ Long : `A Prometheus exporter that exposes CrowdSec decisions with rich geographical
39+ and ASN information as metrics, compatible with Grafana dashboards.` ,
4340
44- // Bind flags to viper
45- viper .BindPFlag ("crowdsec.url" , rootCmd .PersistentFlags ().Lookup ("crowdsec-url" ))
46- viper .BindPFlag ("crowdsec.login" , rootCmd .PersistentFlags ().Lookup ("crowdsec-login" ))
47- viper .BindPFlag ("crowdsec.password" , rootCmd .PersistentFlags ().Lookup ("crowdsec-password" ))
48- viper .BindPFlag ("crowdsec.registration_token" , rootCmd .PersistentFlags ().Lookup ("crowdsec-registration-token" ))
49- viper .BindPFlag ("crowdsec.machine_name" , rootCmd .PersistentFlags ().Lookup ("crowdsec-machine-name" ))
50- viper .BindPFlag ("server.listen_address" , rootCmd .PersistentFlags ().Lookup ("listen-address" ))
51- viper .BindPFlag ("server.metrics_path" , rootCmd .PersistentFlags ().Lookup ("metrics-path" ))
52- viper .BindPFlag ("exporter.instance_name" , rootCmd .PersistentFlags ().Lookup ("instance-name" ))
53- viper .BindPFlag ("log_level" , rootCmd .PersistentFlags ().Lookup ("log-level" ))
54- }
41+ SilenceUsage : true , // Don't show usage on errors
42+ SilenceErrors : true , // We handle errors manually
43+ RunE : func (cmd * cobra.Command , args []string ) error {
44+ return runExporter ()
45+ },
46+ }
5547
56- func runExporter (cmd * cobra.Command , args []string ) error {
57- // Set up viper to read from environment variables and flags
48+ // Setup Viper for automatic env binding
5849 viper .SetEnvPrefix ("CROWDSEC_EXPORTER" )
5950 viper .AutomaticEnv ()
51+ viper .SetEnvKeyReplacer (strings .NewReplacer ("-" , "_" , "." , "_" ))
52+
53+ flags := cmd .Flags ()
54+ flags .String ("crowdsec-url" , "http://localhost:8080" , "CrowdSec Local API URL" )
55+ flags .String ("crowdsec-login" , "" , "CrowdSec machine login" )
56+ flags .String ("crowdsec-password" , "" , "CrowdSec machine password" )
57+ flags .String ("crowdsec-registration-token" , "" , "CrowdSec auto-registration token" )
58+ flags .String ("crowdsec-machine-name" , "" , "Machine name for auto-registration (defaults to hostname)" )
59+ flags .String ("listen-address" , ":9090" , "Address to listen on for web interface and metrics" )
60+ flags .String ("metrics-path" , "/metrics" , "Path under which to expose metrics" )
61+ flags .String ("instance-name" , "crowdsec" , "Instance name to use in metrics labels" )
62+ flags .String ("log-level" , "info" , "Log level (debug, info, warn, error)" )
63+
64+ // Bind flags to viper
65+ if err := viper .BindPFlag ("crowdsec.url" , flags .Lookup ("crowdsec-url" )); err != nil {
66+ panic (fmt .Sprintf ("failed to bind crowdsec-url flag: %v" , err ))
67+ }
68+ if err := viper .BindPFlag ("crowdsec.login" , flags .Lookup ("crowdsec-login" )); err != nil {
69+ panic (fmt .Sprintf ("failed to bind crowdsec-login flag: %v" , err ))
70+ }
71+ if err := viper .BindPFlag ("crowdsec.password" , flags .Lookup ("crowdsec-password" )); err != nil {
72+ panic (fmt .Sprintf ("failed to bind crowdsec-password flag: %v" , err ))
73+ }
74+ if err := viper .BindPFlag ("crowdsec.registration_token" , flags .Lookup ("crowdsec-registration-token" )); err != nil {
75+ panic (fmt .Sprintf ("failed to bind crowdsec-registration-token flag: %v" , err ))
76+ }
77+ if err := viper .BindPFlag ("crowdsec.machine_name" , flags .Lookup ("crowdsec-machine-name" )); err != nil {
78+ panic (fmt .Sprintf ("failed to bind crowdsec-machine-name flag: %v" , err ))
79+ }
80+ if err := viper .BindPFlag ("server.listen_address" , flags .Lookup ("listen-address" )); err != nil {
81+ panic (fmt .Sprintf ("failed to bind listen-address flag: %v" , err ))
82+ }
83+ if err := viper .BindPFlag ("server.metrics_path" , flags .Lookup ("metrics-path" )); err != nil {
84+ panic (fmt .Sprintf ("failed to bind metrics-path flag: %v" , err ))
85+ }
86+ if err := viper .BindPFlag ("exporter.instance_name" , flags .Lookup ("instance-name" )); err != nil {
87+ panic (fmt .Sprintf ("failed to bind instance-name flag: %v" , err ))
88+ }
89+ if err := viper .BindPFlag ("log_level" , flags .Lookup ("log-level" )); err != nil {
90+ panic (fmt .Sprintf ("failed to bind log-level flag: %v" , err ))
91+ }
6092
93+ cmd .AddCommand (newVersionCmd ())
94+
95+ return cmd
96+ }
97+
98+ func newVersionCmd () * cobra.Command {
99+ return & cobra.Command {
100+ Use : "version" ,
101+ Short : "Show version information" ,
102+ Run : func (cmd * cobra.Command , args []string ) {
103+ fmt .Printf ("crowdsec-exporter %s\n " , version )
104+ fmt .Printf ("commit: %s\n " , commit )
105+ fmt .Printf ("built: %s\n " , date )
106+ },
107+ }
108+ }
109+
110+ func runExporter () error {
61111 // Load config from flags and env vars
62- cfg = & config.Config {}
112+ cfg : = & config.Config {}
63113 if err := viper .Unmarshal (cfg ); err != nil {
64114 return fmt .Errorf ("unable to decode config: %w" , err )
65115 }
@@ -127,10 +177,3 @@ func runExporter(cmd *cobra.Command, args []string) error {
127177 slog .Info ("Server exited" )
128178 return nil
129179}
130-
131- func main () {
132- if err := rootCmd .Execute (); err != nil {
133- fmt .Println (err )
134- os .Exit (1 )
135- }
136- }
0 commit comments