From 45ccb2457dea8ff505676fce34e7fb725cc67e8a Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Mon, 12 Oct 2020 17:02:00 +0300 Subject: [PATCH 01/10] Update readme --- README.md | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 7f1c7bf4b..60e0d8072 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ > :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev) -- [Docs](https://redis.uptrace.dev) +- Join [Discord](https://discord.gg/rWtp5Aj) to ask questions. +- [Documentation](https://redis.uptrace.dev) - [Reference](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc) - [Examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples) - [RealWorld example app](https://github.com/uptrace/go-treemux-realworld-example-app) -- Join [Discord](https://discord.gg/rWtp5Aj) to ask questions. ## Ecosystem @@ -40,18 +40,18 @@ ## Installation -go-redis requires a Go version with [Modules](https://github.com/golang/go/wiki/Modules) support and -uses import versioning. So please make sure to initialize a Go module before installing go-redis: +go-redis supports 2 last Go versions and requires a Go version with +[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go +module: ```shell go mod init github.com/my/repo -go get github.com/go-redis/redis/v8 ``` -Import: +And then install go-redis (note _v8_ in the import; omitting it is a popular mistake): -```go -import "github.com/go-redis/redis/v8" +```shell +go get github.com/go-redis/redis/v8 ``` ## Quickstart @@ -64,24 +64,13 @@ import ( var ctx = context.Background() -func ExampleNewClient() { - rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - - pong, err := rdb.Ping(ctx).Result() - fmt.Println(pong, err) - // Output: PONG -} - func ExampleClient() { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", // no password set DB: 0, // use default DB }) + err := rdb.Set(ctx, "key", "value", 0).Err() if err != nil { panic(err) @@ -106,11 +95,6 @@ func ExampleClient() { } ``` -## Howto - -Please go through [examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples) -to get an idea how to use this package. - ## Look and feel Some corner cases: From 36223c9349f0c78a386df4a38c00a077cc38cdce Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Mon, 12 Oct 2020 17:03:45 +0300 Subject: [PATCH 02/10] readme: mention OpenCensus --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60e0d8072..28c1f0b2d 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ ## Ecosystem -- [redisext](https://github.com/go-redis/redisext) - tracing using OpenTelemetryHook. +- [redisext](https://github.com/go-redis/redisext) - tracing using OpenTelemetry and OpenCensus. +- [Distributed Locks](https://github.com/bsm/redislock). - [Redis Cache](https://github.com/go-redis/cache). - [Rate limiting](https://github.com/go-redis/redis_rate). -- [Distributed Locks](https://github.com/bsm/redislock). ## Features From c0501bf52e6d4505eacc24b628ad494c8664a98d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 12 Oct 2020 18:26:09 +0000 Subject: [PATCH 03/10] chore(deps): update module onsi/gomega to v1.10.3 --- go.mod | 2 +- go.sum | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 272478a98..0ab672caa 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.1 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f github.com/onsi/ginkgo v1.14.1 - github.com/onsi/gomega v1.10.2 + github.com/onsi/gomega v1.10.3 go.opentelemetry.io/otel v0.13.0 ) diff --git a/go.sum b/go.sum index 6f2355b79..9b47d20dd 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J 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/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -39,21 +40,27 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA= go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f169894120399c2f323c493ebf6ee81d26948a6f Mon Sep 17 00:00:00 2001 From: Roman Suvorov Date: Tue, 13 Oct 2020 09:13:43 +0300 Subject: [PATCH 04/10] Fix/issue 1384 (#1529) Add support for scanning time.TIme --- internal/proto/scan.go | 7 +++++++ internal/proto/scan_test.go | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/internal/proto/scan.go b/internal/proto/scan.go index 8fa032317..08d18d35e 100644 --- a/internal/proto/scan.go +++ b/internal/proto/scan.go @@ -4,10 +4,13 @@ import ( "encoding" "fmt" "reflect" + "time" "github.com/go-redis/redis/v8/internal/util" ) +// Scan parses bytes `b` to `v` with appropriate type. +// nolint: gocyclo func Scan(b []byte, v interface{}) error { switch v := v.(type) { case nil: @@ -99,6 +102,10 @@ func Scan(b []byte, v interface{}) error { case *bool: *v = len(b) == 1 && b[0] == '1' return nil + case *time.Time: + var err error + *v, err = time.Parse(time.RFC3339Nano, util.BytesToString(b)) + return err case encoding.BinaryUnmarshaler: return v.UnmarshalBinary(b) default: diff --git a/internal/proto/scan_test.go b/internal/proto/scan_test.go index fadcd0561..034648c79 100644 --- a/internal/proto/scan_test.go +++ b/internal/proto/scan_test.go @@ -1,8 +1,14 @@ -package proto +package proto_test import ( + "context" "encoding/json" + "errors" + "testing" + "time" + "github.com/go-redis/redis/v8" + "github.com/go-redis/redis/v8/internal/proto" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -28,7 +34,7 @@ var _ = Describe("ScanSlice", func() { It("[]testScanSliceStruct", func() { var slice []testScanSliceStruct - err := ScanSlice(data, &slice) + err := proto.ScanSlice(data, &slice) Expect(err).NotTo(HaveOccurred()) Expect(slice).To(Equal([]testScanSliceStruct{ {-1, "Back Yu"}, @@ -38,7 +44,7 @@ var _ = Describe("ScanSlice", func() { It("var testContainer []*testScanSliceStruct", func() { var slice []*testScanSliceStruct - err := ScanSlice(data, &slice) + err := proto.ScanSlice(data, &slice) Expect(err).NotTo(HaveOccurred()) Expect(slice).To(Equal([]*testScanSliceStruct{ {-1, "Back Yu"}, @@ -46,3 +52,28 @@ var _ = Describe("ScanSlice", func() { })) }) }) + +func TestScan(t *testing.T) { + t.Parallel() + + t.Run("time", func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: ":6379", + }) + + tm := time.Now() + rdb.Set(ctx, "now", tm, 0) + + var tm2 time.Time + rdb.Get(ctx, "now").Scan(&tm2) + + if !tm2.Equal(tm) { + t.Fatal(errors.New("tm2 and tm are not equal")) + } + }) + +} From a6876ad84ae11c0dac85129748c9dcb6581507a4 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Tue, 13 Oct 2020 09:33:12 +0300 Subject: [PATCH 05/10] Move Scan test --- internal/proto/scan_test.go | 30 ------------------------------ redis_test.go | 12 ++++++++++++ 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/internal/proto/scan_test.go b/internal/proto/scan_test.go index 034648c79..5df3a6af6 100644 --- a/internal/proto/scan_test.go +++ b/internal/proto/scan_test.go @@ -1,13 +1,8 @@ package proto_test import ( - "context" "encoding/json" - "errors" - "testing" - "time" - "github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8/internal/proto" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -52,28 +47,3 @@ var _ = Describe("ScanSlice", func() { })) }) }) - -func TestScan(t *testing.T) { - t.Parallel() - - t.Run("time", func(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - rdb := redis.NewClient(&redis.Options{ - Addr: ":6379", - }) - - tm := time.Now() - rdb.Set(ctx, "now", tm, 0) - - var tm2 time.Time - rdb.Get(ctx, "now").Scan(&tm2) - - if !tm2.Equal(tm) { - t.Fatal(errors.New("tm2 and tm are not equal")) - } - }) - -} diff --git a/redis_test.go b/redis_test.go index c00afc0d0..a9e88c9d6 100644 --- a/redis_test.go +++ b/redis_test.go @@ -283,6 +283,18 @@ var _ = Describe("Client", func() { Expect(err).NotTo(HaveOccurred()) Expect(got).To(Equal(bigVal)) }) + + It("should set and scan time", func() { + tm := time.Now() + err := rdb.Set(ctx, "now", tm, 0).Err() + Expect(err).NotTo(HaveOccurred()) + + var tm2 time.Time + err = rdb.Get(ctx, "now").Scan(&tm2) + Expect(err).NotTo(HaveOccurred()) + + Expect(tm2).To(BeTemporally("==", tm)) + }) }) var _ = Describe("Client timeout", func() { From 319b0236e7c41ac77fc37fde18ef40464383de20 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 13 Oct 2020 06:36:17 +0000 Subject: [PATCH 06/10] chore(deps): update module onsi/ginkgo to v1.14.2 --- go.mod | 2 +- go.sum | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0ab672caa..7be1b7f13 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.11 require ( github.com/cespare/xxhash/v2 v2.1.1 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f - github.com/onsi/ginkgo v1.14.1 + github.com/onsi/ginkgo v1.14.2 github.com/onsi/gomega v1.10.3 go.opentelemetry.io/otel v0.13.0 ) diff --git a/go.sum b/go.sum index 9b47d20dd..eb9e06f93 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 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= From e99a39201b71bbdd37e2b1c03ef9d9c45d910d64 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Sat, 17 Oct 2020 15:21:09 +0300 Subject: [PATCH 07/10] Add hooks to Conn. Fixes #1539 --- redis.go | 11 ++++++++++- redis_test.go | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/redis.go b/redis.go index b70fef319..f3b35838c 100644 --- a/redis.go +++ b/redis.go @@ -719,6 +719,7 @@ type conn struct { baseClient cmdable statefulCmdable + hooks // TODO: inherit hooks } // Conn is like Client, but its pool contains single connection. @@ -743,7 +744,15 @@ func newConn(ctx context.Context, opt *Options, connPool pool.Pooler) *Conn { } func (c *Conn) Process(ctx context.Context, cmd Cmder) error { - return c.baseClient.process(ctx, cmd) + return c.hooks.process(ctx, cmd, c.baseClient.process) +} + +func (c *Conn) processPipeline(ctx context.Context, cmds []Cmder) error { + return c.hooks.processPipeline(ctx, cmds, c.baseClient.processPipeline) +} + +func (c *Conn) processTxPipeline(ctx context.Context, cmds []Cmder) error { + return c.hooks.processTxPipeline(ctx, cmds, c.baseClient.processTxPipeline) } func (c *Conn) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) { diff --git a/redis_test.go b/redis_test.go index a9e88c9d6..32a3cbea9 100644 --- a/redis_test.go +++ b/redis_test.go @@ -295,6 +295,11 @@ var _ = Describe("Client", func() { Expect(tm2).To(BeTemporally("==", tm)) }) + + It("should Conn", func() { + err := rdb.Conn(ctx).Get(ctx, "this-key-does-not-exist").Err() + Expect(err).To(Equal(redis.Nil)) + }) }) var _ = Describe("Client timeout", func() { From 7b1a844a5ba3c94babe0d6119acc778a31fc0083 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 21 Oct 2020 15:19:27 +0300 Subject: [PATCH 08/10] Add extra modules --- README.md | 1 - extra/rediscensus/go.mod | 11 +++ extra/rediscensus/rediscensus.go | 45 ++++++++++ extra/rediscmd/go.mod | 9 ++ extra/rediscmd/rediscmd.go | 149 +++++++++++++++++++++++++++++++ extra/rediscmd/rediscmd_test.go | 32 +++++++ extra/rediscmd/safe.go | 11 +++ extra/rediscmd/unsafe.go | 20 +++++ extra/redisotel/go.mod | 11 +++ extra/redisotel/redisotel.go | 72 +++++++++++++++ go.sum | 9 +- 11 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 extra/rediscensus/go.mod create mode 100644 extra/rediscensus/rediscensus.go create mode 100644 extra/rediscmd/go.mod create mode 100644 extra/rediscmd/rediscmd.go create mode 100644 extra/rediscmd/rediscmd_test.go create mode 100644 extra/rediscmd/safe.go create mode 100644 extra/rediscmd/unsafe.go create mode 100644 extra/redisotel/go.mod create mode 100644 extra/redisotel/redisotel.go diff --git a/README.md b/README.md index 28c1f0b2d..da5d0fb4c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ ## Ecosystem -- [redisext](https://github.com/go-redis/redisext) - tracing using OpenTelemetry and OpenCensus. - [Distributed Locks](https://github.com/bsm/redislock). - [Redis Cache](https://github.com/go-redis/cache). - [Rate limiting](https://github.com/go-redis/redis_rate). diff --git a/extra/rediscensus/go.mod b/extra/rediscensus/go.mod new file mode 100644 index 000000000..0bd8adf20 --- /dev/null +++ b/extra/rediscensus/go.mod @@ -0,0 +1,11 @@ +module github.com/go-redis/redis/extra/rediscensus + +go 1.15 + +replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd + +require ( + github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000 + github.com/go-redis/redis/v8 v8.3.2 + go.opencensus.io v0.22.5 +) diff --git a/extra/rediscensus/rediscensus.go b/extra/rediscensus/rediscensus.go new file mode 100644 index 000000000..48a7ca60c --- /dev/null +++ b/extra/rediscensus/rediscensus.go @@ -0,0 +1,45 @@ +package rediscensus + +import ( + "context" + + "github.com/go-redis/redis/extra/rediscmd" + "github.com/go-redis/redis/v8" + "go.opencensus.io/trace" +) + +type TracingHook struct{} + +var _ redis.Hook = TracingHook{} + +func (TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) { + ctx, span := trace.StartSpan(ctx, cmd.FullName()) + span.AddAttributes(trace.StringAttribute("db.system", "redis"), + trace.StringAttribute("redis.cmd", rediscmd.CmdString(cmd))) + + return ctx, nil +} + +func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error { + span := trace.FromContext(ctx) + if err := cmd.Err(); err != nil { + recordErrorOnOCSpan(ctx, span, err) + } + span.End() + return nil +} + +func (TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) { + return ctx, nil +} + +func (TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error { + return nil +} + +func recordErrorOnOCSpan(ctx context.Context, span *trace.Span, err error) { + if err != redis.Nil { + span.AddAttributes(trace.BoolAttribute("error", true)) + span.Annotate([]trace.Attribute{trace.StringAttribute("Error", "redis error")}, err.Error()) + } +} diff --git a/extra/rediscmd/go.mod b/extra/rediscmd/go.mod new file mode 100644 index 000000000..2d9f1ac55 --- /dev/null +++ b/extra/rediscmd/go.mod @@ -0,0 +1,9 @@ +module github.com/go-redis/redis/extra/rediscmd + +go 1.15 + +require ( + github.com/go-redis/redis/v8 v8.3.2 + github.com/onsi/ginkgo v1.14.2 + github.com/onsi/gomega v1.10.3 +) diff --git a/extra/rediscmd/rediscmd.go b/extra/rediscmd/rediscmd.go new file mode 100644 index 000000000..12ea39fc1 --- /dev/null +++ b/extra/rediscmd/rediscmd.go @@ -0,0 +1,149 @@ +package rediscmd + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" + "time" + + "github.com/go-redis/redis/v8" +) + +func CmdString(cmd redis.Cmder) string { + b := make([]byte, 0, 32) + b = AppendCmd(b, cmd) + return String(b) +} + +func CmdsString(cmds []redis.Cmder) (string, string) { + const numCmdLimit = 100 + const numNameLimit = 10 + + seen := make(map[string]struct{}, numNameLimit) + unqNames := make([]string, 0, numNameLimit) + + b := make([]byte, 0, 32*len(cmds)) + + for i, cmd := range cmds { + if i > numCmdLimit { + break + } + + if i > 0 { + b = append(b, '\n') + } + b = AppendCmd(b, cmd) + + if len(unqNames) >= numNameLimit { + continue + } + + name := cmd.FullName() + if _, ok := seen[name]; !ok { + seen[name] = struct{}{} + unqNames = append(unqNames, name) + } + } + + summary := strings.Join(unqNames, " ") + return summary, String(b) +} + +func AppendCmd(b []byte, cmd redis.Cmder) []byte { + const numArgLimit = 32 + + for i, arg := range cmd.Args() { + if i > numArgLimit { + break + } + if i > 0 { + b = append(b, ' ') + } + b = appendArg(b, arg) + } + + if err := cmd.Err(); err != nil { + b = append(b, ": "...) + b = append(b, err.Error()...) + } + + return b +} + +func appendArg(b []byte, v interface{}) []byte { + const argLenLimit = 64 + + switch v := v.(type) { + case nil: + return append(b, ""...) + case string: + if len(v) > argLenLimit { + v = v[:argLenLimit] + } + return appendUTF8String(b, Bytes(v)) + case []byte: + if len(v) > argLenLimit { + v = v[:argLenLimit] + } + return appendUTF8String(b, v) + case int: + return strconv.AppendInt(b, int64(v), 10) + case int8: + return strconv.AppendInt(b, int64(v), 10) + case int16: + return strconv.AppendInt(b, int64(v), 10) + case int32: + return strconv.AppendInt(b, int64(v), 10) + case int64: + return strconv.AppendInt(b, v, 10) + case uint: + return strconv.AppendUint(b, uint64(v), 10) + case uint8: + return strconv.AppendUint(b, uint64(v), 10) + case uint16: + return strconv.AppendUint(b, uint64(v), 10) + case uint32: + return strconv.AppendUint(b, uint64(v), 10) + case uint64: + return strconv.AppendUint(b, v, 10) + case float32: + return strconv.AppendFloat(b, float64(v), 'f', -1, 64) + case float64: + return strconv.AppendFloat(b, v, 'f', -1, 64) + case bool: + if v { + return append(b, "true"...) + } + return append(b, "false"...) + case time.Time: + return v.AppendFormat(b, time.RFC3339Nano) + default: + return append(b, fmt.Sprint(v)...) + } +} + +func appendUTF8String(dst []byte, src []byte) []byte { + if isSimple(src) { + dst = append(dst, src...) + return dst + } + + s := len(dst) + dst = append(dst, make([]byte, hex.EncodedLen(len(src)))...) + hex.Encode(dst[s:], src) + return dst +} + +func isSimple(b []byte) bool { + for _, c := range b { + if !isSimpleByte(c) { + return false + } + } + return true +} + +func isSimpleByte(c byte) bool { + return c >= 0x21 && c <= 0x7e +} diff --git a/extra/rediscmd/rediscmd_test.go b/extra/rediscmd/rediscmd_test.go new file mode 100644 index 000000000..259a66ac9 --- /dev/null +++ b/extra/rediscmd/rediscmd_test.go @@ -0,0 +1,32 @@ +package rediscmd + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "redisext") +} + +var _ = Describe("AppendArg", func() { + DescribeTable("...", + func(src string, wanted string) { + b := appendArg(nil, src) + Expect(string(b)).To(Equal(wanted)) + }, + + Entry("", "-inf", "-inf"), + Entry("", "+inf", "+inf"), + Entry("", "foo.bar", "foo.bar"), + Entry("", "foo:bar", "foo:bar"), + Entry("", "foo{bar}", "foo{bar}"), + Entry("", "foo-123_BAR", "foo-123_BAR"), + Entry("", "foo\nbar", "666f6f0a626172"), + Entry("", "\000", "00"), + ) +}) diff --git a/extra/rediscmd/safe.go b/extra/rediscmd/safe.go new file mode 100644 index 000000000..efe92f6e8 --- /dev/null +++ b/extra/rediscmd/safe.go @@ -0,0 +1,11 @@ +// +build appengine + +package rediscmd + +func String(b []byte) string { + return string(b) +} + +func Bytes(s string) []byte { + return []byte(s) +} diff --git a/extra/rediscmd/unsafe.go b/extra/rediscmd/unsafe.go new file mode 100644 index 000000000..a90a48b71 --- /dev/null +++ b/extra/rediscmd/unsafe.go @@ -0,0 +1,20 @@ +// +build !appengine + +package rediscmd + +import "unsafe" + +// String converts byte slice to string. +func String(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// Bytes converts string to byte slice. +func Bytes(s string) []byte { + return *(*[]byte)(unsafe.Pointer( + &struct { + string + Cap int + }{s, len(s)}, + )) +} diff --git a/extra/redisotel/go.mod b/extra/redisotel/go.mod new file mode 100644 index 000000000..e01080c87 --- /dev/null +++ b/extra/redisotel/go.mod @@ -0,0 +1,11 @@ +module github.com/go-redis/redis/extra/rediscensus + +go 1.15 + +replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd + +require ( + github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000 + github.com/go-redis/redis/v8 v8.3.2 + go.opentelemetry.io/otel v0.13.0 +) diff --git a/extra/redisotel/redisotel.go b/extra/redisotel/redisotel.go new file mode 100644 index 000000000..d0ab602bf --- /dev/null +++ b/extra/redisotel/redisotel.go @@ -0,0 +1,72 @@ +package redisotel + +import ( + "context" + + "github.com/go-redis/redis/extra/rediscmd" + "github.com/go-redis/redis/v8" + "go.opentelemetry.io/otel/api/global" + "go.opentelemetry.io/otel/api/trace" + "go.opentelemetry.io/otel/label" +) + +var tracer = global.Tracer("github.com/go-redis/redis") + +type TracingHook struct{} + +var _ redis.Hook = TracingHook{} + +func (TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) { + if !trace.SpanFromContext(ctx).IsRecording() { + return ctx, nil + } + + ctx, span := tracer.Start(ctx, cmd.FullName()) + span.SetAttributes( + label.String("db.system", "redis"), + label.String("redis.cmd", rediscmd.CmdString(cmd)), + ) + + return ctx, nil +} + +func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error { + span := trace.SpanFromContext(ctx) + if err := cmd.Err(); err != nil { + recordError(ctx, span, err) + } + span.End() + return nil +} + +func (TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) { + if !trace.SpanFromContext(ctx).IsRecording() { + return ctx, nil + } + + summary, cmdsString := rediscmd.CmdsString(cmds) + + ctx, span := tracer.Start(ctx, "pipeline "+summary) + span.SetAttributes( + label.String("db.system", "redis"), + label.Int("redis.num_cmd", len(cmds)), + label.String("redis.cmds", cmdsString), + ) + + return ctx, nil +} + +func (TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error { + span := trace.SpanFromContext(ctx) + if err := cmds[0].Err(); err != nil { + recordError(ctx, span, err) + } + span.End() + return nil +} + +func recordError(ctx context.Context, span trace.Span, err error) { + if err != redis.Nil { + span.RecordError(ctx, err) + } +} diff --git a/go.sum b/go.sum index eb9e06f93..f76f073d2 100644 --- a/go.sum +++ b/go.sum @@ -25,13 +25,11 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 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/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -46,6 +44,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -57,10 +56,12 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= From 7247b90e3e95322f3c5c505e1774c1425033fbb4 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 21 Oct 2020 15:23:08 +0300 Subject: [PATCH 09/10] Fix module name --- extra/redisotel/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/redisotel/go.mod b/extra/redisotel/go.mod index e01080c87..ecb9cd950 100644 --- a/extra/redisotel/go.mod +++ b/extra/redisotel/go.mod @@ -1,4 +1,4 @@ -module github.com/go-redis/redis/extra/rediscensus +module github.com/go-redis/redis/extra/redisotel go 1.15 From f93ba2815bf0c7eeedc1d916fe0a41a36e3ec373 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Wed, 21 Oct 2020 15:26:05 +0300 Subject: [PATCH 10/10] Fix version --- extra/rediscensus/go.mod | 2 +- extra/redisotel/go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/rediscensus/go.mod b/extra/rediscensus/go.mod index 0bd8adf20..1cf7df871 100644 --- a/extra/rediscensus/go.mod +++ b/extra/rediscensus/go.mod @@ -5,7 +5,7 @@ go 1.15 replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd require ( - github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000 + github.com/go-redis/redis/extra/rediscmd v0.1.0 github.com/go-redis/redis/v8 v8.3.2 go.opencensus.io v0.22.5 ) diff --git a/extra/redisotel/go.mod b/extra/redisotel/go.mod index ecb9cd950..31111ab3f 100644 --- a/extra/redisotel/go.mod +++ b/extra/redisotel/go.mod @@ -5,7 +5,7 @@ go 1.15 replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd require ( - github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000 + github.com/go-redis/redis/extra/rediscmd v0.1.0 github.com/go-redis/redis/v8 v8.3.2 go.opentelemetry.io/otel v0.13.0 )