github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/metrics/metrics.go (about)

     1  package metrics
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"strconv"
     7  	"time"
     8  
     9  	"github.com/gramework/gramework"
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/prometheus/client_golang/prometheus/promhttp"
    12  )
    13  
    14  // Middleware handles metrics data
    15  type Middleware struct {
    16  	httpReqCounter *prometheus.CounterVec
    17  	reqDuration    *prometheus.HistogramVec
    18  }
    19  
    20  const (
    21  	typeHTTPS = "https"
    22  	typeHTTP  = "http"
    23  
    24  	millisecond = float64(time.Millisecond)
    25  
    26  	uvKey = "gramework.metrics.startTime"
    27  )
    28  
    29  var metricsPath = []byte("/metrics")
    30  
    31  // Register the middlewares
    32  func Register(app *gramework.App, serviceName ...string) error {
    33  	var m Middleware
    34  	name := os.Args[0]
    35  	if len(serviceName) > 0 {
    36  		name = serviceName[0]
    37  	}
    38  
    39  	hostname, err := os.Hostname()
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	m.httpReqCounter = prometheus.NewCounterVec(
    45  		prometheus.CounterOpts{
    46  			Name: "gramework_http_requests_total",
    47  			Help: "Total count of HTTP requests processed, partitioned by code, method, path and type (HTTP/HTTPS)",
    48  			ConstLabels: prometheus.Labels{
    49  				"service": name,
    50  				"node":    hostname,
    51  			},
    52  		},
    53  		[]string{"code", "method", "path", "type"},
    54  	)
    55  	if err = prometheus.Register(m.httpReqCounter); err != nil {
    56  		return err
    57  	}
    58  
    59  	m.reqDuration = prometheus.NewHistogramVec(
    60  		prometheus.HistogramOpts{
    61  			Name: "gramework_http_requests_duration_seconds",
    62  			Help: "Request processing duration, partitioned by code, method, path and type (HTTP/HTTPS)",
    63  			ConstLabels: prometheus.Labels{
    64  				"service": name,
    65  				"node":    hostname,
    66  			},
    67  		},
    68  		[]string{"code", "method", "path", "type"},
    69  	)
    70  
    71  	if err = prometheus.Register(m.reqDuration); err != nil {
    72  		return err
    73  	}
    74  
    75  	app.GET(string(metricsPath), gramework.NewGrameHandler(promhttp.Handler()))
    76  
    77  	if err = app.UsePre(m.startReq); err != nil {
    78  		return err
    79  	}
    80  
    81  	return app.UseAfterRequest(m.endReq)
    82  }
    83  
    84  func (m *Middleware) startReq(ctx *gramework.Context) {
    85  	if bytes.Equal(ctx.Path(), metricsPath) {
    86  		return
    87  	}
    88  	ctx.SetUserValue(uvKey, gramework.Nanotime())
    89  }
    90  
    91  func (m *Middleware) endReq(ctx *gramework.Context) {
    92  	if bytes.Equal(ctx.Path(), metricsPath) {
    93  		return
    94  	}
    95  
    96  	opts := []string{
    97  		strconv.FormatInt(int64(ctx.Response.StatusCode()), 10),
    98  		string(ctx.Method()),
    99  		string(ctx.Path()),
   100  		typeHTTP,
   101  	}
   102  
   103  	if ctx.IsTLS() {
   104  		opts[3] = typeHTTPS
   105  	}
   106  
   107  	m.httpReqCounter.WithLabelValues(opts...).Add(1)
   108  
   109  	startTime, _ := ctx.UserValue(uvKey).(int64)
   110  	duration := float64(gramework.Nanotime()-startTime) / millisecond
   111  
   112  	m.reqDuration.WithLabelValues(opts...).Observe(duration)
   113  }