|
| 1 | +# Pyroscope CPU Profiler HTTP Handler |
| 2 | + |
| 3 | +This is an example of how you can use `github.com/grafana/pyroscope-go/http/pprof` package to use standard Go `/debug/pprof/profile` profiler along with Pyroscope continuous profiler. |
| 4 | + |
| 5 | +### Problem |
| 6 | + |
| 7 | +The standard Go pprof HTTP endpoint `/debug/pprof/profile` returns an error if profiling |
| 8 | +is already started: |
| 9 | + |
| 10 | +> Could not enable CPU profiling: CPU profiling already in use |
| 11 | +
|
| 12 | +This prevents you from using the standard Go pprof HTTP endpoint `/debug/pprof/profile` along with Pyroscope continuous profiler. |
| 13 | + |
| 14 | +### Solution |
| 15 | + |
| 16 | +This package facilitates the collection of CPU profiles via HTTP without disrupting the background operation of the Pyroscope profiler. It enables you to seamlessly gather CPU profiles through HTTP while continuously sending them to Pyroscope. |
| 17 | + |
| 18 | +### How Does It Work |
| 19 | + |
| 20 | +With each invocation of the handler, it suspends the Pyroscope profiler, gathers a CPU profile, dispatches the collected profile to both the caller and the Pyroscope profiler, and subsequently resumes the Pyroscope profiler. |
| 21 | + |
| 22 | +### Recommendations |
| 23 | + |
| 24 | +Standard `net/http/pprof` package registers its handlers automatically for the default HTTP server, but this package does not to avoid runtime errors due to the standard Go pprof HTTP endpoint `/debug/pprof/profile` being already registered. |
| 25 | + |
| 26 | +It is highly recommended that you create a separate HTTP server and register pprof handlers on it. Then you can use Pyroscope's `github.com/grafana/pyroscope-go/http/pprof` package to register a handler that will collect CPU profiles and send them to Pyroscope while allowing you to still use the standard Go pprof HTTP endpoint `/debug/pprof/profile`. |
| 27 | + |
| 28 | +### Example |
| 29 | + |
| 30 | +```go |
| 31 | +package main |
| 32 | + |
| 33 | +import ( |
| 34 | + "net/http" |
| 35 | + "net/http/pprof" |
| 36 | + "time" |
| 37 | + |
| 38 | + "github.com/grafana/pyroscope-go" |
| 39 | + pyroscope_pprof "github.com/grafana/pyroscope-go/http/pprof" |
| 40 | +) |
| 41 | + |
| 42 | +func main() { |
| 43 | + // Starting pyroscope profiler |
| 44 | + pyroscope.Start(pyroscope.Config{ |
| 45 | + ApplicationName: "example-app", |
| 46 | + ServerAddress: "http://pyroscope:4040", |
| 47 | + }) |
| 48 | + |
| 49 | + // Setting up HTTP server |
| 50 | + mux := http.NewServeMux() |
| 51 | + server := &http.Server{ |
| 52 | + Addr: ":8080", |
| 53 | + Handler: mux, |
| 54 | + } |
| 55 | + |
| 56 | + // Standard pprof routes (copied from /net/http/pprof) |
| 57 | + mux.HandleFunc("/debug/pprof/", pprof.Index) |
| 58 | + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) |
| 59 | + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) |
| 60 | + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) |
| 61 | + |
| 62 | + // This route is special: note that we're using Pyroscope handler here |
| 63 | + mux.HandleFunc("/debug/pprof/profile", pyroscope_pprof.Profile) |
| 64 | + |
| 65 | + go doMeaninglessWork() |
| 66 | + |
| 67 | + server.ListenAndServe() |
| 68 | +} |
| 69 | + |
| 70 | +// doMeaninglessWork does meaningless work to show CPU usage |
| 71 | +func doMeaninglessWork() { |
| 72 | + for { |
| 73 | + for t := time.Now(); time.Now().Sub(t).Seconds() < 1; { |
| 74 | + } |
| 75 | + } |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +### Docker Compose Example |
| 80 | + |
| 81 | +You can find a complete example of how to use this package in the [docker-compose.yml](./docker-compose.yml) file. |
| 82 | + |
| 83 | +To run it: |
| 84 | +```bash |
| 85 | +docker-compose up --build |
| 86 | +``` |
| 87 | + |
| 88 | +You can see Pyroscope data at [`http://localhost:4040/`](http://localhost:4040/?query=process_cpu%3Acpu%3Ananoseconds%3Acpu%3Ananoseconds%7Bservice_name%3D%22example-app%22%7D&from=now-5m), and if you want to see data from the standard Go pprof HTTP endpoint you can go to `http://localhost:8080/debug/pprof/profile`. Note that when you do that it does not disrupt the Pyroscope profiler: |
| 89 | + |
| 90 | +```bash |
| 91 | +curl http://localhost:8080/debug/pprof/profile > profile.pprof |
| 92 | +pprof -http=:8081 profile.pprof |
| 93 | +``` |
| 94 | + |
0 commit comments