mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
feat: support statistics summary for load testing
This commit is contained in:
@@ -43,6 +43,7 @@ var boomCmd = &cobra.Command{
|
||||
hrpBoomer.SetDisableCompression(disableCompression)
|
||||
hrpBoomer.EnableCPUProfile(cpuProfile, cpuProfileDuration)
|
||||
hrpBoomer.EnableMemoryProfile(memoryProfile, memoryProfileDuration)
|
||||
hrpBoomer.EnableGracefulQuit()
|
||||
hrpBoomer.Run(paths...)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package boomer
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -95,6 +98,16 @@ func (b *Boomer) EnableMemoryProfile(memoryProfile string, duration time.Duratio
|
||||
b.memoryProfileDuration = duration
|
||||
}
|
||||
|
||||
// EnableGracefulQuit catch SIGINT and SIGTERM signals to quit gracefully
|
||||
func (b *Boomer) EnableGracefulQuit() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
<-c
|
||||
b.Quit()
|
||||
}()
|
||||
}
|
||||
|
||||
// Run accepts a slice of Task and connects to the locust master.
|
||||
func (b *Boomer) Run(tasks ...*Task) {
|
||||
if b.cpuProfile != "" {
|
||||
|
||||
@@ -5,10 +5,13 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -144,6 +147,36 @@ func (r *runner) reportStats() {
|
||||
r.outputOnEvent(data)
|
||||
}
|
||||
|
||||
func (r *runner) reportTestResult() {
|
||||
// convert stats in total
|
||||
var statsTotal interface{} = r.stats.total.serialize()
|
||||
entryTotalOutput, err := deserializeStatsEntry(statsTotal)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
duration := time.Duration(entryTotalOutput.LastRequestTimestamp-entryTotalOutput.StartTime) * time.Second
|
||||
currentTime := time.Now()
|
||||
println(fmt.Sprint("=========================================== Statistics Summary =========================================="))
|
||||
println(fmt.Sprintf("Current time: %s, Users: %v, Duration: %v, Accumulated Transactions: %d Passed, %d Failed",
|
||||
currentTime.Format("2006/01/02 15:04:05"), atomic.LoadInt32(&r.currentClientsNum), duration, r.stats.transactionPassed, r.stats.transactionFailed))
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Name", "# requests", "# fails", "Median", "Average", "Min", "Max", "Content Size", "# reqs/sec", "# fails/sec"})
|
||||
row := make([]string, 10)
|
||||
row[0] = entryTotalOutput.Name
|
||||
row[1] = strconv.FormatInt(entryTotalOutput.NumRequests, 10)
|
||||
row[2] = strconv.FormatInt(entryTotalOutput.NumFailures, 10)
|
||||
row[3] = strconv.FormatInt(entryTotalOutput.medianResponseTime, 10)
|
||||
row[4] = strconv.FormatFloat(entryTotalOutput.avgResponseTime, 'f', 2, 64)
|
||||
row[5] = strconv.FormatInt(entryTotalOutput.MinResponseTime, 10)
|
||||
row[6] = strconv.FormatInt(entryTotalOutput.MaxResponseTime, 10)
|
||||
row[7] = strconv.FormatInt(entryTotalOutput.avgContentLength, 10)
|
||||
row[8] = strconv.FormatFloat(entryTotalOutput.currentRps, 'f', 2, 64)
|
||||
row[9] = strconv.FormatFloat(entryTotalOutput.currentFailPerSec, 'f', 2, 64)
|
||||
table.Append(row)
|
||||
table.Render()
|
||||
println()
|
||||
}
|
||||
|
||||
func (r *localRunner) spawnWorkers(spawnCount int, spawnRate float64, quit chan bool, spawnCompleteFunc func()) {
|
||||
log.Info().
|
||||
Int("spawnCount", spawnCount).
|
||||
@@ -324,6 +357,9 @@ func (r *localRunner) start() {
|
||||
r.rateLimiter.Stop()
|
||||
}
|
||||
|
||||
// report test result
|
||||
r.reportTestResult()
|
||||
|
||||
// output teardown
|
||||
r.outputOnStop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user