forked from tsenart/vegeta
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plot.go
113 lines (94 loc) · 2.75 KB
/
plot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package main
import (
"flag"
"fmt"
"io"
"os"
"os/signal"
vegeta "github.com/tsenart/vegeta/lib"
"github.com/tsenart/vegeta/lib/plot"
)
const plotUsage = `Usage: vegeta plot [options] [<file>...]
Outputs an HTML time series plot of request latencies over time.
The X axis represents elapsed time in seconds from the beginning
of the earliest attack in all input files. The Y axis represents
request latency in milliseconds.
Click and drag to select a region to zoom into. Double click to zoom out.
Choose a different number on the bottom left corner input field
to change the moving average window size (in data points).
Arguments:
<file> A file output by running vegeta attack [default: stdin]
Options:
--title Title and header of the resulting HTML page.
[default: Vegeta Plot]
--threshold Threshold of data points to downsample series to.
Series with less than --threshold number of data
points are not downsampled. [default: 4000]
Examples:
echo "GET http://:80" | vegeta attack -name=50qps -rate=50 -duration=5s > results.50qps.bin
cat results.50qps.bin | vegeta plot > plot.50qps.html
echo "GET http://:80" | vegeta attack -name=100qps -rate=100 -duration=5s > results.100qps.bin
vegeta plot results.50qps.bin results.100qps.bin > plot.html
`
func plotCmd() command {
fs := flag.NewFlagSet("vegeta plot", flag.ExitOnError)
title := fs.String("title", "Vegeta Plot", "Title and header of the resulting HTML page")
threshold := fs.Int("threshold", 4000, "Threshold of data points above which series are downsampled.")
output := fs.String("output", "stdout", "Output file")
fs.Usage = func() {
fmt.Fprintln(os.Stderr, plotUsage)
}
return command{fs, func(args []string) error {
fs.Parse(args)
files := fs.Args()
if len(files) == 0 {
files = append(files, "stdin")
}
return plotRun(files, *threshold, *title, *output)
}}
}
func plotRun(files []string, threshold int, title, output string) error {
srcs := make([]vegeta.Decoder, len(files))
for i, f := range files {
in, err := file(f, false)
if err != nil {
return err
}
defer in.Close()
srcs[i] = vegeta.NewDecoder(in)
}
dec := vegeta.NewRoundRobinDecoder(srcs...)
out, err := file(output, true)
if err != nil {
return err
}
defer out.Close()
sigch := make(chan os.Signal, 1)
signal.Notify(sigch, os.Interrupt)
p := plot.New(
plot.Title(title),
plot.Downsample(threshold),
plot.Label(plot.ErrorLabeler),
)
decode:
for {
select {
case <-sigch:
break decode
default:
var r vegeta.Result
if err = dec.Decode(&r); err != nil {
if err == io.EOF {
break decode
}
return err
}
if err = p.Add(&r); err != nil {
return err
}
}
}
p.Close()
_, err = p.WriteTo(out)
return err
}