From be9ae84c6f2d1c3b54ffbab6141e6c43059acae7 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 11 Jun 2020 15:24:04 +0800 Subject: [PATCH 1/2] support for slowlog command --- command.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++ commands.go | 8 +++- commands_test.go | 18 ++++++++ example_test.go | 18 ++++++++ 4 files changed, 155 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index 81e0c4db..60bd23db 100644 --- a/command.go +++ b/command.go @@ -2167,3 +2167,116 @@ func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) { }) return c.cmds, err } + +//------------------------------------------------------------------------------ + +type SlowLog struct { + ID int64 + CreatedAt time.Time + Costs time.Duration + /* + ClientAddress,ClientName + There are also optional fields emitted only by Redis 4.0 or greater: + https://redis.io/commands/slowlog#output-format + */ + ClientAddress, ClientName string + Args []string +} + +type SlowLogCmd struct { + baseCmd + + val []SlowLog +} + +var _ Cmder = (*SlowLogCmd)(nil) + +func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd { + return &SlowLogCmd{ + baseCmd: baseCmd{ + ctx: ctx, + args: args, + }, + } +} + +func (cmd *SlowLogCmd) Val() []SlowLog { + return cmd.val +} + +func (cmd *SlowLogCmd) Result() ([]SlowLog, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *SlowLogCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { + _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { + cmd.val = make([]SlowLog, n) + for i := 0; i < len(cmd.val); i++ { + n, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } + if n < 4 { + err := fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", n) + return nil, err + } + + id, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + + createdAt, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + createdAtTime := time.Unix(createdAt, 0) + costs, err := rd.ReadIntReply() + if err != nil { + return nil, err + } + costsDuration := time.Duration(costs) * time.Microsecond + cmdLen, err := rd.ReadArrayLen() + if cmdLen < 1 { + err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen) + return nil, err + } + if err != nil { + return nil, err + } + cmdString := make([]string, cmdLen) + for i := 0; i < int(cmdLen); i++ { + cmdString[i], err = rd.ReadString() + if err != nil { + return nil, err + } + } + var address, name string + for i := 4; i < int(n); i++ { + str, err := rd.ReadString() + if err != nil { + return nil, err + } + if i == 4 { + address = str + } else if i == 5 { + name = str + } + } + cmd.val[i] = SlowLog{ + ID: id, + CreatedAt: createdAtTime, + Costs: costsDuration, + Args: cmdString, + ClientAddress: address, + ClientName: name, + } + } + return nil, nil + }) + return cmd.err +} diff --git a/commands.go b/commands.go index 46b88eb9..f7946c59 100644 --- a/commands.go +++ b/commands.go @@ -4,6 +4,7 @@ import ( "context" "errors" "io" + "strconv" "time" "github.com/go-redis/redis/v8/internal" @@ -2306,8 +2307,11 @@ func (c cmdable) SlaveOf(ctx context.Context, host, port string) *StatusCmd { return cmd } -func (c cmdable) SlowLog(ctx context.Context) { - panic("not implemented") +func (c cmdable) SlowLog(ctx context.Context, num int64) *SlowLogCmd { + n := strconv.FormatInt(num, 10) + cmd := NewSlowLogCmd(context.Background(), "slowlog", "get", n) + _ = c(ctx, cmd) + return cmd } func (c cmdable) Sync(ctx context.Context) { diff --git a/commands_test.go b/commands_test.go index 0fcea97a..fe0b61a9 100644 --- a/commands_test.go +++ b/commands_test.go @@ -4013,6 +4013,24 @@ var _ = Describe("Commands", func() { Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"})) }) }) + + Describe("SlowLog", func() { + It("returns slow query result", func() { + var CONFIGSLOWKEY string = "slowlog-log-slower-than" + old := client.ConfigGet(ctx, CONFIGSLOWKEY).Val() + client.ConfigSet(ctx, CONFIGSLOWKEY, "0") + defer client.ConfigSet(ctx, CONFIGSLOWKEY, old[1].(string)) + _, e := client.Eval(ctx, "redis.call('slowlog','reset')", []string{}).Result() + if e != nil && e != redis.Nil { + fmt.Println(e) + return + } + client.Set(ctx, "test", "true", 0) + result, err := client.SlowLog(ctx, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(len(result)).To(Equal(3)) + }) + }) }) type numberStruct struct { diff --git a/example_test.go b/example_test.go index c4c6469c..f1f8dc11 100644 --- a/example_test.go +++ b/example_test.go @@ -510,3 +510,21 @@ func ExampleNewUniversalClient_cluster() { rdb.Ping(ctx) } + +func ExampleClient_SlowLog() { + var ( + CONFIGSLOWKEY string = "slowlog-log-slower-than" + ) + old := rdb.ConfigGet(ctx, CONFIGSLOWKEY).Val() + rdb.ConfigSet(ctx, CONFIGSLOWKEY, "0") + defer rdb.ConfigSet(ctx, CONFIGSLOWKEY, old[1].(string)) + _, e := rdb.Eval(ctx, "redis.call('slowlog','reset')", []string{}).Result() + if e != nil && e != redis.Nil { + fmt.Println(e) + return + } + rdb.Set(ctx, "test", "true", 0) + result, err := rdb.SlowLog(ctx, -1).Result() + fmt.Println(len(result), err) + // Output: 3 +} From dfae0ec28bd7bbf933ec85c3aef30a6318ed6fe0 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 9 Sep 2020 12:49:45 +0300 Subject: [PATCH 2/2] Cleanup --- command.go | 41 ++++++++++++++++++++++------------------- commands_test.go | 21 +++++++++++---------- example_test.go | 27 +++++++++++++++------------ go.sum | 1 + 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/command.go b/command.go index 60bd23db..1cf76168 100644 --- a/command.go +++ b/command.go @@ -2171,16 +2171,14 @@ func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) { //------------------------------------------------------------------------------ type SlowLog struct { - ID int64 - CreatedAt time.Time - Costs time.Duration - /* - ClientAddress,ClientName - There are also optional fields emitted only by Redis 4.0 or greater: - https://redis.io/commands/slowlog#output-format - */ - ClientAddress, ClientName string - Args []string + ID int64 + Time time.Time + Duration time.Duration + Args []string + // These are also optional fields emitted only by Redis 4.0 or greater: + // https://redis.io/commands/slowlog#output-format + ClientAddr string + ClientName string } type SlowLogCmd struct { @@ -2235,19 +2233,22 @@ func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { return nil, err } createdAtTime := time.Unix(createdAt, 0) + costs, err := rd.ReadIntReply() if err != nil { return nil, err } costsDuration := time.Duration(costs) * time.Microsecond + cmdLen, err := rd.ReadArrayLen() + if err != nil { + return nil, err + } if cmdLen < 1 { err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen) return nil, err } - if err != nil { - return nil, err - } + cmdString := make([]string, cmdLen) for i := 0; i < int(cmdLen); i++ { cmdString[i], err = rd.ReadString() @@ -2255,6 +2256,7 @@ func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { return nil, err } } + var address, name string for i := 4; i < int(n); i++ { str, err := rd.ReadString() @@ -2267,13 +2269,14 @@ func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { name = str } } + cmd.val[i] = SlowLog{ - ID: id, - CreatedAt: createdAtTime, - Costs: costsDuration, - Args: cmdString, - ClientAddress: address, - ClientName: name, + ID: id, + Time: createdAtTime, + Duration: costsDuration, + Args: cmdString, + ClientAddr: address, + ClientName: name, } } return nil, nil diff --git a/commands_test.go b/commands_test.go index fe0b61a9..0dc4c960 100644 --- a/commands_test.go +++ b/commands_test.go @@ -4016,19 +4016,20 @@ var _ = Describe("Commands", func() { Describe("SlowLog", func() { It("returns slow query result", func() { - var CONFIGSLOWKEY string = "slowlog-log-slower-than" - old := client.ConfigGet(ctx, CONFIGSLOWKEY).Val() - client.ConfigSet(ctx, CONFIGSLOWKEY, "0") - defer client.ConfigSet(ctx, CONFIGSLOWKEY, old[1].(string)) - _, e := client.Eval(ctx, "redis.call('slowlog','reset')", []string{}).Result() - if e != nil && e != redis.Nil { - fmt.Println(e) - return - } + const key = "slowlog-log-slower-than" + + old := client.ConfigGet(ctx, key).Val() + client.ConfigSet(ctx, key, "0") + defer client.ConfigSet(ctx, key, old[1].(string)) + + err := rdb.Do(ctx, "slowlog", "reset").Err() + Expect(err).NotTo(HaveOccurred()) + client.Set(ctx, "test", "true", 0) + result, err := client.SlowLog(ctx, -1).Result() Expect(err).NotTo(HaveOccurred()) - Expect(len(result)).To(Equal(3)) + Expect(len(result)).To(Equal(2)) }) }) }) diff --git a/example_test.go b/example_test.go index f1f8dc11..0d1cac97 100644 --- a/example_test.go +++ b/example_test.go @@ -512,19 +512,22 @@ func ExampleNewUniversalClient_cluster() { } func ExampleClient_SlowLog() { - var ( - CONFIGSLOWKEY string = "slowlog-log-slower-than" - ) - old := rdb.ConfigGet(ctx, CONFIGSLOWKEY).Val() - rdb.ConfigSet(ctx, CONFIGSLOWKEY, "0") - defer rdb.ConfigSet(ctx, CONFIGSLOWKEY, old[1].(string)) - _, e := rdb.Eval(ctx, "redis.call('slowlog','reset')", []string{}).Result() - if e != nil && e != redis.Nil { - fmt.Println(e) - return + const key = "slowlog-log-slower-than" + + old := rdb.ConfigGet(ctx, key).Val() + rdb.ConfigSet(ctx, key, "0") + defer rdb.ConfigSet(ctx, key, old[1].(string)) + + if err := rdb.Do(ctx, "slowlog", "reset").Err(); err != nil { + panic(err) } + rdb.Set(ctx, "test", "true", 0) + result, err := rdb.SlowLog(ctx, -1).Result() - fmt.Println(len(result), err) - // Output: 3 + if err != nil { + panic(err) + } + fmt.Println(len(result)) + // Output: 2 } diff --git a/go.sum b/go.sum index ec5d1d9f..a9b89c52 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,7 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=