Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pprof profiller for debugging #114

Merged
merged 5 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions pkg/zprofiller/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# ZProfiller

## Overview

`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.

## Features

- **Easy Integration**: Seamlessly integrates with the Chi router.
- **Automatic Profiling Endpoints**: Automatically registers standard `pprof` endpoints.
- **Custom Configuration**: Allows customization of server timeouts and logging.

## Getting Started

### Installation

To use `zprofiller` in your project, ensure you have Go installed and your workspace is set up, then add `zprofiller` to your dependencies:

```
go get -u github.com/zondax/zprofiller
```

### Integration

Integrate `zprofiller` into your Go application:

1. **Import the Package**

```go
import (
"github.com/zondax/zprofiller"
)
```

2. **Create a Config Object**

```go
config := &zprofiller.Config{
ReadTimeOut: 5 * time.Second,
WriteTimeOut: 5 * time.Second,
Logger: logger.NewLogger(),
}
```

3. **Instantiate zprofiller**

```go
profiler := zprofiller.New(nil, config)
```

4. **Run the Profiler**

```go
err := profiler.Run(":9999")
if err != nil {
log.Fatalf("Failed to start profiler: %v", err)
}
```

### Usage

Access the profiling endpoints at `http://localhost:<port>/debug/pprof/`, where `<port>` is the port you specified.

## Viewing pprof Results on the Web

### Accessing pprof via Web Browser

Navigate to:

```
http://localhost:<port>/debug/pprof/
```

This index page links to profiles like Heap, Goroutine, Threadcreate, Block, and Mutex.

### Visualizing Profiles

Use tools like `Go Tool Pprof` or `Graphviz` for deeper analysis:

- **Go Tool Pprof**:

```
go tool pprof -http=:8081 http://localhost:<port>/debug/pprof/profile
```

This command downloads the CPU profile data from your application and opens it in an interactive web interface on `http://localhost:8081`.

- **Graphviz**:

```
sudo apt-get install graphviz
go tool pprof -http=:8081 --graph http://localhost:<port>/debug/pprof/profile
```

### Online Tools and Extensions

Consider using online tools or browser extensions like **pprof++** for Chrome for in-browser visualization of pprof data.

## Performance Considerations

When integrating profiling tools such as `pprof` into your application, it is essential to consider the potential impact on performance:

- **Resource Usage**: Profiling operations can consume significant CPU and memory resources, particularly when capturing and analyzing high-frequency data such as CPU profiles.
- **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.
- **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.
- **Security**: Exposing profiling information can introduce security risks. Ensure that profiling endpoints are protected with authentication mechanisms and are only accessible by authorized personnel.
- **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.

## Security Considerations

Ensure that access to profiling endpoints is secured, especially in production environments, to protect sensitive application data.
91 changes: 91 additions & 0 deletions pkg/zprofiller/zprofiller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package zprofiller

import (
"fmt"
"github.com/go-chi/chi/v5"
"github.com/zondax/golem/pkg/logger"
"github.com/zondax/golem/pkg/metrics"
"net/http"
"net/http/pprof"
pprofRuntime "runtime/pprof"
"time"
)

const (
defaultAddress = ":8888"
defaultTimeOut = 240000
)

type Config struct {
ReadTimeOut time.Duration
WriteTimeOut time.Duration
Logger *logger.Logger
}

type zprofiller struct {
router *chi.Mux
config *Config
}

type ZProfiller interface {
Run(addr ...string) error
}

func (c *Config) setDefaultValues() {
if c.ReadTimeOut == 0 {
c.ReadTimeOut = time.Duration(defaultTimeOut) * time.Millisecond
}

if c.WriteTimeOut == 0 {
c.WriteTimeOut = time.Duration(defaultTimeOut) * time.Millisecond
}

if c.Logger == nil {
l := logger.NewLogger()
c.Logger = l
}
}

func New(_ metrics.TaskMetrics, config *Config) ZProfiller {
if config == nil {
config = &Config{}
}

config.setDefaultValues()

router := chi.NewRouter()
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)

for _, profile := range pprofRuntime.Profiles() {
router.Handle(fmt.Sprintf("/debug/pprof/%s", profile.Name()), pprof.Handler(profile.Name()))
}

zr := &zprofiller{
router: router,
config: config,
}

return zr
}

func (r *zprofiller) Run(addr ...string) error {
address := defaultAddress
if len(addr) > 0 {
address = addr[0]
}

r.config.Logger.Infof("Start profiller server at %v", address)

server := &http.Server{
Addr: address,
Handler: r.router,
ReadTimeout: r.config.ReadTimeOut,
WriteTimeout: r.config.WriteTimeOut,
}

return server.ListenAndServe()
}
Loading