diff --git a/printer/printer.go b/printer/printer.go index 7d9c77d0..9740524f 100644 --- a/printer/printer.go +++ b/printer/printer.go @@ -144,8 +144,29 @@ func (rp *ReportPrinter) getInfluxTags(addErrors bool) string { s = append(s, fmt.Sprintf(`call="%v"`, options.Call)) s = append(s, fmt.Sprintf(`host="%v"`, options.Host)) s = append(s, fmt.Sprintf("n=%v", options.Total)) - s = append(s, fmt.Sprintf("c=%v", options.Concurrency)) - s = append(s, fmt.Sprintf("rps=%v", options.RPS)) + + if options.CSchedule == "const" { + s = append(s, fmt.Sprintf("c=%v", options.Concurrency)) + } else { + s = append(s, fmt.Sprintf("concurrency-schedule=%v", options.CSchedule)) + s = append(s, fmt.Sprintf("concurrency-start=%v", options.CStart)) + s = append(s, fmt.Sprintf("concurrency-end=%v", options.CEnd)) + s = append(s, fmt.Sprintf("concurrency-step=%v", options.CStep)) + s = append(s, fmt.Sprintf("concurrency-step-duration=%v", options.CStepDuration)) + s = append(s, fmt.Sprintf("concurrency-max-duration=%v", options.CMaxDuration)) + } + + if options.LoadSchedule == "const" { + s = append(s, fmt.Sprintf("rps=%v", options.RPS)) + } else { + s = append(s, fmt.Sprintf("load-schedule=%v", options.LoadSchedule)) + s = append(s, fmt.Sprintf("load-start=%v", options.LoadStart)) + s = append(s, fmt.Sprintf("load-end=%v", options.LoadEnd)) + s = append(s, fmt.Sprintf("load-step=%v", options.LoadStep)) + s = append(s, fmt.Sprintf("load-step-duration=%v", options.LoadStepDuration)) + s = append(s, fmt.Sprintf("load-max-duration=%v", options.LoadMaxDuration)) + } + s = append(s, fmt.Sprintf("z=%v", options.Duration.Nanoseconds())) s = append(s, fmt.Sprintf("timeout=%v", options.Timeout.Seconds())) s = append(s, fmt.Sprintf("dial_timeout=%v", options.DialTimeout.Seconds())) @@ -374,424 +395,3 @@ func cleanInfluxString(input string) string { input = strings.Replace(input, "=", "\\=", -1) return input } - -var ( - defaultTmpl = ` -Summary: -{{ if .Name }} Name: {{ .Name }} -{{ end }} Count: {{ .Count }} - Total: {{ formatNanoUnit .Total }} - Slowest: {{ formatNanoUnit .Slowest }} - Fastest: {{ formatNanoUnit .Fastest }} - Average: {{ formatNanoUnit .Average }} - Requests/sec: {{ formatSeconds .Rps }} - -Response time histogram: -{{ histogram .Histogram }} -Latency distribution:{{ range .LatencyDistribution }} - {{ .Percentage }} % in {{ formatNanoUnit .Latency }} {{ end }} - -{{ if gt (len .StatusCodeDist) 0 }}Status code distribution: -{{ formatStatusCode .StatusCodeDist }}{{ end }} -{{ if gt (len .ErrorDist) 0 }}Error distribution: -{{ formatErrorDist .ErrorDist }}{{ end }} -` - - csvTmpl = ` -duration (ms),status,error{{ range $i, $v := .Details }} -{{ formatMilli .Latency.Seconds }},{{ .Status }},{{ .Error }}{{ end }} -` - - htmlTmpl = ` - - - - - - ghz{{ if .Name }} - {{ .Name }}{{end}} - - - - - - - - - - - -
- -
- {{ if .Name }} -

{{ .Name }}

- {{ end }} - {{ if .Date }} -

{{ formatDate .Date }}

- {{ end }} -
-
- -
- -
-
- - {{ if gt (len .Tags) 0 }} - -
-
- - {{ range $tag, $val := .Tags }} - -
-
- {{ $tag }} - {{ $val }} -
-
- - {{ end }} - -
-
-
- {{ end }} - -
-
-
-
- -

Summary

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Count{{ .Count }}
Total{{ formatNanoUnit .Total }}
Slowest{{ formatNanoUnit .Slowest }}
Fastest{{ formatNanoUnit .Fastest }}
Average{{ formatNanoUnit .Average }}
Requests / sec{{ formatSeconds .Rps }}
-
-
-
-
- - Options - -
-
-
{{ jsonify .Options true }}
-
-
-
-
-
-
- -
-
-
- -

Histogram

-
-

-

-

-
-
- -
-
-
- -

Latency distribution

-
- - - - {{ range .LatencyDistribution }} - - {{ end }} - - - - - {{ range .LatencyDistribution }} - - {{ end }} - - -
{{ .Percentage }} %
{{ formatNanoUnit .Latency }}
-
-
- -
-
-
-
-
- -

Status distribution

-
- - - - - - - - - - {{ range $code, $num := .StatusCodeDist }} - - - - - - {{ end }} - -
StatusCount% of Total
{{ $code }}{{ $num }}{{ formatPercent $num $.Count }} %
-
-
-
-
- - {{ if gt (len .ErrorDist) 0 }} - -
-
-
-
-
- -

Errors

-
- - - - - - - - - - {{ range $err, $num := .ErrorDist }} - - - - - - {{ end }} - -
ErrorCount% of Total
{{ $err }}{{ $num }}{{ formatPercent $num $.Count }} %
-
-
-
-
- - {{ end }} - -
-
-
-
-
- -

Data

-
- - JSON - CSV -
-
-
-
- -
-
-
-

- Generated by ghz -

- -
-
- -
- - - - - - - - -` -) diff --git a/printer/printer_test.go b/printer/printer_test.go index 247f5815..8f23b6a9 100644 --- a/printer/printer_test.go +++ b/printer/printer_test.go @@ -19,11 +19,6 @@ func TestPrinter_getInfluxLine(t *testing.T) { report runner.Report expected string }{ - { - "empty", - runner.Report{}, - `ghz_run,call="",host="",n=0,c=0,rps=0,z=0,timeout=0,dial_timeout=0,keepalive=0,data="null",metadata="",tags="",errors=0,has_errors=false count=0,total=0,average=0,fastest=0,slowest=0,rps=0.00,errors=0 0`, - }, { "basic", runner.Report{ @@ -44,11 +39,13 @@ func TestPrinter_getInfluxLine(t *testing.T) { "Internal": 3, "DeadlineExceeded": 2}, Options: runner.Options{ - Call: "helloworld.Greeter.SayHello", - Proto: "/apis/greeter.proto", - Host: "0.0.0.0:50051", - Total: 200, - Concurrency: 50, + Call: "helloworld.Greeter.SayHello", + Proto: "/apis/greeter.proto", + Host: "0.0.0.0:50051", + LoadSchedule: "const", + CSchedule: "const", + Total: 200, + Concurrency: 50, Data: map[string]interface{}{ "name": "Bob Smith", }, @@ -138,11 +135,6 @@ func TestPrinter_printInfluxDetails(t *testing.T) { report runner.Report expected string }{ - { - "empty", - runner.Report{}, - ``, - }, { "basic", runner.Report{ @@ -163,11 +155,13 @@ func TestPrinter_printInfluxDetails(t *testing.T) { "Internal": 3, "DeadlineExceeded": 2}, Options: runner.Options{ - Call: "helloworld.Greeter.SayHello", - Proto: "/apis/greeter.proto", - Host: "0.0.0.0:50051", - Total: 200, - Concurrency: 50, + Call: "helloworld.Greeter.SayHello", + Proto: "/apis/greeter.proto", + Host: "0.0.0.0:50051", + Total: 200, + Concurrency: 50, + LoadSchedule: "const", + CSchedule: "const", Data: map[string]interface{}{ "name": "Bob Smith", }, diff --git a/printer/template.go b/printer/template.go new file mode 100644 index 00000000..1e3e4e81 --- /dev/null +++ b/printer/template.go @@ -0,0 +1,422 @@ +package printer + +var ( + defaultTmpl = ` +Summary: +{{ if .Name }} Name: {{ .Name }} +{{ end }} Count: {{ .Count }} + Total: {{ formatNanoUnit .Total }} + Slowest: {{ formatNanoUnit .Slowest }} + Fastest: {{ formatNanoUnit .Fastest }} + Average: {{ formatNanoUnit .Average }} + Requests/sec: {{ formatSeconds .Rps }} + +Response time histogram: +{{ histogram .Histogram }} +Latency distribution:{{ range .LatencyDistribution }} + {{ .Percentage }} % in {{ formatNanoUnit .Latency }} {{ end }} + +{{ if gt (len .StatusCodeDist) 0 }}Status code distribution: +{{ formatStatusCode .StatusCodeDist }}{{ end }} +{{ if gt (len .ErrorDist) 0 }}Error distribution: +{{ formatErrorDist .ErrorDist }}{{ end }} +` + + csvTmpl = ` +duration (ms),status,error{{ range $i, $v := .Details }} +{{ formatMilli .Latency.Seconds }},{{ .Status }},{{ .Error }}{{ end }} +` + + htmlTmpl = ` + + + + + + ghz{{ if .Name }} - {{ .Name }}{{end}} + + + + + + + + + + + +
+ +
+ {{ if .Name }} +

{{ .Name }}

+ {{ end }} + {{ if .Date }} +

{{ formatDate .Date }}

+ {{ end }} +
+
+ +
+ +
+
+ + {{ if gt (len .Tags) 0 }} + +
+
+ + {{ range $tag, $val := .Tags }} + +
+
+ {{ $tag }} + {{ $val }} +
+
+ + {{ end }} + +
+
+
+ {{ end }} + +
+
+
+
+ +

Summary

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Count{{ .Count }}
Total{{ formatNanoUnit .Total }}
Slowest{{ formatNanoUnit .Slowest }}
Fastest{{ formatNanoUnit .Fastest }}
Average{{ formatNanoUnit .Average }}
Requests / sec{{ formatSeconds .Rps }}
+
+
+
+
+ + Options + +
+
+
{{ jsonify .Options true }}
+
+
+
+
+
+
+ +
+
+
+ +

Histogram

+
+

+

+

+
+
+ +
+
+
+ +

Latency distribution

+
+ + + + {{ range .LatencyDistribution }} + + {{ end }} + + + + + {{ range .LatencyDistribution }} + + {{ end }} + + +
{{ .Percentage }} %
{{ formatNanoUnit .Latency }}
+
+
+ +
+
+
+
+
+ +

Status distribution

+
+ + + + + + + + + + {{ range $code, $num := .StatusCodeDist }} + + + + + + {{ end }} + +
StatusCount% of Total
{{ $code }}{{ $num }}{{ formatPercent $num $.Count }} %
+
+
+
+
+ + {{ if gt (len .ErrorDist) 0 }} + +
+
+
+
+
+ +

Errors

+
+ + + + + + + + + + {{ range $err, $num := .ErrorDist }} + + + + + + {{ end }} + +
ErrorCount% of Total
{{ $err }}{{ $num }}{{ formatPercent $num $.Count }} %
+
+
+
+
+ + {{ end }} + +
+
+
+
+
+ +

Data

+
+ + JSON + CSV +
+
+
+
+ +
+
+
+

+ Generated by ghz +

+ +
+
+ +
+ + + + + + + + +` +) diff --git a/runner/reporter.go b/runner/reporter.go index 89f4b862..6687d78b 100644 --- a/runner/reporter.go +++ b/runner/reporter.go @@ -24,22 +24,40 @@ type Reporter struct { // Options represents the request options type Options struct { - Host string `json:"host,omitempty"` - Proto string `json:"proto,omitempty"` - Protoset string `json:"protoset,omitempty"` - ImportPaths []string `json:"import-paths,omitempty"` - Call string `json:"call,omitempty"` - CACert string `json:"cacert,omitempty"` - Cert string `json:"cert,omitempty"` - Key string `json:"key,omitempty"` - SkipTLS bool `json:"skipTLS,omitempty"` - SkipFirst uint `json:"skipFirst,omitempty"` - CName string `json:"cname,omitempty"` - Authority string `json:"authority,omitempty"` - Insecure bool `json:"insecure"` - Total uint `json:"total,omitempty"` + Call string `json:"call,omitempty"` + Host string `json:"host,omitempty"` + Proto string `json:"proto,omitempty"` + Protoset string `json:"protoset,omitempty"` + ImportPaths []string `json:"import-paths,omitempty"` + EnableCompression bool `json:"enable-compression,omitempty"` + + CACert string `json:"cacert,omitempty"` + Cert string `json:"cert,omitempty"` + Key string `json:"key,omitempty"` + CName string `json:"cname,omitempty"` + SkipTLS bool `json:"skipTLS,omitempty"` + Insecure bool `json:"insecure"` + Authority string `json:"authority,omitempty"` + + RPS uint `json:"rps,omitempty"` + LoadSchedule string `json:"load-schedule"` + LoadStart uint `json:"load-start"` + LoadEnd uint `json:"load-end"` + LoadStep int `json:"load-step"` + LoadStepDuration time.Duration `json:"load-step-duration"` + LoadMaxDuration time.Duration `json:"load-max-duration"` + Concurrency uint `json:"concurrency,omitempty"` - RPS uint `json:"rps,omitempty"` + CSchedule string `json:"concurrency-schedule"` + CStart uint `json:"concurrency-start"` + CEnd uint `json:"concurrency-end"` + CStep int `json:"concurrency-step"` + CStepDuration time.Duration `json:"concurrency-step-duration"` + CMaxDuration time.Duration `json:"concurrency-max-duration"` + + Total uint `json:"total,omitempty"` + Async bool `json:"async,omitempty"` + Connections uint `json:"connections,omitempty"` Duration time.Duration `json:"duration,omitempty"` Timeout time.Duration `json:"timeout,omitempty"` @@ -52,6 +70,8 @@ type Options struct { CPUs int `json:"CPUs"` Name string `json:"name,omitempty"` + + SkipFirst uint `json:"skipFirst,omitempty"` } // Report holds the data for the full test @@ -175,30 +195,50 @@ func (r *Reporter) Finalize(stopReason StopReason, total time.Duration) *Report StatusCodeDist: r.statusCodeDist} rep.Options = Options{ - Host: r.config.host, - Proto: r.config.proto, - Protoset: r.config.protoset, - ImportPaths: r.config.importPaths, - Call: r.config.call, - CACert: r.config.cacert, - Cert: r.config.cert, - Key: r.config.key, - CName: r.config.cname, - SkipTLS: r.config.skipVerify, - SkipFirst: uint(r.config.skipFirst), - Insecure: r.config.insecure, - Authority: r.config.authority, - Total: uint(r.config.n), + Call: r.config.call, + Host: r.config.host, + Proto: r.config.proto, + Protoset: r.config.protoset, + ImportPaths: r.config.importPaths, + EnableCompression: r.config.enableCompression, + + CACert: r.config.cacert, + Cert: r.config.cert, + Key: r.config.key, + CName: r.config.cname, + SkipTLS: r.config.skipVerify, + Insecure: r.config.insecure, + Authority: r.config.authority, + + RPS: uint(r.config.rps), + LoadSchedule: r.config.loadSchedule, + LoadStart: r.config.loadStart, + LoadEnd: r.config.loadEnd, + LoadStep: r.config.loadStep, + LoadStepDuration: r.config.loadStepDuration, + LoadMaxDuration: r.config.loadDuration, + Concurrency: uint(r.config.c), - RPS: uint(r.config.rps), + CSchedule: r.config.cSchedule, + CStart: r.config.cStart, + CEnd: r.config.cEnd, + CStep: r.config.cStep, + CStepDuration: r.config.cStepDuration, + CMaxDuration: r.config.cMaxDuration, + + Total: uint(r.config.n), + Async: r.config.async, + Connections: uint(r.config.nConns), Duration: r.config.z, Timeout: r.config.timeout, DialTimeout: r.config.dialTimeout, KeepaliveTime: r.config.keepaliveTime, - Binary: r.config.binary, - CPUs: r.config.cpus, - Name: r.config.name, + + Binary: r.config.binary, + CPUs: r.config.cpus, + Name: r.config.name, + SkipFirst: uint(r.config.skipFirst), } _ = json.Unmarshal(r.config.data, &rep.Options.Data) diff --git a/runner/reporter_test.go b/runner/reporter_test.go index b5963dfc..d0f9d596 100644 --- a/runner/reporter_test.go +++ b/runner/reporter_test.go @@ -25,7 +25,7 @@ func TestReport_MarshalJSON(t *testing.T) { json, err := json.Marshal(&r) assert.NoError(t, err) - expected := `{"date":"2006-01-02T15:04:00-07:00","options":{"insecure":false,"binary":false,"CPUs":0},"count":1000,"total":10000000000,"average":500000000,"fastest":10000000,"slowest":1000000000,"rps":34567.89,"errorDistribution":null,"statusCodeDistribution":null,"latencyDistribution":null,"histogram":null,"details":null}` + expected := `{"date":"2006-01-02T15:04:00-07:00","options":{"insecure":false,"load-schedule":"","load-start":0,"load-end":0,"load-step":0,"load-step-duration":0,"load-max-duration":0,"concurrency-schedule":"","concurrency-start":0,"concurrency-end":0,"concurrency-step":0,"concurrency-step-duration":0,"concurrency-max-duration":0,"binary":false,"CPUs":0},"count":1000,"total":10000000000,"average":500000000,"fastest":10000000,"slowest":1000000000,"rps":34567.89,"errorDistribution":null,"statusCodeDistribution":null,"latencyDistribution":null,"histogram":null,"details":null}` assert.Equal(t, expected, string(json)) }