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  }