Skip to content

Commit ad42980

Browse files
authored
add statsd reporter support (#85)
1 parent d8af4ef commit ad42980

File tree

9 files changed

+180
-16
lines changed

9 files changed

+180
-16
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/pkg/errors v0.9.1
2727
github.com/robfig/cron/v3 v3.0.1
2828
github.com/smallnest/weighted v0.0.0-20230419055410-36b780e40a7a
29+
github.com/smira/go-statsd v1.3.3
2930
github.com/spf13/cobra v1.8.0
3031
github.com/spf13/viper v1.18.2
3132
github.com/stretchr/testify v1.8.4

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,8 @@ github.com/smallnest/weighted v0.0.0-20230419055410-36b780e40a7a h1:eieNTZmrnPzI
703703
github.com/smallnest/weighted v0.0.0-20230419055410-36b780e40a7a/go.mod h1:xc9CoZ+ZBGwajnWto5Aqw/wWg8euy4HtOr6K9Fxp9iw=
704704
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
705705
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
706+
github.com/smira/go-statsd v1.3.3 h1:WnMlmGTyMpzto+HvOJWRPoLaLlk5EGfzsnlQBcvj4yI=
707+
github.com/smira/go-statsd v1.3.3/go.mod h1:RjdsESPgDODtg1VpVVf9MJrEW2Hw0wtRNbmB1CAhu6A=
706708
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
707709
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
708710
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=

internal/config/config.go

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type (
3939
Cron CronConfig `mapstructure:"cron"`
4040
SLA SLAConfig `mapstructure:"sla"`
4141
FunctionalTest FunctionalTestConfig `mapstructure:"functional_test"`
42+
StatsD *StatsDConfig `mapstructure:"statsd"`
4243

4344
namespace string
4445
env Env
@@ -394,6 +395,11 @@ type (
394395
PerClientRPS int `mapstructure:"per_client_rps"`
395396
}
396397

398+
StatsDConfig struct {
399+
Address string `mapstructure:"address" validate:"required"`
400+
Prefix string `mapstructure:"prefix"`
401+
}
402+
397403
ConfigOption func(options *configOptions)
398404

399405
Env string

internal/storage/blobstorage/gcs/blob_storage_integration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"testing"
66

7+
"github.com/stretchr/testify/suite"
78
"go.uber.org/fx"
89
"google.golang.org/protobuf/proto"
910

@@ -13,7 +14,6 @@ import (
1314
"github.com/coinbase/chainstorage/internal/utils/testutil"
1415
"github.com/coinbase/chainstorage/protos/coinbase/c3/common"
1516
api "github.com/coinbase/chainstorage/protos/coinbase/chainstorage"
16-
"github.com/stretchr/testify/suite"
1717
)
1818

1919
type gcpBlobStorageTestSuite struct {

internal/storage/blobstorage/module.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package blobstorage
22

33
import (
4-
"github.com/coinbase/chainstorage/internal/storage/blobstorage/gcs"
54
"go.uber.org/fx"
65

6+
"github.com/coinbase/chainstorage/internal/storage/blobstorage/gcs"
77
"github.com/coinbase/chainstorage/internal/storage/blobstorage/internal"
88
"github.com/coinbase/chainstorage/internal/storage/blobstorage/s3"
99
)

internal/tally/module.go

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ import (
66

77
var Module = fx.Options(
88
fx.Provide(NewRootScope),
9+
fx.Provide(NewStatsReporter),
910
)

internal/tally/stats_reporter.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package tally
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
smirastatsd "github.com/smira/go-statsd"
8+
"github.com/uber-go/tally/v4"
9+
"go.uber.org/fx"
10+
"go.uber.org/zap"
11+
12+
"github.com/coinbase/chainstorage/internal/config"
13+
)
14+
15+
type (
16+
StatsReporterParams struct {
17+
fx.In
18+
Lifecycle fx.Lifecycle
19+
Logger *zap.Logger
20+
Config *config.Config
21+
}
22+
23+
reporter struct {
24+
client *smirastatsd.Client
25+
}
26+
)
27+
28+
const (
29+
reportingInterval = time.Second
30+
)
31+
32+
var (
33+
// hardcoding this to be datadog format
34+
// we need think about whats the best way to set it up in config such that
35+
// when we switch reporter impl, config will still be backward compatible
36+
tagFormat = smirastatsd.TagFormatDatadog
37+
)
38+
39+
func NewStatsReporter(params StatsReporterParams) tally.StatsReporter {
40+
if params.Config.StatsD == nil {
41+
return tally.NullStatsReporter
42+
}
43+
cfg := params.Config.StatsD
44+
client := smirastatsd.NewClient(
45+
cfg.Address,
46+
smirastatsd.MetricPrefix(cfg.Prefix),
47+
smirastatsd.TagStyle(tagFormat),
48+
smirastatsd.ReportInterval(reportingInterval),
49+
)
50+
params.Logger.Info("initialized statsd client")
51+
params.Lifecycle.Append(fx.Hook{
52+
OnStop: func(ctx context.Context) error {
53+
return client.Close()
54+
},
55+
})
56+
return &reporter{
57+
client: client,
58+
}
59+
}
60+
61+
func convertTags(tagsMap map[string]string) []smirastatsd.Tag {
62+
tags := make([]smirastatsd.Tag, 0, len(tagsMap))
63+
for key, value := range tagsMap {
64+
tags = append(tags, smirastatsd.StringTag(key, value))
65+
}
66+
return tags
67+
}
68+
69+
func (r *reporter) ReportCounter(name string, tags map[string]string, value int64) {
70+
r.client.Incr(name, value, convertTags(tags)...)
71+
}
72+
73+
func (r *reporter) ReportGauge(name string, tags map[string]string, value float64) {
74+
r.client.FGauge(name, value, convertTags(tags)...)
75+
}
76+
77+
func (r *reporter) ReportTimer(name string, tags map[string]string, value time.Duration) {
78+
r.client.PrecisionTiming(name, value, convertTags(tags)...)
79+
}
80+
81+
func (r *reporter) ReportHistogramValueSamples(
82+
name string,
83+
tags map[string]string,
84+
buckets tally.Buckets,
85+
bucketLowerBound,
86+
bucketUpperBound float64,
87+
samples int64) {
88+
panic("no implemented")
89+
}
90+
91+
func (r *reporter) ReportHistogramDurationSamples(
92+
name string,
93+
tags map[string]string,
94+
buckets tally.Buckets,
95+
bucketLowerBound,
96+
bucketUpperBound time.Duration,
97+
samples int64) {
98+
panic("no implemented")
99+
}
100+
101+
func (r *reporter) Capabilities() tally.Capabilities {
102+
return r
103+
}
104+
105+
func (r *reporter) Reporting() bool {
106+
return true
107+
}
108+
109+
func (r *reporter) Tagging() bool {
110+
return true
111+
}
112+
113+
func (r *reporter) Flush() {
114+
// no-op
115+
}

internal/tally/stats_reporter_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package tally
2+
3+
import (
4+
"testing"
5+
6+
"github.com/uber-go/tally/v4"
7+
"go.uber.org/fx"
8+
9+
"github.com/coinbase/chainstorage/internal/config"
10+
"github.com/coinbase/chainstorage/internal/utils/testapp"
11+
"github.com/coinbase/chainstorage/internal/utils/testutil"
12+
)
13+
14+
func TestNewReporterDefaultNoStatsD(t *testing.T) {
15+
testapp.TestAllConfigs(t, func(t *testing.T, cfg *config.Config) {
16+
require := testutil.Require(t)
17+
18+
var reporter tally.StatsReporter
19+
testapp.New(
20+
t,
21+
testapp.WithConfig(cfg),
22+
fx.Provide(NewStatsReporter),
23+
fx.Populate(&reporter),
24+
)
25+
26+
require.Equal(tally.NullStatsReporter, reporter)
27+
require.Equal(false, reporter.Capabilities().Reporting())
28+
require.Equal(false, reporter.Capabilities().Tagging())
29+
})
30+
}
31+
32+
func TestNewReporterDefaultWithStatsD(t *testing.T) {
33+
testapp.TestAllConfigs(t, func(t *testing.T, cfg *config.Config) {
34+
require := testutil.Require(t)
35+
cfg.StatsD = &config.StatsDConfig{
36+
Address: "localhost:8125",
37+
}
38+
var reporter tally.StatsReporter
39+
testapp.New(
40+
t,
41+
testapp.WithConfig(cfg),
42+
fx.Provide(NewStatsReporter),
43+
fx.Populate(&reporter),
44+
)
45+
require.NotEqual(tally.NullStatsReporter, reporter)
46+
require.Equal(true, reporter.Capabilities().Reporting())
47+
require.Equal(true, reporter.Capabilities().Tagging())
48+
})
49+
}

internal/tally/tally.go

+4-14
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package tally
22

33
import (
44
"context"
5-
"time"
65

76
"github.com/uber-go/tally/v4"
87
"go.uber.org/fx"
@@ -16,27 +15,18 @@ type (
1615
fx.In
1716
Lifecycle fx.Lifecycle
1817
Config *config.Config
19-
Reporter tally.StatsReporter `optional:"true"`
18+
Reporter tally.StatsReporter
2019
}
2120
)
2221

23-
const (
24-
reportingInterval = time.Second
25-
)
26-
2722
func NewRootScope(params MetricParams) tally.Scope {
28-
// XXX: Inject your own reporter here.
29-
reporter := params.Reporter
30-
if reporter == nil {
31-
reporter = tally.NullStatsReporter
32-
}
33-
3423
opts := tally.ScopeOptions{
3524
Prefix: consts.ServiceName,
36-
Reporter: reporter,
25+
Reporter: params.Reporter,
3726
Tags: params.Config.GetCommonTags(),
3827
}
39-
scope, closer := tally.NewRootScope(opts, reportingInterval)
28+
//report interval will be set on reporter
29+
scope, closer := tally.NewRootScope(opts, 0)
4030
params.Lifecycle.Append(fx.Hook{
4131
OnStop: func(ctx context.Context) error {
4232
return closer.Close()

0 commit comments

Comments
 (0)