diff --git a/meter/meter.go b/meter/meter.go index 1c94b8be..b7602f9c 100644 --- a/meter/meter.go +++ b/meter/meter.go @@ -2,6 +2,8 @@ package meter import ( + "io" + "sort" "time" ) @@ -23,13 +25,14 @@ var ( // Meter is an interface for collecting and instrumenting metrics type Meter interface { Init(...Option) error - Counter(string, map[string]string) Counter - FloatCounter(string, map[string]string) FloatCounter - Gauge(string, func() float64, map[string]string) Gauge - Set(map[string]string) Meter - Histogram(string, map[string]string) Histogram - Summary(string, map[string]string) Summary - SummaryExt(string, time.Duration, []float64, map[string]string) Summary + Counter(string, ...Option) Counter + FloatCounter(string, ...Option) FloatCounter + Gauge(string, func() float64, ...Option) Gauge + Set(...Option) Meter + Histogram(string, ...Option) Histogram + Summary(string, ...Option) Summary + SummaryExt(string, time.Duration, []float64, ...Option) Summary + Write(io.Writer, bool) error Options() Options String() string } @@ -69,3 +72,55 @@ type Summary interface { Update(float64) UpdateDuration(time.Time) } + +type Labels struct { + keys []string + vals []string +} + +func (ls Labels) Len() int { + return len(ls.keys) +} + +func (ls Labels) Swap(i, j int) { + ls.keys[i], ls.keys[j] = ls.keys[j], ls.keys[i] + ls.vals[i], ls.vals[j] = ls.vals[j], ls.vals[i] +} + +func (ls Labels) Less(i, j int) bool { + return ls.vals[i] < ls.vals[j] +} + +func (ls Labels) Sort() { + sort.Sort(ls) +} + +func (ls Labels) Append(nls Labels) Labels { + for n := range nls.keys { + ls.keys = append(ls.keys, nls.keys[n]) + ls.vals = append(ls.vals, nls.vals[n]) + } + return ls +} + +type LabelIter struct { + labels Labels + cnt int + cur int +} + +func (ls Labels) Iter() *LabelIter { + ls.Sort() + return &LabelIter{labels: ls, cnt: len(ls.keys)} +} + +func (iter *LabelIter) Next(k, v *string) bool { + if iter.cur+1 > iter.cnt { + return false + } + + *k = iter.labels.keys[iter.cur] + *v = iter.labels.vals[iter.cur] + iter.cur++ + return true +} diff --git a/meter/meter_test.go b/meter/meter_test.go index c9af6425..14a47322 100644 --- a/meter/meter_test.go +++ b/meter/meter_test.go @@ -11,4 +11,53 @@ func TestNoopMeter(t *testing.T) { assert.NotNil(t, meter) assert.Equal(t, "/noop", meter.Options().Path) assert.Implements(t, new(Meter), meter) + + cnt := meter.Counter("counter", Label("server", "noop")) + cnt.Inc() + +} + +func TestLabels(t *testing.T) { + var ls Labels + ls.keys = []string{"type", "server"} + ls.vals = []string{"noop", "http"} + + ls.Sort() + + if ls.keys[0] != "server" || ls.vals[0] != "http" { + t.Fatalf("sort error: %v", ls) + } +} + +func TestLabelsAppend(t *testing.T) { + var ls Labels + ls.keys = []string{"type", "server"} + ls.vals = []string{"noop", "http"} + + var nls Labels + nls.keys = []string{"registry"} + nls.vals = []string{"gossip"} + ls = ls.Append(nls) + + ls.Sort() + + if ls.keys[0] != "registry" || ls.vals[0] != "gossip" { + t.Fatalf("append error: %v", ls) + } +} + +func TestIterator(t *testing.T) { + var ls Labels + ls.keys = []string{"type", "server", "registry"} + ls.vals = []string{"noop", "http", "gossip"} + + iter := ls.Iter() + var k, v string + cnt := 0 + for iter.Next(&k, &v) { + if cnt == 1 && (k != "server" || v != "http") { + t.Fatalf("iter error: %s != %s || %s != %s", k, "server", v, "http") + } + cnt++ + } } diff --git a/meter/noop.go b/meter/noop.go index f18f9fdf..d33258e7 100644 --- a/meter/noop.go +++ b/meter/noop.go @@ -1,22 +1,19 @@ package meter import ( + "io" "time" - - "github.com/unistack-org/micro/v3/metadata" ) // NoopMeter is an noop implementation of Meter type noopMeter struct { - opts Options - md map[string]string + opts Options + labels Labels } // NewMeter returns a configured noop reporter: func NewMeter(opts ...Option) Meter { - return &noopMeter{ - opts: NewOptions(opts...), - } + return &noopMeter{opts: NewOptions(opts...)} } // Init initialize options @@ -28,38 +25,52 @@ func (r *noopMeter) Init(opts ...Option) error { } // Counter implements the Meter interface -func (r *noopMeter) Counter(name string, md map[string]string) Counter { - return &noopCounter{} +func (r *noopMeter) Counter(name string, opts ...Option) Counter { + options := Options{} + for _, o := range opts { + o(&options) + } + return &noopCounter{labels: options.Labels} } // FloatCounter implements the Meter interface -func (r *noopMeter) FloatCounter(name string, md map[string]string) FloatCounter { +func (r *noopMeter) FloatCounter(name string, opts ...Option) FloatCounter { return &noopFloatCounter{} } // Gauge implements the Meter interface -func (r *noopMeter) Gauge(name string, f func() float64, md map[string]string) Gauge { +func (r *noopMeter) Gauge(name string, f func() float64, opts ...Option) Gauge { return &noopGauge{} } // Summary implements the Meter interface -func (r *noopMeter) Summary(name string, md map[string]string) Summary { +func (r *noopMeter) Summary(name string, opts ...Option) Summary { return &noopSummary{} } // SummaryExt implements the Meter interface -func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, md map[string]string) Summary { +func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary { return &noopSummary{} } // Histogram implements the Meter interface -func (r *noopMeter) Histogram(name string, md map[string]string) Histogram { +func (r *noopMeter) Histogram(name string, opts ...Option) Histogram { return &noopHistogram{} } // Set implements the Meter interface -func (r *noopMeter) Set(md map[string]string) Meter { - return &noopMeter{opts: r.opts, md: metadata.Copy(md)} +func (r *noopMeter) Set(opts ...Option) Meter { + m := &noopMeter{opts: r.opts} + + for _, o := range opts { + o(&m.opts) + } + + return m +} + +func (r *noopMeter) Write(w io.Writer, withProcessMetrics bool) error { + return nil } // Options implements the Meter interface @@ -72,7 +83,9 @@ func (r *noopMeter) String() string { return "noop" } -type noopCounter struct{} +type noopCounter struct { + labels Labels +} func (r *noopCounter) Add(int) { diff --git a/meter/options.go b/meter/options.go index e6e6e10f..52c3d765 100644 --- a/meter/options.go +++ b/meter/options.go @@ -11,9 +11,9 @@ type Option func(*Options) // Options for metrics implementations: type Options struct { - Address string - Path string - Metadata map[string]string + Address string + Path string + Labels Labels //TimingObjectives map[float64]float64 Logger logger.Logger Context context.Context @@ -25,7 +25,6 @@ type Options struct { func NewOptions(opt ...Option) Options { opts := Options{ Address: DefaultAddress, - Metadata: make(map[string]string, 3), // 3 elements contains service name, version and id Path: DefaultPath, Context: context.Background(), Logger: logger.DefaultLogger, @@ -61,14 +60,14 @@ func Address(value string) Option { } } -// Metadata will be added to every metric -func Metadata(md map[string]string) Option { +/* +// Labels be added to every metric +func Labels(labels []string) Option { return func(o *Options) { - for k, v := range md { - o.Metadata[k] = v - } + o.Labels = labels } } +*/ /* // TimingObjectives defines the desired spread of statistics for histogram / timing metrics: @@ -85,3 +84,11 @@ func Logger(l logger.Logger) Option { o.Logger = l } } + +// Label sets the label +func Label(key, val string) Option { + return func(o *Options) { + o.Labels.keys = append(o.Labels.keys, key) + o.Labels.vals = append(o.Labels.vals, val) + } +}