summaryrefslogtreecommitdiff
path: root/bind-exporter/main.go
blob: 55b1eac3d79b6983737c67575096d56ff5e924c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

type BindStats struct {
	Version            string                 `json:"json-stats-version"`
	BindVersion        string                 `json:"version"`
	BootTime           time.Time              `json:"boot-time"`
	ConfigTime         time.Time              `json:"config-time"`
	OperationCodeCount map[string]int         `json:"opcodes"`
	ResponseCodeCount  map[string]int         `json:"rcodes"`
	QueryTypeCount     map[string]int         `json:"qtypes"`
	NameserverStats    map[string]int         `json:"nsstats"`
	ZoneStats          map[string]int         `json:"zonestats"`
	MemoryStats        MemoryStats            `json:"memory"`
	ResolverStats      struct{ Mismatch int } `json:"resstats"`
	SocketStats        map[string]int         `json:"sockstats"`
	TrafficHistograms  map[string]Histogram   `json:"traffic"`
	Views              map[string]View        `json:"views"`
}

type Histogram struct {
	Buckets []HistogramBucket
}

func (h *Histogram) UnmarshalJSON(v []byte) error {
	mv := map[string]int{}

	if err := json.Unmarshal(v, &mv); err != nil {
		return err
	}

	if h.Buckets == nil {
		h.Buckets = make([]HistogramBucket, 0, len(mv))
	}

	for k, v := range mv {
		var min, max int

		if strings.HasSuffix(k, "+") {
			min, _ = strconv.Atoi(k[:len(k)-1])
			max = 0
		} else {
			mm := strings.Split(k, "-")
			min, _ = strconv.Atoi(mm[0])
			max, _ = strconv.Atoi(mm[1])
		}
		h.Buckets = append(h.Buckets, HistogramBucket{
			Min:          min,
			Max:          max,
			Observations: v,
		})
	}

	return nil
}

type HistogramBucket struct {
	Min          int
	Max          int
	Observations int
}

type View struct {
	Zones    []Zone            `json:"zones"`
	Resolver ViewResolverStats `json:"resolver"`
}

type Zone struct {
	Name          string         `json:"name"`
	Class         string         `json:"class"`
	Serial        int            `json:"serial"`
	Type          string         `json:"type"`
	LoadTime      time.Time      `json:"loaded"`
	ResponseCodes map[string]int `json:"rcodes"`
	QueryTypes    map[string]int `json:"qtypes"`
}

type ViewResolverStats struct {
	Stats            map[string]int `json:"stats"`
	CacheStats       map[string]int `json:"cachestats"`
	QueryTypes       map[string]int `json:"qtypes"`
	CacheRecordTypes map[string]int `json:"cache"`
	ADB              struct {
		NumberOfEntries int `json:"nentries"`
		EntryCount      int `json:"entriescnt"`
		NumberOfNames   int `json:"nnames"`
		NamesCount      int `json:"namescnt"`
	} `json:"adb"`
}

type MemoryStats struct {
	TotalUse    int `json:"TotalUse"`
	InUse       int `json:"InUse"`
	Malloced    int `json:"Malloced"`
	BlockSize   int `json:"BlockSize"`
	ContextSize int `json:"ContextSize"`
	Lost        int `json:"Lost"`
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		target := r.URL.Query().Get("target")
		if target == "" {
			http.Error(w, "'target' must be specified exactly once", http.StatusBadRequest)
			return
		}

		u, err := url.Parse(fmt.Sprintf("http://%s", target))
		if err != nil {
			http.Error(w, "unable to parse target URL", http.StatusBadRequest)
			return
		}

		u.Scheme = "http"
		u.Path = "/json"

		ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
		defer cancel()

		registry := prometheus.NewRegistry()
		c := NewBindCollector(ctx, u.String())
		registry.MustRegister(c)
		h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
		h.ServeHTTP(w, r)
	})

	http.ListenAndServe(":8080", nil)
}