Skip to content

Commit

Permalink
Redis: include per-db keyspace info
Browse files Browse the repository at this point in the history
Closes #205
  • Loading branch information
sparrc committed Sep 23, 2015
1 parent b92a0d5 commit 7c787a2
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 76 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## v0.1.10 [unreleased]

### Release Notes

### Features
- [#205](https://github.com/influxdb/telegraf/issues/205): Include per-db redis keyspace info

### Bugfixes

## v0.1.9 [2015-09-22]

### Release Notes
Expand All @@ -14,7 +23,9 @@ file with only the cpu plugin defined, and the influxdb output defined.
- **Breaking Change**: The CPU collection plugin has been refactored to fix some
bugs and outdated dependency issues. At the same time, I also decided to fix
a naming consistency issue, so cpu_percentageIdle will become cpu_usage_idle.
Also, all CPU time measurements now have it indicated in their name, so cpu_idle will become cpu_time_idle. Additionally, cpu_time measurements are going to be dropped in the default config.
Also, all CPU time measurements now have it indicated in their name, so cpu_idle
will become cpu_time_idle. Additionally, cpu_time measurements are going to be
dropped in the default config.
- **Breaking Change**: The memory plugin has been refactored and some measurements
have been renamed for consistency. Some measurements have also been removed from being outputted. They are still being collected by gopsutil, and could easily be
re-added in a "verbose" mode if there is demand for it.
Expand Down
154 changes: 79 additions & 75 deletions plugins/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"net"
"net/url"
// "strconv"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -137,88 +137,92 @@ func (r *Redis) gatherServer(addr *url.URL, acc plugins.Accumulator) error {
}
}

c.Write([]byte("info\r\n"))

c.Write([]byte("INFO\r\n"))
rdr := bufio.NewReader(c)
scanner := bufio.NewScanner(rdr)
for scanner.Scan() {
fmt.Println(scanner.Text())

// Figure out how much we need to read:
line, err := rdr.ReadString('\n')
if err != nil {
return err
}
if line[0] != '$' {
return fmt.Errorf("bad line start: %s", ErrProtocolError)
}
if err := scanner.Err(); err != nil {
fmt.Println("reading standard input:", err)
line = strings.TrimSpace(line)
szStr := line[1:]
sz, err := strconv.Atoi(szStr)
if err != nil {
return fmt.Errorf("bad size string <<%s>>: %s", szStr, ErrProtocolError)
}

// Setup tags for all redis metrics
_, rPort, err := net.SplitHostPort(addr.Host)
if err != nil {
rPort = defaultPort
}
tags := map[string]string{"host": addr.String(), "port": rPort}

// line, err := rdr.ReadString('\n')
// if err != nil {
// return err
// }

// if line[0] != '$' {
// return fmt.Errorf("bad line start: %s", ErrProtocolError)
// }

// line = strings.TrimSpace(line)

// szStr := line[0:]

// sz, err := strconv.Atoi(szStr)
// if err != nil {
// return fmt.Errorf("bad size string <<%s>>: %s", szStr, ErrProtocolError)
// }

// var read int

// for read < sz {
// line, err := rdr.ReadString('\n')
// fmt.Printf(line)
// if err != nil {
// return err
// }

// read += len(line)
// if len(line) == 1 || line[0] == '#' {
// continue
// }

// _, rPort, err := net.SplitHostPort(addr.Host)
// if err != nil {
// rPort = defaultPort
// }
// tags := map[string]string{"host": addr.String(), "port": rPort}

// parts := strings.SplitN(line, ":", 2)
// if len(parts) < 2 {
// continue
// }
// name := string(parts[0])
// metric, ok := Tracking[name]
// if !ok {
// // See if this is the keyspace line
// if strings.Contains(string(parts[1]), "keys=") {
// tags["database"] = name
// acc.Add("foo", 999, tags)
// }
// continue
// }

// val := strings.TrimSpace(parts[1])
// ival, err := strconv.ParseUint(val, 10, 64)
// if err == nil {
// acc.Add(metric, ival, tags)
// continue
// }

// fval, err := strconv.ParseFloat(val, 64)
// if err != nil {
// return err
// }

// acc.Add(metric, fval, tags)
// }
// Read redis INFO output
var readsz int
for readsz < sz {
line, err := rdr.ReadString('\n')
if err != nil {
return err
}

readsz += len(line)
if len(line) == 1 || line[0] == '#' {
continue
}

parts := strings.SplitN(line, ":", 2)
if len(parts) < 2 {
continue
}
name := string(parts[0])
metric, ok := Tracking[name]
if !ok {
kline := strings.TrimSpace(string(parts[1]))
gatherKeyspaceLine(line, acc)
continue
}

val := strings.TrimSpace(parts[1])
ival, err := strconv.ParseUint(val, 10, 64)
if err == nil {
acc.Add(metric, ival, tags)
continue
}

fval, err := strconv.ParseFloat(val, 64)
if err != nil {
return err
}

acc.Add(metric, fval, tags)
}

return nil
}

// Parse the special Keyspace line at end of redis stats
// This is a special line that looks something like:
// db0:keys=2,expires=0,avg_ttl=0
// And there is one for each db on the redis instance
func gatherKeyspaceLine(line string, acc plugins.Accumulator) {
if strings.Contains(line, "keys=") {
tags["database"] = name
dbparts := strings.Split(line, ",")
for _, dbp := range dbparts {
kv := strings.Split(dbp, "=")
ival, err := strconv.ParseUint(kv[1], 10, 64)
if err == nil {
acc.Add(kv[0], ival, tags)
}
}
}
}

func init() {
plugins.Add("redis", func() plugins.Plugin {
return &Redis{}
Expand Down
4 changes: 4 additions & 0 deletions plugins/redis/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ func TestRedisCanPullStatsFromMultipleServers(t *testing.T) {
{"repl_backlog_active", 0},
{"repl_backlog_size", 1048576},
{"repl_backlog_histlen", 0},
{"keys", 2},
{"expires", 0},
{"average_ttl", 0},
}

for _, c := range checkInt {
Expand Down Expand Up @@ -284,5 +287,6 @@ used_cpu_sys_children:0.00
used_cpu_user_children:0.00
# Keyspace
db0:keys=2,expires=0,avg_ttl=0
`

0 comments on commit 7c787a2

Please sign in to comment.