From 77c816b30917fd86c95bb68f9384ce624021b01a Mon Sep 17 00:00:00 2001 From: Mark Adams Date: Tue, 22 Aug 2017 18:00:56 -0500 Subject: [PATCH] Fix pprof endpoints when -web.route-prefix or -web.external-url is used (#3054) Whenever a route prefix is applied, the router prepends the prefix to the URL path on the request. For most handlers, this is not an issue because the request's path is only used for routing and is not actually needed by the handler itself. However, Prometheus delegates the handling of the /debug/* endpoints to the http.DefaultServeMux which has it's own routing logic that depends on the url.Path. As a result, whenever a prefix is applied, the prefixed URL is passed to the DefaultServeMux which has no awareness of the prefix and returns a 404. This change fixes the issue by creating a new serveDebug handler which routes requests /debug/* requests to appropriate net/http/pprof handler and removing the net/http/pprof import in cmd/prometheus since it is no longer necessary. Fixes #2183. --- cmd/prometheus/main.go | 1 - web/web.go | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index cf49c4792a..5e117704bd 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -17,7 +17,6 @@ package main import ( "flag" "fmt" - _ "net/http/pprof" // Comment this line to disable pprof endpoint. "os" "os/signal" "runtime/debug" diff --git a/web/web.go b/web/web.go index fd433c1124..23d5d97337 100644 --- a/web/web.go +++ b/web/web.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "net" "net/http" + "net/http/pprof" "net/url" "os" "path" @@ -226,8 +227,8 @@ func New(o *Options) *Handler { fmt.Fprintf(w, "This endpoint requires a POST request.\n") }) - router.Get("/debug/*subpath", readyf(http.DefaultServeMux.ServeHTTP)) - router.Post("/debug/*subpath", readyf(http.DefaultServeMux.ServeHTTP)) + router.Get("/debug/*subpath", readyf(serveDebug)) + router.Post("/debug/*subpath", readyf(serveDebug)) router.Get("/-/healthy", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) @@ -241,6 +242,26 @@ func New(o *Options) *Handler { return h } +func serveDebug(w http.ResponseWriter, req *http.Request) { + ctx := req.Context() + subpath := route.Param(ctx, "subpath") + + // Based off paths from init() in golang.org/src/net/http/pprof/pprof.go + if subpath == "/pprof/" { + pprof.Index(w, req) + } else if subpath == "/pprof/cmdline" { + pprof.Cmdline(w, req) + } else if subpath == "/pprof/profile" { + pprof.Profile(w, req) + } else if subpath == "/pprof/symbol" { + pprof.Symbol(w, req) + } else if subpath == "/pprof/trace" { + pprof.Trace(w, req) + } else { + http.NotFound(w, req) + } +} + func serveStaticAsset(w http.ResponseWriter, req *http.Request) { fp := route.Param(req.Context(), "filepath") fp = filepath.Join("web/ui/static", fp)