rename metrics to meter

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2021-01-19 16:26:00 +03:00
parent 770e8425bd
commit 74a52eed9d
8 changed files with 193 additions and 61 deletions

22
meter/README.md Normal file
View File

@@ -0,0 +1,22 @@
metrics
=======
The metrics package provides a simple metrics "Reporter" interface which allows the user to submit counters, gauges and timings (along with key/value tags).
Implementations
---------------
* Prometheus (pull): will be first
* Prometheus (push): certainly achievable
* InfluxDB: could quite easily be done
* Telegraf: almost identical to the InfluxDB implementation
* Micro: Could we provide metrics over Micro's server interface?
Todo
----
* Include a handler middleware which uses the Reporter interface to generate per-request level metrics
- Throughput
- Errors
- Duration

47
meter/noop.go Normal file
View File

@@ -0,0 +1,47 @@
package metrics
import (
"time"
"github.com/unistack-org/micro/v3/metadata"
)
// NoopReporter is an noop implementation of Reporter:
type noopReporter struct {
opts Options
}
// NewReporter returns a configured noop reporter:
func NewReporter(opts ...Option) Reporter {
return &noopReporter{
opts: NewOptions(opts...),
}
}
// Init initialize options
func (r *noopReporter) 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
}
// Gauge implements the Reporter interface Gauge method:
func (r *noopReporter) Gauge(metricName string, value float64, md metadata.Metadata) error {
return nil
}
// Timing implements the Reporter interface Timing method:
func (r *noopReporter) Timing(metricName string, value time.Duration, md metadata.Metadata) error {
return nil
}
// Options implements the Reporter interface Optios method:
func (r *noopReporter) Options() Options {
return r.opts
}

89
meter/options.go Normal file
View File

@@ -0,0 +1,89 @@
package metrics
import (
"context"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
)
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"
// 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}
)
// Option powers the configuration for metrics implementations:
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
}
// 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(),
}
for _, o := range opt {
o(&opts)
}
return opts
}
// Cntext sets the metrics context
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// 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:
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 {
return func(o *Options) {
o.DefaultTags = 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 {
return func(o *Options) {
o.Logger = l
}
}

21
meter/reporter.go Normal file
View File

@@ -0,0 +1,21 @@
// Package metrics is for instrumentation and debugging
package metrics
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
}

17
meter/reporter_test.go Normal file
View File

@@ -0,0 +1,17 @@
package metrics
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

@@ -0,0 +1,50 @@
package wrapper
import (
"context"
"time"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/server"
)
// Wrapper provides a HandlerFunc for meter.Reporter implementations:
type Wrapper struct {
reporter meter.Reporter
}
// New returns a *Wrapper configured with the given meter.Reporter:
func New(reporter meter.Reporter) *Wrapper {
return &Wrapper{
reporter: reporter,
}
}
// HandlerFunc instruments handlers registered to a service:
func (w *Wrapper) HandlerFunc(handlerFunction server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
// Build some tags to describe the call:
tags := metadata.New(2)
tags.Set("method", req.Method())
// Start the clock:
callTime := time.Now()
// Run the handlerFunction:
err := handlerFunction(ctx, req, rsp)
// Add a result tag:
if err != nil {
tags["status"] = "failure"
} else {
tags["status"] = "success"
}
// Instrument the result (if the DefaultClient has been configured):
w.reporter.Timing("service.handler", time.Since(callTime), tags)
return err
}
}