github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/cli/format.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package cli 12 13 import ( 14 "fmt" 15 "io" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/workload/histogram" 19 ) 20 21 // outputFormat is the interface used to output results incrementally 22 // during a workload run. 23 type outputFormat interface { 24 // rampDone is called once when the ramp-up period completes, if 25 // configured. 26 rampDone() 27 // outputError is called when an error is encountered. 28 outputError(err error) 29 // outputTick is called when the main loop considers it useful 30 // to emit one row of results. 31 outputTick(startElapsed time.Duration, t histogram.Tick) 32 // outputTotal is called at the end, using the main histogram 33 // collector. 34 outputTotal(startElapsed time.Duration, t histogram.Tick) 35 // outputResult is called at the end, using the result histogram 36 // collector. 37 outputResult(startElapsed time.Duration, t histogram.Tick) 38 } 39 40 // textFormatter produces output meant for quick parsing by humans. The 41 // data is printed as fixed-width columns. Summary rows 42 // are printed at the end. 43 type textFormatter struct { 44 i int 45 numErr int 46 } 47 48 func (f *textFormatter) rampDone() { 49 f.i = 0 50 } 51 52 func (f *textFormatter) outputError(_ error) { 53 f.numErr++ 54 } 55 56 func (f *textFormatter) outputTick(startElapsed time.Duration, t histogram.Tick) { 57 if f.i%20 == 0 { 58 fmt.Println("_elapsed___errors__ops/sec(inst)___ops/sec(cum)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)") 59 } 60 f.i++ 61 fmt.Printf("%7.1fs %8d %14.1f %14.1f %8.1f %8.1f %8.1f %8.1f %s\n", 62 startElapsed.Seconds(), 63 f.numErr, 64 float64(t.Hist.TotalCount())/t.Elapsed.Seconds(), 65 float64(t.Cumulative.TotalCount())/startElapsed.Seconds(), 66 time.Duration(t.Hist.ValueAtQuantile(50)).Seconds()*1000, 67 time.Duration(t.Hist.ValueAtQuantile(95)).Seconds()*1000, 68 time.Duration(t.Hist.ValueAtQuantile(99)).Seconds()*1000, 69 time.Duration(t.Hist.ValueAtQuantile(100)).Seconds()*1000, 70 t.Name, 71 ) 72 } 73 74 const totalHeader = "\n_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)" 75 76 func (f *textFormatter) outputTotal(startElapsed time.Duration, t histogram.Tick) { 77 f.outputFinal(startElapsed, t, "__total") 78 } 79 80 func (f *textFormatter) outputResult(startElapsed time.Duration, t histogram.Tick) { 81 f.outputFinal(startElapsed, t, "__result") 82 } 83 84 func (f *textFormatter) outputFinal( 85 startElapsed time.Duration, t histogram.Tick, titleSuffix string, 86 ) { 87 fmt.Println(totalHeader + titleSuffix) 88 if t.Cumulative == nil { 89 return 90 } 91 if t.Cumulative.TotalCount() == 0 { 92 return 93 } 94 fmt.Printf("%7.1fs %8d %14d %14.1f %8.1f %8.1f %8.1f %8.1f %8.1f %s\n", 95 startElapsed.Seconds(), 96 f.numErr, 97 t.Cumulative.TotalCount(), 98 float64(t.Cumulative.TotalCount())/startElapsed.Seconds(), 99 time.Duration(t.Cumulative.Mean()).Seconds()*1000, 100 time.Duration(t.Cumulative.ValueAtQuantile(50)).Seconds()*1000, 101 time.Duration(t.Cumulative.ValueAtQuantile(95)).Seconds()*1000, 102 time.Duration(t.Cumulative.ValueAtQuantile(99)).Seconds()*1000, 103 time.Duration(t.Cumulative.ValueAtQuantile(100)).Seconds()*1000, 104 t.Name, 105 ) 106 } 107 108 // jsonFormatter produces output that is machine-readable. The time is 109 // printed using absolute timestamps. No summary row is printed at the 110 // end. 111 type jsonFormatter struct { 112 w io.Writer 113 numErr int 114 } 115 116 func (f *jsonFormatter) rampDone() {} 117 118 func (f *jsonFormatter) outputError(_ error) { 119 f.numErr++ 120 } 121 122 func (f *jsonFormatter) outputTick(startElapsed time.Duration, t histogram.Tick) { 123 // Note: we use fmt.Printf here instead of json.Marshal to ensure 124 // that float values do not get printed with a uselessly large 125 // number of decimals. 126 fmt.Fprintf(f.w, `{"time":"%s",`+ 127 `"errs":%d,`+ 128 `"avgt":%.1f,`+ 129 `"avgl":%.1f,`+ 130 `"p50l":%.1f,`+ 131 `"p95l":%.1f,`+ 132 `"p99l":%.1f,`+ 133 `"maxl":%.1f,`+ 134 `"type":"%s"`+ 135 "}\n", 136 t.Now.UTC().Format(time.RFC3339Nano), 137 f.numErr, 138 float64(t.Hist.TotalCount())/t.Elapsed.Seconds(), 139 float64(t.Cumulative.TotalCount())/startElapsed.Seconds(), 140 time.Duration(t.Hist.ValueAtQuantile(50)).Seconds()*1000, 141 time.Duration(t.Hist.ValueAtQuantile(95)).Seconds()*1000, 142 time.Duration(t.Hist.ValueAtQuantile(99)).Seconds()*1000, 143 time.Duration(t.Hist.ValueAtQuantile(100)).Seconds()*1000, 144 t.Name, 145 ) 146 } 147 148 func (f *jsonFormatter) outputTotal(startElapsed time.Duration, t histogram.Tick) {} 149 150 func (f *jsonFormatter) outputResult(startElapsed time.Duration, t histogram.Tick) {}