...

Source file src/github.com/penglongli/gin-metrics/ginmetrics/middleware.go

Documentation: github.com/penglongli/gin-metrics/ginmetrics

     1  package ginmetrics
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/gin-gonic/gin"
     9  	"github.com/prometheus/client_golang/prometheus/promhttp"
    10  
    11  	"github.com/penglongli/gin-metrics/bloom"
    12  )
    13  
    14  var (
    15  	metricRequestTotal    = "gin_request_total"
    16  	metricRequestUVTotal  = "gin_request_uv_total"
    17  	metricURIRequestTotal = "gin_uri_request_total"
    18  	metricRequestBody     = "gin_request_body_total"
    19  	metricResponseBody    = "gin_response_body_total"
    20  	metricRequestDuration = "gin_request_duration"
    21  	metricSlowRequest     = "gin_slow_request_total"
    22  
    23  	bloomFilter *bloom.BloomFilter
    24  )
    25  
    26  // Use set gin metrics middleware
    27  func (m *Monitor) Use(r gin.IRoutes) {
    28  	m.initGinMetrics()
    29  
    30  	r.Use(m.monitorInterceptor)
    31  	r.GET(m.metricPath, func(ctx *gin.Context) {
    32  		promhttp.Handler().ServeHTTP(ctx.Writer, ctx.Request)
    33  	})
    34  }
    35  
    36  // UseWithoutExposingEndpoint is used to add monitor interceptor to gin router
    37  // It can be called multiple times to intercept from multiple gin.IRoutes
    38  // http path is not set, to do that use Expose function
    39  func (m *Monitor) UseWithoutExposingEndpoint(r gin.IRoutes) {
    40  	m.initGinMetrics()
    41  	r.Use(m.monitorInterceptor)
    42  }
    43  
    44  // Expose adds metric path to a given router.
    45  // The router can be different with the one passed to UseWithoutExposingEndpoint.
    46  // This allows to expose metrics on different port.
    47  func (m *Monitor) Expose(r gin.IRoutes) {
    48  	r.GET(m.metricPath, func(ctx *gin.Context) {
    49  		promhttp.Handler().ServeHTTP(ctx.Writer, ctx.Request)
    50  	})
    51  }
    52  
    53  // initGinMetrics used to init gin metrics
    54  func (m *Monitor) initGinMetrics() {
    55  	bloomFilter = bloom.NewBloomFilter()
    56  
    57  	_ = monitor.AddMetric(&Metric{
    58  		Type:        Counter,
    59  		Name:        metricRequestTotal,
    60  		Description: "all the server received request num.",
    61  		Labels:      nil,
    62  	})
    63  	_ = monitor.AddMetric(&Metric{
    64  		Type:        Counter,
    65  		Name:        metricRequestUVTotal,
    66  		Description: "all the server received ip num.",
    67  		Labels:      nil,
    68  	})
    69  	_ = monitor.AddMetric(&Metric{
    70  		Type:        Counter,
    71  		Name:        metricURIRequestTotal,
    72  		Description: "all the server received request num with every uri.",
    73  		Labels:      []string{"uri", "method", "code"},
    74  	})
    75  	_ = monitor.AddMetric(&Metric{
    76  		Type:        Counter,
    77  		Name:        metricRequestBody,
    78  		Description: "the server received request body size, unit byte",
    79  		Labels:      nil,
    80  	})
    81  	_ = monitor.AddMetric(&Metric{
    82  		Type:        Counter,
    83  		Name:        metricResponseBody,
    84  		Description: "the server send response body size, unit byte",
    85  		Labels:      nil,
    86  	})
    87  	_ = monitor.AddMetric(&Metric{
    88  		Type:        Histogram,
    89  		Name:        metricRequestDuration,
    90  		Description: "the time server took to handle the request.",
    91  		Labels:      []string{"uri"},
    92  		Buckets:     m.reqDuration,
    93  	})
    94  	_ = monitor.AddMetric(&Metric{
    95  		Type:        Counter,
    96  		Name:        metricSlowRequest,
    97  		Description: fmt.Sprintf("the server handled slow requests counter, t=%d.", m.slowTime),
    98  		Labels:      []string{"uri", "method", "code"},
    99  	})
   100  }
   101  
   102  // monitorInterceptor as gin monitor middleware.
   103  func (m *Monitor) monitorInterceptor(ctx *gin.Context) {
   104  	if ctx.Request.URL.Path == m.metricPath {
   105  		ctx.Next()
   106  		return
   107  	}
   108  	startTime := time.Now()
   109  
   110  	// execute normal process.
   111  	ctx.Next()
   112  
   113  	// after request
   114  	m.ginMetricHandle(ctx, startTime)
   115  }
   116  
   117  func (m *Monitor) ginMetricHandle(ctx *gin.Context, start time.Time) {
   118  	r := ctx.Request
   119  	w := ctx.Writer
   120  
   121  	// set request total
   122  	_ = m.GetMetric(metricRequestTotal).Inc(nil)
   123  
   124  	// set uv
   125  	if clientIP := ctx.ClientIP(); !bloomFilter.Contains(clientIP) {
   126  		bloomFilter.Add(clientIP)
   127  		_ = m.GetMetric(metricRequestUVTotal).Inc(nil)
   128  	}
   129  
   130  	// set uri request total
   131  	_ = m.GetMetric(metricURIRequestTotal).Inc([]string{ctx.FullPath(), r.Method, strconv.Itoa(w.Status())})
   132  
   133  	// set request body size
   134  	// since r.ContentLength can be negative (in some occasions) guard the operation
   135  	if r.ContentLength >= 0 {
   136  		_ = m.GetMetric(metricRequestBody).Add(nil, float64(r.ContentLength))
   137  	}
   138  
   139  	// set slow request
   140  	latency := time.Since(start)
   141  	if int32(latency.Seconds()) > m.slowTime {
   142  		_ = m.GetMetric(metricSlowRequest).Inc([]string{ctx.FullPath(), r.Method, strconv.Itoa(w.Status())})
   143  	}
   144  
   145  	// set request duration
   146  	_ = m.GetMetric(metricRequestDuration).Observe([]string{ctx.FullPath()}, latency.Seconds())
   147  
   148  	// set response size
   149  	if w.Size() > 0 {
   150  		_ = m.GetMetric(metricResponseBody).Add(nil, float64(w.Size()))
   151  	}
   152  }
   153  

View as plain text