123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /*
- Copyright 2015 The Kubernetes Authors.
- 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 contentType and code.",
- },
- []string{"verb", "resource", "client", "contentType", "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, contentType string, httpCode int, reqStart time.Time) {
- elapsed := float64((time.Since(reqStart)) / time.Microsecond)
- requestCounter.WithLabelValues(*verb, *resource, client, contentType, 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), rw.Header().Get("Content-Type"), 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)
- }
- }
|