github.com/GuanceCloud/cliutils@v1.1.21/network/http/api_metric.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package http
     7  
     8  import (
     9  	"time"
    10  )
    11  
    12  var (
    13  	metricCh = make(chan *APIMetric, 32)
    14  	qch      = make(chan *qAPIStats)
    15  	exitch   = make(chan interface{})
    16  )
    17  
    18  type APIMetric struct {
    19  	API        string
    20  	Latency    time.Duration
    21  	StatusCode int
    22  	Limited    bool
    23  }
    24  
    25  // APIMetricReporter used to collects API metrics during API handing.
    26  type APIMetricReporter interface {
    27  	Report(*APIMetric) // report these metrics
    28  }
    29  
    30  // ReporterImpl used to report API stats
    31  // TODO: We should implemente a default API metric reporter under cliutils.
    32  type ReporterImpl struct{}
    33  
    34  func (r *ReporterImpl) Report(m *APIMetric) {
    35  	select {
    36  	case metricCh <- m:
    37  	default: // unblocking
    38  	}
    39  }
    40  
    41  type APIStat struct {
    42  	Total int
    43  	LatencyMax,
    44  	LatencyAvg,
    45  	Latency time.Duration
    46  
    47  	Status1XX,
    48  	Status2XX,
    49  	Status3XX,
    50  	Status4XX,
    51  	Status5XX int
    52  
    53  	Limited int
    54  }
    55  
    56  type qAPIStats struct {
    57  	stats chan map[string]*APIStat
    58  }
    59  
    60  func GetStats() map[string]*APIStat {
    61  	q := &qAPIStats{stats: make(chan map[string]*APIStat)}
    62  
    63  	defer close(q.stats)
    64  
    65  	tick := time.NewTicker(time.Second * 1)
    66  	defer tick.Stop()
    67  	qch <- q
    68  	select {
    69  	case r := <-q.stats:
    70  		return r
    71  	case <-tick.C:
    72  		return nil
    73  	}
    74  }
    75  
    76  func copyStats(s map[string]*APIStat) map[string]*APIStat {
    77  	res := map[string]*APIStat{}
    78  	for k, v := range s {
    79  		res[k] = &APIStat{
    80  			Total:      v.Total,
    81  			LatencyMax: v.LatencyMax,
    82  			LatencyAvg: v.LatencyAvg,
    83  			Latency:    v.Latency,
    84  			Status1XX:  v.Status1XX,
    85  			Status2XX:  v.Status2XX,
    86  			Status3XX:  v.Status3XX,
    87  			Status4XX:  v.Status4XX,
    88  			Status5XX:  v.Status5XX,
    89  			Limited:    v.Limited,
    90  		}
    91  	}
    92  	return res
    93  }
    94  
    95  func StopReporter() {
    96  	select {
    97  	case <-exitch: // closed?
    98  		return
    99  	default:
   100  		close(exitch)
   101  	}
   102  }
   103  
   104  func StartReporter() {
   105  	stats := map[string]*APIStat{}
   106  	for {
   107  		select {
   108  		case <-exitch:
   109  			return
   110  
   111  		case q := <-qch:
   112  			select {
   113  			case <-q.stats: // is closed?
   114  			case q.stats <- copyStats(stats):
   115  			default: // unblocking
   116  			}
   117  
   118  		case m := <-metricCh:
   119  			v, ok := stats[m.API]
   120  			if !ok {
   121  				v = &APIStat{}
   122  				stats[m.API] = v
   123  			}
   124  
   125  			v.Total++
   126  			v.Latency += m.Latency
   127  			v.LatencyAvg = v.Latency / time.Duration(v.Total)
   128  			if m.Limited {
   129  				v.Limited++
   130  			}
   131  			if m.Latency > v.LatencyMax {
   132  				v.LatencyMax = m.Latency
   133  			}
   134  			switch m.StatusCode / 100 {
   135  			case 1:
   136  				v.Status1XX++
   137  			case 2:
   138  				v.Status2XX++
   139  			case 3:
   140  				v.Status3XX++
   141  			case 4:
   142  				v.Status4XX++
   143  			case 5:
   144  				v.Status5XX++
   145  			}
   146  		}
   147  	}
   148  }