Skip to content

Commit 097afca

Browse files
committed
docs: improves http example
1 parent dc74771 commit 097afca

File tree

8 files changed

+503
-18
lines changed

8 files changed

+503
-18
lines changed

example/http.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

example/http/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM golang:1.21.5
2+
3+
WORKDIR /go/src/app
4+
5+
COPY main.go go.mod go.sum ./
6+
7+
RUN go get -d ./
8+
RUN go build -o main .
9+
10+
CMD ["./main"]

example/http/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+

example/http/docker-compose.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
version: '3.9'
3+
services:
4+
pyroscope:
5+
image: 'grafana/pyroscope:latest'
6+
ports:
7+
- '4040:4040'
8+
9+
app:
10+
build: .
11+
ports:
12+
- '8080:8080'

example/http/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module http
2+
3+
go 1.21.3
4+
5+
require github.com/grafana/pyroscope-go v1.1.0

example/http/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/grafana/pyroscope-go v1.1.0 h1:Ds35iZ+xyZCx3+sw1qfSbPujSiMeyGvvXoH5BX4+J7Y=
2+
github.com/grafana/pyroscope-go v1.1.0/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88=

example/http/main.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
"net/http/pprof"
6+
"time"
7+
8+
"github.com/grafana/pyroscope-go"
9+
pyroscope_pprof "github.com/grafana/pyroscope-go/http/pprof"
10+
)
11+
12+
func main() {
13+
// Starting pyroscope profiler
14+
pyroscope.Start(pyroscope.Config{
15+
ApplicationName: "example-app",
16+
ServerAddress: "http://pyroscope:4040",
17+
})
18+
19+
// Setting up HTTP server
20+
mux := http.NewServeMux()
21+
server := &http.Server{
22+
Addr: ":8080",
23+
Handler: mux,
24+
}
25+
26+
// Standard pprof routes (copied from /net/http/pprof)
27+
mux.HandleFunc("/debug/pprof/", pprof.Index)
28+
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
29+
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
30+
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
31+
32+
// This route is special: note that we're using Pyroscope handler here
33+
mux.HandleFunc("/debug/pprof/profile", pyroscope_pprof.Profile)
34+
35+
go doMeaninglessWork()
36+
37+
server.ListenAndServe()
38+
}
39+
40+
// doMeaninglessWork does meaningless work to show CPU usage
41+
func doMeaninglessWork() {
42+
for {
43+
for t := time.Now(); time.Now().Sub(t).Seconds() < 1; {
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)