diff --git a/README.md b/README.md
index 93cea0f0..ea222c22 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,7 @@ Options:
"csv" outputs the response metrics in comma-separated values format.
"json" outputs the metrics report in JSON format.
"pretty" outputs the metrics report in pretty JSON format.
+ "html" outputs the metrics report as HTML.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.
@@ -186,6 +187,10 @@ duration (ms),status,error
...
```
+HTML output can be generated using `html` as format in the `-O` option. See [sample output](http://bojand.github.io/sample.html).
+
+Using `-O json` outputs JSON data, and `-O pretty` outputs JSON in pretty format.
+
## Credit
Icon made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
diff --git a/cmd/grpcannon/main.go b/cmd/grpcannon/main.go
index 809e5f02..e68d2c23 100644
--- a/cmd/grpcannon/main.go
+++ b/cmd/grpcannon/main.go
@@ -85,6 +85,7 @@ Options:
"csv" outputs the response metrics in comma-separated values format.
"json" outputs the metrics report in JSON format.
"pretty" outputs the metrics report in pretty JSON format.
+ "html" outputs the metrics report as HTML.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.
diff --git a/printer/printer.go b/printer/printer.go
index e9d58056..1fe37866 100644
--- a/printer/printer.go
+++ b/printer/printer.go
@@ -60,6 +60,15 @@ func (rp *ReportPrinter) Print(format string) {
}
rp.printf(string(rep))
+ case "html":
+ buf := &bytes.Buffer{}
+ templ := template.Must(template.New("tmpl").Funcs(tmplFuncMap).Parse(htmlTmpl))
+ if err := templ.Execute(buf, *rp.Report); err != nil {
+ log.Println("error:", err.Error())
+ return
+ }
+
+ rp.printf(buf.String())
}
}
@@ -72,6 +81,8 @@ var tmplFuncMap = template.FuncMap{
"formatSeconds": formatSeconds,
"histogram": histogram,
"jsonify": jsonify,
+ "formatMark": formatMarkMs,
+ "formatPercent": formatPercent,
}
func jsonify(v interface{}) string {
@@ -87,6 +98,11 @@ func formatSeconds(duration float64) string {
return fmt.Sprintf("%4.2f", duration)
}
+func formatPercent(num int, total uint64) string {
+ p := float64(num) / float64(total)
+ return fmt.Sprintf("%.2f", p*100)
+}
+
func histogram(buckets []grpcannon.Bucket) string {
max := 0
for _, b := range buckets {
@@ -106,6 +122,10 @@ func histogram(buckets []grpcannon.Bucket) string {
return res.String()
}
+func formatMarkMs(m float64) string {
+ return fmt.Sprintf("'%4.3f ms'", m*1000)
+}
+
var (
defaultTmpl = `
Summary:
@@ -129,5 +149,355 @@ Status code distribution:{{ range $code, $num := .StatusCodeDist }}
csvTmpl = `
duration (ms),status,error{{ range $i, $v := .Details }}
{{ formatMilli .Latency.Seconds }},{{ .Status }},{{ .Error }}{{ end }}
+`
+
+ htmlTmpl = `
+
+
+
+
+
+ Results
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Summary
+
+
+
+
+ Count |
+ {{ .Count }} |
+
+
+ Total |
+ {{ formatMilli .Total.Seconds }} ms |
+
+
+ Slowest |
+ {{ formatMilli .Slowest.Seconds }} ms |
+
+
+ Fastest |
+ {{ formatMilli .Fastest.Seconds }} ms |
+
+
+ Average |
+ {{ formatMilli .Average.Seconds }} ms |
+
+
+ Requests / sec |
+ {{ formatSeconds .Rps }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Latency distribution
+
+
+
+
+ {{ range .LatencyDistribution }}
+ {{ .Percentage }} %% |
+ {{ end }}
+
+
+
+
+ {{ range .LatencyDistribution }}
+ {{ formatMilli .Latency.Seconds }} ms |
+ {{ end }}
+
+
+
+
+
+
+
+
+
+
+
+
+ Status distribution
+
+
+
+
+ Status |
+ Count |
+ %% of Total |
+
+
+
+ {{ range $code, $num := .StatusCodeDist }}
+
+ {{ $code }} |
+ {{ $num }} |
+ {{ formatPercent $num $.Count }} %% |
+
+ {{ end }}
+
+
+
+
+
+
+
+ {{ if gt (len .ErrorDist) 0 }}
+
+
+
+
+
+
+
+ Errors
+
+
+
+
+ Error |
+ Count |
+ %% of Total |
+
+
+
+ {{ range $err, $num := .ErrorDist }}
+
+ {{ $err }} |
+ {{ $num }} |
+ {{ formatPercent $num $.Count }} %% |
+
+ {{ end }}
+
+
+
+
+
+
+
+ {{ end }}
+
+
+
+
+
+
+
+
+ Generated by ghz
+
+
+
+
+
+
+
+
+
+
+
+
+
+