Add response time stats

This commit is contained in:
Justin Li 2014-07-22 14:57:36 -04:00
parent 0a4c290ecb
commit 7fce8c9ad4
3 changed files with 47 additions and 3 deletions

View file

@ -38,13 +38,16 @@ func makeHandler(handler ResponseHandler) httprouter.Handle {
http.Error(w, err.Error(), httpCode) http.Error(w, err.Error(), httpCode)
} }
duration := time.Since(start)
stats.RecordTiming(stats.ResponseTime, duration)
if glog.V(2) { if glog.V(2) {
glog.Infof( glog.Infof(
"Completed %v %s %s in %v", "Completed %v %s %s in %v",
httpCode, httpCode,
http.StatusText(httpCode), http.StatusText(httpCode),
r.URL.Path, r.URL.Path,
time.Since(start), duration,
) )
} }
} }

View file

@ -1,6 +1,7 @@
package stats package stats
import ( import (
"encoding/json"
"math" "math"
"sort" "sort"
"sync/atomic" "sync/atomic"
@ -78,6 +79,10 @@ func (p *Percentile) index() int64 {
return idx return idx
} }
func (p *Percentile) MarshalJSON() ([]byte, error) {
return json.Marshal(p.Value())
}
func round(value float64) int64 { func round(value float64) int64 {
if value < 0.0 { if value < 0.0 {
value -= 0.5 value -= 0.5

View file

@ -37,6 +37,8 @@ const (
HandledRequest HandledRequest
ErroredRequest ErroredRequest
ResponseTime
) )
// DefaultStats is a default instance of stats tracking that uses an unbuffered // DefaultStats is a default instance of stats tracking that uses an unbuffered
@ -60,6 +62,12 @@ type PeerStats struct {
SeedsReaped uint64 `json:"seeds_reaped"` SeedsReaped uint64 `json:"seeds_reaped"`
} }
type PercentileTimes struct {
P50 *Percentile
P90 *Percentile
P95 *Percentile
}
type Stats struct { type Stats struct {
Start time.Time `json:"start_time"` Start time.Time `json:"start_time"`
@ -80,16 +88,27 @@ type Stats struct {
RequestsHandled uint64 `json:"requests_handled"` RequestsHandled uint64 `json:"requests_handled"`
RequestsErrored uint64 `json:"requests_errored"` RequestsErrored uint64 `json:"requests_errored"`
ResponseTime PercentileTimes `json:"response_time"`
events chan int events chan int
responseTimeEvents chan time.Duration
} }
func New(chanSize int) *Stats { func New(chanSize int) *Stats {
s := &Stats{ s := &Stats{
Start: time.Now(), Start: time.Now(),
events: make(chan int, chanSize), events: make(chan int, chanSize),
responseTimeEvents: make(chan time.Duration, chanSize),
ResponseTime: PercentileTimes{
P50: NewPercentile(0.5, 128),
P90: NewPercentile(0.9, 128),
P95: NewPercentile(0.95, 128),
},
} }
go s.handleEvents() go s.handleEvents()
go s.handleTimings()
return s return s
} }
@ -107,7 +126,12 @@ func (s *Stats) RecordEvent(event int) {
} }
func (s *Stats) RecordTiming(event int, duration time.Duration) { func (s *Stats) RecordTiming(event int, duration time.Duration) {
// s.timingEvents <- event switch event {
case ResponseTime:
s.responseTimeEvents <- duration
default:
panic("stats: RecordTiming called with an unknown event")
}
} }
func (s *Stats) handleEvents() { func (s *Stats) handleEvents() {
@ -182,6 +206,18 @@ func (s *Stats) handleEvents() {
} }
} }
func (s *Stats) handleTimings() {
for {
select {
case duration := <-s.responseTimeEvents:
f := float64(duration) / float64(time.Millisecond)
s.ResponseTime.P50.AddSample(f)
s.ResponseTime.P90.AddSample(f)
s.ResponseTime.P95.AddSample(f)
}
}
}
// RecordEvent broadcasts an event to the default stats queue. // RecordEvent broadcasts an event to the default stats queue.
func RecordEvent(event int) { func RecordEvent(event int) {
DefaultStats.RecordEvent(event) DefaultStats.RecordEvent(event)