@@ -3,6 +3,7 @@ package httpmw
3
3
import (
4
4
"net/http"
5
5
"strconv"
6
+ "strings"
6
7
"time"
7
8
8
9
"github.com/go-chi/chi/v5"
@@ -22,18 +23,18 @@ func Prometheus(register prometheus.Registerer) func(http.Handler) http.Handler
22
23
Name : "requests_processed_total" ,
23
24
Help : "The total number of processed API requests" ,
24
25
}, []string {"code" , "method" , "path" })
25
- requestsConcurrent := factory .NewGauge (prometheus.GaugeOpts {
26
+ requestsConcurrent := factory .NewGaugeVec (prometheus.GaugeOpts {
26
27
Namespace : "coderd" ,
27
28
Subsystem : "api" ,
28
29
Name : "concurrent_requests" ,
29
30
Help : "The number of concurrent API requests." ,
30
- })
31
- websocketsConcurrent := factory .NewGauge (prometheus.GaugeOpts {
31
+ }, [] string { "method" , "path" } )
32
+ websocketsConcurrent := factory .NewGaugeVec (prometheus.GaugeOpts {
32
33
Namespace : "coderd" ,
33
34
Subsystem : "api" ,
34
35
Name : "concurrent_websockets" ,
35
36
Help : "The total number of concurrent API websockets." ,
36
- })
37
+ }, [] string { "path" } )
37
38
websocketsDist := factory .NewHistogramVec (prometheus.HistogramOpts {
38
39
Namespace : "coderd" ,
39
40
Subsystem : "api" ,
@@ -61,7 +62,6 @@ func Prometheus(register prometheus.Registerer) func(http.Handler) http.Handler
61
62
var (
62
63
start = time .Now ()
63
64
method = r .Method
64
- rctx = chi .RouteContext (r .Context ())
65
65
)
66
66
67
67
sw , ok := w .(* tracing.StatusWriter )
@@ -72,24 +72,25 @@ func Prometheus(register prometheus.Registerer) func(http.Handler) http.Handler
72
72
var (
73
73
dist * prometheus.HistogramVec
74
74
distOpts []string
75
+ path = getRoutePattern (r )
75
76
)
77
+
76
78
// We want to count WebSockets separately.
77
79
if httpapi .IsWebsocketUpgrade (r ) {
78
- websocketsConcurrent .Inc ()
79
- defer websocketsConcurrent .Dec ()
80
+ websocketsConcurrent .WithLabelValues ( path ). Inc ()
81
+ defer websocketsConcurrent .WithLabelValues ( path ). Dec ()
80
82
81
83
dist = websocketsDist
82
84
} else {
83
- requestsConcurrent .Inc ()
84
- defer requestsConcurrent .Dec ()
85
+ requestsConcurrent .WithLabelValues ( method , path ). Inc ()
86
+ defer requestsConcurrent .WithLabelValues ( method , path ). Dec ()
85
87
86
88
dist = requestsDist
87
89
distOpts = []string {method }
88
90
}
89
91
90
92
next .ServeHTTP (w , r )
91
93
92
- path := rctx .RoutePattern ()
93
94
distOpts = append (distOpts , path )
94
95
statusStr := strconv .Itoa (sw .Status )
95
96
@@ -98,3 +99,34 @@ func Prometheus(register prometheus.Registerer) func(http.Handler) http.Handler
98
99
})
99
100
}
100
101
}
102
+
103
+ func getRoutePattern (r * http.Request ) string {
104
+ rctx := chi .RouteContext (r .Context ())
105
+ if rctx == nil {
106
+ return ""
107
+ }
108
+
109
+ if pattern := rctx .RoutePattern (); pattern != "" {
110
+ // Pattern is already available
111
+ return pattern
112
+ }
113
+
114
+ routePath := r .URL .Path
115
+ if r .URL .RawPath != "" {
116
+ routePath = r .URL .RawPath
117
+ }
118
+
119
+ tctx := chi .NewRouteContext ()
120
+ routes := rctx .Routes
121
+ if routes != nil && ! routes .Match (tctx , r .Method , routePath ) {
122
+ // No matching pattern. /api/* requests will be matched as "UNKNOWN"
123
+ // All other ones will be matched as "STATIC".
124
+ if strings .HasPrefix (routePath , "/api/" ) {
125
+ return "UNKNOWN"
126
+ }
127
+ return "STATIC"
128
+ }
129
+
130
+ // tctx has the updated pattern, since Match mutates it
131
+ return tctx .RoutePattern ()
132
+ }
0 commit comments