diff --git a/config/configuration.go b/config/configuration.go index 08f48d1..451e1f3 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -18,7 +18,7 @@ type Config struct { AdguardProtocol string `config:"adguard_protocol"` AdguardHostname string `config:"adguard_hostname"` AdguardPort uint16 `config:"adguard_port"` - AdguardUsername string `config:"adguard_username"` + AdguardUsername string `config:"adguard_username"` AdguardPassword string `config:"adguard_password"` Port string `config:"port"` Interval time.Duration `config:"interval"` @@ -29,10 +29,10 @@ func getDefaultConfig() *Config { AdguardProtocol: "http", AdguardHostname: "127.0.0.1", AdguardPort: 80, - AdguardUsername: "", + AdguardUsername: "", AdguardPassword: "", - Port: "9617", - Interval: 10 * time.Second, + Port: "9617", + Interval: 10 * time.Second, } } diff --git a/internal/adguard/client.go b/internal/adguard/client.go index eb3aafd..da61136 100644 --- a/internal/adguard/client.go +++ b/internal/adguard/client.go @@ -1,123 +1,123 @@ package adguard import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "time" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "time" - "github.com/ebrianne/adguard-exporter/internal/metrics" + "github.com/ebrianne/adguard-exporter/internal/metrics" ) var ( - statsURLPattern = "%s://%s:%d/control/stats" + statsURLPattern = "%s://%s:%d/control/stats" ) // Client struct is a AdGuard client to request an instance of a AdGuard ad blocker. type Client struct { - httpClient http.Client - interval time.Duration - protocol string - hostname string - port uint16 - b64password string + httpClient http.Client + interval time.Duration + protocol string + hostname string + port uint16 + b64password string } // NewClient method initializes a new AdGuard client. func NewClient(protocol, hostname string, port uint16, b64password string, interval time.Duration) *Client { - if protocol != "http" { - log.Printf("protocol %s is invalid. Must be http.", protocol) - os.Exit(1) - } + if protocol != "http" { + log.Printf("protocol %s is invalid. Must be http.", protocol) + os.Exit(1) + } - return &Client{ - protocol: protocol, - hostname: hostname, - port: port, - b64password: b64password, - interval: interval, - httpClient: http.Client{}, - } + return &Client{ + protocol: protocol, + hostname: hostname, + port: port, + b64password: b64password, + interval: interval, + httpClient: http.Client{}, + } } // Scrape method authenticates and retrieves statistics from AdGuard JSON API // and then pass them as Prometheus metrics. func (c *Client) Scrape() { - for range time.Tick(c.interval) { - stats := c.getStatistics() + for range time.Tick(c.interval) { + stats := c.getStatistics() - c.setMetrics(stats) + c.setMetrics(stats) - log.Printf("New tick of statistics: %s", stats.ToString()) - } + log.Printf("New tick of statistics: %s", stats.ToString()) + } } func (c *Client) setMetrics(stats *Stats) { - metrics.AvgProcessingTime.WithLabelValues(c.hostname).Set(float64(stats.AvgProcessingTime)) - metrics.DnsQueries.WithLabelValues(c.hostname).Set(float64(stats.DnsQueries)) - metrics.BlockedFiltering.WithLabelValues(c.hostname).Set(float64(stats.BlockedFiltering)) - metrics.ParentalFiltering.WithLabelValues(c.hostname).Set(float64(stats.ParentalFiltering)) - metrics.SafeBrowsingFiltering.WithLabelValues(c.hostname).Set(float64(stats.SafeBrowsingFiltering)) - metrics.SafeSearchFiltering.WithLabelValues(c.hostname).Set(float64(stats.SafeSearchFiltering)) + metrics.AvgProcessingTime.WithLabelValues(c.hostname).Set(float64(stats.AvgProcessingTime)) + metrics.DnsQueries.WithLabelValues(c.hostname).Set(float64(stats.DnsQueries)) + metrics.BlockedFiltering.WithLabelValues(c.hostname).Set(float64(stats.BlockedFiltering)) + metrics.ParentalFiltering.WithLabelValues(c.hostname).Set(float64(stats.ParentalFiltering)) + metrics.SafeBrowsingFiltering.WithLabelValues(c.hostname).Set(float64(stats.SafeBrowsingFiltering)) + metrics.SafeSearchFiltering.WithLabelValues(c.hostname).Set(float64(stats.SafeSearchFiltering)) - for l := range stats.TopQueries { - for domain, value := range stats.TopQueries[l] { - metrics.TopQueries.WithLabelValues(c.hostname, domain).Set(float64(value)) - } - } + for l := range stats.TopQueries { + for domain, value := range stats.TopQueries[l] { + metrics.TopQueries.WithLabelValues(c.hostname, domain).Set(float64(value)) + } + } - for l := range stats.TopBlocked { - for domain, value := range stats.TopBlocked[l] { - metrics.TopBlocked.WithLabelValues(c.hostname, domain).Set(float64(value)) - } - } + for l := range stats.TopBlocked { + for domain, value := range stats.TopBlocked[l] { + metrics.TopBlocked.WithLabelValues(c.hostname, domain).Set(float64(value)) + } + } - for l := range stats.TopClients { - for source, value := range stats.TopClients[l] { - metrics.TopClients.WithLabelValues(c.hostname, source).Set(float64(value)) - } - } + for l := range stats.TopClients { + for source, value := range stats.TopClients[l] { + metrics.TopClients.WithLabelValues(c.hostname, source).Set(float64(value)) + } + } } func (c *Client) getStatistics() *Stats { - var stats Stats + var stats Stats - statsURL := fmt.Sprintf(statsURLPattern, c.protocol, c.hostname, c.port) + statsURL := fmt.Sprintf(statsURLPattern, c.protocol, c.hostname, c.port) - req, err := http.NewRequest("GET", statsURL, nil) - if err != nil { - log.Fatal("An error has occurred when creating HTTP statistics request ", err) - } + req, err := http.NewRequest("GET", statsURL, nil) + if err != nil { + log.Fatal("An error has occurred when creating HTTP statistics request ", err) + } - if c.isUsingPassword() { - c.authenticateRequest(req) - } + if c.isUsingPassword() { + c.authenticateRequest(req) + } - resp, err := c.httpClient.Do(req) - if err != nil { - log.Printf("An error has occurred during login to Adguard: %v", err) - } + resp, err := c.httpClient.Do(req) + if err != nil { + log.Printf("An error has occurred during login to Adguard: %v", err) + } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Println("Unable to read Adguard statistics HTTP response", err) - } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("Unable to read Adguard statistics HTTP response", err) + } - err = json.Unmarshal(body, &stats) - if err != nil { - log.Println("Unable to unmarshal Adguard statistics to statistics struct model", err) - } + err = json.Unmarshal(body, &stats) + if err != nil { + log.Println("Unable to unmarshal Adguard statistics to statistics struct model", err) + } - return &stats + return &stats } func (c *Client) isUsingPassword() bool { - return len(c.b64password) > 0 + return len(c.b64password) > 0 } func (c *Client) authenticateRequest(req *http.Request) { - req.Header.Add("Authorization", "Basic " + c.b64password) + req.Header.Add("Authorization", "Basic "+c.b64password) } diff --git a/internal/adguard/model.go b/internal/adguard/model.go index 95f765d..a60ed99 100644 --- a/internal/adguard/model.go +++ b/internal/adguard/model.go @@ -4,18 +4,18 @@ import "fmt" // Stats struct is the Adguard statistics JSON API corresponding model. type Stats struct { - AvgProcessingTime float64 `json:"avg_processing_time"` - DnsQueries int `json:"num_dns_queries"` - BlockedFiltering int `json:"num_blocked_filtering"` - ParentalFiltering int `json:"num_replaced_parental"` - SafeBrowsingFiltering int `json:"num_replaced_safebrowsing"` - SafeSearchFiltering int `json:"num_replaced_safesearch"` - TopQueries []map[string]int `json:"top_queried_domains"` - TopBlocked []map[string]int `json:"top_blocked_domains"` - TopClients []map[string]int `json:"top_clients"` + AvgProcessingTime float64 `json:"avg_processing_time"` + DnsQueries int `json:"num_dns_queries"` + BlockedFiltering int `json:"num_blocked_filtering"` + ParentalFiltering int `json:"num_replaced_parental"` + SafeBrowsingFiltering int `json:"num_replaced_safebrowsing"` + SafeSearchFiltering int `json:"num_replaced_safesearch"` + TopQueries []map[string]int `json:"top_queried_domains"` + TopBlocked []map[string]int `json:"top_blocked_domains"` + TopClients []map[string]int `json:"top_clients"` } // ToString method returns a string of the current statistics struct. func (s *Stats) ToString() string { - return fmt.Sprintf("%d ads blocked / %d total DNS queries", s.BlockedFiltering, s.DnsQueries) + return fmt.Sprintf("%d ads blocked / %d total DNS queries", s.BlockedFiltering, s.DnsQueries) } diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index ccd4700..f568dd7 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -1,117 +1,117 @@ package metrics import ( - "log" + "log" - "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus" ) var ( - // AvgProcessingTime - Average processing time for a DNS query - AvgProcessingTime = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "avg_processing_time", - Namespace: "adguard", - Help: "This represent the average processing time for a DNS query in s", - }, - []string{"hostname"}, - ) + // AvgProcessingTime - Average processing time for a DNS query + AvgProcessingTime = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "avg_processing_time", + Namespace: "adguard", + Help: "This represent the average processing time for a DNS query in s", + }, + []string{"hostname"}, + ) - // DnsQueries - Number of DNS queries - DnsQueries = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "num_dns_queries", - Namespace: "adguard", - Help: "Number of DNS queries", - }, - []string{"hostname"}, - ) + // DnsQueries - Number of DNS queries + DnsQueries = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "num_dns_queries", + Namespace: "adguard", + Help: "Number of DNS queries", + }, + []string{"hostname"}, + ) - // BlockedFiltering - Number of DNS queries blocked - BlockedFiltering = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "num_blocked_filtering", - Namespace: "adguard", - Help: "This represent the number of domains blocked", - }, - []string{"hostname"}, - ) + // BlockedFiltering - Number of DNS queries blocked + BlockedFiltering = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "num_blocked_filtering", + Namespace: "adguard", + Help: "This represent the number of domains blocked", + }, + []string{"hostname"}, + ) - // ParentalFiltering - Number of DNS queries replaced by parental control - ParentalFiltering = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "num_replaced_parental", - Namespace: "adguard", - Help: "This represent the number of domains blocked (parental)", - }, - []string{"hostname"}, - ) + // ParentalFiltering - Number of DNS queries replaced by parental control + ParentalFiltering = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "num_replaced_parental", + Namespace: "adguard", + Help: "This represent the number of domains blocked (parental)", + }, + []string{"hostname"}, + ) - // SafeBrowsingFiltering - Number of DNS queries replaced by safe browsing - SafeBrowsingFiltering = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "num_replaced_safebrowsing", - Namespace: "adguard", - Help: "This represent the number of domains blocked (safe browsing)", - }, - []string{"hostname"}, - ) + // SafeBrowsingFiltering - Number of DNS queries replaced by safe browsing + SafeBrowsingFiltering = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "num_replaced_safebrowsing", + Namespace: "adguard", + Help: "This represent the number of domains blocked (safe browsing)", + }, + []string{"hostname"}, + ) - // SafeSearchFiltering - Number of DNS queries replaced by safe search - SafeSearchFiltering = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "num_replaced_safesearch", - Namespace: "adguard", - Help: "This represent the number of domains blocked (safe search)", - }, - []string{"hostname"}, - ) + // SafeSearchFiltering - Number of DNS queries replaced by safe search + SafeSearchFiltering = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "num_replaced_safesearch", + Namespace: "adguard", + Help: "This represent the number of domains blocked (safe search)", + }, + []string{"hostname"}, + ) - // TopQueries - The number of top queries - TopQueries = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "top_queried_domains", - Namespace: "adguard", - Help: "This represent the top queried domains", - }, - []string{"hostname", "domain"}, - ) + // TopQueries - The number of top queries + TopQueries = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "top_queried_domains", + Namespace: "adguard", + Help: "This represent the top queried domains", + }, + []string{"hostname", "domain"}, + ) - // TopBlocked - The number of top domains blocked - TopBlocked = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "top_blocked_domains", - Namespace: "adguard", - Help: "This represent the top bloacked domains", - }, - []string{"hostname", "domain"}, - ) + // TopBlocked - The number of top domains blocked + TopBlocked = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "top_blocked_domains", + Namespace: "adguard", + Help: "This represent the top bloacked domains", + }, + []string{"hostname", "domain"}, + ) - // TopClients - The number of top clients - TopClients = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "top_clients", - Namespace: "adguard", - Help: "This represent the top clients", - }, - []string{"hostname", "client"}, - ) + // TopClients - The number of top clients + TopClients = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "top_clients", + Namespace: "adguard", + Help: "This represent the top clients", + }, + []string{"hostname", "client"}, + ) ) // Init initializes all Prometheus metrics made available by AdGuard exporter. func Init() { - initMetric("avg_processing_time", AvgProcessingTime) - initMetric("num_dns_queries", DnsQueries) - initMetric("num_blocked_filtering", BlockedFiltering) - initMetric("num_replaced_parental", ParentalFiltering) - initMetric("num_replaced_safebrowsing", SafeBrowsingFiltering) - initMetric("num_replaced_safesearch", SafeSearchFiltering) - initMetric("top_queried_domains", TopQueries) - initMetric("top_blocked_domains", TopBlocked) - initMetric("top_clients", TopClients) + initMetric("avg_processing_time", AvgProcessingTime) + initMetric("num_dns_queries", DnsQueries) + initMetric("num_blocked_filtering", BlockedFiltering) + initMetric("num_replaced_parental", ParentalFiltering) + initMetric("num_replaced_safebrowsing", SafeBrowsingFiltering) + initMetric("num_replaced_safesearch", SafeSearchFiltering) + initMetric("top_queried_domains", TopQueries) + initMetric("top_blocked_domains", TopBlocked) + initMetric("top_clients", TopClients) } func initMetric(name string, metric *prometheus.GaugeVec) { - prometheus.MustRegister(metric) - log.Printf("New Prometheus metric registered: %s", name) + prometheus.MustRegister(metric) + log.Printf("New Prometheus metric registered: %s", name) } diff --git a/main.go b/main.go index fa32fbc..3500d08 100644 --- a/main.go +++ b/main.go @@ -1,64 +1,64 @@ package main import ( - "fmt" - "os" - "os/signal" - "syscall" - "time" - "encoding/base64" + "encoding/base64" + "fmt" + "os" + "os/signal" + "syscall" + "time" - "github.com/ebrianne/adguard-exporter/config" - "github.com/ebrianne/adguard-exporter/internal/metrics" - "github.com/ebrianne/adguard-exporter/internal/adguard" - "github.com/ebrianne/adguard-exporter/internal/server" + "github.com/ebrianne/adguard-exporter/config" + "github.com/ebrianne/adguard-exporter/internal/adguard" + "github.com/ebrianne/adguard-exporter/internal/metrics" + "github.com/ebrianne/adguard-exporter/internal/server" ) const ( - name = "adguard-exporter" + name = "adguard-exporter" ) var ( - s *server.Server + s *server.Server ) func main() { - conf := config.Load() + conf := config.Load() - metrics.Init() + metrics.Init() - initAdguardClient(conf.AdguardProtocol, conf.AdguardHostname, conf.AdguardPort, conf.AdguardUsername, conf.AdguardPassword, conf.Interval) - initHttpServer(conf.Port) + initAdguardClient(conf.AdguardProtocol, conf.AdguardHostname, conf.AdguardPort, conf.AdguardUsername, conf.AdguardPassword, conf.Interval) + initHttpServer(conf.Port) - handleExitSignal() + handleExitSignal() } func basicAuth(username, password string) string { - auth := username + ":" + password - return base64.StdEncoding.EncodeToString([]byte(auth)) + auth := username + ":" + password + return base64.StdEncoding.EncodeToString([]byte(auth)) } func initAdguardClient(protocol, hostname string, port uint16, username, password string, interval time.Duration) { - b64password := "" - if len(username) > 0 && len(password) > 0 { - b64password = basicAuth(username, password) - } + b64password := "" + if len(username) > 0 && len(password) > 0 { + b64password = basicAuth(username, password) + } - client := adguard.NewClient(protocol, hostname, port, b64password, interval) - go client.Scrape() + client := adguard.NewClient(protocol, hostname, port, b64password, interval) + go client.Scrape() } func initHttpServer(port string) { - s = server.NewServer(port) - go s.ListenAndServe() + s = server.NewServer(port) + go s.ListenAndServe() } func handleExitSignal() { - stop := make(chan os.Signal, 1) - signal.Notify(stop, os.Interrupt, syscall.SIGTERM) + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGTERM) - <-stop + <-stop - s.Stop() - fmt.Println(fmt.Sprintf("\n%s HTTP server stopped", name)) + s.Stop() + fmt.Println(fmt.Sprintf("\n%s HTTP server stopped", name)) }