forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
info.go
129 lines (106 loc) · 2.83 KB
/
info.go
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
package info
import (
"strings"
"time"
rd "github.com/garyburd/redigo/redis"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/metricbeat/helper"
)
var (
debugf = logp.MakeDebug("redis-info")
)
func init() {
if err := helper.Registry.AddMetricSeter("redis", "info", New); err != nil {
panic(err)
}
}
// New creates new instance of MetricSeter
func New() helper.MetricSeter {
return &MetricSeter{
redisPools: map[string]*rd.Pool{},
}
}
type MetricSeter struct {
redisPools map[string]*rd.Pool
}
// Configure connection pool for each Redis host
func (m *MetricSeter) Setup(ms *helper.MetricSet) error {
// Additional configuration options
config := struct {
Network string `config:"network"`
MaxConn int `config:"maxconn"`
Password string `config:"password"`
}{
Network: "tcp",
MaxConn: 10,
Password: "",
}
if err := ms.Module.ProcessConfig(&config); err != nil {
return err
}
for _, host := range ms.Config.Hosts {
redisPool := createPool(host, config.Password, config.Network, config.MaxConn, ms.Module.Timeout)
m.redisPools[host] = redisPool
}
return nil
}
func createPool(host, password, network string, maxConn int, timeout time.Duration) *rd.Pool {
return &rd.Pool{
MaxIdle: maxConn,
IdleTimeout: timeout,
Dial: func() (rd.Conn, error) {
c, err := rd.Dial(network, host)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
}
}
func (m *MetricSeter) Fetch(ms *helper.MetricSet, host string) (events common.MapStr, err error) {
// Fetch default INFO
info, err := m.fetchRedisStats(host, "default")
if err != nil {
return nil, err
}
debugf("Redis INFO from %s: %+v", host, info)
return eventMapping(info), nil
}
// fetchRedisStats returns a map of requested stats
func (m *MetricSeter) fetchRedisStats(host string, stat string) (map[string]string, error) {
c := m.redisPools[host].Get()
defer c.Close()
out, err := rd.String(c.Do("INFO", stat))
if err != nil {
logp.Err("Error retrieving INFO stats: %v", err)
return nil, err
}
return parseRedisInfo(out), nil
}
// parseRedisInfo parses the string returned by the INFO command
// Every line is split up into key and value
func parseRedisInfo(info string) map[string]string {
// Feed every line into
result := strings.Split(info, "\r\n")
// Load redis info values into array
values := map[string]string{}
for _, value := range result {
// Values are separated by :
parts := parseRedisLine(value, ":")
if len(parts) == 2 {
values[parts[0]] = parts[1]
}
}
return values
}
// parseRedisLine parses a single line returned by INFO
func parseRedisLine(s string, delimeter string) []string {
return strings.Split(s, delimeter)
}