github.com/cloudwego/kitex@v0.9.0/pkg/rpcinfo/rpcstats.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package rpcinfo 18 19 import ( 20 "context" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "github.com/cloudwego/kitex/internal" 26 "github.com/cloudwego/kitex/pkg/stats" 27 ) 28 29 var ( 30 _ RPCStats = (*rpcStats)(nil) 31 _ MutableRPCStats = &rpcStats{} 32 _ internal.Reusable = (*rpcStats)(nil) 33 _ internal.Reusable = (*event)(nil) 34 rpcStatsPool sync.Pool 35 eventPool sync.Pool 36 once sync.Once 37 maxEventNum int 38 ) 39 40 type event struct { 41 event stats.Event 42 status stats.Status 43 info string 44 time time.Time 45 } 46 47 // Event implements the Event interface. 48 func (e *event) Event() stats.Event { 49 return e.event 50 } 51 52 // Status implements the Event interface. 53 func (e *event) Status() stats.Status { 54 return e.status 55 } 56 57 // Info implements the Event interface. 58 func (e *event) Info() string { 59 return e.info 60 } 61 62 // Time implements the Event interface. 63 func (e *event) Time() time.Time { 64 return e.time 65 } 66 67 // IsNil implements the Event interface. 68 func (e *event) IsNil() bool { 69 return e == nil 70 } 71 72 func newEvent() interface{} { 73 return &event{} 74 } 75 76 func (e *event) zero() { 77 e.event = nil 78 e.status = 0 79 e.info = "" 80 e.time = time.Time{} 81 } 82 83 // Recycle reuses the event. 84 func (e *event) Recycle() { 85 e.zero() 86 eventPool.Put(e) 87 } 88 89 type atomicErr struct { 90 err error 91 } 92 93 type atomicPanicErr struct { 94 panicErr interface{} 95 } 96 97 type rpcStats struct { 98 sync.RWMutex 99 level stats.Level 100 101 eventMap []Event 102 103 sendSize uint64 104 lastSendSize uint64 // for Streaming APIs, record the size of the last sent message 105 recvSize uint64 106 lastRecvSize uint64 // for Streaming APIs, record the size of the last received message 107 108 err atomic.Value 109 panicErr atomic.Value 110 } 111 112 func init() { 113 rpcStatsPool.New = newRPCStats 114 eventPool.New = newEvent 115 } 116 117 func newRPCStats() interface{} { 118 return &rpcStats{ 119 eventMap: make([]Event, maxEventNum), 120 } 121 } 122 123 // Record implements the RPCStats interface. 124 func (r *rpcStats) Record(ctx context.Context, e stats.Event, status stats.Status, info string) { 125 if e.Level() > r.level { 126 return 127 } 128 eve := NewEvent(e, status, info) 129 idx := e.Index() 130 r.Lock() 131 r.eventMap[idx] = eve 132 r.Unlock() 133 } 134 135 // NewEvent creates a new Event based on the given event, status and info. 136 func NewEvent(statsEvent stats.Event, status stats.Status, info string) Event { 137 eve := eventPool.Get().(*event) 138 eve.event = statsEvent 139 eve.status = status 140 eve.info = info 141 eve.time = time.Now() 142 return eve 143 } 144 145 // SendSize implements the RPCStats interface. 146 func (r *rpcStats) SendSize() uint64 { 147 return atomic.LoadUint64(&r.sendSize) 148 } 149 150 // LastSendSize implements the RPCStats interface. 151 func (r *rpcStats) LastSendSize() uint64 { 152 return atomic.LoadUint64(&r.lastSendSize) 153 } 154 155 // RecvSize implements the RPCStats interface. 156 func (r *rpcStats) RecvSize() uint64 { 157 return atomic.LoadUint64(&r.recvSize) 158 } 159 160 // LastRecvSize implements the RPCStats interface. 161 func (r *rpcStats) LastRecvSize() uint64 { 162 return atomic.LoadUint64(&r.lastRecvSize) 163 } 164 165 // Error implements the RPCStats interface. 166 func (r *rpcStats) Error() error { 167 ae, _ := r.err.Load().(atomicErr) 168 return ae.err 169 } 170 171 // Panicked implements the RPCStats interface. 172 func (r *rpcStats) Panicked() (bool, interface{}) { 173 ape, _ := r.panicErr.Load().(atomicPanicErr) 174 return ape.panicErr != nil, ape.panicErr 175 } 176 177 // GetEvent implements the RPCStats interface. 178 func (r *rpcStats) GetEvent(e stats.Event) Event { 179 idx := e.Index() 180 r.RLock() 181 evt := r.eventMap[idx] 182 r.RUnlock() 183 if evt == nil || evt.IsNil() { 184 return nil 185 } 186 return evt 187 } 188 189 // Level implements the RPCStats interface. 190 func (r *rpcStats) Level() stats.Level { 191 return r.level 192 } 193 194 // CopyForRetry implements the RPCStats interface, it copies a RPCStats from the origin one 195 // to pass through info of the first request to retrying requests. 196 func (r *rpcStats) CopyForRetry() RPCStats { 197 // Copied rpc stats is for request retrying and cannot be reused, so no need to get from pool. 198 nr := newRPCStats().(*rpcStats) 199 r.Lock() 200 startIdx := int(stats.RPCStart.Index()) 201 userIdx := stats.PredefinedEventNum() 202 for i := 0; i < len(nr.eventMap); i++ { 203 // Ignore none RPCStart events to avoid incorrect tracing. 204 if i == startIdx || i >= userIdx { 205 nr.eventMap[i] = r.eventMap[i] 206 } 207 } 208 r.Unlock() 209 return nr 210 } 211 212 // SetSendSize sets send size. 213 // This should be called by Ping-Pong APIs which only send once. 214 func (r *rpcStats) SetSendSize(size uint64) { 215 atomic.StoreUint64(&r.sendSize, size) 216 } 217 218 // IncrSendSize increments send size. 219 // This should be called by Streaming APIs which may send multiple times. 220 func (r *rpcStats) IncrSendSize(size uint64) { 221 atomic.AddUint64(&r.sendSize, size) 222 atomic.StoreUint64(&r.lastSendSize, size) 223 } 224 225 // SetRecvSize sets recv size. 226 // This should be called by Ping-Pong APIs which only recv once. 227 func (r *rpcStats) SetRecvSize(size uint64) { 228 atomic.StoreUint64(&r.recvSize, size) 229 } 230 231 // IncrRecvSize increments recv size. 232 // This should be called by Streaming APIs which may recv multiple times. 233 func (r *rpcStats) IncrRecvSize(size uint64) { 234 atomic.AddUint64(&r.recvSize, size) 235 atomic.StoreUint64(&r.lastRecvSize, size) 236 } 237 238 // SetError sets error. 239 func (r *rpcStats) SetError(err error) { 240 r.err.Store(atomicErr{err: err}) 241 } 242 243 // SetPanicked sets if panicked. 244 func (r *rpcStats) SetPanicked(x interface{}) { 245 r.panicErr.Store(atomicPanicErr{panicErr: x}) 246 } 247 248 // SetLevel sets the level. 249 func (r *rpcStats) SetLevel(level stats.Level) { 250 r.level = level 251 } 252 253 // Reset resets the stats. 254 func (r *rpcStats) Reset() { 255 r.level = 0 256 if ae, _ := r.err.Load().(atomicErr); ae.err != nil { 257 r.err.Store(atomicErr{}) 258 } 259 if ape, _ := r.panicErr.Load().(atomicPanicErr); ape.panicErr != nil { 260 r.panicErr.Store(atomicPanicErr{}) 261 } 262 atomic.StoreUint64(&r.recvSize, 0) 263 atomic.StoreUint64(&r.sendSize, 0) 264 for i := range r.eventMap { 265 if r.eventMap[i] != nil { 266 r.eventMap[i].(*event).Recycle() 267 r.eventMap[i] = nil 268 } 269 } 270 } 271 272 // ImmutableView restricts the rpcStats into a read-only rpcinfo.RPCStats. 273 func (r *rpcStats) ImmutableView() RPCStats { 274 return r 275 } 276 277 // Recycle reuses the rpcStats. 278 func (r *rpcStats) Recycle() { 279 r.Reset() 280 rpcStatsPool.Put(r) 281 } 282 283 // NewRPCStats creates a new RPCStats. 284 func NewRPCStats() RPCStats { 285 once.Do(func() { 286 stats.FinishInitialization() 287 maxEventNum = stats.MaxEventNum() 288 }) 289 return rpcStatsPool.Get().(*rpcStats) 290 }