github.com/zeebo/mon@v0.0.0-20211012163247-13d39bdb54fa/time.go (about)

     1  // +build !nomon
     2  
     3  package mon
     4  
     5  import (
     6  	"strings"
     7  	"sync/atomic"
     8  	_ "unsafe"
     9  
    10  	"github.com/zeebo/this"
    11  )
    12  
    13  //go:linkname nanotime runtime.nanotime
    14  func nanotime() (mono int64)
    15  
    16  // Thunk is a type that allows one to get the benefits of Time without having to
    17  // compute the caller every time it's called. Zero values are valid.
    18  type Thunk struct {
    19  	val atomic.Value
    20  }
    21  
    22  // Time returns a Timer where the name is chosen the first time by the caller. Don't
    23  // use the same Thunk from different functions/methods.
    24  func (t *Thunk) Start() Timer {
    25  	name := t.val.Load()
    26  	if name == nil {
    27  		name = this.ThisN(1)
    28  		t.val.Store(name)
    29  	}
    30  	return StartNamed(name.(string))
    31  }
    32  
    33  // Start returns a Timer using the calling function for the name.
    34  func Start() (t Timer) {
    35  	return StartNamed(this.ThisN(1))
    36  }
    37  
    38  // StartNamed returns a Timer that records a duration when its Done method is called.
    39  func StartNamed(name string) Timer {
    40  	return Timer{
    41  		now:   nanotime(),
    42  		state: GetState(name),
    43  	}
    44  }
    45  
    46  // Timer keeps track of the state necessary to record timing info.
    47  type Timer struct {
    48  	now   int64
    49  	state *State
    50  }
    51  
    52  // Stop records the timing info.
    53  func (r Timer) Stop(err *error) {
    54  	kind := ""
    55  	if err != nil {
    56  		kind = getKind(*err)
    57  	}
    58  
    59  	r.state.done(nanotime()-r.now, kind)
    60  }
    61  
    62  // getKind returns a string that attemps to be representative of the error.
    63  func getKind(err error) string {
    64  	if n, ok := err.(interface{ Name() (string, bool) }); ok {
    65  		if name, ok := n.Name(); ok {
    66  			return name
    67  		}
    68  	}
    69  
    70  	if err != nil {
    71  		s := err.Error()
    72  		if i := strings.IndexByte(s, ':'); i > 0 {
    73  			return s[:i]
    74  		} else if strings.IndexByte(s, ' ') == -1 {
    75  			return s
    76  		} else {
    77  			return "error"
    78  		}
    79  	}
    80  
    81  	return ""
    82  }