167 lines
4.1 KiB
Go
167 lines
4.1 KiB
Go
package bind
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/metric"
|
|
)
|
|
|
|
type jsonStats struct {
|
|
OpCodes map[string]int
|
|
QTypes map[string]int
|
|
NSStats map[string]int
|
|
SockStats map[string]int
|
|
Views map[string]jsonView
|
|
Memory jsonMemory
|
|
}
|
|
|
|
type jsonMemory struct {
|
|
TotalUse int
|
|
InUse int
|
|
BlockSize int
|
|
ContextSize int
|
|
Lost int
|
|
Contexts []struct {
|
|
Id string
|
|
Name string
|
|
Total int
|
|
InUse int
|
|
}
|
|
}
|
|
|
|
type jsonView struct {
|
|
Resolver map[string]map[string]int
|
|
}
|
|
|
|
// addJSONCounter adds a counter array to a Telegraf Accumulator, with the specified tags.
|
|
func addJSONCounter(acc telegraf.Accumulator, commonTags map[string]string, stats map[string]int) {
|
|
grouper := metric.NewSeriesGrouper()
|
|
ts := time.Now()
|
|
for name, value := range stats {
|
|
if commonTags["type"] == "opcode" && strings.HasPrefix(name, "RESERVED") {
|
|
continue
|
|
}
|
|
|
|
tags := make(map[string]string)
|
|
|
|
// Create local copy of tags since maps are reference types
|
|
for k, v := range commonTags {
|
|
tags[k] = v
|
|
}
|
|
|
|
grouper.Add("bind_counter", tags, ts, name, value)
|
|
}
|
|
|
|
//Add grouped metrics
|
|
for _, metric := range grouper.Metrics() {
|
|
acc.AddMetric(metric)
|
|
}
|
|
}
|
|
|
|
// addStatsJson walks a jsonStats struct and adds the values to the telegraf.Accumulator.
|
|
func (b *Bind) addStatsJSON(stats jsonStats, acc telegraf.Accumulator, urlTag string) {
|
|
grouper := metric.NewSeriesGrouper()
|
|
ts := time.Now()
|
|
tags := map[string]string{"url": urlTag}
|
|
host, port, _ := net.SplitHostPort(urlTag)
|
|
tags["source"] = host
|
|
tags["port"] = port
|
|
|
|
// Opcodes
|
|
tags["type"] = "opcode"
|
|
addJSONCounter(acc, tags, stats.OpCodes)
|
|
|
|
// Query RDATA types
|
|
tags["type"] = "qtype"
|
|
addJSONCounter(acc, tags, stats.QTypes)
|
|
|
|
// Nameserver stats
|
|
tags["type"] = "nsstat"
|
|
addJSONCounter(acc, tags, stats.NSStats)
|
|
|
|
// Socket statistics
|
|
tags["type"] = "sockstat"
|
|
addJSONCounter(acc, tags, stats.SockStats)
|
|
|
|
// Memory stats
|
|
fields := map[string]interface{}{
|
|
"total_use": stats.Memory.TotalUse,
|
|
"in_use": stats.Memory.InUse,
|
|
"block_size": stats.Memory.BlockSize,
|
|
"context_size": stats.Memory.ContextSize,
|
|
"lost": stats.Memory.Lost,
|
|
}
|
|
acc.AddGauge("bind_memory", fields, map[string]string{"url": urlTag, "source": host, "port": port})
|
|
|
|
// Detailed, per-context memory stats
|
|
if b.GatherMemoryContexts {
|
|
for _, c := range stats.Memory.Contexts {
|
|
tags := map[string]string{"url": urlTag, "id": c.Id, "name": c.Name, "source": host, "port": port}
|
|
fields := map[string]interface{}{"total": c.Total, "in_use": c.InUse}
|
|
|
|
acc.AddGauge("bind_memory_context", fields, tags)
|
|
}
|
|
}
|
|
|
|
// Detailed, per-view stats
|
|
if b.GatherViews {
|
|
for vName, view := range stats.Views {
|
|
for cntrType, counters := range view.Resolver {
|
|
for cntrName, value := range counters {
|
|
tags := map[string]string{
|
|
"url": urlTag,
|
|
"source": host,
|
|
"port": port,
|
|
"view": vName,
|
|
"type": cntrType,
|
|
}
|
|
|
|
grouper.Add("bind_counter", tags, ts, cntrName, value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Add grouped metrics
|
|
for _, metric := range grouper.Metrics() {
|
|
acc.AddMetric(metric)
|
|
}
|
|
}
|
|
|
|
// readStatsJSON takes a base URL to probe, and requests the individual statistics blobs that we
|
|
// are interested in. These individual blobs have a combined size which is significantly smaller
|
|
// than if we requested everything at once (e.g. taskmgr and socketmgr can be omitted).
|
|
func (b *Bind) readStatsJSON(addr *url.URL, acc telegraf.Accumulator) error {
|
|
var stats jsonStats
|
|
|
|
// Progressively build up full jsonStats struct by parsing the individual HTTP responses
|
|
for _, suffix := range [...]string{"/server", "/net", "/mem"} {
|
|
scrapeUrl := addr.String() + suffix
|
|
|
|
resp, err := client.Get(scrapeUrl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("%s returned HTTP status: %s", scrapeUrl, resp.Status)
|
|
}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
|
|
return fmt.Errorf("Unable to decode JSON blob: %s", err)
|
|
}
|
|
}
|
|
|
|
b.addStatsJSON(stats, acc, addr.Host)
|
|
return nil
|
|
}
|