-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib/gomodule/redigo: copied from contrib/garyburd/redigo (#422)
github.com/garyburd/redigo has been archived and moved to github.com/gomodule/redigo . Fixes #421
- Loading branch information
1 parent
2555c24
commit 8c8aefe
Showing
4 changed files
with
505 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package redigo_test | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"time" | ||
|
||
"github.com/gomodule/redigo/redis" | ||
redigotrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" | ||
) | ||
|
||
// To start tracing Redis commands, use the TracedDial function to create a connection, | ||
// passing in a service name of choice. | ||
func Example() { | ||
c, err := redigotrace.Dial("tcp", "127.0.0.1:6379") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Emit spans per command by using your Redis connection as usual | ||
c.Do("SET", "vehicle", "truck") | ||
|
||
// Use a context to pass information down the call chain | ||
root, ctx := tracer.StartSpanFromContext(context.Background(), "parent.request", | ||
tracer.ServiceName("web"), | ||
tracer.ResourceName("/home"), | ||
) | ||
|
||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request' | ||
c.Do("SET", "food", "cheese", ctx) | ||
root.Finish() | ||
} | ||
|
||
func Example_tracedConn() { | ||
c, err := redigotrace.Dial("tcp", "127.0.0.1:6379", | ||
redigotrace.WithServiceName("my-redis-backend"), | ||
redis.DialKeepAlive(time.Minute), | ||
) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Emit spans per command by using your Redis connection as usual | ||
c.Do("SET", "vehicle", "truck") | ||
|
||
// Use a context to pass information down the call chain | ||
root, ctx := tracer.StartSpanFromContext(context.Background(), "parent.request", | ||
tracer.ServiceName("web"), | ||
tracer.ResourceName("/home"), | ||
) | ||
|
||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request' | ||
c.Do("SET", "food", "cheese", ctx) | ||
root.Finish() | ||
} | ||
|
||
// Alternatively, provide a redis URL to the TracedDialURL function | ||
func Example_dialURL() { | ||
c, err := redigotrace.DialURL("redis://127.0.0.1:6379") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
c.Do("SET", "vehicle", "truck") | ||
} | ||
|
||
// When using a redigo Pool, set your Dial function to return a traced connection | ||
func Example_pool() { | ||
pool := &redis.Pool{ | ||
Dial: func() (redis.Conn, error) { | ||
return redigotrace.Dial("tcp", "127.0.0.1:6379", | ||
redigotrace.WithServiceName("my-redis-backend"), | ||
) | ||
}, | ||
} | ||
|
||
c := pool.Get() | ||
c.Do("SET", " whiskey", " glass") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package redigo // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo" | ||
|
||
type dialConfig struct { | ||
serviceName string | ||
analyticsRate float64 | ||
} | ||
|
||
// DialOption represents an option that can be passed to Dial. | ||
type DialOption func(*dialConfig) | ||
|
||
func defaults(cfg *dialConfig) { | ||
cfg.serviceName = "redis.conn" | ||
// cfg.analyticsRate = globalconfig.AnalyticsRate() | ||
} | ||
|
||
// WithServiceName sets the given service name for the dialled connection. | ||
func WithServiceName(name string) DialOption { | ||
return func(cfg *dialConfig) { | ||
cfg.serviceName = name | ||
} | ||
} | ||
|
||
// WithAnalytics enables Trace Analytics for all started spans. | ||
func WithAnalytics(on bool) DialOption { | ||
if on { | ||
return WithAnalyticsRate(1.0) | ||
} | ||
return WithAnalyticsRate(0.0) | ||
} | ||
|
||
// WithAnalyticsRate sets the sampling rate for Trace Analytics events | ||
// correlated to started spans. | ||
func WithAnalyticsRate(rate float64) DialOption { | ||
return func(cfg *dialConfig) { | ||
cfg.analyticsRate = rate | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// Package redigo provides functions to trace the gomodule/redigo package (https://github.com/gomodule/redigo). | ||
package redigo | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"net" | ||
"net/url" | ||
"strconv" | ||
|
||
redis "github.com/gomodule/redigo/redis" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" | ||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" | ||
) | ||
|
||
// Conn is an implementation of the redis.Conn interface that supports tracing | ||
type Conn struct { | ||
redis.Conn | ||
*params | ||
} | ||
|
||
// params contains fields and metadata useful for command tracing | ||
type params struct { | ||
config *dialConfig | ||
network string | ||
host string | ||
port string | ||
} | ||
|
||
// parseOptions parses a set of arbitrary options (which can be of type redis.DialOption | ||
// or the local DialOption) and returns the corresponding redis.DialOption set as well as | ||
// a configured dialConfig. | ||
func parseOptions(options ...interface{}) ([]redis.DialOption, *dialConfig) { | ||
dialOpts := []redis.DialOption{} | ||
cfg := new(dialConfig) | ||
defaults(cfg) | ||
for _, opt := range options { | ||
switch o := opt.(type) { | ||
case redis.DialOption: | ||
dialOpts = append(dialOpts, o) | ||
case DialOption: | ||
o(cfg) | ||
} | ||
} | ||
return dialOpts, cfg | ||
} | ||
|
||
// Dial dials into the network address and returns a traced redis.Conn. | ||
// The set of supported options must be either of type redis.DialOption or this package's DialOption. | ||
func Dial(network, address string, options ...interface{}) (redis.Conn, error) { | ||
dialOpts, cfg := parseOptions(options...) | ||
c, err := redis.Dial(network, address, dialOpts...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
host, port, err := net.SplitHostPort(address) | ||
if err != nil { | ||
return nil, err | ||
} | ||
tc := Conn{c, ¶ms{cfg, network, host, port}} | ||
return tc, nil | ||
} | ||
|
||
// DialURL connects to a Redis server at the given URL using the Redis | ||
// URI scheme. URLs should follow the draft IANA specification for the | ||
// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis). | ||
// The returned redis.Conn is traced. | ||
func DialURL(rawurl string, options ...interface{}) (redis.Conn, error) { | ||
dialOpts, cfg := parseOptions(options...) | ||
u, err := url.Parse(rawurl) | ||
if err != nil { | ||
return Conn{}, err | ||
} | ||
host, port, err := net.SplitHostPort(u.Host) | ||
if err != nil { | ||
host = u.Host | ||
port = "6379" | ||
} | ||
if host == "" { | ||
host = "localhost" | ||
} | ||
network := "tcp" | ||
c, err := redis.DialURL(rawurl, dialOpts...) | ||
tc := Conn{c, ¶ms{cfg, network, host, port}} | ||
return tc, err | ||
} | ||
|
||
// newChildSpan creates a span inheriting from the given context. It adds to the span useful metadata about the traced Redis connection | ||
func (tc Conn) newChildSpan(ctx context.Context) ddtrace.Span { | ||
p := tc.params | ||
opts := []ddtrace.StartSpanOption{ | ||
tracer.SpanType(ext.SpanTypeRedis), | ||
tracer.ServiceName(p.config.serviceName), | ||
} | ||
if rate := p.config.analyticsRate; rate > 0 { | ||
opts = append(opts, tracer.Tag(ext.EventSampleRate, rate)) | ||
} | ||
span, _ := tracer.StartSpanFromContext(ctx, "redis.command", opts...) | ||
span.SetTag("out.network", p.network) | ||
span.SetTag(ext.TargetPort, p.port) | ||
span.SetTag(ext.TargetHost, p.host) | ||
return span | ||
} | ||
|
||
// Do wraps redis.Conn.Do. It sends a command to the Redis server and returns the received reply. | ||
// In the process it emits a span containing key information about the command sent. | ||
// When passed a context.Context as the final argument, Do will ensure that any span created | ||
// inherits from this context. The rest of the arguments are passed through to the Redis server unchanged. | ||
func (tc Conn) Do(commandName string, args ...interface{}) (reply interface{}, err error) { | ||
var ( | ||
ctx context.Context | ||
ok bool | ||
) | ||
if n := len(args); n > 0 { | ||
ctx, ok = args[n-1].(context.Context) | ||
if ok { | ||
args = args[:n-1] | ||
} | ||
} | ||
|
||
span := tc.newChildSpan(ctx) | ||
defer func() { | ||
span.Finish(tracer.WithError(err)) | ||
}() | ||
|
||
span.SetTag("redis.args_length", strconv.Itoa(len(args))) | ||
|
||
if len(commandName) > 0 { | ||
span.SetTag(ext.ResourceName, commandName) | ||
} else { | ||
// When the command argument to the Do method is "", then the Do method will flush the output buffer | ||
// See https://godoc.org/github.com/gomodule/redigo/redis#hdr-Pipelining | ||
span.SetTag(ext.ResourceName, "redigo.Conn.Flush") | ||
} | ||
var b bytes.Buffer | ||
b.WriteString(commandName) | ||
for _, arg := range args { | ||
b.WriteString(" ") | ||
switch arg := arg.(type) { | ||
case string: | ||
b.WriteString(arg) | ||
case int: | ||
b.WriteString(strconv.Itoa(arg)) | ||
case int32: | ||
b.WriteString(strconv.FormatInt(int64(arg), 10)) | ||
case int64: | ||
b.WriteString(strconv.FormatInt(arg, 10)) | ||
case fmt.Stringer: | ||
b.WriteString(arg.String()) | ||
} | ||
} | ||
span.SetTag("redis.raw_command", b.String()) | ||
return tc.Conn.Do(commandName, args...) | ||
} |
Oops, something went wrong.