|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- // Copyright 2017 The Prometheus 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 promhttp
-
- import (
- "crypto/tls"
- "net/http"
- "net/http/httptrace"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- )
-
- // The RoundTripperFunc type is an adapter to allow the use of ordinary
- // functions as RoundTrippers. If f is a function with the appropriate
- // signature, RountTripperFunc(f) is a RoundTripper that calls f.
- type RoundTripperFunc func(req *http.Request) (*http.Response, error)
-
- // RoundTrip implements the RoundTripper interface.
- func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
- return rt(r)
- }
-
- // InstrumentRoundTripperInFlight is a middleware that wraps the provided
- // http.RoundTripper. It sets the provided prometheus.Gauge to the number of
- // requests currently handled by the wrapped http.RoundTripper.
- //
- // See the example for ExampleInstrumentRoundTripperDuration for example usage.
- func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
- return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
- gauge.Inc()
- defer gauge.Dec()
- return next.RoundTrip(r)
- })
- }
-
- // InstrumentRoundTripperCounter is a middleware that wraps the provided
- // http.RoundTripper to observe the request result with the provided CounterVec.
- // The CounterVec must have zero, one, or two non-const non-curried labels. For
- // those, the only allowed label names are "code" and "method". The function
- // panics otherwise. Partitioning of the CounterVec happens by HTTP status code
- // and/or HTTP method if the respective instance label names are present in the
- // CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
- //
- // If the wrapped RoundTripper panics or returns a non-nil error, the Counter
- // is not incremented.
- //
- // See the example for ExampleInstrumentRoundTripperDuration for example usage.
- func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
- code, method := checkLabels(counter)
-
- return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
- resp, err := next.RoundTrip(r)
- if err == nil {
- counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
- }
- return resp, err
- })
- }
-
- // InstrumentRoundTripperDuration is a middleware that wraps the provided
- // http.RoundTripper to observe the request duration with the provided
- // ObserverVec. The ObserverVec must have zero, one, or two non-const
- // non-curried labels. For those, the only allowed label names are "code" and
- // "method". The function panics otherwise. The Observe method of the Observer
- // in the ObserverVec is called with the request duration in
- // seconds. Partitioning happens by HTTP status code and/or HTTP method if the
- // respective instance label names are present in the ObserverVec. For
- // unpartitioned observations, use an ObserverVec with zero labels. Note that
- // partitioning of Histograms is expensive and should be used judiciously.
- //
- // If the wrapped RoundTripper panics or returns a non-nil error, no values are
- // reported.
- //
- // Note that this method is only guaranteed to never observe negative durations
- // if used with Go1.9+.
- func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
- code, method := checkLabels(obs)
-
- return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
- start := time.Now()
- resp, err := next.RoundTrip(r)
- if err == nil {
- obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
- }
- return resp, err
- })
- }
-
- // InstrumentTrace is used to offer flexibility in instrumenting the available
- // httptrace.ClientTrace hook functions. Each function is passed a float64
- // representing the time in seconds since the start of the http request. A user
- // may choose to use separately buckets Histograms, or implement custom
- // instance labels on a per function basis.
- type InstrumentTrace struct {
- GotConn func(float64)
- PutIdleConn func(float64)
- GotFirstResponseByte func(float64)
- Got100Continue func(float64)
- DNSStart func(float64)
- DNSDone func(float64)
- ConnectStart func(float64)
- ConnectDone func(float64)
- TLSHandshakeStart func(float64)
- TLSHandshakeDone func(float64)
- WroteHeaders func(float64)
- Wait100Continue func(float64)
- WroteRequest func(float64)
- }
-
- // InstrumentRoundTripperTrace is a middleware that wraps the provided
- // RoundTripper and reports times to hook functions provided in the
- // InstrumentTrace struct. Hook functions that are not present in the provided
- // InstrumentTrace struct are ignored. Times reported to the hook functions are
- // time since the start of the request. Only with Go1.9+, those times are
- // guaranteed to never be negative. (Earlier Go versions are not using a
- // monotonic clock.) Note that partitioning of Histograms is expensive and
- // should be used judiciously.
- //
- // For hook functions that receive an error as an argument, no observations are
- // made in the event of a non-nil error value.
- //
- // See the example for ExampleInstrumentRoundTripperDuration for example usage.
- func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc {
- return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
- start := time.Now()
-
- trace := &httptrace.ClientTrace{
- GotConn: func(_ httptrace.GotConnInfo) {
- if it.GotConn != nil {
- it.GotConn(time.Since(start).Seconds())
- }
- },
- PutIdleConn: func(err error) {
- if err != nil {
- return
- }
- if it.PutIdleConn != nil {
- it.PutIdleConn(time.Since(start).Seconds())
- }
- },
- DNSStart: func(_ httptrace.DNSStartInfo) {
- if it.DNSStart != nil {
- it.DNSStart(time.Since(start).Seconds())
- }
- },
- DNSDone: func(_ httptrace.DNSDoneInfo) {
- if it.DNSDone != nil {
- it.DNSDone(time.Since(start).Seconds())
- }
- },
- ConnectStart: func(_, _ string) {
- if it.ConnectStart != nil {
- it.ConnectStart(time.Since(start).Seconds())
- }
- },
- ConnectDone: func(_, _ string, err error) {
- if err != nil {
- return
- }
- if it.ConnectDone != nil {
- it.ConnectDone(time.Since(start).Seconds())
- }
- },
- GotFirstResponseByte: func() {
- if it.GotFirstResponseByte != nil {
- it.GotFirstResponseByte(time.Since(start).Seconds())
- }
- },
- Got100Continue: func() {
- if it.Got100Continue != nil {
- it.Got100Continue(time.Since(start).Seconds())
- }
- },
- TLSHandshakeStart: func() {
- if it.TLSHandshakeStart != nil {
- it.TLSHandshakeStart(time.Since(start).Seconds())
- }
- },
- TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
- if err != nil {
- return
- }
- if it.TLSHandshakeDone != nil {
- it.TLSHandshakeDone(time.Since(start).Seconds())
- }
- },
- WroteHeaders: func() {
- if it.WroteHeaders != nil {
- it.WroteHeaders(time.Since(start).Seconds())
- }
- },
- Wait100Continue: func() {
- if it.Wait100Continue != nil {
- it.Wait100Continue(time.Since(start).Seconds())
- }
- },
- WroteRequest: func(_ httptrace.WroteRequestInfo) {
- if it.WroteRequest != nil {
- it.WroteRequest(time.Since(start).Seconds())
- }
- },
- }
- r = r.WithContext(httptrace.WithClientTrace(r.Context(), trace))
-
- return next.RoundTrip(r)
- })
- }
|