Skip to content

Commit 7b1a844

Browse files
committed
Add extra modules
1 parent 26ade92 commit 7b1a844

File tree

11 files changed

+365
-5
lines changed

11 files changed

+365
-5
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
## Ecosystem
1717

18-
- [redisext](https://github.com/go-redis/redisext) - tracing using OpenTelemetry and OpenCensus.
1918
- [Distributed Locks](https://github.com/bsm/redislock).
2019
- [Redis Cache](https://github.com/go-redis/cache).
2120
- [Rate limiting](https://github.com/go-redis/redis_rate).

extra/rediscensus/go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/go-redis/redis/extra/rediscensus
2+
3+
go 1.15
4+
5+
replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd
6+
7+
require (
8+
github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000
9+
github.com/go-redis/redis/v8 v8.3.2
10+
go.opencensus.io v0.22.5
11+
)

extra/rediscensus/rediscensus.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package rediscensus
2+
3+
import (
4+
"context"
5+
6+
"github.com/go-redis/redis/extra/rediscmd"
7+
"github.com/go-redis/redis/v8"
8+
"go.opencensus.io/trace"
9+
)
10+
11+
type TracingHook struct{}
12+
13+
var _ redis.Hook = TracingHook{}
14+
15+
func (TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) {
16+
ctx, span := trace.StartSpan(ctx, cmd.FullName())
17+
span.AddAttributes(trace.StringAttribute("db.system", "redis"),
18+
trace.StringAttribute("redis.cmd", rediscmd.CmdString(cmd)))
19+
20+
return ctx, nil
21+
}
22+
23+
func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
24+
span := trace.FromContext(ctx)
25+
if err := cmd.Err(); err != nil {
26+
recordErrorOnOCSpan(ctx, span, err)
27+
}
28+
span.End()
29+
return nil
30+
}
31+
32+
func (TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) {
33+
return ctx, nil
34+
}
35+
36+
func (TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error {
37+
return nil
38+
}
39+
40+
func recordErrorOnOCSpan(ctx context.Context, span *trace.Span, err error) {
41+
if err != redis.Nil {
42+
span.AddAttributes(trace.BoolAttribute("error", true))
43+
span.Annotate([]trace.Attribute{trace.StringAttribute("Error", "redis error")}, err.Error())
44+
}
45+
}

extra/rediscmd/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module github.com/go-redis/redis/extra/rediscmd
2+
3+
go 1.15
4+
5+
require (
6+
github.com/go-redis/redis/v8 v8.3.2
7+
github.com/onsi/ginkgo v1.14.2
8+
github.com/onsi/gomega v1.10.3
9+
)

extra/rediscmd/rediscmd.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package rediscmd
2+
3+
import (
4+
"encoding/hex"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
"time"
9+
10+
"github.com/go-redis/redis/v8"
11+
)
12+
13+
func CmdString(cmd redis.Cmder) string {
14+
b := make([]byte, 0, 32)
15+
b = AppendCmd(b, cmd)
16+
return String(b)
17+
}
18+
19+
func CmdsString(cmds []redis.Cmder) (string, string) {
20+
const numCmdLimit = 100
21+
const numNameLimit = 10
22+
23+
seen := make(map[string]struct{}, numNameLimit)
24+
unqNames := make([]string, 0, numNameLimit)
25+
26+
b := make([]byte, 0, 32*len(cmds))
27+
28+
for i, cmd := range cmds {
29+
if i > numCmdLimit {
30+
break
31+
}
32+
33+
if i > 0 {
34+
b = append(b, '\n')
35+
}
36+
b = AppendCmd(b, cmd)
37+
38+
if len(unqNames) >= numNameLimit {
39+
continue
40+
}
41+
42+
name := cmd.FullName()
43+
if _, ok := seen[name]; !ok {
44+
seen[name] = struct{}{}
45+
unqNames = append(unqNames, name)
46+
}
47+
}
48+
49+
summary := strings.Join(unqNames, " ")
50+
return summary, String(b)
51+
}
52+
53+
func AppendCmd(b []byte, cmd redis.Cmder) []byte {
54+
const numArgLimit = 32
55+
56+
for i, arg := range cmd.Args() {
57+
if i > numArgLimit {
58+
break
59+
}
60+
if i > 0 {
61+
b = append(b, ' ')
62+
}
63+
b = appendArg(b, arg)
64+
}
65+
66+
if err := cmd.Err(); err != nil {
67+
b = append(b, ": "...)
68+
b = append(b, err.Error()...)
69+
}
70+
71+
return b
72+
}
73+
74+
func appendArg(b []byte, v interface{}) []byte {
75+
const argLenLimit = 64
76+
77+
switch v := v.(type) {
78+
case nil:
79+
return append(b, "<nil>"...)
80+
case string:
81+
if len(v) > argLenLimit {
82+
v = v[:argLenLimit]
83+
}
84+
return appendUTF8String(b, Bytes(v))
85+
case []byte:
86+
if len(v) > argLenLimit {
87+
v = v[:argLenLimit]
88+
}
89+
return appendUTF8String(b, v)
90+
case int:
91+
return strconv.AppendInt(b, int64(v), 10)
92+
case int8:
93+
return strconv.AppendInt(b, int64(v), 10)
94+
case int16:
95+
return strconv.AppendInt(b, int64(v), 10)
96+
case int32:
97+
return strconv.AppendInt(b, int64(v), 10)
98+
case int64:
99+
return strconv.AppendInt(b, v, 10)
100+
case uint:
101+
return strconv.AppendUint(b, uint64(v), 10)
102+
case uint8:
103+
return strconv.AppendUint(b, uint64(v), 10)
104+
case uint16:
105+
return strconv.AppendUint(b, uint64(v), 10)
106+
case uint32:
107+
return strconv.AppendUint(b, uint64(v), 10)
108+
case uint64:
109+
return strconv.AppendUint(b, v, 10)
110+
case float32:
111+
return strconv.AppendFloat(b, float64(v), 'f', -1, 64)
112+
case float64:
113+
return strconv.AppendFloat(b, v, 'f', -1, 64)
114+
case bool:
115+
if v {
116+
return append(b, "true"...)
117+
}
118+
return append(b, "false"...)
119+
case time.Time:
120+
return v.AppendFormat(b, time.RFC3339Nano)
121+
default:
122+
return append(b, fmt.Sprint(v)...)
123+
}
124+
}
125+
126+
func appendUTF8String(dst []byte, src []byte) []byte {
127+
if isSimple(src) {
128+
dst = append(dst, src...)
129+
return dst
130+
}
131+
132+
s := len(dst)
133+
dst = append(dst, make([]byte, hex.EncodedLen(len(src)))...)
134+
hex.Encode(dst[s:], src)
135+
return dst
136+
}
137+
138+
func isSimple(b []byte) bool {
139+
for _, c := range b {
140+
if !isSimpleByte(c) {
141+
return false
142+
}
143+
}
144+
return true
145+
}
146+
147+
func isSimpleByte(c byte) bool {
148+
return c >= 0x21 && c <= 0x7e
149+
}

extra/rediscmd/rediscmd_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package rediscmd
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo"
7+
. "github.com/onsi/ginkgo/extensions/table"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
func TestGinkgo(t *testing.T) {
12+
RegisterFailHandler(Fail)
13+
RunSpecs(t, "redisext")
14+
}
15+
16+
var _ = Describe("AppendArg", func() {
17+
DescribeTable("...",
18+
func(src string, wanted string) {
19+
b := appendArg(nil, src)
20+
Expect(string(b)).To(Equal(wanted))
21+
},
22+
23+
Entry("", "-inf", "-inf"),
24+
Entry("", "+inf", "+inf"),
25+
Entry("", "foo.bar", "foo.bar"),
26+
Entry("", "foo:bar", "foo:bar"),
27+
Entry("", "foo{bar}", "foo{bar}"),
28+
Entry("", "foo-123_BAR", "foo-123_BAR"),
29+
Entry("", "foo\nbar", "666f6f0a626172"),
30+
Entry("", "\000", "00"),
31+
)
32+
})

extra/rediscmd/safe.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// +build appengine
2+
3+
package rediscmd
4+
5+
func String(b []byte) string {
6+
return string(b)
7+
}
8+
9+
func Bytes(s string) []byte {
10+
return []byte(s)
11+
}

extra/rediscmd/unsafe.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// +build !appengine
2+
3+
package rediscmd
4+
5+
import "unsafe"
6+
7+
// String converts byte slice to string.
8+
func String(b []byte) string {
9+
return *(*string)(unsafe.Pointer(&b))
10+
}
11+
12+
// Bytes converts string to byte slice.
13+
func Bytes(s string) []byte {
14+
return *(*[]byte)(unsafe.Pointer(
15+
&struct {
16+
string
17+
Cap int
18+
}{s, len(s)},
19+
))
20+
}

extra/redisotel/go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/go-redis/redis/extra/rediscensus
2+
3+
go 1.15
4+
5+
replace github.com/go-redis/redis/extra/rediscmd => ../rediscmd
6+
7+
require (
8+
github.com/go-redis/redis/extra/rediscmd v0.0.0-00010101000000-000000000000
9+
github.com/go-redis/redis/v8 v8.3.2
10+
go.opentelemetry.io/otel v0.13.0
11+
)

extra/redisotel/redisotel.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package redisotel
2+
3+
import (
4+
"context"
5+
6+
"github.com/go-redis/redis/extra/rediscmd"
7+
"github.com/go-redis/redis/v8"
8+
"go.opentelemetry.io/otel/api/global"
9+
"go.opentelemetry.io/otel/api/trace"
10+
"go.opentelemetry.io/otel/label"
11+
)
12+
13+
var tracer = global.Tracer("github.com/go-redis/redis")
14+
15+
type TracingHook struct{}
16+
17+
var _ redis.Hook = TracingHook{}
18+
19+
func (TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) {
20+
if !trace.SpanFromContext(ctx).IsRecording() {
21+
return ctx, nil
22+
}
23+
24+
ctx, span := tracer.Start(ctx, cmd.FullName())
25+
span.SetAttributes(
26+
label.String("db.system", "redis"),
27+
label.String("redis.cmd", rediscmd.CmdString(cmd)),
28+
)
29+
30+
return ctx, nil
31+
}
32+
33+
func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
34+
span := trace.SpanFromContext(ctx)
35+
if err := cmd.Err(); err != nil {
36+
recordError(ctx, span, err)
37+
}
38+
span.End()
39+
return nil
40+
}
41+
42+
func (TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) {
43+
if !trace.SpanFromContext(ctx).IsRecording() {
44+
return ctx, nil
45+
}
46+
47+
summary, cmdsString := rediscmd.CmdsString(cmds)
48+
49+
ctx, span := tracer.Start(ctx, "pipeline "+summary)
50+
span.SetAttributes(
51+
label.String("db.system", "redis"),
52+
label.Int("redis.num_cmd", len(cmds)),
53+
label.String("redis.cmds", cmdsString),
54+
)
55+
56+
return ctx, nil
57+
}
58+
59+
func (TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error {
60+
span := trace.SpanFromContext(ctx)
61+
if err := cmds[0].Err(); err != nil {
62+
recordError(ctx, span, err)
63+
}
64+
span.End()
65+
return nil
66+
}
67+
68+
func recordError(ctx context.Context, span trace.Span, err error) {
69+
if err != redis.Nil {
70+
span.RecordError(ctx, err)
71+
}
72+
}

0 commit comments

Comments
 (0)