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) {}