This commit is contained in:
Manfred Touron
2017-05-18 18:54:23 +02:00
parent dc386661ca
commit 5448f25fd6
645 changed files with 55908 additions and 33297 deletions

View File

@@ -49,7 +49,7 @@ import (
)
func main() {
var dur metrics.Histogram = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
var dur metrics.Histogram = prometheus.NewSummary(stdprometheus.SummaryOpts{
Namespace: "myservice",
Subsystem: "api",
Name: "request_duration_seconds",
@@ -93,5 +93,3 @@ func exportGoroutines(g metrics.Gauge) {
}
}
```
For more information, see [the package documentation](https://godoc.org/github.com/go-kit/kit/metrics).

View File

@@ -0,0 +1,85 @@
// Package circonus provides a Circonus backend for metrics.
package circonus
import (
"github.com/circonus-labs/circonus-gometrics"
"github.com/go-kit/kit/metrics"
)
// Circonus wraps a CirconusMetrics object and provides constructors for each of
// the Go kit metrics. The CirconusMetrics object manages aggregation of
// observations and emission to the Circonus server.
type Circonus struct {
m *circonusgometrics.CirconusMetrics
}
// New creates a new Circonus object wrapping the passed CirconusMetrics, which
// the caller should create and set in motion. The Circonus object can be used
// to construct individual Go kit metrics.
func New(m *circonusgometrics.CirconusMetrics) *Circonus {
return &Circonus{
m: m,
}
}
// NewCounter returns a counter metric with the given name.
func (c *Circonus) NewCounter(name string) *Counter {
return &Counter{
name: name,
m: c.m,
}
}
// NewGauge returns a gauge metric with the given name.
func (c *Circonus) NewGauge(name string) *Gauge {
return &Gauge{
name: name,
m: c.m,
}
}
// NewHistogram returns a histogram metric with the given name.
func (c *Circonus) NewHistogram(name string) *Histogram {
return &Histogram{
h: c.m.NewHistogram(name),
}
}
// Counter is a Circonus implementation of a counter metric.
type Counter struct {
name string
m *circonusgometrics.CirconusMetrics
}
// With implements Counter, but is a no-op, because Circonus metrics have no
// concept of per-observation label values.
func (c *Counter) With(labelValues ...string) metrics.Counter { return c }
// Add implements Counter. Delta is converted to uint64; precision will be lost.
func (c *Counter) Add(delta float64) { c.m.Add(c.name, uint64(delta)) }
// Gauge is a Circonus implementation of a gauge metric.
type Gauge struct {
name string
m *circonusgometrics.CirconusMetrics
}
// With implements Gauge, but is a no-op, because Circonus metrics have no
// concept of per-observation label values.
func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }
// Set implements Gauge.
func (g *Gauge) Set(value float64) { g.m.SetGauge(g.name, value) }
// Histogram is a Circonus implementation of a histogram metric.
type Histogram struct {
h *circonusgometrics.Histogram
}
// With implements Histogram, but is a no-op, because Circonus metrics have no
// concept of per-observation label values.
func (h *Histogram) With(labelValues ...string) metrics.Histogram { return h }
// Observe implements Histogram. No precision is lost.
func (h *Histogram) Observe(value float64) { h.h.RecordValue(value) }

View File

@@ -0,0 +1,120 @@
package circonus
import (
"encoding/json"
"net/http"
"net/http/httptest"
"regexp"
"strconv"
"testing"
"github.com/circonus-labs/circonus-gometrics"
"github.com/circonus-labs/circonus-gometrics/checkmgr"
"github.com/go-kit/kit/metrics/generic"
"github.com/go-kit/kit/metrics/teststat"
)
func TestCounter(t *testing.T) {
// The only way to extract values from Circonus is to pose as a Circonus
// server and receive real HTTP writes.
const name = "abc"
var val int64
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var res map[string]struct {
Value int64 `json:"_value"` // reverse-engineered :\
}
json.NewDecoder(r.Body).Decode(&res)
val = res[name].Value
}))
defer s.Close()
// Set up a Circonus object, submitting to our HTTP server.
m := newCirconusMetrics(s.URL)
counter := New(m).NewCounter(name).With("label values", "not supported")
value := func() float64 { m.Flush(); return float64(val) }
// Engage.
if err := teststat.TestCounter(counter, value); err != nil {
t.Fatal(err)
}
}
func TestGauge(t *testing.T) {
const name = "def"
var val float64
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var res map[string]struct {
Value string `json:"_value"`
}
json.NewDecoder(r.Body).Decode(&res)
val, _ = strconv.ParseFloat(res[name].Value, 64)
}))
defer s.Close()
m := newCirconusMetrics(s.URL)
gauge := New(m).NewGauge(name).With("label values", "not supported")
value := func() float64 { m.Flush(); return val }
if err := teststat.TestGauge(gauge, value); err != nil {
t.Fatal(err)
}
}
func TestHistogram(t *testing.T) {
const name = "ghi"
// Circonus just emits bucketed counts. We'll dump them into a generic
// histogram (losing some precision) and take statistics from there. Note
// this does assume that the generic histogram computes statistics properly,
// but we have another test for that :)
re := regexp.MustCompile(`^H\[([0-9\.e\+]+)\]=([0-9]+)$`) // H[1.2e+03]=456
var p50, p90, p95, p99 float64
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var res map[string]struct {
Values []string `json:"_value"` // reverse-engineered :\
}
json.NewDecoder(r.Body).Decode(&res)
h := generic.NewHistogram("dummy", len(res[name].Values)) // match tbe bucket counts
for _, v := range res[name].Values {
match := re.FindStringSubmatch(v)
f, _ := strconv.ParseFloat(match[1], 64)
n, _ := strconv.ParseInt(match[2], 10, 64)
for i := int64(0); i < n; i++ {
h.Observe(f)
}
}
p50 = h.Quantile(0.50)
p90 = h.Quantile(0.90)
p95 = h.Quantile(0.95)
p99 = h.Quantile(0.99)
}))
defer s.Close()
m := newCirconusMetrics(s.URL)
histogram := New(m).NewHistogram(name).With("label values", "not supported")
quantiles := func() (float64, float64, float64, float64) { m.Flush(); return p50, p90, p95, p99 }
// Circonus metrics, because they do their own bucketing, are less precise
// than other systems. So, we bump the tolerance to 5 percent.
if err := teststat.TestHistogram(histogram, quantiles, 0.05); err != nil {
t.Fatal(err)
}
}
func newCirconusMetrics(url string) *circonusgometrics.CirconusMetrics {
m, err := circonusgometrics.NewCirconusMetrics(&circonusgometrics.Config{
CheckManager: checkmgr.Config{
Check: checkmgr.CheckConfig{
SubmissionURL: url,
},
},
})
if err != nil {
panic(err)
}
return m
}

View File

@@ -25,9 +25,6 @@ func (g gauge) With(labelValues ...string) metrics.Gauge { return g }
// Set implements Gauge.
func (g gauge) Set(value float64) {}
// Add implements metrics.Gauge.
func (g gauge) Add(delta float64) {}
type histogram struct{}
// NewHistogram returns a new no-op histogram.

View File

@@ -1,48 +1,17 @@
// Package metrics provides a framework for application instrumentation. It's
// primarily designed to help you get started with good and robust
// instrumentation, and to help you migrate from a less-capable system like
// Graphite to a more-capable system like Prometheus. If your organization has
// already standardized on an instrumentation system like Prometheus, and has no
// plans to change, it may make sense to use that system's instrumentation
// library directly.
// Package metrics provides a framework for application instrumentation. All
// metrics are safe for concurrent use. Considerable design influence has been
// taken from https://github.com/codahale/metrics and https://prometheus.io.
//
// This package provides three core metric abstractions (Counter, Gauge, and
// Histogram) and implementations for almost all common instrumentation
// backends. Each metric has an observation method (Add, Set, or Observe,
// respectively) used to record values, and a With method to "scope" the
// observation by various parameters. For example, you might have a Histogram to
// record request durations, parameterized by the method that's being called.
//
// var requestDuration metrics.Histogram
// // ...
// requestDuration.With("method", "MyMethod").Observe(time.Since(begin))
//
// This allows a single high-level metrics object (requestDuration) to work with
// many code paths somewhat dynamically. The concept of With is fully supported
// in some backends like Prometheus, and not supported in other backends like
// Graphite. So, With may be a no-op, depending on the concrete implementation
// you choose. Please check the implementation to know for sure. For
// implementations that don't provide With, it's necessary to fully parameterize
// each metric in the metric name, e.g.
//
// // Statsd
// c := statsd.NewCounter("request_duration_MyMethod_200")
// c.Add(1)
//
// // Prometheus
// c := prometheus.NewCounter(stdprometheus.CounterOpts{
// Name: "request_duration",
// ...
// }, []string{"method", "status_code"})
// c.With("method", "MyMethod", "status_code", strconv.Itoa(code)).Add(1)
// This package contains the common interfaces. Your code should take these
// interfaces as parameters. Implementations are provided for different
// instrumentation systems in the various subdirectories.
//
// Usage
//
// Metrics are dependencies, and should be passed to the components that need
// Metrics are dependencies and should be passed to the components that need
// them in the same way you'd construct and pass a database handle, or reference
// to another component. Metrics should *not* be created in the global scope.
// Instead, instantiate metrics in your func main, using whichever concrete
// implementation is appropriate for your organization.
// to another component. So, create metrics in your func main, using whichever
// concrete implementation is appropriate for your organization.
//
// latency := prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
// Namespace: "myteam",
@@ -71,14 +40,8 @@
// api := NewAPI(store, logger, latency)
// http.ListenAndServe("/", api)
//
// Note that metrics are "write-only" interfaces.
//
// Implementation details
//
// All metrics are safe for concurrent use. Considerable design influence has
// been taken from https://github.com/codahale/metrics and
// https://prometheus.io.
//
// Each telemetry system has different semantics for label values, push vs.
// pull, support for histograms, etc. These properties influence the design of
// their respective packages. This table attempts to summarize the key points of
@@ -91,6 +54,7 @@
// expvar 1 atomic atomic synthetic, batch, in-place expose
// influx n custom custom custom
// prometheus n native native native
// circonus 1 native native native
// pcp 1 native native native
//
package metrics

View File

@@ -72,7 +72,6 @@ func (d *Dogstatsd) NewGauge(name string) *Gauge {
return &Gauge{
name: d.prefix + name,
obs: d.gauges.Observe,
add: d.gauges.Add,
}
}
@@ -245,7 +244,6 @@ type Gauge struct {
name string
lvs lv.LabelValues
obs observeFunc
add observeFunc
}
// With implements metrics.Gauge.
@@ -254,7 +252,6 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge {
name: g.name,
lvs: g.lvs.With(labelValues...),
obs: g.obs,
add: g.add,
}
}
@@ -263,11 +260,6 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, g.lvs, value)
}
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, g.lvs, delta)
}
// Timing is a DogStatsD timing, or metrics.Histogram. Observations are
// forwarded to a Dogstatsd object, and collected (but not aggregated) per
// timeseries.

View File

@@ -50,9 +50,6 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }
// Set implements Gauge.
func (g *Gauge) Set(value float64) { g.f.Set(value) }
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) { g.f.Add(delta) }
// Histogram implements the histogram metric with a combination of the generic
// Histogram object and several expvar Floats, one for each of the 50th, 90th,
// 95th, and 99th quantiles of observed values, with the quantile attached to

View File

@@ -33,7 +33,6 @@ func NewCounter(name string) *Counter {
// With implements Counter.
func (c *Counter) With(labelValues ...string) metrics.Counter {
return &Counter{
Name: c.Name,
bits: atomic.LoadUint64(&c.bits),
lvs: c.lvs.With(labelValues...),
}
@@ -96,7 +95,6 @@ func NewGauge(name string) *Gauge {
// With implements Gauge.
func (g *Gauge) With(labelValues ...string) metrics.Gauge {
return &Gauge{
Name: g.Name,
bits: atomic.LoadUint64(&g.bits),
lvs: g.lvs.With(labelValues...),
}
@@ -107,20 +105,6 @@ func (g *Gauge) Set(value float64) {
atomic.StoreUint64(&g.bits, math.Float64bits(value))
}
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
for {
var (
old = atomic.LoadUint64(&g.bits)
newf = math.Float64frombits(old) + delta
new = math.Float64bits(newf)
)
if atomic.CompareAndSwapUint64(&g.bits, old, new) {
break
}
}
}
// Value returns the current value of the gauge.
func (g *Gauge) Value() float64 {
return math.Float64frombits(atomic.LoadUint64(&g.bits))
@@ -137,7 +121,7 @@ func (g *Gauge) LabelValues() []string {
type Histogram struct {
Name string
lvs lv.LabelValues
h *safeHistogram
h gohistogram.Histogram
}
// NewHistogram returns a numeric histogram based on VividCortex/gohistogram. A
@@ -145,30 +129,25 @@ type Histogram struct {
func NewHistogram(name string, buckets int) *Histogram {
return &Histogram{
Name: name,
h: &safeHistogram{Histogram: gohistogram.NewHistogram(buckets)},
h: gohistogram.NewHistogram(buckets),
}
}
// With implements Histogram.
func (h *Histogram) With(labelValues ...string) metrics.Histogram {
return &Histogram{
Name: h.Name,
lvs: h.lvs.With(labelValues...),
h: h.h,
lvs: h.lvs.With(labelValues...),
h: h.h,
}
}
// Observe implements Histogram.
func (h *Histogram) Observe(value float64) {
h.h.Lock()
defer h.h.Unlock()
h.h.Add(value)
}
// Quantile returns the value of the quantile q, 0.0 < q < 1.0.
func (h *Histogram) Quantile(q float64) float64 {
h.h.RLock()
defer h.h.RUnlock()
return h.h.Quantile(q)
}
@@ -180,17 +159,9 @@ func (h *Histogram) LabelValues() []string {
// Print writes a string representation of the histogram to the passed writer.
// Useful for printing to a terminal.
func (h *Histogram) Print(w io.Writer) {
h.h.RLock()
defer h.h.RUnlock()
fmt.Fprintf(w, h.h.String())
}
// safeHistogram exists as gohistogram.Histogram is not goroutine-safe.
type safeHistogram struct {
sync.RWMutex
gohistogram.Histogram
}
// Bucket is a range in a histogram which aggregates observations.
type Bucket struct {
From, To, Count int64
@@ -237,7 +208,7 @@ func (h *SimpleHistogram) Observe(value float64) {
// ApproximateMovingAverage returns the approximate moving average of observations.
func (h *SimpleHistogram) ApproximateMovingAverage() float64 {
h.mtx.RLock()
defer h.mtx.RUnlock()
h.mtx.RUnlock()
return h.avg
}

View File

@@ -7,7 +7,6 @@ package generic_test
import (
"math"
"math/rand"
"sync"
"testing"
"github.com/go-kit/kit/metrics/generic"
@@ -15,11 +14,7 @@ import (
)
func TestCounter(t *testing.T) {
name := "my_counter"
counter := generic.NewCounter(name).With("label", "counter").(*generic.Counter)
if want, have := name, counter.Name; want != have {
t.Errorf("Name: want %q, have %q", want, have)
}
counter := generic.NewCounter("my_counter").With("label", "counter").(*generic.Counter)
value := func() float64 { return counter.Value() }
if err := teststat.TestCounter(counter, value); err != nil {
t.Fatal(err)
@@ -40,11 +35,7 @@ func TestValueReset(t *testing.T) {
}
func TestGauge(t *testing.T) {
name := "my_gauge"
gauge := generic.NewGauge(name).With("label", "gauge").(*generic.Gauge)
if want, have := name, gauge.Name; want != have {
t.Errorf("Name: want %q, have %q", want, have)
}
gauge := generic.NewGauge("my_gauge").With("label", "gauge").(*generic.Gauge)
value := func() float64 { return gauge.Value() }
if err := teststat.TestGauge(gauge, value); err != nil {
t.Fatal(err)
@@ -52,11 +43,7 @@ func TestGauge(t *testing.T) {
}
func TestHistogram(t *testing.T) {
name := "my_histogram"
histogram := generic.NewHistogram(name, 50).With("label", "histogram").(*generic.Histogram)
if want, have := name, histogram.Name; want != have {
t.Errorf("Name: want %q, have %q", want, have)
}
histogram := generic.NewHistogram("my_histogram", 50).With("label", "histogram").(*generic.Histogram)
quantiles := func() (float64, float64, float64, float64) {
return histogram.Quantile(0.50), histogram.Quantile(0.90), histogram.Quantile(0.95), histogram.Quantile(0.99)
}
@@ -65,27 +52,6 @@ func TestHistogram(t *testing.T) {
}
}
func TestIssue424(t *testing.T) {
var (
histogram = generic.NewHistogram("dont_panic", 50)
concurrency = 100
operations = 1000
wg sync.WaitGroup
)
wg.Add(concurrency)
for i := 0; i < concurrency; i++ {
go func() {
defer wg.Done()
for j := 0; j < operations; j++ {
histogram.Observe(float64(j))
histogram.Observe(histogram.Quantile(0.5))
}
}()
}
wg.Wait()
}
func TestSimpleHistogram(t *testing.T) {
histogram := generic.NewSimpleHistogram().With("label", "simple_histogram").(*generic.SimpleHistogram)
var (

View File

@@ -38,7 +38,7 @@ type Graphite struct {
logger log.Logger
}
// New returns a Graphite object that may be used to create metrics. Prefix is
// New returns a Statsd object that may be used to create metrics. Prefix is
// applied to all created metrics. Callers must ensure that regular calls to
// WriteTo are performed, either manually or with one of the helper methods.
func New(prefix string, logger log.Logger) *Graphite {
@@ -182,9 +182,6 @@ func (g *Gauge) With(...string) metrics.Gauge { return g }
// Set implements gauge.
func (g *Gauge) Set(value float64) { g.g.Set(value) }
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) { g.g.Add(delta) }
// Histogram is a Graphite histogram metric. Observations are bucketed into
// per-quantile gauges.
type Histogram struct {

View File

@@ -36,7 +36,7 @@ func TestGauge(t *testing.T) {
func TestHistogram(t *testing.T) {
// The histogram test is actually like 4 gauge tests.
prefix, name := "graphite.", "histogram_test"
prefix, name := "statsd.", "histogram_test"
label, value := "abc", "def" // ignored for Graphite
re50 := regexp.MustCompile(prefix + name + `.p50 ([0-9\.]+) [0-9]+`)
re90 := regexp.MustCompile(prefix + name + `.p90 ([0-9\.]+) [0-9]+`)

View File

@@ -44,14 +44,11 @@ func ExampleGauge() {
gauge.With("error", "true").Set(1)
gauge.With("error", "false").Set(2)
gauge.Set(50)
gauge.With("test", "true").Set(1)
gauge.With("test", "true").Add(1)
client := &bufWriter{}
in.WriteTo(client)
expectedLines := []string{
`(influx_gauge,a=b,test=true value=2) [0-9]{19}`,
`(influx_gauge,a=b value=50) [0-9]{19}`,
`(influx_gauge,a=b,error=true value=1) [0-9]{19}`,
`(influx_gauge,a=b,error=false value=2) [0-9]{19}`,
@@ -62,7 +59,6 @@ func ExampleGauge() {
}
// Output:
// influx_gauge,a=b,test=true value=2
// influx_gauge,a=b value=50
// influx_gauge,a=b,error=true value=1
// influx_gauge,a=b,error=false value=2

View File

@@ -66,7 +66,6 @@ func (in *Influx) NewGauge(name string) *Gauge {
return &Gauge{
name: name,
obs: in.gauges.Observe,
add: in.gauges.Add,
}
}
@@ -169,14 +168,10 @@ func mergeTags(tags map[string]string, labelValues []string) map[string]string {
if len(labelValues)%2 != 0 {
panic("mergeTags received a labelValues with an odd number of strings")
}
ret := make(map[string]string, len(tags)+len(labelValues)/2)
for k, v := range tags {
ret[k] = v
}
for i := 0; i < len(labelValues); i += 2 {
ret[labelValues[i]] = labelValues[i+1]
tags[labelValues[i]] = labelValues[i+1]
}
return ret
return tags
}
func sum(a []float64) float64 {
@@ -221,7 +216,6 @@ type Gauge struct {
name string
lvs lv.LabelValues
obs observeFunc
add observeFunc
}
// With implements metrics.Gauge.
@@ -230,7 +224,6 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge {
name: g.name,
lvs: g.lvs.With(labelValues...),
obs: g.obs,
add: g.add,
}
}
@@ -239,11 +232,6 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, g.lvs, value)
}
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, g.lvs, delta)
}
// Histogram is an Influx histrogram. Observations are aggregated into a
// generic.Histogram and emitted as per-quantile gauges to the Influx server.
type Histogram struct {

View File

@@ -82,37 +82,6 @@ func TestHistogramLabels(t *testing.T) {
}
}
func TestIssue404(t *testing.T) {
in := New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger())
counterOne := in.NewCounter("influx_counter_one").With("a", "b")
counterOne.Add(123)
counterTwo := in.NewCounter("influx_counter_two").With("c", "d")
counterTwo.Add(456)
w := &bufWriter{}
in.WriteTo(w)
lines := strings.Split(strings.TrimSpace(w.buf.String()), "\n")
if want, have := 2, len(lines); want != have {
t.Fatalf("want %d, have %d", want, have)
}
for _, line := range lines {
if strings.HasPrefix(line, "influx_counter_one") {
if !strings.HasPrefix(line, "influx_counter_one,a=b count=123 ") {
t.Errorf("invalid influx_counter_one: %s", line)
}
} else if strings.HasPrefix(line, "influx_counter_two") {
if !strings.HasPrefix(line, "influx_counter_two,c=d count=456 ") {
t.Errorf("invalid influx_counter_two: %s", line)
}
} else {
t.Errorf("unexpected line: %s", line)
}
}
}
type bufWriter struct {
buf bytes.Buffer
}

View File

@@ -21,13 +21,6 @@ func (s *Space) Observe(name string, lvs LabelValues, value float64) {
s.nodeFor(name).observe(lvs, value)
}
// Add locates the time series identified by the name and label values in
// the vector space, and appends the delta to the last value in the list of
// observations.
func (s *Space) Add(name string, lvs LabelValues, delta float64) {
s.nodeFor(name).add(lvs, delta)
}
// Walk traverses the vector space and invokes fn for each non-empty time series
// which is encountered. Return false to abort the traversal.
func (s *Space) Walk(fn func(name string, lvs LabelValues, observations []float64) bool) {
@@ -98,34 +91,6 @@ func (n *node) observe(lvs LabelValues, value float64) {
child.observe(tail, value)
}
func (n *node) add(lvs LabelValues, delta float64) {
n.mtx.Lock()
defer n.mtx.Unlock()
if len(lvs) == 0 {
var value float64
if len(n.observations) > 0 {
value = last(n.observations) + delta
} else {
value = delta
}
n.observations = append(n.observations, value)
return
}
if len(lvs) < 2 {
panic("too few LabelValues; programmer error!")
}
head, tail := pair{lvs[0], lvs[1]}, lvs[2:]
if n.children == nil {
n.children = map[pair]*node{}
}
child, ok := n.children[head]
if !ok {
child = &node{}
n.children[head] = child
}
child.add(tail, delta)
}
func (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool {
n.mtx.RLock()
defer n.mtx.RUnlock()
@@ -139,7 +104,3 @@ func (n *node) walk(lvs LabelValues, fn func(LabelValues, []float64) bool) bool
}
return true
}
func last(a []float64) float64 {
return a[len(a)-1]
}

View File

@@ -12,7 +12,6 @@ type Counter interface {
type Gauge interface {
With(labelValues ...string) Gauge
Set(value float64)
Add(delta float64)
}
// Histogram describes a metric that takes repeated observations of the same

View File

@@ -54,13 +54,6 @@ func (g Gauge) With(labelValues ...string) metrics.Gauge {
return next
}
// Add implements metrics.Gauge.
func (g Gauge) Add(delta float64) {
for _, gauge := range g {
gauge.Add(delta)
}
}
// Histogram collects multiple individual histograms and treats them as a unit.
type Histogram []metrics.Histogram

View File

@@ -33,9 +33,8 @@ func TestMultiGauge(t *testing.T) {
mg.Set(9)
mg.Set(8)
mg.Set(7)
mg.Add(3)
want := "[9 8 7 10]"
want := "[9 8 7]"
for i, m := range []fmt.Stringer{g1, g2, g3} {
if have := m.String(); want != have {
t.Errorf("g%d: want %q, have %q", i+1, want, have)
@@ -77,15 +76,6 @@ type mockGauge struct {
func (g *mockGauge) Set(value float64) { g.obs = append(g.obs, value) }
func (g *mockGauge) With(...string) metrics.Gauge { return g }
func (g *mockGauge) String() string { return fmt.Sprintf("%v", g.obs) }
func (g *mockGauge) Add(delta float64) {
var value float64
if len(g.obs) > 0 {
value = g.obs[len(g.obs)-1] + delta
} else {
value = delta
}
g.obs = append(g.obs, value)
}
type mockHistogram struct {
obs []float64

View File

@@ -82,7 +82,7 @@ func (g *Gauge) With(labelValues ...string) metrics.Gauge { return g }
func (g *Gauge) Set(value float64) { g.g.Set(value) }
// Add adds a value to the gauge.
func (g *Gauge) Add(delta float64) { g.g.Inc(delta) }
func (g *Gauge) Add(value float64) { g.g.Inc(value) }
// Histogram wraps a speed Histogram.
type Histogram struct {

View File

@@ -0,0 +1,36 @@
package provider
import (
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/circonus"
)
type circonusProvider struct {
c *circonus.Circonus
}
// NewCirconusProvider takes the given Circonnus object and returns a Provider
// that produces Circonus metrics.
func NewCirconusProvider(c *circonus.Circonus) Provider {
return &circonusProvider{
c: c,
}
}
// NewCounter implements Provider.
func (p *circonusProvider) NewCounter(name string) metrics.Counter {
return p.c.NewCounter(name)
}
// NewGauge implements Provider.
func (p *circonusProvider) NewGauge(name string) metrics.Gauge {
return p.c.NewGauge(name)
}
// NewHistogram implements Provider. The buckets parameter is ignored.
func (p *circonusProvider) NewHistogram(name string, _ int) metrics.Histogram {
return p.c.NewHistogram(name)
}
// Stop implements Provider, but is a no-op.
func (p *circonusProvider) Stop() {}

View File

@@ -74,7 +74,6 @@ func (s *Statsd) NewGauge(name string) *Gauge {
return &Gauge{
name: s.prefix + name,
obs: s.gauges.Observe,
add: s.gauges.Add,
}
}
@@ -202,7 +201,6 @@ func (c *Counter) Add(delta float64) {
type Gauge struct {
name string
obs observeFunc
add observeFunc
}
// With is a no-op.
@@ -215,11 +213,6 @@ func (g *Gauge) Set(value float64) {
g.obs(g.name, lv.LabelValues{}, value)
}
// Add implements metrics.Gauge.
func (g *Gauge) Add(delta float64) {
g.add(g.name, lv.LabelValues{}, delta)
}
// Timing is a StatsD timing, or metrics.Histogram. Observations are
// forwarded to a Statsd object, and collected (but not aggregated) per
// timeseries.

View File

@@ -44,12 +44,6 @@ func TestGauge(gauge metrics.Gauge, value func() float64) error {
want = f
}
for i := 0; i < n; i++ {
f := float64(a[i])
gauge.Add(f)
want += f
}
if have := value(); want != have {
return fmt.Errorf("want %f, have %f", want, have)
}