Skip to content

Commit c4ca66b

Browse files
authored
add pprof profiller for debugging (#114)
* feat: add pprof profiller for debugging * feat: load profiller on a more generic way * fix: lint issues * feat: adding docs * feat: adding some extra lines about performance
1 parent ecca9f3 commit c4ca66b

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

pkg/zprofiller/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# ZProfiller
2+
3+
## Overview
4+
5+
`zprofiller` is a Go package designed to facilitate the integration of Go's built-in `pprof` profiling into web applications using the Chi router. It offers a structured way to expose profiling endpoints to monitor and analyze the performance of Go applications in both development and production environments.
6+
7+
## Features
8+
9+
- **Easy Integration**: Seamlessly integrates with the Chi router.
10+
- **Automatic Profiling Endpoints**: Automatically registers standard `pprof` endpoints.
11+
- **Custom Configuration**: Allows customization of server timeouts and logging.
12+
13+
## Getting Started
14+
15+
### Installation
16+
17+
To use `zprofiller` in your project, ensure you have Go installed and your workspace is set up, then add `zprofiller` to your dependencies:
18+
19+
```
20+
go get -u github.com/zondax/zprofiller
21+
```
22+
23+
### Integration
24+
25+
Integrate `zprofiller` into your Go application:
26+
27+
1. **Import the Package**
28+
29+
```go
30+
import (
31+
"github.com/zondax/zprofiller"
32+
)
33+
```
34+
35+
2. **Create a Config Object**
36+
37+
```go
38+
config := &zprofiller.Config{
39+
ReadTimeOut: 5 * time.Second,
40+
WriteTimeOut: 5 * time.Second,
41+
Logger: logger.NewLogger(),
42+
}
43+
```
44+
45+
3. **Instantiate zprofiller**
46+
47+
```go
48+
profiler := zprofiller.New(nil, config)
49+
```
50+
51+
4. **Run the Profiler**
52+
53+
```go
54+
err := profiler.Run(":9999")
55+
if err != nil {
56+
log.Fatalf("Failed to start profiler: %v", err)
57+
}
58+
```
59+
60+
### Usage
61+
62+
Access the profiling endpoints at `http://localhost:<port>/debug/pprof/`, where `<port>` is the port you specified.
63+
64+
## Viewing pprof Results on the Web
65+
66+
### Accessing pprof via Web Browser
67+
68+
Navigate to:
69+
70+
```
71+
http://localhost:<port>/debug/pprof/
72+
```
73+
74+
This index page links to profiles like Heap, Goroutine, Threadcreate, Block, and Mutex.
75+
76+
### Visualizing Profiles
77+
78+
Use tools like `Go Tool Pprof` or `Graphviz` for deeper analysis:
79+
80+
- **Go Tool Pprof**:
81+
82+
```
83+
go tool pprof -http=:8081 http://localhost:<port>/debug/pprof/profile
84+
```
85+
86+
This command downloads the CPU profile data from your application and opens it in an interactive web interface on `http://localhost:8081`.
87+
88+
- **Graphviz**:
89+
90+
```
91+
sudo apt-get install graphviz
92+
go tool pprof -http=:8081 --graph http://localhost:<port>/debug/pprof/profile
93+
```
94+
95+
### Online Tools and Extensions
96+
97+
Consider using online tools or browser extensions like **pprof++** for Chrome for in-browser visualization of pprof data.
98+
99+
## Performance Considerations
100+
101+
When integrating profiling tools such as `pprof` into your application, it is essential to consider the potential impact on performance:
102+
103+
- **Resource Usage**: Profiling operations can consume significant CPU and memory resources, particularly when capturing and analyzing high-frequency data such as CPU profiles.
104+
- **Production Use**: While `pprof` can be invaluable for diagnosing issues in production, it should be enabled selectively. Consider using environment variables or configuration files to control access to profiling endpoints.
105+
- **Sampling Rate**: Adjust the sampling rate of profiles according to the performance impact and the level of detail required. Lower rates can reduce overhead but may miss critical details.
106+
- **Security**: Exposing profiling information can introduce security risks. Ensure that profiling endpoints are protected with authentication mechanisms and are only accessible by authorized personnel.
107+
- **Impact Measurement**: Continuously monitor the impact of enabling profiling on your system’s response times and resource usage. Disable profiling when not needed to avoid unnecessary overhead.
108+
109+
## Security Considerations
110+
111+
Ensure that access to profiling endpoints is secured, especially in production environments, to protect sensitive application data.

pkg/zprofiller/zprofiller.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package zprofiller
2+
3+
import (
4+
"fmt"
5+
"github.com/go-chi/chi/v5"
6+
"github.com/zondax/golem/pkg/logger"
7+
"github.com/zondax/golem/pkg/metrics"
8+
"net/http"
9+
"net/http/pprof"
10+
pprofRuntime "runtime/pprof"
11+
"time"
12+
)
13+
14+
const (
15+
defaultAddress = ":8888"
16+
defaultTimeOut = 240000
17+
)
18+
19+
type Config struct {
20+
ReadTimeOut time.Duration
21+
WriteTimeOut time.Duration
22+
Logger *logger.Logger
23+
}
24+
25+
type zprofiller struct {
26+
router *chi.Mux
27+
config *Config
28+
}
29+
30+
type ZProfiller interface {
31+
Run(addr ...string) error
32+
}
33+
34+
func (c *Config) setDefaultValues() {
35+
if c.ReadTimeOut == 0 {
36+
c.ReadTimeOut = time.Duration(defaultTimeOut) * time.Millisecond
37+
}
38+
39+
if c.WriteTimeOut == 0 {
40+
c.WriteTimeOut = time.Duration(defaultTimeOut) * time.Millisecond
41+
}
42+
43+
if c.Logger == nil {
44+
l := logger.NewLogger()
45+
c.Logger = l
46+
}
47+
}
48+
49+
func New(_ metrics.TaskMetrics, config *Config) ZProfiller {
50+
if config == nil {
51+
config = &Config{}
52+
}
53+
54+
config.setDefaultValues()
55+
56+
router := chi.NewRouter()
57+
router.HandleFunc("/debug/pprof/", pprof.Index)
58+
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
59+
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
60+
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
61+
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
62+
63+
for _, profile := range pprofRuntime.Profiles() {
64+
router.Handle(fmt.Sprintf("/debug/pprof/%s", profile.Name()), pprof.Handler(profile.Name()))
65+
}
66+
67+
zr := &zprofiller{
68+
router: router,
69+
config: config,
70+
}
71+
72+
return zr
73+
}
74+
75+
func (r *zprofiller) Run(addr ...string) error {
76+
address := defaultAddress
77+
if len(addr) > 0 {
78+
address = addr[0]
79+
}
80+
81+
r.config.Logger.Infof("Start profiller server at %v", address)
82+
83+
server := &http.Server{
84+
Addr: address,
85+
Handler: r.router,
86+
ReadTimeout: r.config.ReadTimeOut,
87+
WriteTimeout: r.config.WriteTimeOut,
88+
}
89+
90+
return server.ListenAndServe()
91+
}

0 commit comments

Comments
 (0)