summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/memcached/collect.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/plugin/go.d/modules/memcached/collect.go')
-rw-r--r--src/go/plugin/go.d/modules/memcached/collect.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/memcached/collect.go b/src/go/plugin/go.d/modules/memcached/collect.go
new file mode 100644
index 000000000..9ead8f47b
--- /dev/null
+++ b/src/go/plugin/go.d/modules/memcached/collect.go
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package memcached
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "strconv"
+ "strings"
+)
+
+// https://github.com/memcached/memcached/blob/b1aefcdf8a265f8a5126e8aa107a50988fa1ec35/doc/protocol.txt#L1267
+var statsMetrics = map[string]bool{
+ "limit_maxbytes": true,
+ "bytes": true,
+ "bytes_read": true,
+ "bytes_written": true,
+ "cas_badval": true,
+ "cas_hits": true,
+ "cas_misses": true,
+ "cmd_get": true,
+ "cmd_set": true,
+ "cmd_touch": true,
+ "curr_connections": true,
+ "curr_items": true,
+ "decr_hits": true,
+ "decr_misses": true,
+ "delete_hits": true,
+ "delete_misses": true,
+ "evictions": true,
+ "get_hits": true,
+ "get_misses": true,
+ "incr_hits": true,
+ "incr_misses": true,
+ "reclaimed": true,
+ "rejected_connections": true,
+ "total_connections": true,
+ "total_items": true,
+ "touch_hits": true,
+ "touch_misses": true,
+}
+
+func (m *Memcached) collect() (map[string]int64, error) {
+ if m.conn == nil {
+ conn, err := m.establishConn()
+ if err != nil {
+ return nil, err
+ }
+ m.conn = conn
+ }
+
+ stats, err := m.conn.queryStats()
+ if err != nil {
+ m.conn.disconnect()
+ m.conn = nil
+ return nil, err
+ }
+
+ mx := make(map[string]int64)
+
+ if err := m.collectStats(mx, stats); err != nil {
+ return nil, err
+ }
+
+ return mx, nil
+}
+
+func (m *Memcached) collectStats(mx map[string]int64, stats []byte) error {
+ if len(stats) == 0 {
+ return errors.New("empty stats response")
+ }
+
+ var n int
+ sc := bufio.NewScanner(bytes.NewReader(stats))
+
+ for sc.Scan() {
+ line := strings.TrimSpace(sc.Text())
+
+ switch {
+ case strings.HasPrefix(line, "STAT"):
+ key, value := getStatKeyValue(line)
+ if !statsMetrics[key] {
+ continue
+ }
+ if v, err := strconv.ParseInt(value, 10, 64); err == nil {
+ mx[key] = v
+ n++
+ }
+ case strings.HasPrefix(line, "ERROR"):
+ return errors.New("received ERROR response")
+ }
+ }
+
+ if n == 0 {
+ return errors.New("unexpected memcached response")
+ }
+
+ mx["avail"] = mx["limit_maxbytes"] - mx["bytes"]
+
+ return nil
+}
+
+func (m *Memcached) establishConn() (memcachedConn, error) {
+ conn := m.newMemcachedConn(m.Config)
+
+ if err := conn.connect(); err != nil {
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func getStatKeyValue(line string) (string, string) {
+ line = strings.TrimPrefix(line, "STAT ")
+ i := strings.IndexByte(line, ' ')
+ if i < 0 {
+ return "", ""
+ }
+ return line[:i], line[i+1:]
+}