This is not a bug. This is yet another recipe for using "/debug/*" handlers. Also see https://github.com/echo-contrib/echopprof
package main
import (
"net/http"
_ "expvar"
_ "net/http/pprof"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func main() {
e := echo.New()
// Middleware
e.Use(
middleware.Logger(),
middleware.Recover(),
middleware.Gzip(),
)
// Routes
e.Get("/", hello)
// ... some other handlers...
// Group, Middleware and Routes for /debug/* from Go's stdlib
// GET handlers (or POST if it needs)
d := e.Group("/debug",
middleware.Gzip(),
)
d.Get("/vars", wrapStdHandler)
d.Get("/pprof/heap", wrapStdHandler)
d.Get("/pprof/goroutine", wrapStdHandler)
d.Get("/pprof/block", wrapStdHandler)
d.Get("/pprof/threadcreate", wrapStdHandler)
d.Get("/pprof/cmdline", wrapStdHandler)
d.Get("/pprof/profile", wrapStdHandler)
d.Get("/pprof/symbol", wrapStdHandler)
d.Get("/pprof/trace", wrapStdHandler)
// Start server
e.Run(":1323")
}
// Some handler
func hello(c *echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
}
// Wrapper for all stdlib /debug/* handlers
func wrapStdHandler(c *echo.Context) error {
w, r := c.Response().Writer(), c.Request()
if h, p := http.DefaultServeMux.Handler(r); len(p) != 0 {
h.ServeHTTP(w, r)
return nil
}
return echo.NewHTTPError(http.StatusNotFound)
}
Closing to mention in #269.
This is a better alternative than https://github.com/echo-contrib/echopprof/blob/master/echopprof.go
For those curious on how you might want to rename the handlers.
Here is a simple alternative
admin := e.Group("/admin")
admin.Get("/pprof/", http.HandlerFunc(pprof.Index))
admin.Get("/pprof/heap", pprof.Handler("heap").ServeHTTP)
admin.Get("/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
admin.Get("/pprof/block", pprof.Handler("block").ServeHTTP)
admin.Get("/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
admin.Get("/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
admin.Get("/pprof/profile", http.HandlerFunc(pprof.Profile))
admin.Get("/pprof/symbol", http.HandlerFunc(pprof.Symbol))
admin.Get("/pprof/trace", http.HandlerFunc(pprof.Trace))
@fzakaria
I think that your suggestion is simpler and more accurately (without additional wrapper). Therefore it's better. Thanks for sharing.
I found that with small wrapper still better (because "/debug/vars" is available too).
_ "expvar"
_ "net/http/pprof"
r := echo.New()
// expvar
r.Get("/debug/vars", digStdHandler)
// net/http/pprof
r.Get("/debug/pprof/", digStdHandler)
r.Get("/debug/pprof/cmdline", digStdHandler)
r.Get("/debug/pprof/profile", digStdHandler)
r.Get("/debug/pprof/symbol", digStdHandler)
r.Get("/debug/pprof/trace", digStdHandler)
// runtime/pprof
r.Get("/debug/pprof/goroutine", digStdHandler)
r.Get("/debug/pprof/threadcreate", digStdHandler)
r.Get("/debug/pprof/heap", digStdHandler)
r.Get("/debug/pprof/block", digStdHandler)
func digStdHandler(w http.ResponseWriter, r *http.Request) {
if h, p := http.DefaultServeMux.Handler(r); p != "" {
h.ServeHTTP(w, r)
}
}
And this one wrapper may be improve for checking access permissions to these methods, for example:
func digStdHandler(w http.ResponseWriter, r *http.Request) {
if !flag.Debug { // check a flag or secret key
forbidden(w, r)
return
}
if h, p := http.DefaultServeMux.Handler(r); p != "" {
h.ServeHTTP(w, r)
}
}
The above solutions do not work with recent echo versions. please check following example on how to integrate expvar with echo:
dbg := e.Group("/debug")
// expvar
dbg.Get("/vars", func(c echo.Context) error {
w := c.Response().(*standard.Response).ResponseWriter
r := c.Request().(*standard.Request).Request
if h, p := http.DefaultServeMux.Handler(r); p != "" {
h.ServeHTTP(w, r)
return nil
}
return echo.NewHTTPError(http.StatusNotFound)
})
please note that the same approach will integrate pprof aswell, without any external dependency.
this is IMHO the best approach, if the pprof package changes and adds new handlers you will be able to automatically wire them up to your routes without changing your code
dbg.Get("/pprof/*", func(c echo.Context) error {
w := c.Response().(*standard.Response).ResponseWriter
r := c.Request().(*standard.Request).Request
if h, p := http.DefaultServeMux.Handler(r); p != "" {
h.ServeHTTP(w, r)
return nil
}
return echo.NewHTTPError(http.StatusNotFound)
})
I found the simpler solution is to just use:
dbg.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux))
So this one-liner is enough to make all of net/http/pprof's endpoints available?
@andradei yes. Don't forget to include the _ "net/http/pprof" import.
Most helpful comment
I found the simpler solution is to just use: