/caprice

Measuring software execution

Primary LanguageGoApache License 2.0Apache-2.0

Caprice

Caprice allows you to have better visibility in your time-intensive code by emitting CloudEvents and building a statistical model as well as visualizing the distribution of time spent.

Usage

Basic

[TODO] Upon execution, STDOUT should have a report of the following:

{
  "caprice": {
    "host": "https://caprice.corp",
    "scribe": "uuid-uuid-uuid-uuid",
    "view": "https://caprice.corp/view/uuid-uuid-uuid-uuid"
  }
}

Throughout execution, users can see in the linked URL above to follow the progress of execution. CAPRICE_HOST= determines the output values above.

Generic

import (
  "go.husin.dev/caprice/scribe"
)

func thing1() {}

func thing2() error {}

func RunSomething() {
  s := scribe.New("RunSomething")
  s.Run("thing1", thing1)
  if err := s.RunErr("thing2", thing2); err != nil {
    // err here is just passed from the returned value of thing2
    panic(err)
  }
  s.Done(nil)
}

Testing

import (
  "testing"

  "go.husin.dev/caprice/scribe"
)

func runStuff() error {}

func TestMyApp(t *testing.T) {
  s := scribe.NewT(t, "TestMyApp")

  s.RunT("runStuff", runStuff) // internally calls t.Fatal() on error

  err := runMoreStuff()
  s.Done(err) // if err != nil, internally call t.Fatal()
}

Advanced

The powerful part comes in when stages are incorporated.

import (
  "testing"

  "go.husin.dev/caprice/scribe"
)

func TestMyApp(t *testing.T) {
  s := scribe.NewT(t, "TestMyApp")

  s.Run("prepare", prepareFunc)

  thingsDone := s.NewStage("things")
  s.RunT("do thing1", func() error {
    return thing1("some", "param")
  })
  s.RunT("do thing2", func() error {
    return thing2("some", "other", "param")
  })
  thingsDone()

  s.Run("cleanup", cleanUpPotentiallySlowInfrastructure)
  s.Done(nil)
}

This allows Caprice to understand that there are stages and sub-stages. If the visualization were transformed to ASCII output, it will roughly look like this:

00.000s  ┌ TestMyApp
00.000s  │ ┌ prepare
00.002s  │ └ prepare [DONE 0.002s]
00.002s  │ ┌ things
00.003s  │ │ ┌ do thing1
02.497s  │ │ └ do thing1 [DONE 02.494s]
02.498s  │ │ ┌ do thing2
20.012s  │ │ └ do thing2 [DONE 17.514s]
20.013s  │ └ things [DONE 20.011s]
40.130s  └ TestMyApp [DONE 40.130s]

Identifiers

There are valid reasons to add more metadata into a given execution.

The Go interface supports multiple keys with flexible value (i.e. interface{})

func init() {
  scribe.AddMetadata("release", "1.21")  // accepts key-value pair
}

caprice exec provides a simplier interface:

CAPRICE_ANNOTATION=some-pipeline-test-123

which would be equivalent to the following:

scribe.AddMetadata("annotation", "some-pipeline-test-123")

Trivia

The name Caprice was taken from 24 Caprices by Niccolò Paganini. The musical piece was an inspiration to many other composers.

Just like the musical piece, this project brings significantly more value through repetition of execution.