github.com/blend/go-sdk@v1.20220411.3/grpcutil/rpc_event.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package grpcutil 9 10 import ( 11 "context" 12 "fmt" 13 "io" 14 "time" 15 16 "google.golang.org/grpc/codes" 17 "google.golang.org/grpc/status" 18 19 "github.com/blend/go-sdk/ansi" 20 "github.com/blend/go-sdk/logger" 21 "github.com/blend/go-sdk/timeutil" 22 ) 23 24 // Logger flags 25 const ( 26 FlagRPC = "rpc" 27 ) 28 29 // these are compile time assertions 30 var ( 31 _ logger.Event = (*RPCEvent)(nil) 32 _ logger.TextWritable = (*RPCEvent)(nil) 33 _ logger.JSONWritable = (*RPCEvent)(nil) 34 ) 35 36 // NewRPCEvent creates a new rpc event. 37 func NewRPCEvent(method string, elapsed time.Duration, options ...RPCEventOption) RPCEvent { 38 rpe := RPCEvent{ 39 Engine: EngineGRPC, 40 Method: method, 41 Elapsed: elapsed, 42 } 43 for _, opt := range options { 44 opt(&rpe) 45 } 46 return rpe 47 } 48 49 // NewRPCEventListener returns a new web request event listener. 50 func NewRPCEventListener(listener func(context.Context, RPCEvent)) logger.Listener { 51 return func(ctx context.Context, e logger.Event) { 52 if typed, isTyped := e.(RPCEvent); isTyped { 53 listener(ctx, typed) 54 } 55 } 56 } 57 58 // NewRPCEventFilter returns a new rpc event filter. 59 func NewRPCEventFilter(filter func(context.Context, RPCEvent) (RPCEvent, bool)) logger.Filter { 60 return func(ctx context.Context, e logger.Event) (logger.Event, bool) { 61 if typed, isTyped := e.(RPCEvent); isTyped { 62 return filter(ctx, typed) 63 } 64 return e, false 65 } 66 } 67 68 // RPCEventOption is a mutator for RPCEvents. 69 type RPCEventOption func(*RPCEvent) 70 71 // OptRPCEngine sets a field on the event. 72 func OptRPCEngine(value string) RPCEventOption { 73 return func(e *RPCEvent) { e.Engine = value } 74 } 75 76 // OptRPCPeer sets a field on the event. 77 func OptRPCPeer(value string) RPCEventOption { 78 return func(e *RPCEvent) { e.Peer = value } 79 } 80 81 // OptRPCMethod sets a field on the event. 82 func OptRPCMethod(value string) RPCEventOption { 83 return func(e *RPCEvent) { e.Method = value } 84 } 85 86 // OptRPCUserAgent sets a field on the event. 87 func OptRPCUserAgent(value string) RPCEventOption { 88 return func(e *RPCEvent) { e.UserAgent = value } 89 } 90 91 // OptRPCAuthority sets a field on the event. 92 func OptRPCAuthority(value string) RPCEventOption { 93 return func(e *RPCEvent) { e.Authority = value } 94 } 95 96 // OptRPCContentType sets a field on the event. 97 func OptRPCContentType(value string) RPCEventOption { 98 return func(e *RPCEvent) { e.ContentType = value } 99 } 100 101 // OptRPCElapsed sets a field on the event. 102 func OptRPCElapsed(value time.Duration) RPCEventOption { 103 return func(e *RPCEvent) { e.Elapsed = value } 104 } 105 106 // OptRPCErr sets a field on the event. 107 func OptRPCErr(value error) RPCEventOption { 108 return func(e *RPCEvent) { e.Err = value } 109 } 110 111 // RPCEvent is an event type for rpc 112 type RPCEvent struct { 113 Engine string 114 Peer string 115 Method string 116 UserAgent string 117 Authority string 118 ContentType string 119 Elapsed time.Duration 120 Err error 121 } 122 123 // GetFlag implements Event. 124 func (e RPCEvent) GetFlag() string { return FlagRPC } 125 126 // WriteText implements TextWritable. 127 func (e RPCEvent) WriteText(tf logger.TextFormatter, wr io.Writer) { 128 if e.Engine != "" { 129 fmt.Fprint(wr, "[") 130 fmt.Fprint(wr, tf.Colorize(e.Engine, ansi.ColorLightWhite)) 131 fmt.Fprint(wr, "]") 132 } 133 if e.Method != "" { 134 if e.Engine != "" { 135 fmt.Fprint(wr, logger.Space) 136 } 137 fmt.Fprint(wr, tf.Colorize(e.Method, ansi.ColorBlue)) 138 } 139 if e.Peer != "" { 140 fmt.Fprint(wr, logger.Space) 141 fmt.Fprint(wr, e.Peer) 142 } 143 if e.Authority != "" { 144 fmt.Fprint(wr, logger.Space) 145 fmt.Fprint(wr, e.Authority) 146 } 147 if e.UserAgent != "" { 148 fmt.Fprint(wr, logger.Space) 149 fmt.Fprint(wr, e.UserAgent) 150 } 151 if e.ContentType != "" { 152 fmt.Fprint(wr, logger.Space) 153 fmt.Fprint(wr, e.ContentType) 154 } 155 156 fmt.Fprint(wr, logger.Space) 157 fmt.Fprint(wr, e.Elapsed.String()) 158 159 if e.Err != nil { 160 fmt.Fprint(wr, logger.Space) 161 162 if s, ok := status.FromError(e.Err); ok { 163 fmt.Fprint(wr, tf.Colorize(fmt.Sprintf("failed (%[1]d - %[1]v)", s.Code()), ansi.ColorRed)) 164 } else { 165 fmt.Fprint(wr, tf.Colorize("failed", ansi.ColorRed)) 166 } 167 } 168 } 169 170 // Decompose implements JSONWritable. 171 func (e RPCEvent) Decompose() map[string]interface{} { 172 var code codes.Code 173 if s, ok := status.FromError(e.Err); ok { 174 code = s.Code() 175 } 176 return map[string]interface{}{ 177 "engine": e.Engine, 178 "peer": e.Peer, 179 "method": e.Method, 180 "userAgent": e.UserAgent, 181 "authority": e.Authority, 182 "contentType": e.ContentType, 183 "elapsed": timeutil.Milliseconds(e.Elapsed), 184 "err": e.Err, 185 "code": code, 186 } 187 }