http.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // Copyright 2014 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 prometheus
  14. import (
  15. "bufio"
  16. "io"
  17. "net"
  18. "net/http"
  19. "strconv"
  20. "strings"
  21. "time"
  22. )
  23. var instLabels = []string{"method", "code"}
  24. type nower interface {
  25. Now() time.Time
  26. }
  27. type nowFunc func() time.Time
  28. func (n nowFunc) Now() time.Time {
  29. return n()
  30. }
  31. var now nower = nowFunc(func() time.Time {
  32. return time.Now()
  33. })
  34. func nowSeries(t ...time.Time) nower {
  35. return nowFunc(func() time.Time {
  36. defer func() {
  37. t = t[1:]
  38. }()
  39. return t[0]
  40. })
  41. }
  42. // InstrumentHandler wraps the given HTTP handler for instrumentation. It
  43. // registers four metric collectors (if not already done) and reports HTTP
  44. // metrics to the (newly or already) registered collectors: http_requests_total
  45. // (CounterVec), http_request_duration_microseconds (Summary),
  46. // http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each
  47. // has a constant label named "handler" with the provided handlerName as
  48. // value. http_requests_total is a metric vector partitioned by HTTP method
  49. // (label name "method") and HTTP status code (label name "code").
  50. func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
  51. return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
  52. }
  53. // InstrumentHandlerFunc wraps the given function for instrumentation. It
  54. // otherwise works in the same way as InstrumentHandler.
  55. func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
  56. return InstrumentHandlerFuncWithOpts(
  57. SummaryOpts{
  58. Subsystem: "http",
  59. ConstLabels: Labels{"handler": handlerName},
  60. },
  61. handlerFunc,
  62. )
  63. }
  64. // InstrumentHandlerWithOpts works like InstrumentHandler but provides more
  65. // flexibility (at the cost of a more complex call syntax). As
  66. // InstrumentHandler, this function registers four metric collectors, but it
  67. // uses the provided SummaryOpts to create them. However, the fields "Name" and
  68. // "Help" in the SummaryOpts are ignored. "Name" is replaced by
  69. // "requests_total", "request_duration_microseconds", "request_size_bytes", and
  70. // "response_size_bytes", respectively. "Help" is replaced by an appropriate
  71. // help string. The names of the variable labels of the http_requests_total
  72. // CounterVec are "method" (get, post, etc.), and "code" (HTTP status code).
  73. //
  74. // If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
  75. // behavior of InstrumentHandler:
  76. //
  77. // prometheus.InstrumentHandlerWithOpts(
  78. // prometheus.SummaryOpts{
  79. // Subsystem: "http",
  80. // ConstLabels: prometheus.Labels{"handler": handlerName},
  81. // },
  82. // handler,
  83. // )
  84. //
  85. // Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
  86. // cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
  87. // and all its fields are set to the equally named fields in the provided
  88. // SummaryOpts.
  89. func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
  90. return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
  91. }
  92. // InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides
  93. // more flexibility (at the cost of a more complex call syntax). See
  94. // InstrumentHandlerWithOpts for details how the provided SummaryOpts are used.
  95. func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
  96. reqCnt := NewCounterVec(
  97. CounterOpts{
  98. Namespace: opts.Namespace,
  99. Subsystem: opts.Subsystem,
  100. Name: "requests_total",
  101. Help: "Total number of HTTP requests made.",
  102. ConstLabels: opts.ConstLabels,
  103. },
  104. instLabels,
  105. )
  106. opts.Name = "request_duration_microseconds"
  107. opts.Help = "The HTTP request latencies in microseconds."
  108. reqDur := NewSummary(opts)
  109. opts.Name = "request_size_bytes"
  110. opts.Help = "The HTTP request sizes in bytes."
  111. reqSz := NewSummary(opts)
  112. opts.Name = "response_size_bytes"
  113. opts.Help = "The HTTP response sizes in bytes."
  114. resSz := NewSummary(opts)
  115. regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
  116. regReqDur := MustRegisterOrGet(reqDur).(Summary)
  117. regReqSz := MustRegisterOrGet(reqSz).(Summary)
  118. regResSz := MustRegisterOrGet(resSz).(Summary)
  119. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  120. now := time.Now()
  121. delegate := &responseWriterDelegator{ResponseWriter: w}
  122. out := make(chan int)
  123. urlLen := 0
  124. if r.URL != nil {
  125. urlLen = len(r.URL.String())
  126. }
  127. go computeApproximateRequestSize(r, out, urlLen)
  128. _, cn := w.(http.CloseNotifier)
  129. _, fl := w.(http.Flusher)
  130. _, hj := w.(http.Hijacker)
  131. _, rf := w.(io.ReaderFrom)
  132. var rw http.ResponseWriter
  133. if cn && fl && hj && rf {
  134. rw = &fancyResponseWriterDelegator{delegate}
  135. } else {
  136. rw = delegate
  137. }
  138. handlerFunc(rw, r)
  139. elapsed := float64(time.Since(now)) / float64(time.Microsecond)
  140. method := sanitizeMethod(r.Method)
  141. code := sanitizeCode(delegate.status)
  142. regReqCnt.WithLabelValues(method, code).Inc()
  143. regReqDur.Observe(elapsed)
  144. regResSz.Observe(float64(delegate.written))
  145. regReqSz.Observe(float64(<-out))
  146. })
  147. }
  148. func computeApproximateRequestSize(r *http.Request, out chan int, s int) {
  149. s += len(r.Method)
  150. s += len(r.Proto)
  151. for name, values := range r.Header {
  152. s += len(name)
  153. for _, value := range values {
  154. s += len(value)
  155. }
  156. }
  157. s += len(r.Host)
  158. // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
  159. if r.ContentLength != -1 {
  160. s += int(r.ContentLength)
  161. }
  162. out <- s
  163. }
  164. type responseWriterDelegator struct {
  165. http.ResponseWriter
  166. handler, method string
  167. status int
  168. written int64
  169. wroteHeader bool
  170. }
  171. func (r *responseWriterDelegator) WriteHeader(code int) {
  172. r.status = code
  173. r.wroteHeader = true
  174. r.ResponseWriter.WriteHeader(code)
  175. }
  176. func (r *responseWriterDelegator) Write(b []byte) (int, error) {
  177. if !r.wroteHeader {
  178. r.WriteHeader(http.StatusOK)
  179. }
  180. n, err := r.ResponseWriter.Write(b)
  181. r.written += int64(n)
  182. return n, err
  183. }
  184. type fancyResponseWriterDelegator struct {
  185. *responseWriterDelegator
  186. }
  187. func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
  188. return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
  189. }
  190. func (f *fancyResponseWriterDelegator) Flush() {
  191. f.ResponseWriter.(http.Flusher).Flush()
  192. }
  193. func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  194. return f.ResponseWriter.(http.Hijacker).Hijack()
  195. }
  196. func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) {
  197. if !f.wroteHeader {
  198. f.WriteHeader(http.StatusOK)
  199. }
  200. n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r)
  201. f.written += n
  202. return n, err
  203. }
  204. func sanitizeMethod(m string) string {
  205. switch m {
  206. case "GET", "get":
  207. return "get"
  208. case "PUT", "put":
  209. return "put"
  210. case "HEAD", "head":
  211. return "head"
  212. case "POST", "post":
  213. return "post"
  214. case "DELETE", "delete":
  215. return "delete"
  216. case "CONNECT", "connect":
  217. return "connect"
  218. case "OPTIONS", "options":
  219. return "options"
  220. case "NOTIFY", "notify":
  221. return "notify"
  222. default:
  223. return strings.ToLower(m)
  224. }
  225. }
  226. func sanitizeCode(s int) string {
  227. switch s {
  228. case 100:
  229. return "100"
  230. case 101:
  231. return "101"
  232. case 200:
  233. return "200"
  234. case 201:
  235. return "201"
  236. case 202:
  237. return "202"
  238. case 203:
  239. return "203"
  240. case 204:
  241. return "204"
  242. case 205:
  243. return "205"
  244. case 206:
  245. return "206"
  246. case 300:
  247. return "300"
  248. case 301:
  249. return "301"
  250. case 302:
  251. return "302"
  252. case 304:
  253. return "304"
  254. case 305:
  255. return "305"
  256. case 307:
  257. return "307"
  258. case 400:
  259. return "400"
  260. case 401:
  261. return "401"
  262. case 402:
  263. return "402"
  264. case 403:
  265. return "403"
  266. case 404:
  267. return "404"
  268. case 405:
  269. return "405"
  270. case 406:
  271. return "406"
  272. case 407:
  273. return "407"
  274. case 408:
  275. return "408"
  276. case 409:
  277. return "409"
  278. case 410:
  279. return "410"
  280. case 411:
  281. return "411"
  282. case 412:
  283. return "412"
  284. case 413:
  285. return "413"
  286. case 414:
  287. return "414"
  288. case 415:
  289. return "415"
  290. case 416:
  291. return "416"
  292. case 417:
  293. return "417"
  294. case 418:
  295. return "418"
  296. case 500:
  297. return "500"
  298. case 501:
  299. return "501"
  300. case 502:
  301. return "502"
  302. case 503:
  303. return "503"
  304. case 504:
  305. return "504"
  306. case 505:
  307. return "505"
  308. case 428:
  309. return "428"
  310. case 429:
  311. return "429"
  312. case 431:
  313. return "431"
  314. case 511:
  315. return "511"
  316. default:
  317. return strconv.Itoa(s)
  318. }
  319. }