Skip to content

Commit

Permalink
breaking up exporter.go and exporter_test.go into smaller units (oliv…
Browse files Browse the repository at this point in the history
…er006#474)

* breaking up exporter.go and exporter_test.go into smaller, more manageable units

* simplify key-value handling
  • Loading branch information
oliver006 authored Jan 29, 2021
1 parent fca0c80 commit 1e42f5a
Show file tree
Hide file tree
Showing 28 changed files with 3,561 additions and 3,295 deletions.
4 changes: 4 additions & 0 deletions contrib/sample-pwd-file.json-malformed
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"redis://redis6:6379": "",
"redis://pwd-redis5:6380": "redis-password"

71 changes: 71 additions & 0 deletions exporter/clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package exporter

import (
"regexp"
"strings"

"github.com/gomodule/redigo/redis"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
)

/*
Valid Examples
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
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
*/
func parseClientListString(clientInfo string) ([]string, bool) {
if matched, _ := regexp.MatchString(`^id=\d+ addr=\d+`, clientInfo); !matched {
return nil, false
}
connectedClient := map[string]string{}
for _, kvPart := range strings.Split(clientInfo, " ") {
vPart := strings.Split(kvPart, "=")
if len(vPart) != 2 {
log.Debugf("Invalid format for client list string, got: %s", kvPart)
return nil, false
}
connectedClient[vPart[0]] = vPart[1]
}

hostPortString := strings.Split(connectedClient["addr"], ":")
if len(hostPortString) != 2 {
return nil, false
}

return []string{
connectedClient["name"],
connectedClient["age"],
connectedClient["idle"],
connectedClient["flags"],
connectedClient["db"],
connectedClient["omem"],
connectedClient["cmd"],

hostPortString[0], // host
hostPortString[1], // port
}, true

}

func (e *Exporter) extractConnectedClientMetrics(ch chan<- prometheus.Metric, c redis.Conn) {
reply, err := redis.String(doRedisCmd(c, "CLIENT", "LIST"))
if err != nil {
log.Errorf("CLIENT LIST err: %s", err)
return
}

for _, c := range strings.Split(reply, "\n") {
if lbls, ok := parseClientListString(c); ok {

// port is the last item, we'll trim it if it's not needed
if !e.options.ExportClientsInclPort {
lbls = lbls[:len(lbls)-1]
}
e.registerConstMetricGauge(
ch, "connected_clients_details", 1.0,
lbls...,
)
}
}
}
89 changes: 89 additions & 0 deletions exporter/clients_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package exporter

import (
"strings"
"testing"

"github.com/prometheus/client_golang/prometheus"
)

func TestParseClientListString(t *testing.T) {
tsts := map[string][]string{
"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"},
"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"},
}

for k, v := range tsts {
lbls, ok := parseClientListString(k)
mismatch := false
for idx, l := range lbls {
if l != v[idx] {
mismatch = true
break
}
}
if !ok || mismatch {
t.Errorf("TestParseClientListString( %s ) error. Given: %s Wanted: %s", k, lbls, v)
}
}
}

func TestExportClientList(t *testing.T) {
for _, isExportClientList := range []bool{true, false} {
e := getTestExporterWithOptions(Options{
Namespace: "test", Registry: prometheus.NewRegistry(),
ExportClientList: isExportClientList,
})

chM := make(chan prometheus.Metric)
go func() {
e.Collect(chM)
close(chM)
}()

found := false
for m := range chM {
if strings.Contains(m.Desc().String(), "connected_clients_details") {
found = true
}
}

if isExportClientList && !found {
t.Errorf("connected_clients_details was *not* found in isExportClientList metrics but expected")
} else if !isExportClientList && found {
t.Errorf("connected_clients_details was *found* in isExportClientList metrics but *not* expected")
}
}
}

func TestExportClientListInclPort(t *testing.T) {
for _, inclPort := range []bool{true, false} {
e := getTestExporterWithOptions(Options{
Namespace: "test", Registry: prometheus.NewRegistry(),
ExportClientList: true,
ExportClientsInclPort: inclPort,
})

chM := make(chan prometheus.Metric)
go func() {
e.Collect(chM)
close(chM)
}()

found := false
for m := range chM {
desc := m.Desc().String()
if strings.Contains(desc, "connected_clients_details") {
if strings.Contains(desc, "port") {
found = true
}
}
}

if inclPort && !found {
t.Errorf(`connected_clients_details did *not* include "port" in isExportClientList metrics but was expected`)
} else if !inclPort && found {
t.Errorf(`connected_clients_details did *include* "port" in isExportClientList metrics but was *not* expected`)
}
}
}
Loading

0 comments on commit 1e42f5a

Please sign in to comment.