Skip to content

Commit

Permalink
feat: add http status chart
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchen authored and six-ddc committed Mar 3, 2024
1 parent c48d821 commit 398d0e2
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 28 deletions.
6 changes: 4 additions & 2 deletions bench_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"log"
"math/rand"
"net/http"
"strconv"
"time"

Expand All @@ -19,9 +20,10 @@ func main() {
log.Println("Starting HTTP server on:", addr)
log.Fatalln(fasthttp.ListenAndServe(addr, func(c *fasthttp.RequestCtx) {
//time.Sleep(time.Duration(rand.Int63n(int64(5 * time.Second))))
if rand.Intn(5) == 0 {
c.SetStatusCode(400)
statusCodes := []int{
http.StatusOK, http.StatusOK, http.StatusBadRequest, http.StatusTooManyRequests, http.StatusBadGateway,
}
c.SetStatusCode(statusCodes[rand.Intn(len(statusCodes))])
_, werr := c.Write(c.Request.Body())
if werr != nil {
log.Println(werr)
Expand Down
83 changes: 81 additions & 2 deletions charts.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ var (
apiPath = "/data/"
latencyView = "latency"
rpsView = "rps"
codeView = "code"
timeFormat = "15:04:05"
refreshInterval = time.Second

templateRegistry = map[string]string{
rpsView: ViewTpl,
latencyView: ViewTpl,
codeView: CodeViewTpl,
}
)

const (
Expand Down Expand Up @@ -71,10 +78,65 @@ function {{ .ViewID }}_sync() {
</html>
{{ end }}
`
CodeViewTpl = `
$(function () { setInterval({{ .ViewID }}_sync, {{ .Interval }}); });
function {{ .ViewID }}_sync() {
$.ajax({
type: "GET",
url: "{{ .APIPath }}{{ .Route }}",
dataType: "json",
success: function (result) {
let opt = goecharts_{{ .ViewID }}.getOption();
let x = opt.xAxis[0].data;
x.push(result.time);
opt.xAxis[0].data = x;
let nameAndSeriesMapping = {};
for (let i = 0; i < opt.series.length; i++) {
nameAndSeriesMapping[opt.series[i].name] = opt.series[i];
}
let code200Count = nameAndSeriesMapping['200'].data.length;
let codes = result.values[0];
if (codes === null){
for (let key in nameAndSeriesMapping) {
let series = nameAndSeriesMapping[key];
series.data.push({value:null});
}
}else{
if (!('200' in codes)) {
codes['200'] = null;
}
for (let code in codes) {
let count = codes[code];
if (code in nameAndSeriesMapping){
let series = nameAndSeriesMapping[code];
series.data.push({value:count});
}else{
let data = [];
for (let i = 0; i < code200Count; i++) {
data.push[null];
}
var newSeries = {
name: code,
type: 'line',
data: data
};
opt.series.push(newSeries);
}
}
}
goecharts_{{ .ViewID }}.setOption(opt);
}
});
}`
)

func (c *Charts) genViewTemplate(vid, route string) string {
tpl, err := template.New("view").Parse(ViewTpl)
tpl, err := template.New("view").Parse(templateRegistry[route])
if err != nil {
panic("failed to parse template " + err.Error())
}
Expand Down Expand Up @@ -141,6 +203,17 @@ func (c *Charts) newRPSView() components.Charter {
return graph
}

func (c *Charts) newCodeView() components.Charter {
graph := c.newBasicView(codeView)
graph.SetGlobalOptions(
charts.WithTitleOpts(opts.Title{Title: "Response Status"}),
charts.WithYAxisOpts(opts.YAxis{Scale: true}),
charts.WithLegendOpts(opts.Legend{Show: true}),
)
graph.AddSeries("200", []opts.LineData{})
return graph
}

type Metrics struct {
Values []interface{} `json:"values"`
Time string `json:"time"`
Expand All @@ -160,7 +233,7 @@ func NewCharts(ln net.Listener, dataFunc func() *ChartsReport, desc string) (*Ch
c.page.PageTitle = "plow"
c.page.AssetsHost = assetsPath
c.page.Assets.JSAssets.Add("jquery.min.js")
c.page.AddCharts(c.newLatencyView(), c.newRPSView())
c.page.AddCharts(c.newLatencyView(), c.newRPSView(), c.newCodeView())

return c, nil
}
Expand All @@ -186,6 +259,12 @@ func (c *Charts) Handler(ctx *fasthttp.RequestCtx) {
} else {
values = append(values, nil)
}
case codeView:
if reportData != nil {
values = append(values, reportData.CodeMap)
} else {
values = append(values, nil)
}
}
metrics := &Metrics{
Time: time.Now().Format(timeFormat),
Expand Down
2 changes: 1 addition & 1 deletion print.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (p *Printer) PrintLoop(snapshot func() *SnapshotReport, interval time.Durat
echo(true)
}

//nolint
// nolint
const (
FgBlackColor int = iota + 30
FgRedColor
Expand Down
23 changes: 17 additions & 6 deletions report.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ var quantilesTarget = map[float64]float64{
0.9999: 0.00001,
}

var httpStatusSectionLabelMap = map[int]string{
1: "1xx",
2: "2xx",
3: "3xx",
4: "4xx",
5: "5xx",
}

type Stats struct {
count int64
sum float64
Expand Down Expand Up @@ -71,7 +79,7 @@ type StreamReport struct {
rpsStats *Stats
latencyQuantile *quantile.Stream
latencyHistogram *histogram.Histogram
codes map[string]int64
codes map[int]int64
errors map[string]int64

latencyWithinSec *Stats
Expand All @@ -88,7 +96,7 @@ func NewStreamReport() *StreamReport {
return &StreamReport{
latencyQuantile: quantile.NewTargeted(quantilesTarget),
latencyHistogram: histogram.New(8),
codes: make(map[string]int64, 1),
codes: make(map[int]int64, 1),
errors: make(map[string]int64, 1),
doneChan: make(chan struct{}, 1),
latencyStats: &Stats{},
Expand Down Expand Up @@ -144,11 +152,11 @@ func (s *StreamReport) Collect(records <-chan *ReportRecord) {
s.lock.Lock()
latencyWithinSecTemp.Update(float64(r.cost))
s.insert(float64(r.cost))
if r.code != "" {
s.codes[r.code] ++
if r.code != 0 {
s.codes[r.code]++
}
if r.error != "" {
s.errors[r.error] ++
s.errors[r.error]++
}
s.readBytes = r.readBytes
s.writeBytes = r.writeBytes
Expand Down Expand Up @@ -222,7 +230,8 @@ func (s *StreamReport) Snapshot() *SnapshotReport {

rs.Codes = make(map[string]int64, len(s.codes))
for k, v := range s.codes {
rs.Codes[k] = v
section := k / 100
rs.Codes[httpStatusSectionLabelMap[section]] = v
}
rs.Errors = make(map[string]int64, len(s.errors))
for k, v := range s.errors {
Expand Down Expand Up @@ -263,6 +272,7 @@ func (s *StreamReport) Done() <-chan struct{} {
type ChartsReport struct {
RPS float64
Latency Stats
CodeMap map[int]int64
}

func (s *StreamReport) Charts() *ChartsReport {
Expand All @@ -274,6 +284,7 @@ func (s *StreamReport) Charts() *ChartsReport {
cr = &ChartsReport{
RPS: s.rpsWithinSec,
Latency: *s.latencyWithinSec,
CodeMap: s.codes,
}
}
s.lock.Unlock()
Expand Down
19 changes: 2 additions & 17 deletions requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (

type ReportRecord struct {
cost time.Duration
code string
code int
error string
readBytes int64
writeBytes int64
Expand Down Expand Up @@ -253,36 +253,21 @@ func (r *Requester) DoRequest(req *fasthttp.Request, resp *fasthttp.Response, rr
} else {
err = r.httpClient.Do(req, resp)
}
var code string

if err != nil {
rr.cost = time.Since(startTime) - t1
rr.code = ""
rr.error = err.Error()
return
}
switch resp.StatusCode() / 100 {
case 1:
code = "1xx"
case 2:
code = "2xx"
case 3:
code = "3xx"
case 4:
code = "4xx"
case 5:
code = "5xx"
}
err = resp.BodyWriteTo(ioutil.Discard)
if err != nil {
rr.cost = time.Since(startTime) - t1
rr.code = ""
rr.error = err.Error()
return
}

rr.cost = time.Since(startTime) - t1
rr.code = code
rr.code = resp.StatusCode()
rr.error = ""
}

Expand Down

0 comments on commit 398d0e2

Please sign in to comment.