Skip to content

Commit

Permalink
Add histogram support to Snapshot (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromefroe authored and robskillington committed Mar 14, 2017
1 parent c5965e0 commit 4b9d9de
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 9 deletions.
72 changes: 65 additions & 7 deletions scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,18 @@ func (s *scope) Snapshot() Snapshot {
}
}
ss.tm.RUnlock()
// TODO: histograms in snapshot
ss.hm.RLock()
for key, h := range ss.histograms {
name := ss.fullyQualifiedName(key)
id := KeyForPrefixedStringMap(name, tags)
snap.histograms[id] = &histogramSnapshot{
name: name,
tags: tags,
values: h.snapshotValues(),
durations: h.snapshotDurations(),
}
}
ss.hm.RUnlock()
}
s.registry.RUnlock()

Expand Down Expand Up @@ -505,6 +516,9 @@ type Snapshot interface {

// Timers returns a snapshot of timer values since last report execution
Timers() map[string]TimerSnapshot

// Histograms returns a snapshot of histogram samples since last report execution
Histograms() map[string]HistogramSnapshot
}

// CounterSnapshot is a snapshot of a counter
Expand Down Expand Up @@ -543,6 +557,21 @@ type TimerSnapshot interface {
Values() []time.Duration
}

// HistogramSnapshot is a snapshot of a histogram
type HistogramSnapshot interface {
// Name returns the name
Name() string

// Tags returns the tags
Tags() map[string]string

// Values returns the sample values by upper bound for a valueHistogram
Values() map[float64]int64

// Durations returns the sample values by upper bound for a durationHistogram
Durations() map[time.Duration]int64
}

// mergeRightTags merges 2 sets of tags with the tags from tagsRight overriding values from tagsLeft
func mergeRightTags(tagsLeft, tagsRight map[string]string) map[string]string {
if tagsLeft == nil && tagsRight == nil {
Expand Down Expand Up @@ -574,16 +603,18 @@ func copyStringMap(stringMap map[string]string) map[string]string {
}

type snapshot struct {
counters map[string]CounterSnapshot
gauges map[string]GaugeSnapshot
timers map[string]TimerSnapshot
counters map[string]CounterSnapshot
gauges map[string]GaugeSnapshot
timers map[string]TimerSnapshot
histograms map[string]HistogramSnapshot
}

func newSnapshot() *snapshot {
return &snapshot{
counters: make(map[string]CounterSnapshot),
gauges: make(map[string]GaugeSnapshot),
timers: make(map[string]TimerSnapshot),
counters: make(map[string]CounterSnapshot),
gauges: make(map[string]GaugeSnapshot),
timers: make(map[string]TimerSnapshot),
histograms: make(map[string]HistogramSnapshot),
}
}

Expand All @@ -599,6 +630,10 @@ func (s *snapshot) Timers() map[string]TimerSnapshot {
return s.timers
}

func (s *snapshot) Histograms() map[string]HistogramSnapshot {
return s.histograms
}

type counterSnapshot struct {
name string
tags map[string]string
Expand Down Expand Up @@ -652,3 +687,26 @@ func (s *timerSnapshot) Tags() map[string]string {
func (s *timerSnapshot) Values() []time.Duration {
return s.values
}

type histogramSnapshot struct {
name string
tags map[string]string
values map[float64]int64
durations map[time.Duration]int64
}

func (s *histogramSnapshot) Name() string {
return s.name
}

func (s *histogramSnapshot) Tags() map[string]string {
return s.tags
}

func (s *histogramSnapshot) Values() map[float64]int64 {
return s.values
}

func (s *histogramSnapshot) Durations() map[time.Duration]int64 {
return s.durations
}
26 changes: 24 additions & 2 deletions scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package tally

import (
"math"
"sync"
"sync/atomic"
"testing"
Expand Down Expand Up @@ -560,11 +561,14 @@ func TestSnapshot(t *testing.T) {
s.Gauge("bzzt").Update(2)
s.Timer("brrr").Record(1 * time.Second)
s.Timer("brrr").Record(2 * time.Second)
s.Histogram("fizz", ValueBuckets{0, 2, 4}).RecordValue(1)
s.Histogram("fizz", ValueBuckets{0, 2, 4}).RecordValue(5)
s.Histogram("buzz", DurationBuckets{time.Second * 2, time.Second * 4}).RecordDuration(time.Second)
child.Counter("boop").Inc(1)

snap := s.Snapshot()
counters, gauges, timers :=
snap.Counters(), snap.Gauges(), snap.Timers()
counters, gauges, timers, histograms :=
snap.Counters(), snap.Gauges(), snap.Timers(), snap.Histograms()

assert.EqualValues(t, 1, counters["foo.beep+env=test"].Value())
assert.EqualValues(t, commonTags, counters["foo.beep+env=test"].Tags())
Expand All @@ -578,6 +582,24 @@ func TestSnapshot(t *testing.T) {
}, timers["foo.brrr+env=test"].Values())
assert.EqualValues(t, commonTags, timers["foo.brrr+env=test"].Tags())

assert.EqualValues(t, map[float64]int64{
0: 0,
2: 1,
4: 0,
math.MaxFloat64: 1,
}, histograms["foo.fizz+env=test"].Values())
assert.EqualValues(t, map[time.Duration]int64(nil), histograms["foo.fizz+env=test"].Durations())
assert.EqualValues(t, commonTags, histograms["foo.fizz+env=test"].Tags())

assert.EqualValues(t, map[float64]int64(nil), histograms["foo.buzz+env=test"].Values())
assert.EqualValues(t, map[time.Duration]int64{
0: 0,
time.Second * 2: 1,
time.Second * 4: 0,
math.MaxInt64: 0,
}, histograms["foo.buzz+env=test"].Durations())
assert.EqualValues(t, commonTags, histograms["foo.buzz+env=test"].Tags())

assert.EqualValues(t, 1, counters["foo.boop+env=test,service=test"].Value())
assert.EqualValues(t, map[string]string{
"env": "test",
Expand Down
26 changes: 26 additions & 0 deletions stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,32 @@ func (h *histogram) RecordStopwatch(stopwatchStart time.Time) {
h.RecordDuration(d)
}

func (h *histogram) snapshotValues() map[float64]int64 {
if h.htype == durationHistogramType {
return nil
}

vals := make(map[float64]int64, len(h.buckets))
for i := range h.buckets {
vals[h.buckets[i].valueUpperBound] = h.buckets[i].samples.value()
}

return vals
}

func (h *histogram) snapshotDurations() map[time.Duration]int64 {
if h.htype == valueHistogramType {
return nil
}

durations := make(map[time.Duration]int64, len(h.buckets))
for i := range h.buckets {
durations[h.buckets[i].durationUpperBound] = h.buckets[i].samples.value()
}

return durations
}

type histogramBucket struct {
h *histogram
samples *counter
Expand Down

0 comments on commit 4b9d9de

Please sign in to comment.