meter: rework meter interface

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2021-01-22 18:21:40 +03:00
parent 8a2c4c511e
commit 8494178b0d
7 changed files with 221 additions and 82 deletions

62
meter/meter.go Normal file
View File

@ -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)
}

14
meter/meter_test.go Normal file
View File

@ -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)
}

View File

@ -6,42 +6,140 @@ import (
"github.com/unistack-org/micro/v3/metadata" "github.com/unistack-org/micro/v3/metadata"
) )
// NoopReporter is an noop implementation of Reporter: // NoopMeter is an noop implementation of Meter
type noopReporter struct { type noopMeter struct {
opts Options opts Options
md metadata.Metadata
} }
// NewReporter returns a configured noop reporter: // NewMeter returns a configured noop reporter:
func NewReporter(opts ...Option) Reporter { func NewMeter(opts ...Option) Meter {
return &noopReporter{ return &noopMeter{
opts: NewOptions(opts...), opts: NewOptions(opts...),
} }
} }
// Init initialize options // Init initialize options
func (r *noopReporter) Init(opts ...Option) error { func (r *noopMeter) Init(opts ...Option) error {
for _, o := range opts { for _, o := range opts {
o(&r.opts) o(&r.opts)
} }
return nil return nil
} }
// Count implements the Reporter interface Count method: // Counter implements the Meter interface
func (r *noopReporter) Count(metricName string, value int64, md metadata.Metadata) error { func (r *noopMeter) Counter(name string, md metadata.Metadata) Counter {
return nil return &noopCounter{}
} }
// Gauge implements the Reporter interface Gauge method: // FloatCounter implements the Meter interface
func (r *noopReporter) Gauge(metricName string, value float64, md metadata.Metadata) error { func (r *noopMeter) FloatCounter(name string, md metadata.Metadata) FloatCounter {
return nil return &noopFloatCounter{}
} }
// Timing implements the Reporter interface Timing method: // Gauge implements the Meter interface
func (r *noopReporter) Timing(metricName string, value time.Duration, md metadata.Metadata) error { func (r *noopMeter) Gauge(name string, f func() float64, md metadata.Metadata) Gauge {
return nil return &noopGauge{}
} }
// Options implements the Reporter interface Optios method: // Summary implements the Meter interface
func (r *noopReporter) Options() Options { 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 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)) {}

View File

@ -8,12 +8,12 @@ import (
) )
var ( var (
// The Prometheus metrics will be made available on this port: // The Meter data will be made available on this port
defaultPrometheusListenAddress = ":9000" DefaultAddress = ":9090"
// This is the endpoint where the Prometheus metrics will be made available ("/metrics" is the default with Prometheus): // This is the endpoint where the Meter data will be made available ("/metrics" is the default)
defaultPath = "/metrics" DefaultPath = "/metrics"
// timingObjectives is the default spread of stats we maintain for timings / histograms: // 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: // Option powers the configuration for metrics implementations:
@ -21,22 +21,23 @@ type Option func(*Options)
// Options for metrics implementations: // Options for metrics implementations:
type Options struct { type Options struct {
Address string Address string
Path string Path string
DefaultTags metadata.Metadata Metadata metadata.Metadata
TimingObjectives map[float64]float64 //TimingObjectives map[float64]float64
Logger logger.Logger Logger logger.Logger
Context context.Context Context context.Context
} }
// NewOptions prepares a set of options: // NewOptions prepares a set of options:
func NewOptions(opt ...Option) Options { func NewOptions(opt ...Option) Options {
opts := Options{ opts := Options{
Address: defaultPrometheusListenAddress, Address: DefaultAddress,
DefaultTags: metadata.New(2), Metadata: metadata.New(3), // 3 elements contains service name, version and id
Path: defaultPath, Path: DefaultPath,
TimingObjectives: defaultTimingObjectives, // TimingObjectives: defaultTimingObjectives,
Context: context.Background(), Context: context.Background(),
Logger: logger.DefaultLogger,
} }
for _, o := range opt { 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 { func Path(value string) Option {
return func(o *Options) { return func(o *Options) {
o.Path = value 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 { func Address(value string) Option {
return func(o *Options) { return func(o *Options) {
o.Address = value o.Address = value
} }
} }
// DefaultTags will be added to every metric: // Metadata will be added to every metric
func DefaultTags(md metadata.Metadata) Option { func Metadata(md metadata.Metadata) Option {
return func(o *Options) { 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: // TimingObjectives defines the desired spread of statistics for histogram / timing metrics:
func TimingObjectives(value map[float64]float64) Option { func TimingObjectives(value map[float64]float64) Option {
return func(o *Options) { return func(o *Options) {
o.TimingObjectives = value o.TimingObjectives = value
} }
} }
*/
// Logger sets the logger // Logger sets the logger
func Logger(l logger.Logger) Option { func Logger(l logger.Logger) Option {

View File

@ -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
}

View File

@ -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)
}

View File

@ -9,15 +9,15 @@ import (
"github.com/unistack-org/micro/v3/server" "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 { type Wrapper struct {
reporter meter.Reporter meter meter.Meter
} }
// New returns a *Wrapper configured with the given meter.Reporter: // New returns a *Wrapper configured with the given meter.Meter
func New(reporter meter.Reporter) *Wrapper { func New(meter meter.Meter) *Wrapper {
return &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): // 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 return err
} }