Skip to content

Commit 1e42f5a

Browse files
authored
breaking up exporter.go and exporter_test.go into smaller units (oliver006#474)
* breaking up exporter.go and exporter_test.go into smaller, more manageable units * simplify key-value handling
1 parent fca0c80 commit 1e42f5a

28 files changed

+3561
-3295
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"redis://redis6:6379": "",
3+
"redis://pwd-redis5:6380": "redis-password"
4+

exporter/clients.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package exporter
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
7+
"github.com/gomodule/redigo/redis"
8+
"github.com/prometheus/client_golang/prometheus"
9+
log "github.com/sirupsen/logrus"
10+
)
11+
12+
/*
13+
Valid Examples
14+
id=11 addr=127.0.0.1:63508 fd=8 name= age=6321 idle=6320 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=setex
15+
id=14 addr=127.0.0.1:64958 fd=9 name= age=5 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
16+
*/
17+
func parseClientListString(clientInfo string) ([]string, bool) {
18+
if matched, _ := regexp.MatchString(`^id=\d+ addr=\d+`, clientInfo); !matched {
19+
return nil, false
20+
}
21+
connectedClient := map[string]string{}
22+
for _, kvPart := range strings.Split(clientInfo, " ") {
23+
vPart := strings.Split(kvPart, "=")
24+
if len(vPart) != 2 {
25+
log.Debugf("Invalid format for client list string, got: %s", kvPart)
26+
return nil, false
27+
}
28+
connectedClient[vPart[0]] = vPart[1]
29+
}
30+
31+
hostPortString := strings.Split(connectedClient["addr"], ":")
32+
if len(hostPortString) != 2 {
33+
return nil, false
34+
}
35+
36+
return []string{
37+
connectedClient["name"],
38+
connectedClient["age"],
39+
connectedClient["idle"],
40+
connectedClient["flags"],
41+
connectedClient["db"],
42+
connectedClient["omem"],
43+
connectedClient["cmd"],
44+
45+
hostPortString[0], // host
46+
hostPortString[1], // port
47+
}, true
48+
49+
}
50+
51+
func (e *Exporter) extractConnectedClientMetrics(ch chan<- prometheus.Metric, c redis.Conn) {
52+
reply, err := redis.String(doRedisCmd(c, "CLIENT", "LIST"))
53+
if err != nil {
54+
log.Errorf("CLIENT LIST err: %s", err)
55+
return
56+
}
57+
58+
for _, c := range strings.Split(reply, "\n") {
59+
if lbls, ok := parseClientListString(c); ok {
60+
61+
// port is the last item, we'll trim it if it's not needed
62+
if !e.options.ExportClientsInclPort {
63+
lbls = lbls[:len(lbls)-1]
64+
}
65+
e.registerConstMetricGauge(
66+
ch, "connected_clients_details", 1.0,
67+
lbls...,
68+
)
69+
}
70+
}
71+
}

exporter/clients_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package exporter
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/prometheus/client_golang/prometheus"
8+
)
9+
10+
func TestParseClientListString(t *testing.T) {
11+
tsts := map[string][]string{
12+
"id=11 addr=127.0.0.1:63508 fd=8 name= age=6321 idle=6320 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=setex": []string{"", "6321", "6320", "N", "0", "0", "setex", "127.0.0.1", "63508"},
13+
"id=14 addr=127.0.0.1:64958 fd=9 name=foo age=5 idle=0 flags=N db=1 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client": []string{"foo", "5", "0", "N", "1", "0", "client", "127.0.0.1", "64958"},
14+
}
15+
16+
for k, v := range tsts {
17+
lbls, ok := parseClientListString(k)
18+
mismatch := false
19+
for idx, l := range lbls {
20+
if l != v[idx] {
21+
mismatch = true
22+
break
23+
}
24+
}
25+
if !ok || mismatch {
26+
t.Errorf("TestParseClientListString( %s ) error. Given: %s Wanted: %s", k, lbls, v)
27+
}
28+
}
29+
}
30+
31+
func TestExportClientList(t *testing.T) {
32+
for _, isExportClientList := range []bool{true, false} {
33+
e := getTestExporterWithOptions(Options{
34+
Namespace: "test", Registry: prometheus.NewRegistry(),
35+
ExportClientList: isExportClientList,
36+
})
37+
38+
chM := make(chan prometheus.Metric)
39+
go func() {
40+
e.Collect(chM)
41+
close(chM)
42+
}()
43+
44+
found := false
45+
for m := range chM {
46+
if strings.Contains(m.Desc().String(), "connected_clients_details") {
47+
found = true
48+
}
49+
}
50+
51+
if isExportClientList && !found {
52+
t.Errorf("connected_clients_details was *not* found in isExportClientList metrics but expected")
53+
} else if !isExportClientList && found {
54+
t.Errorf("connected_clients_details was *found* in isExportClientList metrics but *not* expected")
55+
}
56+
}
57+
}
58+
59+
func TestExportClientListInclPort(t *testing.T) {
60+
for _, inclPort := range []bool{true, false} {
61+
e := getTestExporterWithOptions(Options{
62+
Namespace: "test", Registry: prometheus.NewRegistry(),
63+
ExportClientList: true,
64+
ExportClientsInclPort: inclPort,
65+
})
66+
67+
chM := make(chan prometheus.Metric)
68+
go func() {
69+
e.Collect(chM)
70+
close(chM)
71+
}()
72+
73+
found := false
74+
for m := range chM {
75+
desc := m.Desc().String()
76+
if strings.Contains(desc, "connected_clients_details") {
77+
if strings.Contains(desc, "port") {
78+
found = true
79+
}
80+
}
81+
}
82+
83+
if inclPort && !found {
84+
t.Errorf(`connected_clients_details did *not* include "port" in isExportClientList metrics but was expected`)
85+
} else if !inclPort && found {
86+
t.Errorf(`connected_clients_details did *include* "port" in isExportClientList metrics but was *not* expected`)
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)