From 4cf47dcd0f525bd295011a7ea2bf865c3ea49dfc Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 3 Dec 2015 13:53:37 -0700 Subject: [PATCH] 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. --- plugins/memcached/memcached.go | 55 ++++++------ plugins/memcached/memcached_test.go | 125 ++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 24 deletions(-) diff --git a/plugins/memcached/memcached.go b/plugins/memcached/memcached.go index 1b89238fc..9919b0c24 100644 --- a/plugins/memcached/memcached.go +++ b/plugins/memcached/memcached.go @@ -28,8 +28,8 @@ var sampleConfig = ` var defaultTimeout = 5 * time.Second -// The list of metrics tha should be calculated -var sendAsIs = []string{ +// The list of metrics that should be sent +var sendMetrics = []string{ "get_hits", "get_misses", "evictions", @@ -128,34 +128,16 @@ func (m *Memcached) gatherServer( return err } - // Read response - values := make(map[string]string) - - 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]) + values, err := parseResponse(rw.Reader) + if err != nil { + return err } // Add server address as a tag tags := map[string]string{"server": address} // Process values - for _, key := range sendAsIs { + for _, key := range sendMetrics { if value, ok := values[key]; ok { // Mostly it is the number if iValue, errParse := strconv.ParseInt(value, 10, 64); errParse != nil { @@ -168,6 +150,31 @@ func (m *Memcached) gatherServer( 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() { plugins.Add("memcached", func() plugins.Plugin { return &Memcached{} diff --git a/plugins/memcached/memcached_test.go b/plugins/memcached/memcached_test.go index 0c0fdfbe1..05ff669b3 100644 --- a/plugins/memcached/memcached_test.go +++ b/plugins/memcached/memcached_test.go @@ -1,6 +1,8 @@ package memcached import ( + "bufio" + "strings" "testing" "github.com/influxdb/telegraf/testutil" @@ -33,3 +35,126 @@ func TestMemcachedGeneratesMetrics(t *testing.T) { 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 +`