1// Copyright (c) The Thanos Authors. 2// Licensed under the Apache License 2.0. 3 4package http 5 6import ( 7 "net/http" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/client_golang/prometheus/promhttp" 11) 12 13// InstrumentationMiddleware holds necessary metrics to instrument an http.Server 14// and provides necessary behaviors. 15type InstrumentationMiddleware interface { 16 // NewHandler wraps the given HTTP handler for instrumentation. 17 NewHandler(handlerName string, handler http.Handler) http.HandlerFunc 18} 19 20type nopInstrumentationMiddleware struct{} 21 22func (ins nopInstrumentationMiddleware) NewHandler(handlerName string, handler http.Handler) http.HandlerFunc { 23 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 24 handler.ServeHTTP(w, r) 25 }) 26} 27 28// NewNopInstrumentationMiddleware provides a InstrumentationMiddleware which does nothing. 29func NewNopInstrumentationMiddleware() InstrumentationMiddleware { 30 return nopInstrumentationMiddleware{} 31} 32 33type defaultInstrumentationMiddleware struct { 34 requestDuration *prometheus.HistogramVec 35 requestSize *prometheus.SummaryVec 36 requestsTotal *prometheus.CounterVec 37 responseSize *prometheus.SummaryVec 38} 39 40// NewInstrumentationMiddleware provides default InstrumentationMiddleware. 41func NewInstrumentationMiddleware(reg prometheus.Registerer) InstrumentationMiddleware { 42 ins := defaultInstrumentationMiddleware{ 43 requestDuration: prometheus.NewHistogramVec( 44 prometheus.HistogramOpts{ 45 Name: "http_request_duration_seconds", 46 Help: "Tracks the latencies for HTTP requests.", 47 Buckets: []float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}, 48 }, 49 []string{"code", "handler", "method"}, 50 ), 51 52 requestSize: prometheus.NewSummaryVec( 53 prometheus.SummaryOpts{ 54 Name: "http_request_size_bytes", 55 Help: "Tracks the size of HTTP requests.", 56 }, 57 []string{"code", "handler", "method"}, 58 ), 59 60 requestsTotal: prometheus.NewCounterVec( 61 prometheus.CounterOpts{ 62 Name: "http_requests_total", 63 Help: "Tracks the number of HTTP requests.", 64 }, []string{"code", "handler", "method"}, 65 ), 66 67 responseSize: prometheus.NewSummaryVec( 68 prometheus.SummaryOpts{ 69 Name: "http_response_size_bytes", 70 Help: "Tracks the size of HTTP responses.", 71 }, 72 []string{"code", "handler", "method"}, 73 ), 74 } 75 reg.MustRegister(ins.requestDuration, ins.requestSize, ins.requestsTotal, ins.responseSize) 76 return &ins 77} 78 79// NewHandler wraps the given HTTP handler for instrumentation. It 80// registers four metric collectors (if not already done) and reports HTTP 81// metrics to the (newly or already) registered collectors: http_requests_total 82// (CounterVec), http_request_duration_seconds (Histogram), 83// http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each 84// has a constant label named "handler" with the provided handlerName as 85// value. http_requests_total is a metric vector partitioned by HTTP method 86// (label name "method") and HTTP status code (label name "code"). 87func (ins *defaultInstrumentationMiddleware) NewHandler(handlerName string, handler http.Handler) http.HandlerFunc { 88 return promhttp.InstrumentHandlerDuration( 89 ins.requestDuration.MustCurryWith(prometheus.Labels{"handler": handlerName}), 90 promhttp.InstrumentHandlerRequestSize( 91 ins.requestSize.MustCurryWith(prometheus.Labels{"handler": handlerName}), 92 promhttp.InstrumentHandlerCounter( 93 ins.requestsTotal.MustCurryWith(prometheus.Labels{"handler": handlerName}), 94 promhttp.InstrumentHandlerResponseSize( 95 ins.responseSize.MustCurryWith(prometheus.Labels{"handler": handlerName}), 96 handler, 97 ), 98 ), 99 ), 100 ) 101} 102