From 03f50840209149cfec36210fda7102317c9bc497 Mon Sep 17 00:00:00 2001 From: Hector Date: Mon, 13 Sep 2021 20:25:54 +0100 Subject: [PATCH] refactor: move exporter code to new package Split out all the code to define exporter functions and collect data into a new package. The new package is responsible for all exporter related activity. This makes the code easier to read. Split the code for collecting metrics from the database and from the socket into different files to make the separation more obvious. --- src/export/build.go | 25 ++++ src/export/database.go | 102 +++++++++++++++ src/export/exporter.go | 64 +++++++++ src/export/socket.go | 140 ++++++++++++++++++++ src/exporter.go | 289 +---------------------------------------- 5 files changed, 333 insertions(+), 287 deletions(-) create mode 100644 src/export/build.go create mode 100644 src/export/database.go create mode 100644 src/export/exporter.go create mode 100644 src/export/socket.go diff --git a/src/export/build.go b/src/export/build.go new file mode 100644 index 0000000..a874cc2 --- /dev/null +++ b/src/export/build.go @@ -0,0 +1,25 @@ +package export + +import ( + "fail2ban-prometheus-exporter/cfg" + fail2banDb "fail2ban-prometheus-exporter/db" + "log" +) + +func NewExporter(appSettings *cfg.AppSettings, exporterVersion string) *Exporter { + exporter := &Exporter{ + exporterVersion: exporterVersion, + lastError: nil, + dbErrorCount: 0, + socketConnectionErrorCount: 0, + socketRequestErrorCount: 0, + } + if appSettings.Fail2BanDbPath != "" { + log.Print("database-based metrics have been deprecated and will be removed in a future release") + exporter.db = fail2banDb.MustConnectToDb(appSettings.Fail2BanDbPath) + } + if appSettings.Fail2BanSocketPath != "" { + exporter.socketPath = appSettings.Fail2BanSocketPath + } + return exporter +} diff --git a/src/export/database.go b/src/export/database.go new file mode 100644 index 0000000..f030360 --- /dev/null +++ b/src/export/database.go @@ -0,0 +1,102 @@ +package export + +import ( + "github.com/prometheus/client_golang/prometheus" + "log" +) + +const ( + deprecatedNamespace = "fail2ban" +) + +var ( + deprecatedMetricUp = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "up"), + "(Deprecated) Was the last fail2ban query successful.", + nil, nil, + ) + deprecatedMetricBannedIpsPerJail = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "banned_ips"), + "(Deprecated) Number of banned IPs stored in the database (per jail).", + []string{"jail"}, nil, + ) + deprecatedMetricBadIpsPerJail = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "bad_ips"), + "(Deprecated) Number of bad IPs stored in the database (per jail).", + []string{"jail"}, nil, + ) + deprecatedMetricEnabledJails = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "enabled_jails"), + "(Deprecated) Enabled jails.", + []string{"jail"}, nil, + ) + deprecatedMetricErrorCount = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "errors"), + "(Deprecated) Number of errors found since startup.", + []string{"type"}, nil, + ) +) + +func (e *Exporter) collectDeprecatedUpMetric(ch chan<- prometheus.Metric) { + var upMetricValue float64 = 1 + if e.lastError != nil { + upMetricValue = 0 + } + ch <- prometheus.MustNewConstMetric( + deprecatedMetricUp, prometheus.GaugeValue, upMetricValue, + ) +} + +func (e *Exporter) collectDeprecatedErrorCountMetric(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", + ) +} + +func (e *Exporter) collectDeprecatedBadIpsPerJailMetrics(ch chan<- prometheus.Metric) { + jailNameToCountMap, err := e.db.CountBadIpsPerJail() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToCountMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricBadIpsPerJail, prometheus.GaugeValue, float64(count), jailName, + ) + } +} + +func (e *Exporter) collectDeprecatedBannedIpsPerJailMetrics(ch chan<- prometheus.Metric) { + jailNameToCountMap, err := e.db.CountBannedIpsPerJail() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToCountMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricBannedIpsPerJail, prometheus.GaugeValue, float64(count), jailName, + ) + } +} + +func (e *Exporter) collectDeprecatedEnabledJailMetrics(ch chan<- prometheus.Metric) { + jailNameToEnabledMap, err := e.db.JailNameToEnabledValue() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToEnabledMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricEnabledJails, prometheus.GaugeValue, float64(count), jailName, + ) + } +} diff --git a/src/export/exporter.go b/src/export/exporter.go new file mode 100644 index 0000000..76ebf18 --- /dev/null +++ b/src/export/exporter.go @@ -0,0 +1,64 @@ +package export + +import ( + fail2banDb "fail2ban-prometheus-exporter/db" + "fail2ban-prometheus-exporter/socket" + "github.com/prometheus/client_golang/prometheus" + "log" +) + +type Exporter struct { + db *fail2banDb.Fail2BanDB + socketPath string + exporterVersion string + lastError error + dbErrorCount int + socketConnectionErrorCount int + socketRequestErrorCount int +} + +func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { + if e.db != nil { + ch <- deprecatedMetricUp + ch <- deprecatedMetricBadIpsPerJail + ch <- deprecatedMetricBannedIpsPerJail + ch <- deprecatedMetricEnabledJails + ch <- deprecatedMetricErrorCount + } + if e.socketPath != "" { + ch <- metricServerUp + ch <- metricJailCount + ch <- metricJailFailedCurrent + ch <- metricJailFailedTotal + ch <- metricJailBannedCurrent + ch <- metricJailBannedTotal + } + ch <- metricErrorCount +} + +func (e *Exporter) Collect(ch chan<- prometheus.Metric) { + if e.db != nil { + e.collectDeprecatedBadIpsPerJailMetrics(ch) + e.collectDeprecatedBannedIpsPerJailMetrics(ch) + e.collectDeprecatedEnabledJailMetrics(ch) + e.collectDeprecatedUpMetric(ch) + e.collectDeprecatedErrorCountMetric(ch) + } + if e.socketPath != "" { + s, err := socket.ConnectToSocket(e.socketPath) + if err != nil { + log.Printf("error opening socket: %v", err) + e.socketConnectionErrorCount++ + } else { + defer s.Close() + } + e.collectServerUpMetric(ch, s) + if err == nil && s != nil { + e.collectJailMetrics(ch, s) + } + e.collectVersionMetric(ch, s) + } else { + e.collectVersionMetric(ch, nil) + } + e.collectErrorCountMetric(ch) +} diff --git a/src/export/socket.go b/src/export/socket.go new file mode 100644 index 0000000..ce2a258 --- /dev/null +++ b/src/export/socket.go @@ -0,0 +1,140 @@ +package export + +import ( + "fail2ban-prometheus-exporter/socket" + "github.com/prometheus/client_golang/prometheus" + "log" +) + +const ( + namespace = "f2b" +) + +var ( + metricErrorCount = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "errors"), + "Number of errors found since startup", + []string{"type"}, nil, + ) + metricServerUp = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "up"), + "Check if the fail2ban server is up", + nil, nil, + ) + metricJailCount = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_count"), + "Number of defined jails", + nil, nil, + ) + metricJailFailedCurrent = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_failed_current"), + "Number of current failures on this jail's filter", + []string{"jail"}, nil, + ) + metricJailFailedTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_failed_total"), + "Number of total failures on this jail's filter", + []string{"jail"}, nil, + ) + metricJailBannedCurrent = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_banned_current"), + "Number of IPs currently banned in this jail", + []string{"jail"}, nil, + ) + metricJailBannedTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_banned_total"), + "Total number of IPs banned by this jail (includes expired bans)", + []string{"jail"}, nil, + ) + metricVersionInfo = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "version"), + "Version of the exporter and fail2ban server", + []string{"exporter", "fail2ban"}, nil, + ) +) + +func (e *Exporter) collectErrorCountMetric(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", + ) + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.socketConnectionErrorCount), "socket_conn", + ) + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.socketRequestErrorCount), "socket_req", + ) +} + +func (e *Exporter) collectServerUpMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + var serverUp float64 = 0 + if s != nil { + pingSuccess, err := s.Ping() + if err != nil { + e.socketRequestErrorCount++ + log.Print(err) + } + if err == nil && pingSuccess { + serverUp = 1 + } + } + ch <- prometheus.MustNewConstMetric( + metricServerUp, prometheus.GaugeValue, serverUp, + ) +} + +func (e *Exporter) collectJailMetrics(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + jails, err := s.GetJails() + var count float64 = 0 + if err != nil { + e.socketRequestErrorCount++ + log.Print(err) + } + if err == nil { + count = float64(len(jails)) + } + ch <- prometheus.MustNewConstMetric( + metricJailCount, prometheus.GaugeValue, count, + ) + + for i := range jails { + e.collectJailStatsMetric(ch, s, jails[i]) + } +} + +func (e *Exporter) collectJailStatsMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket, jail string) { + stats, err := s.GetJailStats(jail) + if err != nil { + e.socketRequestErrorCount++ + log.Printf("failed to get stats for jail %s: %v", jail, err) + return + } + + ch <- prometheus.MustNewConstMetric( + metricJailFailedCurrent, prometheus.GaugeValue, float64(stats.FailedCurrent), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailFailedTotal, prometheus.GaugeValue, float64(stats.FailedTotal), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailBannedCurrent, prometheus.GaugeValue, float64(stats.BannedCurrent), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailBannedTotal, prometheus.GaugeValue, float64(stats.BannedTotal), jail, + ) +} + +func (e *Exporter) collectVersionMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + var err error + var fail2banVersion = "" + if s != nil { + fail2banVersion, err = s.GetServerVersion() + if err != nil { + e.socketRequestErrorCount++ + log.Printf("failed to get fail2ban server version: %v", err) + } + } + + ch <- prometheus.MustNewConstMetric( + metricVersionInfo, prometheus.GaugeValue, float64(1), e.exporterVersion, fail2banVersion, + ) +} diff --git a/src/exporter.go b/src/exporter.go index 1cf422f..c30dcce 100644 --- a/src/exporter.go +++ b/src/exporter.go @@ -2,8 +2,7 @@ package main import ( "fail2ban-prometheus-exporter/cfg" - fail2banDb "fail2ban-prometheus-exporter/db" - "fail2ban-prometheus-exporter/socket" + "fail2ban-prometheus-exporter/export" "fmt" "log" "net/http" @@ -13,290 +12,13 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -const ( - deprecatedNamespace = "fail2ban" - namespace = "f2b" -) - var ( version = "dev" commit = "none" date = "unknown" builtBy = "unknown" - - deprecatedMetricUp = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "up"), - "(Deprecated) Was the last fail2ban query successful.", - nil, nil, - ) - deprecatedMetricBannedIpsPerJail = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "banned_ips"), - "(Deprecated) Number of banned IPs stored in the database (per jail).", - []string{"jail"}, nil, - ) - deprecatedMetricBadIpsPerJail = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "bad_ips"), - "(Deprecated) Number of bad IPs stored in the database (per jail).", - []string{"jail"}, nil, - ) - deprecatedMetricEnabledJails = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "enabled_jails"), - "(Deprecated) Enabled jails.", - []string{"jail"}, nil, - ) - deprecatedMetricErrorCount = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "errors"), - "(Deprecated) Number of errors found since startup.", - []string{"type"}, nil, - ) - - metricErrorCount = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "errors"), - "Number of errors found since startup", - []string{"type"}, nil, - ) - metricServerUp = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "up"), - "Check if the fail2ban server is up", - nil, nil, - ) - metricJailCount = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_count"), - "Number of defined jails", - nil, nil, - ) - metricJailFailedCurrent = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_failed_current"), - "Number of current failures on this jail's filter", - []string{"jail"}, nil, - ) - metricJailFailedTotal = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_failed_total"), - "Number of total failures on this jail's filter", - []string{"jail"}, nil, - ) - metricJailBannedCurrent = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_banned_current"), - "Number of IPs currently banned in this jail", - []string{"jail"}, nil, - ) - metricJailBannedTotal = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_banned_total"), - "Total number of IPs banned by this jail (includes expired bans)", - []string{"jail"}, nil, - ) - metricVersionInfo = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "version"), - "Version of the exporter and fail2ban server", - []string{"exporter", "fail2ban"}, nil, - ) ) -type Exporter struct { - db *fail2banDb.Fail2BanDB - socketPath string - lastError error - dbErrorCount int - socketConnectionErrorCount int - socketRequestErrorCount int -} - -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - if e.db != nil { - ch <- deprecatedMetricUp - ch <- deprecatedMetricBadIpsPerJail - ch <- deprecatedMetricBannedIpsPerJail - ch <- deprecatedMetricEnabledJails - ch <- deprecatedMetricErrorCount - } - if e.socketPath != "" { - ch <- metricServerUp - ch <- metricJailCount - ch <- metricJailFailedCurrent - ch <- metricJailFailedTotal - ch <- metricJailBannedCurrent - ch <- metricJailBannedTotal - } - ch <- metricErrorCount -} - -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - if e.db != nil { - e.collectDeprecatedBadIpsPerJailMetrics(ch) - e.collectDeprecatedBannedIpsPerJailMetrics(ch) - e.collectDeprecatedEnabledJailMetrics(ch) - e.collectDeprecatedUpMetric(ch) - e.collectDeprecatedErrorCountMetric(ch) - } - if e.socketPath != "" { - s, err := socket.ConnectToSocket(e.socketPath) - if err != nil { - log.Printf("error opening socket: %v", err) - e.socketConnectionErrorCount++ - } else { - defer s.Close() - } - e.collectServerUpMetric(ch, s) - if err == nil && s != nil { - e.collectJailMetrics(ch, s) - } - e.collectVersionMetric(ch, s) - } else { - e.collectVersionMetric(ch, nil) - } - e.collectErrorCountMetric(ch) -} - -func (e *Exporter) collectDeprecatedUpMetric(ch chan<- prometheus.Metric) { - var upMetricValue float64 = 1 - if e.lastError != nil { - upMetricValue = 0 - } - ch <- prometheus.MustNewConstMetric( - deprecatedMetricUp, prometheus.GaugeValue, upMetricValue, - ) -} - -func (e *Exporter) collectDeprecatedErrorCountMetric(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", - ) -} - -func (e *Exporter) collectDeprecatedBadIpsPerJailMetrics(ch chan<- prometheus.Metric) { - jailNameToCountMap, err := e.db.CountBadIpsPerJail() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToCountMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricBadIpsPerJail, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectDeprecatedBannedIpsPerJailMetrics(ch chan<- prometheus.Metric) { - jailNameToCountMap, err := e.db.CountBannedIpsPerJail() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToCountMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricBannedIpsPerJail, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectDeprecatedEnabledJailMetrics(ch chan<- prometheus.Metric) { - jailNameToEnabledMap, err := e.db.JailNameToEnabledValue() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToEnabledMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricEnabledJails, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectErrorCountMetric(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", - ) - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.socketConnectionErrorCount), "socket_conn", - ) - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.socketRequestErrorCount), "socket_req", - ) -} - -func (e *Exporter) collectServerUpMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - var serverUp float64 = 0 - if s != nil { - pingSuccess, err := s.Ping() - if err != nil { - e.socketRequestErrorCount++ - log.Print(err) - } - if err == nil && pingSuccess { - serverUp = 1 - } - } - ch <- prometheus.MustNewConstMetric( - metricServerUp, prometheus.GaugeValue, serverUp, - ) -} - -func (e *Exporter) collectJailMetrics(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - jails, err := s.GetJails() - var count float64 = 0 - if err != nil { - e.socketRequestErrorCount++ - log.Print(err) - } - if err == nil { - count = float64(len(jails)) - } - ch <- prometheus.MustNewConstMetric( - metricJailCount, prometheus.GaugeValue, count, - ) - - for i := range jails { - e.collectJailStatsMetric(ch, s, jails[i]) - } -} - -func (e *Exporter) collectJailStatsMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket, jail string) { - stats, err := s.GetJailStats(jail) - if err != nil { - e.socketRequestErrorCount++ - log.Printf("failed to get stats for jail %s: %v", jail, err) - return - } - - ch <- prometheus.MustNewConstMetric( - metricJailFailedCurrent, prometheus.GaugeValue, float64(stats.FailedCurrent), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailFailedTotal, prometheus.GaugeValue, float64(stats.FailedTotal), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailBannedCurrent, prometheus.GaugeValue, float64(stats.BannedCurrent), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailBannedTotal, prometheus.GaugeValue, float64(stats.BannedTotal), jail, - ) -} - -func (e *Exporter) collectVersionMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - var err error - var fail2banVersion = "" - if s != nil { - fail2banVersion, err = s.GetServerVersion() - if err != nil { - e.socketRequestErrorCount++ - log.Printf("failed to get fail2ban server version: %v", err) - } - } - - ch <- prometheus.MustNewConstMetric( - metricVersionInfo, prometheus.GaugeValue, float64(1), version, fail2banVersion, - ) -} - func printAppVersion() { fmt.Println(version) fmt.Printf(" build date: %s\r\n commit hash: %s\r\n built by: %s\r\n", date, commit, builtBy) @@ -309,14 +31,7 @@ func main() { } else { log.Print("starting fail2ban exporter") - exporter := &Exporter{} - if appSettings.Fail2BanDbPath != "" { - log.Print("database-based metrics have been deprecated and will be removed in a future release") - exporter.db = fail2banDb.MustConnectToDb(appSettings.Fail2BanDbPath) - } - if appSettings.Fail2BanSocketPath != "" { - exporter.socketPath = appSettings.Fail2BanSocketPath - } + exporter := export.NewExporter(appSettings, version) prometheus.MustRegister(exporter) http.Handle("/metrics", promhttp.Handler())