Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

236 lignes
8.4 KiB

  1. // Copyright 2017 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package promhttp
  14. import (
  15. "crypto/tls"
  16. "net/http"
  17. "net/http/httptrace"
  18. "time"
  19. "github.com/prometheus/client_golang/prometheus"
  20. )
  21. // The RoundTripperFunc type is an adapter to allow the use of ordinary
  22. // functions as RoundTrippers. If f is a function with the appropriate
  23. // signature, RountTripperFunc(f) is a RoundTripper that calls f.
  24. type RoundTripperFunc func(req *http.Request) (*http.Response, error)
  25. // RoundTrip implements the RoundTripper interface.
  26. func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
  27. return rt(r)
  28. }
  29. // InstrumentRoundTripperInFlight is a middleware that wraps the provided
  30. // http.RoundTripper. It sets the provided prometheus.Gauge to the number of
  31. // requests currently handled by the wrapped http.RoundTripper.
  32. //
  33. // See the example for ExampleInstrumentRoundTripperDuration for example usage.
  34. func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
  35. return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
  36. gauge.Inc()
  37. defer gauge.Dec()
  38. return next.RoundTrip(r)
  39. })
  40. }
  41. // InstrumentRoundTripperCounter is a middleware that wraps the provided
  42. // http.RoundTripper to observe the request result with the provided CounterVec.
  43. // The CounterVec must have zero, one, or two non-const non-curried labels. For
  44. // those, the only allowed label names are "code" and "method". The function
  45. // panics otherwise. For the "method" label a predefined default label value set
  46. // is used to filter given values. Values besides predefined values will count
  47. // as `unknown` method.`WithExtraMethods` can be used to add more
  48. // methods to the set. Partitioning of the CounterVec happens by HTTP status code
  49. // and/or HTTP method if the respective instance label names are present in the
  50. // CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
  51. //
  52. // If the wrapped RoundTripper panics or returns a non-nil error, the Counter
  53. // is not incremented.
  54. //
  55. // See the example for ExampleInstrumentRoundTripperDuration for example usage.
  56. func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc {
  57. rtOpts := &option{}
  58. for _, o := range opts {
  59. o(rtOpts)
  60. }
  61. code, method := checkLabels(counter)
  62. return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
  63. resp, err := next.RoundTrip(r)
  64. if err == nil {
  65. counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc()
  66. }
  67. return resp, err
  68. })
  69. }
  70. // InstrumentRoundTripperDuration is a middleware that wraps the provided
  71. // http.RoundTripper to observe the request duration with the provided
  72. // ObserverVec. The ObserverVec must have zero, one, or two non-const
  73. // non-curried labels. For those, the only allowed label names are "code" and
  74. // "method". The function panics otherwise. For the "method" label a predefined
  75. // default label value set is used to filter given values. Values besides
  76. // predefined values will count as `unknown` method. `WithExtraMethods`
  77. // can be used to add more methods to the set. The Observe method of the Observer
  78. // in the ObserverVec is called with the request duration in
  79. // seconds. Partitioning happens by HTTP status code and/or HTTP method if the
  80. // respective instance label names are present in the ObserverVec. For
  81. // unpartitioned observations, use an ObserverVec with zero labels. Note that
  82. // partitioning of Histograms is expensive and should be used judiciously.
  83. //
  84. // If the wrapped RoundTripper panics or returns a non-nil error, no values are
  85. // reported.
  86. //
  87. // Note that this method is only guaranteed to never observe negative durations
  88. // if used with Go1.9+.
  89. func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper, opts ...Option) RoundTripperFunc {
  90. rtOpts := &option{}
  91. for _, o := range opts {
  92. o(rtOpts)
  93. }
  94. code, method := checkLabels(obs)
  95. return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
  96. start := time.Now()
  97. resp, err := next.RoundTrip(r)
  98. if err == nil {
  99. obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Observe(time.Since(start).Seconds())
  100. }
  101. return resp, err
  102. })
  103. }
  104. // InstrumentTrace is used to offer flexibility in instrumenting the available
  105. // httptrace.ClientTrace hook functions. Each function is passed a float64
  106. // representing the time in seconds since the start of the http request. A user
  107. // may choose to use separately buckets Histograms, or implement custom
  108. // instance labels on a per function basis.
  109. type InstrumentTrace struct {
  110. GotConn func(float64)
  111. PutIdleConn func(float64)
  112. GotFirstResponseByte func(float64)
  113. Got100Continue func(float64)
  114. DNSStart func(float64)
  115. DNSDone func(float64)
  116. ConnectStart func(float64)
  117. ConnectDone func(float64)
  118. TLSHandshakeStart func(float64)
  119. TLSHandshakeDone func(float64)
  120. WroteHeaders func(float64)
  121. Wait100Continue func(float64)
  122. WroteRequest func(float64)
  123. }
  124. // InstrumentRoundTripperTrace is a middleware that wraps the provided
  125. // RoundTripper and reports times to hook functions provided in the
  126. // InstrumentTrace struct. Hook functions that are not present in the provided
  127. // InstrumentTrace struct are ignored. Times reported to the hook functions are
  128. // time since the start of the request. Only with Go1.9+, those times are
  129. // guaranteed to never be negative. (Earlier Go versions are not using a
  130. // monotonic clock.) Note that partitioning of Histograms is expensive and
  131. // should be used judiciously.
  132. //
  133. // For hook functions that receive an error as an argument, no observations are
  134. // made in the event of a non-nil error value.
  135. //
  136. // See the example for ExampleInstrumentRoundTripperDuration for example usage.
  137. func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc {
  138. return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
  139. start := time.Now()
  140. trace := &httptrace.ClientTrace{
  141. GotConn: func(_ httptrace.GotConnInfo) {
  142. if it.GotConn != nil {
  143. it.GotConn(time.Since(start).Seconds())
  144. }
  145. },
  146. PutIdleConn: func(err error) {
  147. if err != nil {
  148. return
  149. }
  150. if it.PutIdleConn != nil {
  151. it.PutIdleConn(time.Since(start).Seconds())
  152. }
  153. },
  154. DNSStart: func(_ httptrace.DNSStartInfo) {
  155. if it.DNSStart != nil {
  156. it.DNSStart(time.Since(start).Seconds())
  157. }
  158. },
  159. DNSDone: func(_ httptrace.DNSDoneInfo) {
  160. if it.DNSDone != nil {
  161. it.DNSDone(time.Since(start).Seconds())
  162. }
  163. },
  164. ConnectStart: func(_, _ string) {
  165. if it.ConnectStart != nil {
  166. it.ConnectStart(time.Since(start).Seconds())
  167. }
  168. },
  169. ConnectDone: func(_, _ string, err error) {
  170. if err != nil {
  171. return
  172. }
  173. if it.ConnectDone != nil {
  174. it.ConnectDone(time.Since(start).Seconds())
  175. }
  176. },
  177. GotFirstResponseByte: func() {
  178. if it.GotFirstResponseByte != nil {
  179. it.GotFirstResponseByte(time.Since(start).Seconds())
  180. }
  181. },
  182. Got100Continue: func() {
  183. if it.Got100Continue != nil {
  184. it.Got100Continue(time.Since(start).Seconds())
  185. }
  186. },
  187. TLSHandshakeStart: func() {
  188. if it.TLSHandshakeStart != nil {
  189. it.TLSHandshakeStart(time.Since(start).Seconds())
  190. }
  191. },
  192. TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
  193. if err != nil {
  194. return
  195. }
  196. if it.TLSHandshakeDone != nil {
  197. it.TLSHandshakeDone(time.Since(start).Seconds())
  198. }
  199. },
  200. WroteHeaders: func() {
  201. if it.WroteHeaders != nil {
  202. it.WroteHeaders(time.Since(start).Seconds())
  203. }
  204. },
  205. Wait100Continue: func() {
  206. if it.Wait100Continue != nil {
  207. it.Wait100Continue(time.Since(start).Seconds())
  208. }
  209. },
  210. WroteRequest: func(_ httptrace.WroteRequestInfo) {
  211. if it.WroteRequest != nil {
  212. it.WroteRequest(time.Since(start).Seconds())
  213. }
  214. },
  215. }
  216. r = r.WithContext(httptrace.WithClientTrace(r.Context(), trace))
  217. return next.RoundTrip(r)
  218. })
  219. }