DEV Community


Posted on


Homebrew Performance Evaluator

Performance improvement is life-long work. First of all, you need to know how it does right now. The way to do it usually depends on the scale.

Developed a distributed performance test system before with Python, which took quite a long time to evolve and it's complicated. I wanted to try something different in this brand new project. Before introducing all kinds of performance test framework, I would like to make a simple and handy application.

There we Go.

Keep in mind: simple and handy.

Then it shall be able to

  • Send requests to API server
  • Evaluate the time cost
  • Generate some reports
  • Cookie support
  • Configure

The Evaluator

type Evaluator struct {
  cli *http.Client
  report []Item
  cookie *Cookie

Item to keep the request results

type Item struct {
  Url *url.URL `json:"url,omitempty"`
  Elapsed time.Duration `json:"elapsed,omitempty"`
  CreatedAt time.Time `json:"created_at,omitempty"`

Cookie to keep session-related data.

type Cookie struct {
  data map[string]interface{}

Get some data

func (self *Evaluator) Elapsed(req *http.Request) (*http.Response, error) {
  before := time.Now()
  rsp, err := self.cli.Do(req) = append(, Item{
    Url: req.URL,
    Elapsed: time.Now().Sub(before),
    CreatedAt: before,
  return rsp, err

Time to print some results.

func (self *Evaluator) GenReport() error {
  fmt.Println("-- report --")
  for _, v := range {
  return nil

To get a pretty print, create a String function for Item as well.

func (self Item) String() string {
  return fmt.Sprintf("%v [%s:%v] %s", self.CreatedAt.Unix(), self.Url.Path, self.Url.RawQuery, self.Elapsed.String())

Maybe generate some CSV for further processing. I was using pretty print in Go, good to read but difficult to process later.

func (self *Evaluator) GenReportCSV(path string) error {
  // ...
  w := csv.NewWriter(fd)
  w.Write([]string{"Timestamp", "Api", "Query", "Elapsed"})
  for _, v := range {
    if err = w.Write([]string{
        fmt.Sprintf("%d", v.CreatedAt.Unix()),
        fmt.Sprintf("%s", v.Url.RawQuery),
        fmt.Sprintf("%.4f", v.Elapsed.Seconds())});
      err != nil {
        return err
  return nil

Create a dummy client to do something crazy, wg is a WaitGroup
variable to wait for all crazy clients to finish their operations.

func dummy_terminal() {
  app := apps.NewApp()
  defer app.Close()

  // ... do something crazy to API ...

  if err := app.GenReport(); err != nil {
    fmt.Println("generate report failed: ", err)

  out := fmt.Sprintf("%s/performance-%d.csv", *output_base, time.Now().UnixNano())
  if err := app.GenReportCSV(out); err != nil {
    fmt.Println("generate report csv failed: ", err)

Configure in command line with flag

  terminals   = flag.Int("terminals", 30, "total concurrent terminals")
  output_base = flag.String("output_base", "/tmp", "backend output_base")

Found a great logging module in Fabric :D

go get

The CSV output looks like


The console output is pretty much the same, except the time is formatted.

Oldest comments (0)

Why You Need to Study Javascript Fundamentals

The harsh reality for JS Developers: If you don't study the fundamentals, you'll be just another “Coder”. Top learnings on how to get to the mid/senior level faster as a JavaScript developer by Dragos Nedelcu.