summaryrefslogtreecommitdiff
path: root/bind-exporter/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'bind-exporter/main.go')
-rw-r--r--bind-exporter/main.go141
1 files changed, 141 insertions, 0 deletions
diff --git a/bind-exporter/main.go b/bind-exporter/main.go
new file mode 100644
index 0000000..55b1eac
--- /dev/null
+++ b/bind-exporter/main.go
@@ -0,0 +1,141 @@
1package main
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "net/url"
9 "strconv"
10 "strings"
11 "time"
12
13 "github.com/prometheus/client_golang/prometheus"
14 "github.com/prometheus/client_golang/prometheus/promhttp"
15)
16
17type BindStats struct {
18 Version string `json:"json-stats-version"`
19 BindVersion string `json:"version"`
20 BootTime time.Time `json:"boot-time"`
21 ConfigTime time.Time `json:"config-time"`
22 OperationCodeCount map[string]int `json:"opcodes"`
23 ResponseCodeCount map[string]int `json:"rcodes"`
24 QueryTypeCount map[string]int `json:"qtypes"`
25 NameserverStats map[string]int `json:"nsstats"`
26 ZoneStats map[string]int `json:"zonestats"`
27 MemoryStats MemoryStats `json:"memory"`
28 ResolverStats struct{ Mismatch int } `json:"resstats"`
29 SocketStats map[string]int `json:"sockstats"`
30 TrafficHistograms map[string]Histogram `json:"traffic"`
31 Views map[string]View `json:"views"`
32}
33
34type Histogram struct {
35 Buckets []HistogramBucket
36}
37
38func (h *Histogram) UnmarshalJSON(v []byte) error {
39 mv := map[string]int{}
40
41 if err := json.Unmarshal(v, &mv); err != nil {
42 return err
43 }
44
45 if h.Buckets == nil {
46 h.Buckets = make([]HistogramBucket, 0, len(mv))
47 }
48
49 for k, v := range mv {
50 var min, max int
51
52 if strings.HasSuffix(k, "+") {
53 min, _ = strconv.Atoi(k[:len(k)-1])
54 max = 0
55 } else {
56 mm := strings.Split(k, "-")
57 min, _ = strconv.Atoi(mm[0])
58 max, _ = strconv.Atoi(mm[1])
59 }
60 h.Buckets = append(h.Buckets, HistogramBucket{
61 Min: min,
62 Max: max,
63 Observations: v,
64 })
65 }
66
67 return nil
68}
69
70type HistogramBucket struct {
71 Min int
72 Max int
73 Observations int
74}
75
76type View struct {
77 Zones []Zone `json:"zones"`
78 Resolver ViewResolverStats `json:"resolver"`
79}
80
81type Zone struct {
82 Name string `json:"name"`
83 Class string `json:"class"`
84 Serial int `json:"serial"`
85 Type string `json:"type"`
86 LoadTime time.Time `json:"loaded"`
87 ResponseCodes map[string]int `json:"rcodes"`
88 QueryTypes map[string]int `json:"qtypes"`
89}
90
91type ViewResolverStats struct {
92 Stats map[string]int `json:"stats"`
93 CacheStats map[string]int `json:"cachestats"`
94 QueryTypes map[string]int `json:"qtypes"`
95 CacheRecordTypes map[string]int `json:"cache"`
96 ADB struct {
97 NumberOfEntries int `json:"nentries"`
98 EntryCount int `json:"entriescnt"`
99 NumberOfNames int `json:"nnames"`
100 NamesCount int `json:"namescnt"`
101 } `json:"adb"`
102}
103
104type MemoryStats struct {
105 TotalUse int `json:"TotalUse"`
106 InUse int `json:"InUse"`
107 Malloced int `json:"Malloced"`
108 BlockSize int `json:"BlockSize"`
109 ContextSize int `json:"ContextSize"`
110 Lost int `json:"Lost"`
111}
112
113func main() {
114 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
115 target := r.URL.Query().Get("target")
116 if target == "" {
117 http.Error(w, "'target' must be specified exactly once", http.StatusBadRequest)
118 return
119 }
120
121 u, err := url.Parse(fmt.Sprintf("http://%s", target))
122 if err != nil {
123 http.Error(w, "unable to parse target URL", http.StatusBadRequest)
124 return
125 }
126
127 u.Scheme = "http"
128 u.Path = "/json"
129
130 ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
131 defer cancel()
132
133 registry := prometheus.NewRegistry()
134 c := NewBindCollector(ctx, u.String())
135 registry.MustRegister(c)
136 h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
137 h.ServeHTTP(w, r)
138 })
139
140 http.ListenAndServe(":8080", nil)
141}