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