package vegeta import ( "strconv" "time" "github.com/bmizerany/perks/quantile" ) // Metrics holds the stats computed out of a slice of Results // that is used for some of the Reporters type Metrics struct { Latencies struct { Mean time.Duration `json:"mean"` P95 time.Duration `json:"95th"` // P95 is the 95th percentile upper value P99 time.Duration `json:"99th"` // P99 is the 99th percentile upper value Max time.Duration `json:"max"` } `json:"latencies"` BytesIn struct { Total uint64 `json:"total"` Mean float64 `json:"mean"` } `json:"bytes_in"` BytesOut struct { Total uint64 `json:"total"` Mean float64 `json:"mean"` } `json:"bytes_out"` Requests uint64 `json:"requests"` Success float64 `json:"success"` StatusCodes map[string]int `json:"status_codes"` Errors []string `json:"errors"` } // NewMetrics computes and returns a Metrics struct out of a slice of Results func NewMetrics(results []Result) *Metrics { m := &Metrics{ Requests: uint64(len(results)), StatusCodes: map[string]int{}, } errorSet := map[string]struct{}{} quants := quantile.NewTargeted(0.95, 0.99) totalSuccess, totalLatencies := 0, time.Duration(0) for _, result := range results { quants.Insert(float64(result.Latency)) m.StatusCodes[strconv.Itoa(int(result.Code))]++ totalLatencies += result.Latency m.BytesOut.Total += result.BytesOut m.BytesIn.Total += result.BytesIn if result.Latency > m.Latencies.Max { m.Latencies.Max = result.Latency } if result.Code >= 200 && result.Code < 300 { totalSuccess++ } if result.Error != "" { errorSet[result.Error] = struct{}{} } } m.Latencies.Mean = time.Duration(float64(totalLatencies) / float64(m.Requests)) m.Latencies.P95 = time.Duration(quants.Query(0.95)) m.Latencies.P99 = time.Duration(quants.Query(0.99)) m.BytesIn.Mean = float64(m.BytesIn.Total) / float64(m.Requests) m.BytesOut.Mean = float64(m.BytesOut.Total) / float64(m.Requests) m.Success = float64(totalSuccess) / float64(m.Requests) m.Errors = make([]string, 0, len(errorSet)) for err := range errorSet { m.Errors = append(m.Errors, err) } return m }