1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
15 "github.com/prometheus/client_golang/prometheus"
18 // Inspect serves a report of current requests at "GET
19 // /_inspect/requests", and passes other requests through to the next
22 // If registry is not nil, Inspect registers metrics about current
24 func Inspect(registry *prometheus.Registry, authToken string, next http.Handler) http.Handler {
27 hangupTime atomic.Value
29 current := map[*http.Request]*ent{}
32 registry.MustRegister(prometheus.NewGaugeFunc(
35 Name: "max_active_request_age_seconds",
36 Help: "Age of oldest active request",
41 earliest := time.Time{}
43 for _, e := range current {
44 if _, ok := e.hangupTime.Load().(time.Time); ok {
45 // Don't count abandoned requests here
48 if !any || e.startTime.Before(earliest) {
50 earliest = e.startTime
56 return float64(time.Since(earliest).Seconds())
59 registry.MustRegister(prometheus.NewGaugeFunc(
62 Name: "max_abandoned_request_age_seconds",
63 Help: "Maximum time since client hung up on a request whose processing thread is still running",
68 earliest := time.Time{}
70 for _, e := range current {
71 if hangupTime, ok := e.hangupTime.Load().(time.Time); ok {
72 if !any || hangupTime.Before(earliest) {
81 return float64(time.Since(earliest).Seconds())
85 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
86 if req.Method == "GET" && req.URL.Path == "/_inspect/requests" {
87 if authToken == "" || req.Header.Get("Authorization") != "Bearer "+authToken {
88 Error(w, "unauthorized", http.StatusUnauthorized)
102 outrecs := []outrec{}
103 for req, e := range current {
104 outrecs = append(outrecs, outrec{
105 RequestID: req.Header.Get(HeaderRequestID),
108 URL: req.URL.String(),
109 RemoteAddr: req.RemoteAddr,
110 Elapsed: now.Sub(e.startTime).Seconds(),
113 sort.Slice(outrecs, func(i, j int) bool { return outrecs[i].Elapsed < outrecs[j].Elapsed })
114 w.Header().Set("Content-Type", "application/json")
115 json.NewEncoder(w).Encode(outrecs)
117 e := ent{startTime: time.Now()}
122 <-req.Context().Done()
123 e.hangupTime.Store(time.Now())
130 next.ServeHTTP(w, req)