memcached plugin. Break out metric parsing into it's own func

And unit test it using a sample response string. This will make it
easier to see what other metrics are available to the plugin for adding
future metrics.
This commit is contained in:
Cameron Sparr 2015-12-03 13:53:37 -07:00
parent 03863bd84d
commit 4cf47dcd0f
2 changed files with 156 additions and 24 deletions

View File

@ -28,8 +28,8 @@ var sampleConfig = `
var defaultTimeout = 5 * time.Second var defaultTimeout = 5 * time.Second
// The list of metrics tha should be calculated // The list of metrics that should be sent
var sendAsIs = []string{ var sendMetrics = []string{
"get_hits", "get_hits",
"get_misses", "get_misses",
"evictions", "evictions",
@ -128,34 +128,16 @@ func (m *Memcached) gatherServer(
return err return err
} }
// Read response values, err := parseResponse(rw.Reader)
values := make(map[string]string) if err != nil {
return err
for {
// Read line
line, _, errRead := rw.Reader.ReadLine()
if errRead != nil {
return errRead
}
// Done
if bytes.Equal(line, []byte("END")) {
break
}
// Read values
s := bytes.SplitN(line, []byte(" "), 3)
if len(s) != 3 || !bytes.Equal(s[0], []byte("STAT")) {
return fmt.Errorf("unexpected line in stats response: %q", line)
}
// Save values
values[string(s[1])] = string(s[2])
} }
// Add server address as a tag // Add server address as a tag
tags := map[string]string{"server": address} tags := map[string]string{"server": address}
// Process values // Process values
for _, key := range sendAsIs { for _, key := range sendMetrics {
if value, ok := values[key]; ok { if value, ok := values[key]; ok {
// Mostly it is the number // Mostly it is the number
if iValue, errParse := strconv.ParseInt(value, 10, 64); errParse != nil { if iValue, errParse := strconv.ParseInt(value, 10, 64); errParse != nil {
@ -168,6 +150,31 @@ func (m *Memcached) gatherServer(
return nil return nil
} }
func parseResponse(r *bufio.Reader) (map[string]string, error) {
values := make(map[string]string)
for {
// Read line
line, _, errRead := r.ReadLine()
if errRead != nil {
return values, errRead
}
// Done
if bytes.Equal(line, []byte("END")) {
break
}
// Read values
s := bytes.SplitN(line, []byte(" "), 3)
if len(s) != 3 || !bytes.Equal(s[0], []byte("STAT")) {
return values, fmt.Errorf("unexpected line in stats response: %q", line)
}
// Save values
values[string(s[1])] = string(s[2])
}
return values, nil
}
func init() { func init() {
plugins.Add("memcached", func() plugins.Plugin { plugins.Add("memcached", func() plugins.Plugin {
return &Memcached{} return &Memcached{}

View File

@ -1,6 +1,8 @@
package memcached package memcached
import ( import (
"bufio"
"strings"
"testing" "testing"
"github.com/influxdb/telegraf/testutil" "github.com/influxdb/telegraf/testutil"
@ -33,3 +35,126 @@ func TestMemcachedGeneratesMetrics(t *testing.T) {
assert.True(t, acc.HasIntValue(metric), metric) assert.True(t, acc.HasIntValue(metric), metric)
} }
} }
func TestMemcachedParseMetrics(t *testing.T) {
r := bufio.NewReader(strings.NewReader(memcachedStats))
values, err := parseResponse(r)
require.NoError(t, err, "Error parsing memcached response")
tests := []struct {
key string
value string
}{
{"pid", "23235"},
{"uptime", "194"},
{"time", "1449174679"},
{"version", "1.4.14 (Ubuntu)"},
{"libevent", "2.0.21-stable"},
{"pointer_size", "64"},
{"rusage_user", "0.000000"},
{"rusage_system", "0.007566"},
{"curr_connections", "5"},
{"total_connections", "6"},
{"connection_structures", "6"},
{"reserved_fds", "20"},
{"cmd_get", "0"},
{"cmd_set", "0"},
{"cmd_flush", "0"},
{"cmd_touch", "0"},
{"get_hits", "0"},
{"get_misses", "0"},
{"delete_misses", "0"},
{"delete_hits", "0"},
{"incr_misses", "0"},
{"incr_hits", "0"},
{"decr_misses", "0"},
{"decr_hits", "0"},
{"cas_misses", "0"},
{"cas_hits", "0"},
{"cas_badval", "0"},
{"touch_hits", "0"},
{"touch_misses", "0"},
{"auth_cmds", "0"},
{"auth_errors", "0"},
{"bytes_read", "7"},
{"bytes_written", "0"},
{"limit_maxbytes", "67108864"},
{"accepting_conns", "1"},
{"listen_disabled_num", "0"},
{"threads", "4"},
{"conn_yields", "0"},
{"hash_power_level", "16"},
{"hash_bytes", "524288"},
{"hash_is_expanding", "0"},
{"expired_unfetched", "0"},
{"evicted_unfetched", "0"},
{"bytes", "0"},
{"curr_items", "0"},
{"total_items", "0"},
{"evictions", "0"},
{"reclaimed", "0"},
}
for _, test := range tests {
value, ok := values[test.key]
if !ok {
t.Errorf("Did not find key for metric %s in values", test.key)
continue
}
if value != test.value {
t.Errorf("Metric: %s, Expected: %s, actual: %s",
test.key, test.value, value)
}
}
}
var memcachedStats = `STAT pid 23235
STAT uptime 194
STAT time 1449174679
STAT version 1.4.14 (Ubuntu)
STAT libevent 2.0.21-stable
STAT pointer_size 64
STAT rusage_user 0.000000
STAT rusage_system 0.007566
STAT curr_connections 5
STAT total_connections 6
STAT connection_structures 6
STAT reserved_fds 20
STAT cmd_get 0
STAT cmd_set 0
STAT cmd_flush 0
STAT cmd_touch 0
STAT get_hits 0
STAT get_misses 0
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 0
STAT touch_hits 0
STAT touch_misses 0
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 7
STAT bytes_written 0
STAT limit_maxbytes 67108864
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT hash_power_level 16
STAT hash_bytes 524288
STAT hash_is_expanding 0
STAT expired_unfetched 0
STAT evicted_unfetched 0
STAT bytes 0
STAT curr_items 0
STAT total_items 0
STAT evictions 0
STAT reclaimed 0
END
`