1package jsonapi 2 3import ( 4 "crypto/rand" 5 "fmt" 6 "io" 7 "reflect" 8 "time" 9) 10 11// Event represents a lifecycle event in the marshaling or unmarshalling 12// process. 13type Event int 14 15const ( 16 // UnmarshalStart is the Event that is sent when deserialization of a payload 17 // begins. 18 UnmarshalStart Event = iota 19 20 // UnmarshalStop is the Event that is sent when deserialization of a payload 21 // ends. 22 UnmarshalStop 23 24 // MarshalStart is the Event that is sent sent when serialization of a payload 25 // begins. 26 MarshalStart 27 28 // MarshalStop is the Event that is sent sent when serialization of a payload 29 // ends. 30 MarshalStop 31) 32 33// Runtime has the same methods as jsonapi package for serialization and 34// deserialization but also has a ctx, a map[string]interface{} for storing 35// state, designed for instrumenting serialization timings. 36type Runtime struct { 37 ctx map[string]interface{} 38} 39 40// Events is the func type that provides the callback for handling event timings. 41type Events func(*Runtime, Event, string, time.Duration) 42 43// Instrumentation is a a global Events variable. This is the handler for all 44// timing events. 45var Instrumentation Events 46 47// NewRuntime creates a Runtime for use in an application. 48func NewRuntime() *Runtime { return &Runtime{make(map[string]interface{})} } 49 50// WithValue adds custom state variables to the runtime context. 51func (r *Runtime) WithValue(key string, value interface{}) *Runtime { 52 r.ctx[key] = value 53 54 return r 55} 56 57// Value returns a state variable in the runtime context. 58func (r *Runtime) Value(key string) interface{} { 59 return r.ctx[key] 60} 61 62// Instrument is deprecated. 63func (r *Runtime) Instrument(key string) *Runtime { 64 return r.WithValue("instrument", key) 65} 66 67func (r *Runtime) shouldInstrument() bool { 68 return Instrumentation != nil 69} 70 71// UnmarshalPayload has docs in request.go for UnmarshalPayload. 72func (r *Runtime) UnmarshalPayload(reader io.Reader, model interface{}) error { 73 return r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error { 74 return UnmarshalPayload(reader, model) 75 }) 76} 77 78// UnmarshalManyPayload has docs in request.go for UnmarshalManyPayload. 79func (r *Runtime) UnmarshalManyPayload(reader io.Reader, kind reflect.Type) (elems []interface{}, err error) { 80 r.instrumentCall(UnmarshalStart, UnmarshalStop, func() error { 81 elems, err = UnmarshalManyPayload(reader, kind) 82 return err 83 }) 84 85 return 86} 87 88// MarshalPayload has docs in response.go for MarshalPayload. 89func (r *Runtime) MarshalPayload(w io.Writer, model interface{}) error { 90 return r.instrumentCall(MarshalStart, MarshalStop, func() error { 91 return MarshalPayload(w, model) 92 }) 93} 94 95func (r *Runtime) instrumentCall(start Event, stop Event, c func() error) error { 96 if !r.shouldInstrument() { 97 return c() 98 } 99 100 instrumentationGUID, err := newUUID() 101 if err != nil { 102 return err 103 } 104 105 begin := time.Now() 106 Instrumentation(r, start, instrumentationGUID, time.Duration(0)) 107 108 if err := c(); err != nil { 109 return err 110 } 111 112 diff := time.Duration(time.Now().UnixNano() - begin.UnixNano()) 113 Instrumentation(r, stop, instrumentationGUID, diff) 114 115 return nil 116} 117 118// citation: http://play.golang.org/p/4FkNSiUDMg 119func newUUID() (string, error) { 120 uuid := make([]byte, 16) 121 if _, err := io.ReadFull(rand.Reader, uuid); err != nil { 122 return "", err 123 } 124 // variant bits; see section 4.1.1 125 uuid[8] = uuid[8]&^0xc0 | 0x80 126 // version 4 (pseudo-random); see section 4.1.3 127 uuid[6] = uuid[6]&^0xf0 | 0x40 128 return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil 129} 130