profiling

profiling serves every net/http/pprof endpoint through a single httprouter wildcard route, without touching http.DefaultServeMux.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/profiling"

Exposes Go’s net/http/pprof profiles through httprouter, as one handler on one wildcard route.

router.HandlerFunc(http.MethodGet, "/pprof/*option", profiling.PProfHandler)

The problem with importing net/http/pprof

The standard package registers its handlers on http.DefaultServeMux as an import side effect. That is convenient for a program serving from the default mux and a nuisance otherwise: it mutates global state on import, binds the paths to /debug/pprof/, and does not help at all with a router that is not the default mux.

The usual workaround is to register each handler individually against your router, which means naming Index, Cmdline, Profile, Symbol, Trace, and then a route for every named runtime profile.

PProfHandler reads httprouter’s *option wildcard parameter and dispatches internally. One route covers everything, http.DefaultServeMux is untouched, and the mount prefix is yours: /pprof/*option, /debug/pprof/*option, or anything else.

Named profiles are forwarded to pprof.Handler, so profiles the Go runtime adds later work with no change here.

What is served

/pprof/           index page listing available profiles
/pprof/cmdline    the running program's command line
/pprof/profile    CPU profile, 30 seconds or ?seconds=N
/pprof/symbol     symbol lookup for program counters
/pprof/trace      execution trace, ?seconds=N
/pprof/<name>     any named runtime profile: heap, goroutine, allocs,
                  block, mutex, threadcreate

Three mounting requirements

These come from the package’s own caveats, and each has a visible failure mode.

The wildcard must be named option. The handler reads that parameter by name. Registering /pprof/*path compiles, serves, and returns the index page for every request, including /pprof/heap.

The route must be exempt from the request timeout. profile and trace block for their full duration by design; a 30-second CPU profile under a 30-second request timeout is truncated or killed. httpserver disables the request timeout on the pprof route for exactly this reason.

Keep httprouter’s trailing-slash redirect enabled. The index page links to the other profiles with relative URLs like heap?debug=1, which only resolve correctly from a path ending in a slash. httprouter’s RedirectTrailingSlash sends /pprof to /pprof/ and keeps those links working.

Keep it off the public internet

pprof exposes the internals of a running process: heap contents, full goroutine stacks with function names and arguments, CPU traces. That is enough to map your code and, in a heap dump, to recover data the process was holding.

There is also a denial-of-service edge. /pprof/profile?seconds=3600 ties up a profiling slot for an hour, and CPU profiling costs real overhead while it runs.

Mount this on an internal or administrative listener that is not publicly reachable, and put authentication in front of it. httpserver supports a separate listener for operational routes for this purpose, and the same applies to the metrics endpoint from prometheus.

Dependencies

Reaches github.com/julienschmidt/httprouter.