diff --git a/metrics/dogstatsd/dogstatsd.go b/metrics/dogstatsd/dogstatsd.go index 0cd19bc41..1dec3eef5 100644 --- a/metrics/dogstatsd/dogstatsd.go +++ b/metrics/dogstatsd/dogstatsd.go @@ -141,9 +141,14 @@ func (d *Dogstatsd) SendLoop(ctx context.Context, c <-chan time.Time, network, a // lost if there is a problem with the write. Clients should be sure to call // WriteTo regularly, ideally through the WriteLoop or SendLoop helper methods. func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) { - var n int - - d.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool { + var ( + n int + counters = d.counters.Reset() + timings = d.timings.Reset() + histograms = d.histograms.Reset() + ) + + counters.Walk(func(name string, lvs lv.LabelValues, values []float64) bool { n, err = fmt.Fprintf(w, "%s%s:%f|c%s%s\n", d.prefix, name, sum(values), sampling(d.rates.Get(name)), d.tagValues(lvs)) if err != nil { return false @@ -168,7 +173,7 @@ func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) { } d.mtx.RUnlock() - d.timings.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool { + timings.Walk(func(name string, lvs lv.LabelValues, values []float64) bool { sampleRate := d.rates.Get(name) for _, value := range values { n, err = fmt.Fprintf(w, "%s%s:%f|ms%s%s\n", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs)) @@ -183,7 +188,7 @@ func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) { return count, err } - d.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool { + histograms.Walk(func(name string, lvs lv.LabelValues, values []float64) bool { sampleRate := d.rates.Get(name) for _, value := range values { n, err = fmt.Fprintf(w, "%s%s:%f|h%s%s\n", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs)) diff --git a/metrics/dogstatsd/dogstatsd_test.go b/metrics/dogstatsd/dogstatsd_test.go index 7053dd23d..a8b7e95e9 100644 --- a/metrics/dogstatsd/dogstatsd_test.go +++ b/metrics/dogstatsd/dogstatsd_test.go @@ -1,8 +1,10 @@ package dogstatsd import ( + "errors" "testing" + "github.com/go-kit/kit/metrics/internal/lv" "github.com/go-kit/kit/metrics/teststat" "github.com/go-kit/log" ) @@ -88,3 +90,74 @@ func TestTimingSampled(t *testing.T) { t.Fatal(err) } } + +func TestDogstatsd_WriteToDiscardsMetricsOnError(t *testing.T) { + walker := func(counter *int) func(string, lv.LabelValues, []float64) bool { + return func(string, lv.LabelValues, []float64) bool { + *counter++ + return true + } + } + + d := New("dogstatsd.", log.NewNopLogger()) + + // Add some metrics. + d.NewCounter("counter-1", 1.0).Add(1.0) + d.NewTiming("timing-1", 1.0).Observe(1.0) + d.NewHistogram("histogram-1", 1.0).Observe(1.0) + + // Count metrics buffered in the Dogstatsd object. + var ( + countersCount int + timingsCount int + histogramsCount int + ) + d.counters.Walk(walker(&countersCount)) + d.timings.Walk(walker(&timingsCount)) + d.histograms.Walk(walker(&histogramsCount)) + + // Assert we have one of each type. + if countersCount != 1 { + t.Fatalf("expected counters count to be 1; got %d", countersCount) + } + if timingsCount != 1 { + t.Fatalf("expected timings count to be 1; got %d", timingsCount) + } + if histogramsCount != 1 { + t.Fatalf("expected histograms count to be 1; got %d", histogramsCount) + } + + // Simulate an error while sending metrics. + count, err := d.WriteTo(errorWriter{}) + if count != 0 { + t.Fatalf("expected count to be 0; got %d", count) + } + if err == nil { + t.Fatalf("expected error to be nil; got %v", err) + } + + // Reset counters and count again. + countersCount = 0 + timingsCount = 0 + histogramsCount = 0 + d.counters.Walk(walker(&countersCount)) + d.timings.Walk(walker(&timingsCount)) + d.histograms.Walk(walker(&histogramsCount)) + + // Assert buffered metrics were cleared. + if countersCount != 0 { + t.Fatalf("expected counters count to be 0; got %d", countersCount) + } + if timingsCount != 0 { + t.Fatalf("expected timings count to be 0; got %d", timingsCount) + } + if histogramsCount != 0 { + t.Fatalf("expected histograms count to be 0; got %d", histogramsCount) + } +} + +type errorWriter struct{} + +func (w errorWriter) Write([]byte) (int, error) { + return 0, errors.New("boom") +}