feat: mount pprof and metrics to /api/v2/debug for admins (#20353)

Adds the following debug routes for people with the `debug_info:read`
permission:
- `/api/v2/debug/pprof` for `net/http/pprof`
    - `/`
    - `/cmdline`
    - `/profile`
    - `/symbol`
    - `/trace`
    - `/*`
- `/api/v2/debug/metrics` for Prometheus metrics
This commit is contained in:
Dean Sheather
2025-10-21 14:13:11 +11:00
committed by GitHub
parent 5a18cf4c86
commit 0652b18ebc
5 changed files with 348 additions and 3 deletions
+39 -1
View File
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/http"
httppprof "net/http/pprof"
"net/url"
"path/filepath"
"regexp"
@@ -32,6 +33,7 @@ import (
"github.com/google/uuid"
"github.com/klauspost/compress/zstd"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
httpSwagger "github.com/swaggo/http-swagger/v2"
"go.opentelemetry.io/otel/trace"
"golang.org/x/xerrors"
@@ -1512,7 +1514,8 @@ func New(options *Options) *API {
r.Route("/debug", func(r chi.Router) {
r.Use(
apiKeyMiddleware,
// Ensure only owners can access debug endpoints.
// Ensure only users with the debug_info:read (e.g. only owners)
// can view debug endpoints.
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if !api.Authorize(r, policy.ActionRead, rbac.ResourceDebugInfo) {
@@ -1545,6 +1548,41 @@ func New(options *Options) *API {
})
}
r.Method("GET", "/expvar", expvar.Handler()) // contains DERP metrics as well as cmdline and memstats
r.Route("/pprof", func(r chi.Router) {
r.Use(func(next http.Handler) http.Handler {
// Some of the pprof handlers strip the `/debug/pprof`
// prefix, so we need to strip our additional prefix as
// well.
return http.StripPrefix("/api/v2", next)
})
// Serve the index HTML page.
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
// Redirect to include a trailing slash, otherwise links on
// the generated HTML page will be broken.
if !strings.HasSuffix(r.URL.Path, "/") {
http.Redirect(w, r, "/api/v2/debug/pprof/", http.StatusTemporaryRedirect)
return
}
httppprof.Index(w, r)
})
// Handle any out of the box pprof handlers that don't get
// dealt with by the default index handler. See httppprof.init.
r.Get("/cmdline", httppprof.Cmdline)
r.Get("/profile", httppprof.Profile)
r.Get("/symbol", httppprof.Symbol)
r.Get("/trace", httppprof.Trace)
// Index will handle any standard and custom runtime/pprof
// profiles.
r.Get("/*", httppprof.Index)
})
r.Get("/metrics", promhttp.InstrumentMetricHandler(
options.PrometheusRegistry, promhttp.HandlerFor(options.PrometheusRegistry, promhttp.HandlerOpts{}),
).ServeHTTP)
})
// Manage OAuth2 applications that can use Coder as an OAuth2 provider.
r.Route("/oauth2-provider", func(r chi.Router) {