File: middleware.go

package info (click to toggle)
aptly 1.5.0%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 48,840 kB
  • sloc: python: 7,966; sh: 798; makefile: 81
file content (103 lines) | stat: -rw-r--r-- 3,337 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package api

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
	apiRequestsInFlightGauge = promauto.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "aptly_api_http_requests_in_flight",
			Help: "Number of concurrent HTTP api requests currently handled.",
		},
		[]string{"method", "path"},
	)
	apiRequestsTotalCounter = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "aptly_api_http_requests_total",
			Help: "Total number of api requests.",
		},
		[]string{"code", "method", "path"},
	)
	apiRequestSizeSummary = promauto.NewSummaryVec(
		prometheus.SummaryOpts{
			Name: "aptly_api_http_request_size_bytes",
			Help: "Api HTTP request size in bytes.",
		},
		[]string{"code", "method", "path"},
	)
	apiResponseSizeSummary = promauto.NewSummaryVec(
		prometheus.SummaryOpts{
			Name: "aptly_api_http_response_size_bytes",
			Help: "Api HTTP response size in bytes.",
		},
		[]string{"code", "method", "path"},
	)
	apiRequestsDurationSummary = promauto.NewSummaryVec(
		prometheus.SummaryOpts{
			Name: "aptly_api_http_request_duration_seconds",
			Help: "Duration of api requests in seconds.",
		},
		[]string{"code", "method", "path"},
	)
)

// Only use base path as label value (e.g.: /api/repos) because of time series cardinality
// See https://prometheus.io/docs/practices/naming/#labels
func getBasePath(c *gin.Context) string {
	return fmt.Sprintf("%s%s", getURLSegment(c.Request.URL.Path, 0), getURLSegment(c.Request.URL.Path, 1))
}

func getURLSegment(url string, idx int) string {
	var urlSegments = strings.Split(url, "/")

	// Remove segment at index 0 because it's an empty string
	var segmentAtIndex = urlSegments[1:cap(urlSegments)][idx]
	return fmt.Sprintf("/%s", segmentAtIndex)
}

func instrumentHandlerInFlight(g *prometheus.GaugeVec, pathFunc func(*gin.Context) string) func(*gin.Context) {
	return func(c *gin.Context) {
		g.WithLabelValues(c.Request.Method, pathFunc(c)).Inc()
		defer g.WithLabelValues(c.Request.Method, pathFunc(c)).Dec()
		c.Next()
	}
}

func instrumentHandlerCounter(counter *prometheus.CounterVec, pathFunc func(*gin.Context) string) func(*gin.Context) {
	return func(c *gin.Context) {
		c.Next()
		counter.WithLabelValues(strconv.Itoa(c.Writer.Status()), c.Request.Method, pathFunc(c)).Inc()
	}
}

func instrumentHandlerRequestSize(obs prometheus.ObserverVec, pathFunc func(*gin.Context) string) func(*gin.Context) {
	return func(c *gin.Context) {
		c.Next()
		obs.WithLabelValues(strconv.Itoa(c.Writer.Status()), c.Request.Method, pathFunc(c)).Observe(float64(c.Request.ContentLength))
	}
}

func instrumentHandlerResponseSize(obs prometheus.ObserverVec, pathFunc func(*gin.Context) string) func(*gin.Context) {
	return func(c *gin.Context) {
		c.Next()
		var responseSize = math.Max(float64(c.Writer.Size()), 0)
		obs.WithLabelValues(strconv.Itoa(c.Writer.Status()), c.Request.Method, pathFunc(c)).Observe(responseSize)
	}
}

func instrumentHandlerDuration(obs prometheus.ObserverVec, pathFunc func(*gin.Context) string) func(*gin.Context) {
	return func(c *gin.Context) {
		now := time.Now()
		c.Next()
		obs.WithLabelValues(strconv.Itoa(c.Writer.Status()), c.Request.Method, pathFunc(c)).Observe(time.Since(now).Seconds())
	}
}