@@ -27,7 +27,7 @@ import (
27
27
"time"
28
28
29
29
utilnet "k8s.io/apimachinery/pkg/util/net"
30
- //utilruntime "k8s.io/apimachinery /pkg/util/runtime "
30
+ "k8s.io/apiserver /pkg/endpoints/request "
31
31
32
32
"github.com/emicklei/go-restful"
33
33
"github.com/prometheus/client_golang/prometheus"
43
43
},
44
44
[]string {"verb" , "resource" , "subresource" , "scope" , "client" , "contentType" , "code" },
45
45
)
46
+ longRunningRequestGauge = prometheus .NewGaugeVec (
47
+ prometheus.GaugeOpts {
48
+ Name : "apiserver_longrunning_gauge" ,
49
+ Help : "Gauge of all active long-running apiserver requests broken out by verb, API resource, and scope. Not all requests are tracked this way." ,
50
+ },
51
+ []string {"verb" , "resource" , "subresource" , "scope" },
52
+ )
46
53
requestLatencies = prometheus .NewHistogramVec (
47
54
prometheus.HistogramOpts {
48
55
Name : "apiserver_request_latencies" ,
@@ -77,43 +84,59 @@ var (
77
84
// Register all metrics.
78
85
func Register () {
79
86
prometheus .MustRegister (requestCounter )
87
+ prometheus .MustRegister (longRunningRequestGauge )
80
88
prometheus .MustRegister (requestLatencies )
81
89
prometheus .MustRegister (requestLatenciesSummary )
82
90
prometheus .MustRegister (responseSizes )
83
91
}
84
92
85
- // Monitor records a request to the apiserver endpoints that follow the Kubernetes API conventions. verb must be
86
- // uppercase to be backwards compatible with existing monitoring tooling.
87
- func Monitor (verb , resource , subresource , scope , client , contentType string , httpCode , respSize int , reqStart time.Time ) {
88
- elapsed := float64 ((time .Since (reqStart )) / time .Microsecond )
89
- requestCounter .WithLabelValues (verb , resource , subresource , scope , client , contentType , codeToString (httpCode )).Inc ()
90
- requestLatencies .WithLabelValues (verb , resource , subresource , scope ).Observe (elapsed )
91
- requestLatenciesSummary .WithLabelValues (verb , resource , subresource , scope ).Observe (elapsed )
92
- // We are only interested in response sizes of read requests.
93
- if verb == "GET" || verb == "LIST" {
94
- responseSizes .WithLabelValues (verb , resource , subresource , scope ).Observe (float64 (respSize ))
93
+ // Record records a single request to the standard metrics endpoints. For use by handlers that perform their own
94
+ // processing. All API paths should use InstrumentRouteFunc implicitly. Use this instead of MonitorRequest if
95
+ // you already have a RequestInfo object.
96
+ func Record (req * http.Request , requestInfo * request.RequestInfo , contentType string , code int , responseSizeInBytes int , elapsed time.Duration ) {
97
+ if requestInfo == nil {
98
+ requestInfo = & request.RequestInfo {Verb : req .Method , Path : req .URL .Path }
99
+ }
100
+ scope := cleanScope (requestInfo )
101
+ if requestInfo .IsResourceRequest {
102
+ MonitorRequest (req , strings .ToUpper (requestInfo .Verb ), requestInfo .Resource , requestInfo .Subresource , contentType , scope , code , responseSizeInBytes , elapsed )
103
+ } else {
104
+ MonitorRequest (req , strings .ToUpper (requestInfo .Verb ), "" , requestInfo .Path , contentType , scope , code , responseSizeInBytes , elapsed )
95
105
}
96
106
}
97
107
98
- // MonitorRequest handles standard transformations for client and the reported verb and then invokes Monitor to record
99
- // a request. verb must be uppercase to be backwards compatible with existing monitoring tooling.
100
- func MonitorRequest (request * http.Request , verb , resource , subresource , scope , contentType string , httpCode , respSize int , reqStart time.Time ) {
101
- reportedVerb := verb
102
- if verb == "LIST" {
103
- // see apimachinery/pkg/runtime/conversion.go Convert_Slice_string_To_bool
104
- if values := request .URL .Query ()["watch" ]; len (values ) > 0 {
105
- if value := strings .ToLower (values [0 ]); value != "0" && value != "false" {
106
- reportedVerb = "WATCH"
107
- }
108
- }
108
+ // RecordLongRunning tracks the execution of a long running request against the API server. It provides an accurate count
109
+ // of the total number of open long running requests. requestInfo may be nil if the caller is not in the normal request flow.
110
+ func RecordLongRunning (req * http.Request , requestInfo * request.RequestInfo , fn func ()) {
111
+ if requestInfo == nil {
112
+ requestInfo = & request.RequestInfo {Verb : req .Method , Path : req .URL .Path }
109
113
}
110
- // normalize the legacy WATCHLIST to WATCH to ensure users aren't surprised by metrics
111
- if verb == "WATCHLIST" {
112
- reportedVerb = "WATCH"
114
+ var g prometheus.Gauge
115
+ scope := cleanScope (requestInfo )
116
+ reportedVerb := cleanVerb (strings .ToUpper (requestInfo .Verb ), req )
117
+ if requestInfo .IsResourceRequest {
118
+ g = longRunningRequestGauge .WithLabelValues (reportedVerb , requestInfo .Resource , requestInfo .Subresource , scope )
119
+ } else {
120
+ g = longRunningRequestGauge .WithLabelValues (reportedVerb , "" , requestInfo .Path , scope )
113
121
}
122
+ g .Inc ()
123
+ defer g .Dec ()
124
+ fn ()
125
+ }
114
126
115
- client := cleanUserAgent (utilnet .GetHTTPClient (request ))
116
- Monitor (reportedVerb , resource , subresource , scope , client , contentType , httpCode , respSize , reqStart )
127
+ // MonitorRequest handles standard transformations for client and the reported verb and then invokes Monitor to record
128
+ // a request. verb must be uppercase to be backwards compatible with existing monitoring tooling.
129
+ func MonitorRequest (req * http.Request , verb , resource , subresource , scope , contentType string , httpCode , respSize int , elapsed time.Duration ) {
130
+ reportedVerb := cleanVerb (verb , req )
131
+ client := cleanUserAgent (utilnet .GetHTTPClient (req ))
132
+ elapsedMicroseconds := float64 (elapsed / time .Microsecond )
133
+ requestCounter .WithLabelValues (reportedVerb , resource , subresource , scope , client , contentType , codeToString (httpCode )).Inc ()
134
+ requestLatencies .WithLabelValues (reportedVerb , resource , subresource , scope ).Observe (elapsedMicroseconds )
135
+ requestLatenciesSummary .WithLabelValues (reportedVerb , resource , subresource , scope ).Observe (elapsedMicroseconds )
136
+ // We are only interested in response sizes of read requests.
137
+ if verb == "GET" || verb == "LIST" {
138
+ responseSizes .WithLabelValues (reportedVerb , resource , subresource , scope ).Observe (float64 (respSize ))
139
+ }
117
140
}
118
141
119
142
func Reset () {
@@ -144,10 +167,41 @@ func InstrumentRouteFunc(verb, resource, subresource, scope string, routeFunc re
144
167
145
168
routeFunc (request , response )
146
169
147
- MonitorRequest (request .Request , verb , resource , subresource , scope , delegate .Header ().Get ("Content-Type" ), delegate .Status (), delegate .ContentLength (), now )
170
+ MonitorRequest (request .Request , verb , resource , subresource , scope , delegate .Header ().Get ("Content-Type" ), delegate .Status (), delegate .ContentLength (), time . Now (). Sub ( now ) )
148
171
})
149
172
}
150
173
174
+ func cleanScope (requestInfo * request.RequestInfo ) string {
175
+ if requestInfo .Namespace != "" {
176
+ return "namespace"
177
+ }
178
+ if requestInfo .Name != "" {
179
+ return "resource"
180
+ }
181
+ if requestInfo .IsResourceRequest {
182
+ return "cluster"
183
+ }
184
+ // this is the empty scope
185
+ return ""
186
+ }
187
+
188
+ func cleanVerb (verb string , request * http.Request ) string {
189
+ reportedVerb := verb
190
+ if verb == "LIST" {
191
+ // see apimachinery/pkg/runtime/conversion.go Convert_Slice_string_To_bool
192
+ if values := request .URL .Query ()["watch" ]; len (values ) > 0 {
193
+ if value := strings .ToLower (values [0 ]); value != "0" && value != "false" {
194
+ reportedVerb = "WATCH"
195
+ }
196
+ }
197
+ }
198
+ // normalize the legacy WATCHLIST to WATCH to ensure users aren't surprised by metrics
199
+ if verb == "WATCHLIST" {
200
+ reportedVerb = "WATCH"
201
+ }
202
+ return reportedVerb
203
+ }
204
+
151
205
func cleanUserAgent (ua string ) string {
152
206
// We collapse all "web browser"-type user agents into one "browser" to reduce metric cardinality.
153
207
if strings .HasPrefix (ua , "Mozilla/" ) {
0 commit comments