Skip to content

Commit

Permalink
add metric decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
d.reznichenko committed Apr 18, 2024
1 parent 246dc7e commit 1f8c256
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 1 deletion.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/caarlos0/env/v10 v10.0.0
github.com/dmitrorezn/golang-lru/v2 v2.0.2
github.com/redis/go-redis/v9 v9.5.1
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
golang.org/x/sync v0.6.0
)
Expand All @@ -17,6 +17,8 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
Expand Down
141 changes: 141 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package wredis

import (
"context"
"sync"
"sync/atomic"
"time"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"

"github.com/redis/go-redis/v9"
)

type MetricsClient struct {
UniversalClient
meter *Meter
}

func NewMetricsClient(
name string,
client UniversalClient,
metricProvider metric.MeterProvider,
) *MetricsClient {
return &MetricsClient{
UniversalClient: client,
meter: NewMeter(name, metricProvider.Meter("wredis")),
}
}

type Meter struct {
name string

cmu sync.RWMutex
counters map[string]metric.Int64Counter

hmu sync.RWMutex
histograms map[string]metric.Float64Histogram

gauge map[string]atomic.Int64

meter metric.Meter
}

func NewMeter(name string, meter metric.Meter) *Meter {
return &Meter{
name: name,
meter: meter,
}
}

func (m *Meter) Observe(ctx context.Context, cmd string) func(err error) {
var (
start = time.Now()
attrOpt = metric.WithAttributes(
attribute.String("name", m.name),
attribute.String("cmd", cmd),
)
)
m.cmu.RLock()
cnt, ok := m.counters[cmd]
m.cmu.RUnlock()
var err error
if !ok {
if cnt, err = m.meter.Int64Counter(cmd); err == nil {
m.cmu.Lock()
m.counters[cmd] = cnt
m.cmu.Unlock()
}
}

gauge, ok := m.gauge[cmd]
if !ok {
_, _ = m.meter.Int64ObservableGauge(cmd, metric.WithInt64Callback(func(ctx context.Context, observer metric.Int64Observer) error {
observer.Observe(gauge.Load(), attrOpt)
return nil
}))
}
gauge.Add(1)

m.hmu.RLock()
histogram, ok := m.histograms[cmd]
m.hmu.RUnlock()
if !ok {
if histogram, err = m.meter.Float64Histogram(cmd); err == nil {
m.cmu.Lock()
m.histograms[cmd] = histogram
m.cmu.Unlock()
}
}

return func(err error) {
cnt.Add(ctx, 1, attrOpt, metric.WithAttributes(attribute.Bool("error", err != nil)))

histogram.Record(
ctx,
time.Since(start).Seconds(),
attrOpt,
metric.WithAttributes(
attribute.Bool("error", err != nil),
),
)

gauge.Add(-1)
}
}

func (s *MetricsClient) Get(ctx context.Context, key string) (cmd *redis.StringCmd) {
defer s.meter.Observe(ctx, "Get")(cmd.Err())

return s.UniversalClient.Get(ctx, key)
}

func (s *MetricsClient) HGet(ctx context.Context, key, field string) (cmd *redis.StringCmd) {
defer s.meter.Observe(ctx, "HGet")(cmd.Err())

return s.UniversalClient.HGet(ctx, key, field)
}

func (s *MetricsClient) GetDel(ctx context.Context, key string) (cmd *redis.StringCmd) {
defer s.meter.Observe(ctx, "GetDel")(cmd.Err())

return s.UniversalClient.GetDel(ctx, key)
}

func (s *MetricsClient) HMGet(ctx context.Context, key string, field ...string) (cmd *redis.SliceCmd) {
defer s.meter.Observe(ctx, "HMGet")(cmd.Err())

return s.UniversalClient.HMGet(ctx, key, field...)
}

func (s *MetricsClient) HGetAll(ctx context.Context, key string) (cmd *redis.MapStringStringCmd) {
defer s.meter.Observe(ctx, "HGetAll")(cmd.Err())

return s.UniversalClient.HGetAll(ctx, key)
}

/*
TO DO
....
*/

0 comments on commit 1f8c256

Please sign in to comment.