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

chore(examples): update Go pprof migration example #3768

Merged
merged 2 commits into from
Dec 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
18 changes: 18 additions & 0 deletions docs/sources/configure-client/language-sdks/go_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ func main() {
}
```

Alternatively, if you want more control over the profiling process, you can manually handle the profiler initialization and termination:

```go
profiler, err := pyroscope.Start(pyroscope.Config{
// omitted for brevity
})
if err != nil {
// the only reason this would fail is if the configuration is not valid
log.Fatalf("failed to start Pyroscope: %v", err)
}
defer profiler.Stop()

// your code goes here
}
```

This approach may be necessary if you need to ensure that the last profile is sent before the application exits.

### Add profiling labels to your application

You can add tags (labels) to the profiling data. These tags can be used to filter the data in the UI. There is a custom API that's in line with the go-native pprof API:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Migrating from standard pprof to Pyroscope in a Go application

This README provides a comprehensive guide on migrating from the standard pprof library to Pyroscope in a Go application. The example demonstrates the transition within a detective-themed Go application, enhancing the process of profiling with Pyroscope's advanced capabilities. The actual changes needed to migrate from standard `pprof` to using the Pyroscope SDK is very simple (it extends the standard pprof library with extra functionality and performance improvements). If you would like to use standard `pprof` _algonside_ the pyroscope go sdk simultaneously see the [example here](https://github.com/grafana/pyroscope-go/tree/main/example/http).
This README provides a comprehensive guide on migrating from the standard `pprof` library to Pyroscope in a Go application. The example demonstrates the transition within a detective-themed Go application, showcasing how to enhance the profiling process with Pyroscope's advanced capabilities. The actual changes needed to migrate from the standard `pprof` to using [the Pyroscope SDK](https://grafana.com/docs/pyroscope/latest/configure-client/language-sdks/go_push/) are very simple. See [the source PR](https://github.com/grafana/pyroscope/pull/2830).

The Pyroscope Go SDK extends the standard `pprof` library with extra functionality and performance improvements. If you would like to use the standard `pprof` _alongside_ the Pyroscope Go SDK simultaneously, see the [example here](https://github.com/grafana/pyroscope-go/tree/main/example/http).

See link to [source PR here](https://github.com/grafana/pyroscope/pull/2830)
<img width="1426" alt="image" src="https://github.com/grafana/pyroscope/assets/23323466/f094399a-4a4d-4b47-9f03-5a15b4085fab">

## Changes made
Expand All @@ -16,13 +17,27 @@ Originally in the pre-pyroscope code, the `main.go` file used the standard `net/
In the post-pyroscope code, to leverage the advanced features of Pyroscope, we made the following changes:

1. **Removed Standard pprof Import:** The `_ "net/http/pprof"` import was removed, as Pyroscope replaces its functionality.
2. **Added Pyroscope SDK:** We installed the Pyroscope module using `go get github.com/grafana/pyroscope-go` and imported it in our `main.go`.
3. **Configured Pyroscope:** Inside the `main()` function, we set up Pyroscope using the `pyroscope.Start()` method with the following configuration:


2. **Added Pyroscope SDK:** We installed the Pyroscope module using the following command and imported it in our `main.go`:

> go get github.com/grafana/pyroscope-go

3. **Enabled block and mutex profilers:** This step is only required if you're using mutex or block profiling. Inside the `main()` function, we enabled the block and mutex profilers using the runtime functions:

```go
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)
```

4. **Configured Pyroscope:** Inside the `main()` function, we set up Pyroscope using the `pyroscope.Start()` method with the following configuration:
- Application name and server address.
- Logger configuration.
- Tags for additional metadata.
- Profile types to be captured.
4. Consider using [godeltaprof](https://pkg.go.dev/github.com/grafana/pyroscope-go/godeltaprof) -- which is an optimized way to do memory profiling more efficiently


5. **Consider using [godeltaprof](https://pkg.go.dev/github.com/grafana/pyroscope-go/godeltaprof):** It provides an optimized way to perform memory, mutex, and block profiling more efficiently.

## Benefits of using Pyroscope

Expand All @@ -35,9 +50,8 @@ In the post-pyroscope code, to leverage the advanced features of Pyroscope, we m

## Migration guide

To view the exact changes made during the migration, refer to our [pull request](https://github.com/grafana/pyroscope/pull/2830). This PR clearly illustrates the differences and necessary steps to transition from standard pprof to Pyroscope.
To view the exact changes made during the migration, refer to our [pull request](https://github.com/grafana/pyroscope/pull/2830). This PR clearly illustrates the differences and the necessary steps to transition from the standard `pprof` library to Pyroscope.

## Conclusion

Migrating to Pyroscope SDK in a Go application is a straightforward process that significantly enhances profiling capabilities. By following the steps outlined in this guide and reviewing the provided PR, developers can easily switch from standard pprof to Pyroscope, benefiting from real-time, continuous profiling and advanced performance insights.

Migrating to the Pyroscope SDK in a Go application is a straightforward process that significantly enhances profiling capabilities. By following the steps outlined in this guide and reviewing the provided PR, developers can easily transition from the standard `pprof` library to Pyroscope, benefiting from real-time, continuous profiling and advanced performance insights.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package main

import (
"fmt"
"net/http"
"log"
"os"
"runtime"
"sync"
Expand Down Expand Up @@ -49,18 +49,19 @@ func solveMystery(wg *sync.WaitGroup) {
}

func main() {
// Pyroscope configuration
// These 2 lines are only required if you're using mutex or block profiling
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(5)

pyroscope.Start(pyroscope.Config{
// Pyroscope configuration
profiler, err := pyroscope.Start(pyroscope.Config{
ApplicationName: "detective.mystery.app",
ServerAddress: "https://profiles-prod-001.grafana.net", // if OSS then http://localhost:4040
ServerAddress: "https://profiles-prod-001.grafana.net", // If OSS, then "http://pyroscope.local:4040"
// Optional HTTP Basic authentication
BasicAuthUser: "<User>", // 900009
BasicAuthPassword: "<Password>", // glc_SAMPLEAPIKEY0000000000==
Logger: pyroscope.StandardLogger,
Tags: map[string]string{"hostname": os.Getenv("HOSTNAME")},
// BasicAuthUser: "<User>", // 900009
// BasicAuthPassword: "<Password>", // glc_SAMPLEAPIKEY0000000000==
Logger: pyroscope.StandardLogger,
Tags: map[string]string{"hostname": os.Getenv("HOSTNAME")},
ProfileTypes: []pyroscope.ProfileType{
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
Expand All @@ -74,16 +75,17 @@ func main() {
pyroscope.ProfileBlockDuration,
},
})
if err != nil {
log.Fatalf("Error starting profiler: %v", err)
}
defer profiler.Stop()
Comment on lines +78 to +81
Copy link
Collaborator Author

@kolesnikovae kolesnikovae Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically don't include this in our examples, but I believe it's something we should address. The last bit of information we usually omit can provide valuable insights. OTOH, we don't say anything about it in the README and in the source PR (which I can't update).

In this specific example this is required as the app usually exits before any profiles are sent. Alternatively, we could simply hang, but the mystery wouldn't be solved then!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to update it as well in our SDK docs and README? 🤔


var wg sync.WaitGroup

// Server for pprof
go func() {
fmt.Println(http.ListenAndServe("localhost:6060", nil))
}()
wg.Add(1) // pprof - so we won't exit prematurely
// pyroscope.Start is non-blocking: the profiler will start shortly.
// To ensure we don't miss the investigation, we wait briefly.
time.Sleep(time.Second)

wg.Add(4) // Adding 4 detective tasks
var wg sync.WaitGroup
wg.Add(5) // Adding 5 detective tasks
go gatherClues(&wg)
go analyzeEvidence(&wg)
go interviewWitnesses(&wg)
Expand Down
Loading