sponge/pkg/gin/middleware/metrics/metrics.go

141 lines
3.3 KiB
Go

package metrics
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
namespace = "gin"
labels = []string{"status", "path", "method"}
uptime = prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Name: "uptime",
Help: "HTTP service uptime.",
}, nil,
)
reqCount = prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Name: "http_request_count_total",
Help: "Total number of HTTP requests made.",
}, labels,
)
reqDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Name: "http_request_duration_seconds",
Help: "HTTP request latencies in seconds.",
}, labels,
)
reqSizeBytes = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: namespace,
Name: "http_request_size_bytes",
Help: "HTTP request sizes in bytes.",
}, labels,
)
respSizeBytes = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: namespace,
Name: "http_response_size_bytes",
Help: "HTTP response sizes in bytes.",
}, labels,
)
)
// init registers the prometheus metrics
func initPrometheus() {
prometheus.MustRegister(uptime, reqCount, reqDuration, reqSizeBytes, respSizeBytes)
go recordUptime()
}
// recordUptime increases service uptime per 30 second.
func recordUptime() {
for range time.Tick(time.Second * 30) {
uptime.WithLabelValues().Inc()
}
}
// calcRequestSize returns the size of request object.
func calcRequestSize(r *http.Request) float64 {
size := 0
if r.URL != nil {
size = len(r.URL.String())
}
size += len(r.Method)
size += len(r.Proto)
for name, values := range r.Header {
size += len(name)
for _, value := range values {
size += len(value)
}
}
size += len(r.Host)
// r.Form and r.MultipartForm are assumed to be included in r.URL.
if r.ContentLength != -1 {
size += int(r.ContentLength)
}
return float64(size)
}
// ------------------------------------------------------------------------------------------
// metricsHandler wrappers the standard http.Handler to gin.HandlerFunc
func metricsHandler() gin.HandlerFunc {
return func(c *gin.Context) {
handler := promhttp.Handler()
handler.ServeHTTP(c.Writer, c.Request)
}
}
// Metrics returns a gin.HandlerFunc for exporting some Web metrics
func Metrics(r *gin.Engine, opts ...Option) gin.HandlerFunc {
o := defaultOptions()
o.apply(opts...)
// init prometheus
initPrometheus()
r.GET(o.metricsPath, metricsHandler())
return func(c *gin.Context) {
start := time.Now()
c.Next()
ok := o.isIgnoreCodeStatus(c.Writer.Status()) ||
o.isIgnorePath(c.Request.URL.Path) ||
o.checkIgnoreMethod(c.Request.Method)
if ok {
return
}
// no response content will return -1
respSize := c.Writer.Size()
if respSize < 0 {
respSize = 0
}
lvs := []string{strconv.Itoa(c.Writer.Status()), c.Request.URL.Path, c.Request.Method}
reqCount.WithLabelValues(lvs...).Inc()
reqDuration.WithLabelValues(lvs...).Observe(time.Since(start).Seconds())
reqSizeBytes.WithLabelValues(lvs...).Observe(calcRequestSize(c.Request))
respSizeBytes.WithLabelValues(lvs...).Observe(float64(respSize))
}
}