mirror of https://github.com/go-redis/redis.git
feat(redisotel): ability to override TracerProvider (#1998)
This commit is contained in:
parent
f1dd3d5898
commit
bf8d4aa60c
|
@ -9,6 +9,6 @@ replace github.com/go-redis/redis/extra/rediscmd/v8 => ../rediscmd
|
|||
require (
|
||||
github.com/go-redis/redis/extra/rediscmd/v8 v8.11.4
|
||||
github.com/go-redis/redis/v8 v8.11.4
|
||||
go.opentelemetry.io/otel v1.0.0
|
||||
go.opentelemetry.io/otel/trace v1.0.0
|
||||
go.opentelemetry.io/otel v1.3.0
|
||||
go.opentelemetry.io/otel/trace v1.3.0
|
||||
)
|
||||
|
|
|
@ -8,6 +8,11 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
|
@ -31,13 +36,14 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
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.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
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.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
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=
|
||||
|
@ -45,10 +51,10 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
|
||||
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
|
||||
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
|
||||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
|
||||
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
|
|
@ -6,42 +6,57 @@ import (
|
|||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/go-redis/redis/extra/rediscmd/v8"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var tracer = otel.Tracer("github.com/go-redis/redis")
|
||||
const (
|
||||
// todo: consider using the full module path "github.com/go-redis/redis/extra/redisotel"
|
||||
defaultTracerName = "github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
type TracingHook struct{}
|
||||
|
||||
var _ redis.Hook = (*TracingHook)(nil)
|
||||
|
||||
func NewTracingHook() *TracingHook {
|
||||
return new(TracingHook)
|
||||
type TracingHook struct {
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
func (TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) {
|
||||
func NewTracingHook(opts ...Option) *TracingHook {
|
||||
cfg := &config{
|
||||
tp: otel.GetTracerProvider(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.apply(cfg)
|
||||
}
|
||||
|
||||
tracer := cfg.tp.Tracer(
|
||||
defaultTracerName,
|
||||
// todo: consider adding a version
|
||||
// trace.WithInstrumentationVersion("semver:8.11.4"),
|
||||
)
|
||||
return &TracingHook{tracer: tracer}
|
||||
}
|
||||
|
||||
func (th *TracingHook) BeforeProcess(ctx context.Context, cmd redis.Cmder) (context.Context, error) {
|
||||
if !trace.SpanFromContext(ctx).IsRecording() {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("db.system", "redis"),
|
||||
attribute.String("db.statement", rediscmd.CmdString(cmd)),
|
||||
}
|
||||
opts := []trace.SpanStartOption{
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(attrs...),
|
||||
trace.WithAttributes(
|
||||
semconv.DBSystemRedis,
|
||||
semconv.DBStatementKey.String(rediscmd.CmdString(cmd)),
|
||||
),
|
||||
}
|
||||
|
||||
ctx, _ = tracer.Start(ctx, cmd.FullName(), opts...)
|
||||
ctx, _ = th.tracer.Start(ctx, cmd.FullName(), opts...)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
|
||||
func (th *TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if err := cmd.Err(); err != nil {
|
||||
recordError(ctx, span, err)
|
||||
|
@ -50,29 +65,28 @@ func (TracingHook) AfterProcess(ctx context.Context, cmd redis.Cmder) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) {
|
||||
func (th *TracingHook) BeforeProcessPipeline(ctx context.Context, cmds []redis.Cmder) (context.Context, error) {
|
||||
if !trace.SpanFromContext(ctx).IsRecording() {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
summary, cmdsString := rediscmd.CmdsString(cmds)
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("db.system", "redis"),
|
||||
attribute.Int("db.redis.num_cmd", len(cmds)),
|
||||
attribute.String("db.statement", cmdsString),
|
||||
}
|
||||
opts := []trace.SpanStartOption{
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(attrs...),
|
||||
trace.WithAttributes(
|
||||
semconv.DBSystemRedis,
|
||||
semconv.DBStatementKey.String(cmdsString),
|
||||
attribute.Int("db.redis.num_cmd", len(cmds)),
|
||||
),
|
||||
}
|
||||
|
||||
ctx, _ = tracer.Start(ctx, "pipeline "+summary, opts...)
|
||||
ctx, _ = th.tracer.Start(ctx, "pipeline "+summary, opts...)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error {
|
||||
func (th *TracingHook) AfterProcessPipeline(ctx context.Context, cmds []redis.Cmder) error {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if err := cmds[0].Err(); err != nil {
|
||||
recordError(ctx, span, err)
|
||||
|
@ -87,3 +101,28 @@ func recordError(ctx context.Context, span trace.Span, err error) {
|
|||
span.SetStatus(codes.Error, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type config struct {
|
||||
tp trace.TracerProvider
|
||||
}
|
||||
|
||||
// Option specifies instrumentation configuration options.
|
||||
type Option interface {
|
||||
apply(*config)
|
||||
}
|
||||
|
||||
type optionFunc func(*config)
|
||||
|
||||
func (o optionFunc) apply(c *config) {
|
||||
o(c)
|
||||
}
|
||||
|
||||
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
|
||||
// If none is specified, the global provider is used.
|
||||
func WithTracerProvider(provider trace.TracerProvider) Option {
|
||||
return optionFunc(func(cfg *config) {
|
||||
if provider != nil {
|
||||
cfg.tp = provider
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package redisotel_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-redis/redis/extra/redisotel/v8"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
// this also functions as a compile-time test that the
|
||||
// TracingHook conforms to the Hook interface
|
||||
var _ redis.Hook = redisotel.NewTracingHook()
|
||||
}
|
||||
|
||||
type providerFunc func(name string, opts ...trace.TracerOption) trace.Tracer
|
||||
|
||||
func (fn providerFunc) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
|
||||
return fn(name, opts...)
|
||||
}
|
||||
|
||||
func TestNewWithTracerProvider(t *testing.T) {
|
||||
invoked := false
|
||||
|
||||
tp := providerFunc(func(name string, opts ...trace.TracerOption) trace.Tracer {
|
||||
invoked = true
|
||||
return otel.GetTracerProvider().Tracer(name, opts...)
|
||||
})
|
||||
|
||||
_ = redisotel.NewTracingHook(redisotel.WithTracerProvider(tp))
|
||||
|
||||
if !invoked {
|
||||
t.Fatalf("did not call custom TraceProvider")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue