// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides // more flexibility (at the cost of a more complex call syntax). // // As InstrumentHandlerFunc, this function registers four metric collectors, but it // uses the provided SummaryOpts to create them. However, the fields "Name" and // "Help" in the SummaryOpts are ignored. "Name" is replaced by // "requests_total", "request_duration_microseconds", "request_size_bytes", and // "response_size_bytes", respectively. "Help" is replaced by an appropriate // help string. The names of the variable labels of the http_requests_total // CounterVec are "method" (get, post, etc.), and "code" (HTTP status code). // // If InstrumentHandlerWithOpts is called as follows, it mimics exactly the // behavior of InstrumentHandler: // // prometheus.InstrumentHandlerWithOpts( // prometheus.SummaryOpts{ // Subsystem: "http", // ConstLabels: prometheus.Labels{"handler": handlerName}, // }, // handler, // ) // // Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it // cannot use SummaryOpts. Instead, a CounterOpts struct is created internally, // and all its fields are set to the equally named fields in the provided // SummaryOpts. func InstrumentHandlerFuncWithOpts(opts prometheus.SummaryOpts, handlerFunc gin.HandlerFunc) gin.HandlerFunc { reqCnt := prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: opts.Namespace, Subsystem: opts.Subsystem, Name: "requests_total", Help: "Total number of HTTP requests made.", ConstLabels: opts.ConstLabels, }, instLabels, ) opts.Name = "request_duration_microseconds" opts.Help = "The HTTP request latencies in microseconds." reqDur := prometheus.NewSummary(opts) opts.Name = "request_size_bytes" opts.Help = "The HTTP request sizes in bytes." reqSz := prometheus.NewSummary(opts) opts.Name = "response_size_bytes" opts.Help = "The HTTP response sizes in bytes." resSz := prometheus.NewSummary(opts) regReqCnt := prometheus.MustRegisterOrGet(reqCnt).(*prometheus.CounterVec) regReqDur := prometheus.MustRegisterOrGet(reqDur).(prometheus.Summary) regReqSz := prometheus.MustRegisterOrGet(reqSz).(prometheus.Summary) regResSz := prometheus.MustRegisterOrGet(resSz).(prometheus.Summary) return func(c *gin.Context) { now := time.Now() r := c.Request out := make(chan int) urlLen := 0 if r.URL != nil { urlLen = len(r.URL.String()) } go computeApproximateRequestSize(r, out, urlLen) handlerFunc(c) elapsed := float64(time.Since(now)) / float64(time.Microsecond) method := sanitizeMethod(r.Method) code := sanitizeCode(c.Writer.Status()) regReqCnt.WithLabelValues(method, code).Inc() regReqDur.Observe(elapsed) regResSz.Observe(float64(c.Writer.Size())) regReqSz.Observe(float64(<-out)) } }
// InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps // the go-restful RouteFunction instead of a HandlerFunc func InstrumentRouteFunc(handlerName string, routeFunc restful.RouteFunction) restful.RouteFunction { opts := prometheus.SummaryOpts{ Subsystem: "http", ConstLabels: prometheus.Labels{"handler": handlerName}, } reqCnt := prometheus.NewCounterVec( prometheus.CounterOpts{ Subsystem: opts.Subsystem, Name: "requests_total", Help: "Total number of HTTP requests made.", ConstLabels: opts.ConstLabels, }, instLabels, ) opts.Name = "request_duration_microseconds" opts.Help = "The HTTP request latencies in microseconds." reqDur := prometheus.NewSummary(opts) opts.Name = "request_size_bytes" opts.Help = "The HTTP request sizes in bytes." reqSz := prometheus.NewSummary(opts) opts.Name = "response_size_bytes" opts.Help = "The HTTP response sizes in bytes." resSz := prometheus.NewSummary(opts) regReqCnt := prometheus.MustRegisterOrGet(reqCnt).(*prometheus.CounterVec) regReqDur := prometheus.MustRegisterOrGet(reqDur).(prometheus.Summary) regReqSz := prometheus.MustRegisterOrGet(reqSz).(prometheus.Summary) regResSz := prometheus.MustRegisterOrGet(resSz).(prometheus.Summary) return restful.RouteFunction(func(request *restful.Request, response *restful.Response) { now := time.Now() delegate := &responseWriterDelegator{ResponseWriter: response.ResponseWriter} out := make(chan int) urlLen := 0 if request.Request.URL != nil { urlLen = len(request.Request.URL.String()) } go computeApproximateRequestSize(request.Request, out, urlLen) _, cn := response.ResponseWriter.(http.CloseNotifier) _, fl := response.ResponseWriter.(http.Flusher) _, hj := response.ResponseWriter.(http.Hijacker) _, rf := response.ResponseWriter.(io.ReaderFrom) var rw http.ResponseWriter if cn && fl && hj && rf { rw = &fancyResponseWriterDelegator{delegate} } else { rw = delegate } response.ResponseWriter = rw routeFunc(request, response) elapsed := float64(time.Since(now)) / float64(time.Microsecond) method := strings.ToLower(request.Request.Method) code := strconv.Itoa(delegate.status) regReqCnt.WithLabelValues(method, code).Inc() regReqDur.Observe(elapsed) regResSz.Observe(float64(delegate.written)) regReqSz.Observe(float64(<-out)) }) }