github.com/seeker-insurance/kit@v0.0.13/jsonapi/runtime.go (about)

     1  package jsonapi
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"io"
     7  	"reflect"
     8  	"time"
     9  )
    10  
    11  type Event int
    12  
    13  const (
    14  	UnmarshalStart Event = iota
    15  	UnmarshalStop
    16  	MarshalStart
    17  	MarshalStop
    18  )
    19  
    20  type Runtime struct {
    21  	ctx map[string]interface{}
    22  }
    23  
    24  type Events func(*Runtime, Event, string, time.Duration)
    25  
    26  var Instrumentation Events
    27  
    28  func NewRuntime() *Runtime { return &Runtime{make(map[string]interface{})} }
    29  
    30  func (r *Runtime) WithValue(key string, value interface{}) *Runtime {
    31  	r.ctx[key] = value
    32  
    33  	return r
    34  }
    35  
    36  func (r *Runtime) Value(key string) interface{} {
    37  	return r.ctx[key]
    38  }
    39  
    40  func (r *Runtime) Instrument(key string) *Runtime {
    41  	return r.WithValue("instrument", key)
    42  }
    43  
    44  func (r *Runtime) shouldInstrument() bool {
    45  	return Instrumentation != nil
    46  }
    47  
    48  func (r *Runtime) UnmarshalPayload(reader io.Reader, model interface{}) error {
    49  	return r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error {
    50  		return UnmarshalPayload(reader, model)
    51  	})
    52  }
    53  
    54  func (r *Runtime) UnmarshalManyPayload(reader io.Reader, kind reflect.Type) (elems []interface{}, err error) {
    55  	r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error {
    56  		elems, err = UnmarshalManyPayload(reader, kind)
    57  		return err
    58  	})
    59  
    60  	return
    61  }
    62  
    63  func (r *Runtime) MarshalPayload(w io.Writer, model interface{}) error {
    64  	return r.instrumentCall(MarshalStart, MarshalStop, func() error {
    65  		return MarshalPayload(w, model)
    66  	})
    67  }
    68  
    69  func (r *Runtime) instrumentCall(start Event, stop Event, c func() error) error {
    70  	if !r.shouldInstrument() {
    71  		return c()
    72  	}
    73  
    74  	instrumentationGUID, err := newUUID()
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	begin := time.Now()
    80  	Instrumentation(r, start, instrumentationGUID, time.Duration(0))
    81  
    82  	if err := c(); err != nil {
    83  		return err
    84  	}
    85  
    86  	diff := time.Duration(time.Now().UnixNano() - begin.UnixNano())
    87  	Instrumentation(r, stop, instrumentationGUID, diff)
    88  
    89  	return nil
    90  }
    91  
    92  // citation: http://play.golang.org/p/4FkNSiUDMg
    93  func newUUID() (string, error) {
    94  	uuid := make([]byte, 16)
    95  	if _, err := io.ReadFull(rand.Reader, uuid); err != nil {
    96  		return "", err
    97  	}
    98  	// variant bits; see section 4.1.1
    99  	uuid[8] = uuid[8]&^0xc0 | 0x80
   100  	// version 4 (pseudo-random); see section 4.1.3
   101  	uuid[6] = uuid[6]&^0xf0 | 0x40
   102  	return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
   103  }