github.com/orofarne/hammy@v0.0.0-20130409105742-374fadfd6ecb/src/hammy/metrics.go (about) 1 package hammy 2 3 import ( 4 "expvar" 5 "time" 6 "sync" 7 "encoding/json" 8 ) 9 10 type MetricType int 11 12 const ( 13 METRIC_COUNTER MetricType = iota 14 METRIC_TIMER 15 ) 16 17 type metric struct { 18 Type MetricType 19 Name string 20 Value interface{} 21 } 22 23 type metricState struct { 24 Type MetricType 25 Value interface{} 26 } 27 28 type metricTimerState struct { 29 Counter uint64 30 Sum uint64 31 } 32 33 // Namespaced set of metrics 34 type MetricSet struct { 35 results map[string]interface{} 36 states map[string]*metricState 37 metricChan chan metric 38 mutex sync.Mutex 39 tickTime time.Duration 40 prevTick time.Time 41 } 42 43 // Creates new metric set or panic if namespace exists 44 func NewMetricSet(namespace string, tickTime time.Duration) *MetricSet { 45 ms := new(MetricSet) 46 ms.results = make(map[string]interface{}) 47 ms.states = make(map[string]*metricState) 48 ms.metricChan = make(chan metric, 100) 49 ms.tickTime = tickTime 50 51 var exportFunc expvar.Func 52 exportFunc = func() interface{} { 53 return ms.getVars() 54 } 55 expvar.Publish(namespace, exportFunc) 56 57 go ms.collect() 58 59 return ms 60 } 61 62 func (ms *MetricSet) getVars() interface{} { 63 ms.mutex.Lock() 64 defer func() { ms.mutex.Unlock() }() 65 //FIXME achtung! 66 var res interface{} 67 buf, err := json.Marshal(ms.results) 68 if err != nil { panic(err) } 69 err = json.Unmarshal(buf, &res) 70 if err != nil { panic(err) } 71 return res 72 } 73 74 func (ms *MetricSet) collect() { 75 ticker := time.Tick(ms.tickTime) 76 ms.prevTick = time.Now() 77 78 for { 79 select { 80 case m := <- ms.metricChan: 81 ms.collectMetric(m) 82 case t := <- ticker: 83 Δ := t.Sub(ms.prevTick) 84 ms.prevTick = t 85 ms.doResults(Δ) 86 } 87 } 88 } 89 90 func (ms *MetricSet) collectMetric(m metric) { 91 state, found := ms.states[m.Name] 92 93 if !found { 94 state = &metricState{ 95 Type: m.Type, 96 } 97 } else { 98 if state.Type != m.Type { 99 panic("Invalid metric type") 100 } 101 } 102 103 switch m.Type { 104 case METRIC_COUNTER: 105 if state.Value == nil { 106 state.Value = m.Value.(uint64) 107 } else { 108 state.Value = state.Value.(uint64) + m.Value.(uint64) 109 } 110 case METRIC_TIMER: 111 τ := uint64(m.Value.(time.Duration).Nanoseconds()) 112 if state.Value == nil { 113 state.Value = metricTimerState{ 114 Counter: 1, 115 Sum: τ, 116 } 117 } else { 118 pState := state.Value.(metricTimerState) 119 state.Value = metricTimerState{ 120 Counter: (pState.Counter + 1), 121 Sum: (pState.Sum + τ), 122 } 123 } 124 } 125 126 ms.states[m.Name] = state 127 } 128 129 func (ms *MetricSet) doResults(Δ time.Duration) { 130 ms.mutex.Lock() 131 defer func() { ms.mutex.Unlock() }() 132 133 for k, v := range ms.states { 134 switch v.Type { 135 case METRIC_COUNTER: 136 ms.results[k + "#rps"] = float64(v.Value.(uint64)) / Δ.Seconds() 137 v.Value = uint64(0) 138 case METRIC_TIMER: 139 tState := v.Value.(metricTimerState) 140 var τ float64 141 if tState.Counter != 0 { 142 τ = (float64(tState.Sum) / float64(tState.Counter)) / float64(1000000000) 143 } else { 144 τ = 0 145 } 146 ms.results[k + "#rps"] = float64(tState.Counter) / Δ.Seconds() 147 ms.results[k + "_avgtime#s"] = τ 148 v.Value = metricTimerState{} 149 } 150 } 151 } 152 153 type CounterMetric struct { 154 name string 155 c chan metric 156 } 157 158 func (ms *MetricSet) NewCounter(name string) *CounterMetric { 159 m := new(CounterMetric) 160 m.name = name 161 m.c = ms.metricChan 162 return m 163 } 164 165 func (m *CounterMetric)Add(n uint64) { 166 m.c <- metric{ 167 Type: METRIC_COUNTER, 168 Name: m.name, 169 Value: n, 170 } 171 } 172 173 type TimerMetric struct { 174 name string 175 c chan metric 176 } 177 178 func (ms *MetricSet) NewTimer(name string) *TimerMetric { 179 m := new(TimerMetric) 180 m.name = name 181 m.c = ms.metricChan 182 return m 183 } 184 185 func (m *TimerMetric) Add(τ time.Duration) { 186 m.c <- metric{ 187 Type: METRIC_TIMER, 188 Name: m.name, 189 Value: τ, 190 } 191 } 192 193 type TimerMetricObservation struct { 194 m *TimerMetric 195 beginTime time.Time 196 } 197 198 func (m *TimerMetric) NewObservation() *TimerMetricObservation { 199 τ := new(TimerMetricObservation) 200 τ.m = m 201 τ.beginTime = time.Now() 202 return τ 203 } 204 205 func (τ *TimerMetricObservation) End() { 206 Δ := time.Since(τ.beginTime) 207 τ.m.c <- metric{ 208 Type: METRIC_TIMER, 209 Name: τ.m.name, 210 Value: Δ, 211 } 212 }