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)
}
|