From 3384cc92e796dfa77b99208a0ab6c990169c307e Mon Sep 17 00:00:00 2001 From: Hector Date: Mon, 11 Oct 2021 21:42:52 +0100 Subject: [PATCH] implement new collector for textfile metrics --- src/cfg/cfg.go | 5 +++- src/exporter.go | 13 +++++++++- src/textfile/collector.go | 36 ++++++++++++++++++++++++++ src/textfile/file.go | 21 +++++++++++++++ src/textfile/writer.go | 54 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/textfile/collector.go create mode 100644 src/textfile/file.go create mode 100644 src/textfile/writer.go diff --git a/src/cfg/cfg.go b/src/cfg/cfg.go index b9c4f3e..457a9b3 100644 --- a/src/cfg/cfg.go +++ b/src/cfg/cfg.go @@ -46,8 +46,11 @@ func (settings *AppSettings) validateFlags() { minServerPort, maxServerPort, settings.MetricsPort) flagsValid = false } + if settings.FileCollectorPath != "" { + settings.FileCollectorEnabled = true + } if settings.FileCollectorEnabled && settings.FileCollectorPath == "" { - fmt.Printf("file collector directory must not be empty if collector enabled\n") + fmt.Printf("file collector directory path must not be empty if collector enabled\n") flagsValid = false } } diff --git a/src/exporter.go b/src/exporter.go index b06ca2c..00fb395 100644 --- a/src/exporter.go +++ b/src/exporter.go @@ -3,6 +3,7 @@ package main import ( "fail2ban-prometheus-exporter/cfg" "fail2ban-prometheus-exporter/export" + "fail2ban-prometheus-exporter/textfile" "fmt" "log" "net/http" @@ -43,6 +44,11 @@ func rootHtmlHandler(w http.ResponseWriter, r *http.Request) { } } +func metricHandler(w http.ResponseWriter, r *http.Request, collector *textfile.Collector) { + promhttp.Handler().ServeHTTP(w, r) + collector.WriteTextFileMetrics(w, r) +} + func main() { appSettings := cfg.Parse() if appSettings.VersionMode { @@ -55,8 +61,13 @@ func main() { exporter := export.NewExporter(appSettings, version) prometheus.MustRegister(exporter) + textFileCollector := textfile.NewCollector(appSettings) + prometheus.MustRegister(textFileCollector) + http.HandleFunc("/", rootHtmlHandler) - http.Handle(metricsPath, promhttp.Handler()) + http.HandleFunc(metricsPath, func(w http.ResponseWriter, r *http.Request) { + metricHandler(w, r, textFileCollector) + }) log.Printf("metrics available at '%s'", metricsPath) svrErr := make(chan error) diff --git a/src/textfile/collector.go b/src/textfile/collector.go new file mode 100644 index 0000000..1bcea12 --- /dev/null +++ b/src/textfile/collector.go @@ -0,0 +1,36 @@ +package textfile + +import ( + "fail2ban-prometheus-exporter/cfg" + "github.com/prometheus/client_golang/prometheus" +) + +type Collector struct { + enabled bool + folderPath string + fileMap map[string]fileMetrics +} + +type fileMetrics struct { + readErrors int +} + +func NewCollector(appSettings *cfg.AppSettings) *Collector { + return &Collector{ + enabled: appSettings.FileCollectorEnabled, + folderPath: appSettings.FileCollectorPath, + fileMap: make(map[string]fileMetrics), + } +} + +func (c *Collector) Describe(ch chan<- *prometheus.Desc) { + if c.enabled { + ch <- metricReadError + } +} + +func (c *Collector) Collect(ch chan<- prometheus.Metric) { + if c.enabled { + c.collectFileErrors(ch) + } +} diff --git a/src/textfile/file.go b/src/textfile/file.go new file mode 100644 index 0000000..8e587e2 --- /dev/null +++ b/src/textfile/file.go @@ -0,0 +1,21 @@ +package textfile + +import "github.com/prometheus/client_golang/prometheus" + +const namespace = "textfile" + +var ( + metricReadError = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "error"), + "Checks for errors while reading text files", + []string{"path"}, nil, + ) +) + +func (c *Collector) collectFileErrors(ch chan<- prometheus.Metric) { + for fileName, metrics := range c.fileMap { + ch <- prometheus.MustNewConstMetric( + metricReadError, prometheus.GaugeValue, float64(metrics.readErrors), fileName, + ) + } +} diff --git a/src/textfile/writer.go b/src/textfile/writer.go new file mode 100644 index 0000000..7e49ce8 --- /dev/null +++ b/src/textfile/writer.go @@ -0,0 +1,54 @@ +package textfile + +import ( + "io/ioutil" + "log" + "net/http" + "path/filepath" + "strings" +) + +func (c *Collector) WriteTextFileMetrics(w http.ResponseWriter, r *http.Request) { + files, err := ioutil.ReadDir(c.folderPath) + if err != nil { + c.appendErrorForPath(c.folderPath) + log.Printf("error reading directory '%s': %v", c.folderPath, err) + return + } + + for _, file := range files { + fileName := file.Name() + if !strings.HasSuffix(fileName, ".prom") { + continue + } + + c.setErrorCountForPath(fileName, 0) + + fullPath := filepath.Join(c.folderPath, file.Name()) + content, err := ioutil.ReadFile(fullPath) + if err != nil { + c.appendErrorForPath(fileName) + log.Printf("error reading contents of file '%s': %v", fileName, err) + } + + _, err = w.Write(content) + if err != nil { + c.appendErrorForPath(fileName) + log.Printf("error writing file contents to response writer '%s': %v", fileName, err) + } + } +} + +func (c *Collector) appendErrorForPath(path string) { + if _, ok := c.fileMap[path]; !ok { + c.setErrorCountForPath(path, 1) + } else { + c.setErrorCountForPath(path, c.fileMap[path].readErrors + 1) + } +} + +func (c *Collector) setErrorCountForPath(path string, count int) { + c.fileMap[path] = fileMetrics{ + readErrors: count, + } +}