From 8494178b0d51c64f7b18745b111cbc692a96f452 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Fri, 22 Jan 2021 18:21:40 +0300 Subject: [PATCH] meter: rework meter interface Signed-off-by: Vasiliy Tolstov --- meter/meter.go | 62 +++++++++++++++ meter/meter_test.go | 14 ++++ meter/noop.go | 132 +++++++++++++++++++++++++++---- meter/options.go | 45 ++++++----- meter/reporter.go | 21 ----- meter/reporter_test.go | 17 ---- meter/wrapper/metrics_wrapper.go | 12 +-- 7 files changed, 221 insertions(+), 82 deletions(-) create mode 100644 meter/meter.go create mode 100644 meter/meter_test.go delete mode 100644 meter/reporter.go delete mode 100644 meter/reporter_test.go diff --git a/meter/meter.go b/meter/meter.go new file mode 100644 index 00000000..bf02e4e4 --- /dev/null +++ b/meter/meter.go @@ -0,0 +1,62 @@ +// Package meter is for instrumentation +package meter + +import ( + "time" + + "github.com/unistack-org/micro/v3/metadata" +) + +var ( + DefaultReporter Meter = NewMeter() +) + +// Meter is an interface for collecting and instrumenting metrics +type Meter interface { + Init(...Option) error + Counter(string, metadata.Metadata) Counter + FloatCounter(string, metadata.Metadata) FloatCounter + Gauge(string, func() float64, metadata.Metadata) Gauge + Set(metadata.Metadata) Meter + Histogram(string, metadata.Metadata) Histogram + Summary(string, metadata.Metadata) Summary + SummaryExt(string, time.Duration, []float64, metadata.Metadata) Summary + Options() Options + String() string +} + +// Counter is a counter +type Counter interface { + Add(int) + Dec() + Get() uint64 + Inc() + Set(uint64) +} + +// FloatCounter is a float64 counter +type FloatCounter interface { + Add(float64) + Get() float64 + Set(float64) + Sub(float64) +} + +// Gauge is a float64 gauge +type Gauge interface { + Get() float64 +} + +// Histogram is a histogram for non-negative values with automatically created buckets +type Histogram interface { + Reset() + Update(float64) + UpdateDuration(time.Time) + // VisitNonZeroBuckets(f func(vmrange string, count uint64)) +} + +// Summary is the summary +type Summary interface { + Update(float64) + UpdateDuration(time.Time) +} diff --git a/meter/meter_test.go b/meter/meter_test.go new file mode 100644 index 00000000..c9af6425 --- /dev/null +++ b/meter/meter_test.go @@ -0,0 +1,14 @@ +package meter + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNoopMeter(t *testing.T) { + meter := NewMeter(Path("/noop")) + assert.NotNil(t, meter) + assert.Equal(t, "/noop", meter.Options().Path) + assert.Implements(t, new(Meter), meter) +} diff --git a/meter/noop.go b/meter/noop.go index 25e2c621..f16e0de3 100644 --- a/meter/noop.go +++ b/meter/noop.go @@ -6,42 +6,140 @@ import ( "github.com/unistack-org/micro/v3/metadata" ) -// NoopReporter is an noop implementation of Reporter: -type noopReporter struct { +// NoopMeter is an noop implementation of Meter +type noopMeter struct { opts Options + md metadata.Metadata } -// NewReporter returns a configured noop reporter: -func NewReporter(opts ...Option) Reporter { - return &noopReporter{ +// NewMeter returns a configured noop reporter: +func NewMeter(opts ...Option) Meter { + return &noopMeter{ opts: NewOptions(opts...), } } // Init initialize options -func (r *noopReporter) Init(opts ...Option) error { +func (r *noopMeter) Init(opts ...Option) error { for _, o := range opts { o(&r.opts) } return nil } -// Count implements the Reporter interface Count method: -func (r *noopReporter) Count(metricName string, value int64, md metadata.Metadata) error { - return nil +// Counter implements the Meter interface +func (r *noopMeter) Counter(name string, md metadata.Metadata) Counter { + return &noopCounter{} } -// Gauge implements the Reporter interface Gauge method: -func (r *noopReporter) Gauge(metricName string, value float64, md metadata.Metadata) error { - return nil +// FloatCounter implements the Meter interface +func (r *noopMeter) FloatCounter(name string, md metadata.Metadata) FloatCounter { + return &noopFloatCounter{} } -// Timing implements the Reporter interface Timing method: -func (r *noopReporter) Timing(metricName string, value time.Duration, md metadata.Metadata) error { - return nil +// Gauge implements the Meter interface +func (r *noopMeter) Gauge(name string, f func() float64, md metadata.Metadata) Gauge { + return &noopGauge{} } -// Options implements the Reporter interface Optios method: -func (r *noopReporter) Options() Options { +// Summary implements the Meter interface +func (r *noopMeter) Summary(name string, md metadata.Metadata) Summary { + return &noopSummary{} +} + +// SummaryExt implements the Meter interface +func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, md metadata.Metadata) Summary { + return &noopSummary{} +} + +// Histogram implements the Meter interface +func (r *noopMeter) Histogram(name string, md metadata.Metadata) Histogram { + return &noopHistogram{} +} + +// Set implements the Meter interface +func (r *noopMeter) Set(md metadata.Metadata) Meter { + return &noopMeter{opts: r.opts, md: metadata.Copy(md)} +} + +// Options implements the Meter interface +func (r *noopMeter) Options() Options { return r.opts } + +// String implements the Meter interface +func (r *noopMeter) String() string { + return "noop" +} + +type noopCounter struct{} + +func (r *noopCounter) Add(int) { + +} + +func (r *noopCounter) Dec() { + +} + +func (r *noopCounter) Get() uint64 { + return 0 +} + +func (r *noopCounter) Inc() { + +} + +func (r *noopCounter) Set(uint64) { + +} + +type noopFloatCounter struct{} + +func (r *noopFloatCounter) Add(float64) { + +} + +func (r *noopFloatCounter) Get() float64 { + return 0 +} + +func (r *noopFloatCounter) Set(float64) { + +} + +func (r *noopFloatCounter) Sub(float64) { + +} + +type noopGauge struct{} + +func (r *noopGauge) Get() float64 { + return 0 +} + +type noopSummary struct{} + +func (r *noopSummary) Update(float64) { + +} + +func (r *noopSummary) UpdateDuration(time.Time) { + +} + +type noopHistogram struct{} + +func (r *noopHistogram) Reset() { + +} + +func (r *noopHistogram) Update(float64) { + +} + +func (r *noopHistogram) UpdateDuration(time.Time) { + +} + +//func (r *noopHistogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) {} diff --git a/meter/options.go b/meter/options.go index 419b828e..015cd9c5 100644 --- a/meter/options.go +++ b/meter/options.go @@ -8,12 +8,12 @@ import ( ) var ( - // The Prometheus metrics will be made available on this port: - defaultPrometheusListenAddress = ":9000" - // This is the endpoint where the Prometheus metrics will be made available ("/metrics" is the default with Prometheus): - defaultPath = "/metrics" + // The Meter data will be made available on this port + DefaultAddress = ":9090" + // This is the endpoint where the Meter data will be made available ("/metrics" is the default) + DefaultPath = "/metrics" // timingObjectives is the default spread of stats we maintain for timings / histograms: - defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0} + //defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0} ) // Option powers the configuration for metrics implementations: @@ -21,22 +21,23 @@ type Option func(*Options) // Options for metrics implementations: type Options struct { - Address string - Path string - DefaultTags metadata.Metadata - TimingObjectives map[float64]float64 - Logger logger.Logger - Context context.Context + Address string + Path string + Metadata metadata.Metadata + //TimingObjectives map[float64]float64 + Logger logger.Logger + Context context.Context } // NewOptions prepares a set of options: func NewOptions(opt ...Option) Options { opts := Options{ - Address: defaultPrometheusListenAddress, - DefaultTags: metadata.New(2), - Path: defaultPath, - TimingObjectives: defaultTimingObjectives, - Context: context.Background(), + Address: DefaultAddress, + Metadata: metadata.New(3), // 3 elements contains service name, version and id + Path: DefaultPath, + // TimingObjectives: defaultTimingObjectives, + Context: context.Background(), + Logger: logger.DefaultLogger, } for _, o := range opt { @@ -53,33 +54,35 @@ func Context(ctx context.Context) Option { } } -// Path used to serve metrics over HTTP: +// Path used to serve metrics over HTTP func Path(value string) Option { return func(o *Options) { o.Path = value } } -// Address is the listen address to serve metrics on: +// Address is the listen address to serve metrics func Address(value string) Option { return func(o *Options) { o.Address = value } } -// DefaultTags will be added to every metric: -func DefaultTags(md metadata.Metadata) Option { +// Metadata will be added to every metric +func Metadata(md metadata.Metadata) Option { return func(o *Options) { - o.DefaultTags = metadata.Copy(md) + o.Metadata = metadata.Copy(md) } } +/* // TimingObjectives defines the desired spread of statistics for histogram / timing metrics: func TimingObjectives(value map[float64]float64) Option { return func(o *Options) { o.TimingObjectives = value } } +*/ // Logger sets the logger func Logger(l logger.Logger) Option { diff --git a/meter/reporter.go b/meter/reporter.go deleted file mode 100644 index c4898d57..00000000 --- a/meter/reporter.go +++ /dev/null @@ -1,21 +0,0 @@ -// Package meter is for instrumentation -package meter - -import ( - "time" - - "github.com/unistack-org/micro/v3/metadata" -) - -var ( - DefaultReporter Reporter = NewReporter() -) - -// Reporter is an interface for collecting and instrumenting metrics -type Reporter interface { - Init(...Option) error - Count(id string, value int64, md metadata.Metadata) error - Gauge(id string, value float64, md metadata.Metadata) error - Timing(id string, value time.Duration, md metadata.Metadata) error - Options() Options -} diff --git a/meter/reporter_test.go b/meter/reporter_test.go deleted file mode 100644 index 51379f57..00000000 --- a/meter/reporter_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package meter - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNoopReporter(t *testing.T) { - // Make a Reporter: - reporter := NewReporter(Path("/noop")) - assert.NotNil(t, reporter) - assert.Equal(t, "/noop", reporter.Options().Path) - - // Check that our implementation is valid: - assert.Implements(t, new(Reporter), reporter) -} diff --git a/meter/wrapper/metrics_wrapper.go b/meter/wrapper/metrics_wrapper.go index 303b6113..8ed756a9 100644 --- a/meter/wrapper/metrics_wrapper.go +++ b/meter/wrapper/metrics_wrapper.go @@ -9,15 +9,15 @@ import ( "github.com/unistack-org/micro/v3/server" ) -// Wrapper provides a HandlerFunc for meter.Reporter implementations: +// Wrapper provides a HandlerFunc for meter.Meter implementations type Wrapper struct { - reporter meter.Reporter + meter meter.Meter } -// New returns a *Wrapper configured with the given meter.Reporter: -func New(reporter meter.Reporter) *Wrapper { +// New returns a *Wrapper configured with the given meter.Meter +func New(meter meter.Meter) *Wrapper { return &Wrapper{ - reporter: reporter, + meter: meter, } } @@ -43,7 +43,7 @@ func (w *Wrapper) HandlerFunc(handlerFunction server.HandlerFunc) server.Handler } // Instrument the result (if the DefaultClient has been configured): - w.reporter.Timing("service.handler", time.Since(callTime), tags) + w.meter.Summary("service.handler", tags).Update(float64(time.Since(callTime).Seconds())) return err }