Echo: Yet another recipe for using "/debug/*" handlers

Created on 6 Nov 2015  路  10Comments  路  Source: labstack/echo

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)
}
help wanted

Most helpful comment

I found the simpler solution is to just use:


dbg.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux))

All 10 comments

Closing to mention in #269.

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

toorop picture toorop  路  4Comments

younisshah picture younisshah  路  4Comments

ellisonleao picture ellisonleao  路  3Comments

vishr picture vishr  路  3Comments

alexzorin picture alexzorin  路  3Comments