hook.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package redis
  2. import (
  3. "context"
  4. "strings"
  5. "time"
  6. red "github.com/go-redis/redis/v8"
  7. "github.com/zeromicro/go-zero/core/logx"
  8. "github.com/zeromicro/go-zero/core/mapping"
  9. "github.com/zeromicro/go-zero/core/timex"
  10. "github.com/zeromicro/go-zero/core/trace"
  11. "go.opentelemetry.io/otel"
  12. tracestd "go.opentelemetry.io/otel/trace"
  13. )
  14. // spanName is the span name of the redis calls.
  15. const spanName = "redis"
  16. var (
  17. startTimeKey = contextKey("startTime")
  18. durationHook = hook{tracer: otel.GetTracerProvider().Tracer(trace.TraceName)}
  19. )
  20. type (
  21. contextKey string
  22. hook struct {
  23. tracer tracestd.Tracer
  24. }
  25. )
  26. func (h hook) BeforeProcess(ctx context.Context, _ red.Cmder) (context.Context, error) {
  27. return h.startSpan(context.WithValue(ctx, startTimeKey, timex.Now())), nil
  28. }
  29. func (h hook) AfterProcess(ctx context.Context, cmd red.Cmder) error {
  30. h.endSpan(ctx)
  31. val := ctx.Value(startTimeKey)
  32. if val == nil {
  33. return nil
  34. }
  35. start, ok := val.(time.Duration)
  36. if !ok {
  37. return nil
  38. }
  39. duration := timex.Since(start)
  40. if duration > slowThreshold.Load() {
  41. logDuration(ctx, cmd, duration)
  42. }
  43. return nil
  44. }
  45. func (h hook) BeforeProcessPipeline(ctx context.Context, _ []red.Cmder) (context.Context, error) {
  46. return h.startSpan(context.WithValue(ctx, startTimeKey, timex.Now())), nil
  47. }
  48. func (h hook) AfterProcessPipeline(ctx context.Context, cmds []red.Cmder) error {
  49. h.endSpan(ctx)
  50. if len(cmds) == 0 {
  51. return nil
  52. }
  53. val := ctx.Value(startTimeKey)
  54. if val == nil {
  55. return nil
  56. }
  57. start, ok := val.(time.Duration)
  58. if !ok {
  59. return nil
  60. }
  61. duration := timex.Since(start)
  62. if duration > slowThreshold.Load()*time.Duration(len(cmds)) {
  63. logDuration(ctx, cmds[0], duration)
  64. }
  65. return nil
  66. }
  67. func logDuration(ctx context.Context, cmd red.Cmder, duration time.Duration) {
  68. var buf strings.Builder
  69. for i, arg := range cmd.Args() {
  70. if i > 0 {
  71. buf.WriteByte(' ')
  72. }
  73. buf.WriteString(mapping.Repr(arg))
  74. }
  75. logx.WithContext(ctx).WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
  76. }
  77. func (h hook) startSpan(ctx context.Context) context.Context {
  78. ctx, _ = h.tracer.Start(ctx, spanName)
  79. return ctx
  80. }
  81. func (h hook) endSpan(ctx context.Context) {
  82. tracestd.SpanFromContext(ctx).End()
  83. }