diff --git a/histogram.go b/histogram.go index d39f008..3f4f8ef 100644 --- a/histogram.go +++ b/histogram.go @@ -7,6 +7,7 @@ package circonusgometrics import ( "fmt" "sync" + "time" "github.com/circonus-labs/circonusllhist" ) @@ -28,6 +29,12 @@ func (m *CirconusMetrics) RecordValue(metric string, val float64) { m.SetHistogramValue(metric, val) } +// RecordDuration adds a time.Duration to a histogram (values normalized to +// time.Second, but supports nanosecond granularity). +func (m *CirconusMetrics) RecordDuration(metric string, val time.Duration) { + m.SetHistogramDuration(metric, val) +} + // RecordCountForValue adds count n for value to a histogram func (m *CirconusMetrics) RecordCountForValue(metric string, val float64, n int64) { hist := m.NewHistogram(metric) @@ -50,6 +57,17 @@ func (m *CirconusMetrics) SetHistogramValue(metric string, val float64) { m.hm.Unlock() } +// SetHistogramDuration adds a value to a histogram +func (m *CirconusMetrics) SetHistogramDuration(metric string, val time.Duration) { + hist := m.NewHistogram(metric) + + m.hm.Lock() + hist.rw.Lock() + hist.hist.RecordDuration(val) + hist.rw.Unlock() + m.hm.Unlock() +} + // GetHistogramTest returns the current value for a gauge. (note: it is a function specifically for "testing", disable automatic submission during testing.) func (m *CirconusMetrics) GetHistogramTest(metric string) ([]string, error) { m.hm.Lock() @@ -99,3 +117,11 @@ func (h *Histogram) RecordValue(v float64) { h.hist.RecordValue(v) h.rw.Unlock() } + +// RecordDuration records the given time.Duration to a histogram instance. +// RecordDuration normalizes the value to seconds. +func (h *Histogram) RecordDuration(v time.Duration) { + h.rw.Lock() + h.hist.RecordDuration(v) + h.rw.Unlock() +} diff --git a/histogram_test.go b/histogram_test.go index 5786705..547a5ea 100644 --- a/histogram_test.go +++ b/histogram_test.go @@ -5,8 +5,10 @@ package circonusgometrics import ( + "fmt" "reflect" "testing" + "time" ) func TestTiming(t *testing.T) { @@ -63,6 +65,57 @@ func TestRecordValue(t *testing.T) { } } +func TestRecordDuration(t *testing.T) { + t.Log("Testing histogram.RecordDuration") + + tests := []struct { + metricName string + durs []time.Duration + out string + }{ + { + metricName: "foo", + durs: []time.Duration{1 * time.Second}, + out: "H[1.0e+00]=1", + }, + { + metricName: "foo", + durs: []time.Duration{1 * time.Millisecond}, + out: "H[1.0e-03]=1", + }, + } + + for n, test := range tests { + test := test + t.Run(fmt.Sprintf("%d", n), func(t *testing.T) { + cm := &CirconusMetrics{histograms: make(map[string]*Histogram)} + + for _, dur := range test.durs { + cm.RecordDuration(test.metricName, dur) + } + + hist, ok := cm.histograms[test.metricName] + if !ok { + t.Errorf("Expected to find %q", test.metricName) + } + + if hist == nil { + t.Errorf("Expected *Histogram, found %v", hist) + } + + val := hist.hist.DecStrings() + if len(val) != 1 { + t.Errorf("Expected 1, found '%v'", val) + } + + if val[0] != test.out { + t.Errorf("Expected '%s', found '%s'", test.out, val[0]) + } + }) + } + +} + func TestRecordCountForValue(t *testing.T) { t.Log("Testing histogram.RecordCountForValue")