1
0
mirror of https://github.com/Luzifer/bind-log-metrics.git synced 2024-09-16 18:38:23 +00:00
bind-log-metrics/main.go
Knut Ahlers 426bd3fb85
Handle metrics errors, prevent blocking err chan
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2020-01-26 14:54:31 +01:00

125 lines
2.9 KiB
Go

package main
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/Luzifer/rconfig/v2"
)
type metric string
const (
metricQuery metric = "dns_query"
metricBlock metric = "dns_block"
)
var (
cfg = struct {
InfluxDBName string `flag:"influx-db-name" description:"Database name of the InfluxDB" validate:"nonzero"`
InfluxHost string `flag:"influx-host" description:"Hostname of the InfluxDB" validate:"nonzero"`
InfluxPass string `flag:"influx-pass" description:"Password of the InfluxDB" validate:"nonzero"`
InfluxUser string `flag:"influx-user" description:"Username of the InfluxDB" validate:"nonzero"`
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
metrics *metricsSender
regBlock = regexp.MustCompile(`([0-9:.]+)#.*: rpz QNAME NXDOMAIN rewrite ([^/]+)/([^/]+)/IN`)
regQuery = regexp.MustCompile(`([0-9:.]+)#.*: query: ([^ ]+) IN ([^ ]+) \+`)
version = "dev"
)
func init() {
rconfig.AutoEnv(true)
if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err)
}
if cfg.VersionAndExit {
fmt.Printf("bind-log-metrics %s\n", version)
os.Exit(0)
}
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
log.WithError(err).Fatal("Unable to parse log level")
} else {
log.SetLevel(l)
}
}
func main() {
var err error
if metrics, err = newMetricsSender(cfg.InfluxHost, cfg.InfluxUser, cfg.InfluxPass, cfg.InfluxDBName); err != nil {
log.WithError(err).Fatal("Unable to create metrics client")
}
go func() {
for err := range metrics.Errors() {
log.WithError(err).Error("Metrics processing caused an error")
}
}()
var input io.Reader = os.Stdin
if len(rconfig.Args()) > 1 {
f, err := os.Open(rconfig.Args()[1])
if err != nil {
log.WithError(err).Fatal("Unable to open input file")
}
defer f.Close()
input = f
}
scanner := bufio.NewScanner(input)
for scanner.Scan() {
var line = scanner.Text()
// Re-yield the line scanned from stdin
fmt.Fprintln(os.Stdout, line)
switch {
case regBlock.MatchString(line):
err = handleRecord(metricBlock, regBlock.FindStringSubmatch(line))
case regQuery.MatchString(line):
err = handleRecord(metricQuery, regQuery.FindStringSubmatch(line))
default:
continue
}
if err != nil {
log.WithError(err).Error("Unable to process line")
}
}
if err := scanner.Err(); err != nil {
log.WithError(err).Fatal("Scanner caused an error")
}
}
func handleRecord(m metric, groups []string) error {
return errors.Wrap(
metrics.RecordPoint(string(m), map[string]string{
"client": groups[1],
"domain": groups[2],
"type": groups[3],
}, map[string]interface{}{
"count": 1,
}),
"Unable to add metrics point",
)
}