/pkg/apiserver/metrics/metrics.go
Go | 245 lines | 195 code | 27 blank | 23 comment | 6 complexity | fe249db2ef463cc97dc7bcab045d118e MD5 | raw file
- /*
- Copyright 2015 The Kubernetes Authors All rights reserved.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package metrics
- import (
- "bufio"
- "net"
- "net/http"
- "strconv"
- "time"
- utilnet "k8s.io/kubernetes/pkg/util/net"
- "github.com/emicklei/go-restful"
- "github.com/prometheus/client_golang/prometheus"
- )
- var (
- // TODO(a-robinson): Add unit tests for the handling of these metrics once
- // the upstream library supports it.
- requestCounter = prometheus.NewCounterVec(
- prometheus.CounterOpts{
- Name: "apiserver_request_count",
- Help: "Counter of apiserver requests broken out for each verb, API resource, client, and HTTP response code.",
- },
- []string{"verb", "resource", "client", "code"},
- )
- requestLatencies = prometheus.NewHistogramVec(
- prometheus.HistogramOpts{
- Name: "apiserver_request_latencies",
- Help: "Response latency distribution in microseconds for each verb, resource and client.",
- // Use buckets ranging from 125 ms to 8 seconds.
- Buckets: prometheus.ExponentialBuckets(125000, 2.0, 7),
- },
- []string{"verb", "resource"},
- )
- requestLatenciesSummary = prometheus.NewSummaryVec(
- prometheus.SummaryOpts{
- Name: "apiserver_request_latencies_summary",
- Help: "Response latency summary in microseconds for each verb and resource.",
- // Make the sliding window of 1h.
- MaxAge: time.Hour,
- },
- []string{"verb", "resource"},
- )
- )
- // Register all metrics.
- func Register() {
- prometheus.MustRegister(requestCounter)
- prometheus.MustRegister(requestLatencies)
- prometheus.MustRegister(requestLatenciesSummary)
- }
- func Monitor(verb, resource *string, client string, httpCode int, reqStart time.Time) {
- elapsed := float64((time.Since(reqStart)) / time.Microsecond)
- requestCounter.WithLabelValues(*verb, *resource, client, codeToString(httpCode)).Inc()
- requestLatencies.WithLabelValues(*verb, *resource).Observe(elapsed)
- requestLatenciesSummary.WithLabelValues(*verb, *resource).Observe(elapsed)
- }
- func Reset() {
- requestCounter.Reset()
- requestLatencies.Reset()
- requestLatenciesSummary.Reset()
- }
- // InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps
- // the go-restful RouteFunction instead of a HandlerFunc
- func InstrumentRouteFunc(verb, resource string, routeFunc restful.RouteFunction) restful.RouteFunction {
- return restful.RouteFunction(func(request *restful.Request, response *restful.Response) {
- now := time.Now()
- delegate := &responseWriterDelegator{ResponseWriter: response.ResponseWriter}
- _, cn := response.ResponseWriter.(http.CloseNotifier)
- _, fl := response.ResponseWriter.(http.Flusher)
- _, hj := response.ResponseWriter.(http.Hijacker)
- var rw http.ResponseWriter
- if cn && fl && hj {
- rw = &fancyResponseWriterDelegator{delegate}
- } else {
- rw = delegate
- }
- response.ResponseWriter = rw
- routeFunc(request, response)
- Monitor(&verb, &resource, utilnet.GetHTTPClient(request.Request), delegate.status, now)
- })
- }
- type responseWriterDelegator struct {
- http.ResponseWriter
- status int
- written int64
- wroteHeader bool
- }
- func (r *responseWriterDelegator) WriteHeader(code int) {
- r.status = code
- r.wroteHeader = true
- r.ResponseWriter.WriteHeader(code)
- }
- func (r *responseWriterDelegator) Write(b []byte) (int, error) {
- if !r.wroteHeader {
- r.WriteHeader(http.StatusOK)
- }
- n, err := r.ResponseWriter.Write(b)
- r.written += int64(n)
- return n, err
- }
- type fancyResponseWriterDelegator struct {
- *responseWriterDelegator
- }
- func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
- return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
- }
- func (f *fancyResponseWriterDelegator) Flush() {
- f.ResponseWriter.(http.Flusher).Flush()
- }
- func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- return f.ResponseWriter.(http.Hijacker).Hijack()
- }
- // Small optimization over Itoa
- func codeToString(s int) string {
- switch s {
- case 100:
- return "100"
- case 101:
- return "101"
- case 200:
- return "200"
- case 201:
- return "201"
- case 202:
- return "202"
- case 203:
- return "203"
- case 204:
- return "204"
- case 205:
- return "205"
- case 206:
- return "206"
- case 300:
- return "300"
- case 301:
- return "301"
- case 302:
- return "302"
- case 304:
- return "304"
- case 305:
- return "305"
- case 307:
- return "307"
- case 400:
- return "400"
- case 401:
- return "401"
- case 402:
- return "402"
- case 403:
- return "403"
- case 404:
- return "404"
- case 405:
- return "405"
- case 406:
- return "406"
- case 407:
- return "407"
- case 408:
- return "408"
- case 409:
- return "409"
- case 410:
- return "410"
- case 411:
- return "411"
- case 412:
- return "412"
- case 413:
- return "413"
- case 414:
- return "414"
- case 415:
- return "415"
- case 416:
- return "416"
- case 417:
- return "417"
- case 418:
- return "418"
- case 500:
- return "500"
- case 501:
- return "501"
- case 502:
- return "502"
- case 503:
- return "503"
- case 504:
- return "504"
- case 505:
- return "505"
- case 428:
- return "428"
- case 429:
- return "429"
- case 431:
- return "431"
- case 511:
- return "511"
- default:
- return strconv.Itoa(s)
- }
- }