feat(pprof): add pyroscope profiler support

Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
2026-01-07 12:30:18 +01:00
parent 8e50da9205
commit 126f080612
4 changed files with 173 additions and 0 deletions

127
pprof/options.go Normal file
View File

@@ -0,0 +1,127 @@
package pprof
import (
"os"
"github.com/grafana/pyroscope-go"
)
const (
PyroscopeAddressEnv = "PYROSCOPE_ADDRESS"
PyroscopeUserEnv = "PYROSCOPE_USER"
PyroscopePasswordEnv = "PYROSCOPE_PASSWORD"
)
type Option func(*options)
func WithAddress(address string) Option {
return func(o *options) {
if address != "" {
o.address = address
}
}
}
func WithUser(user string) Option {
return func(o *options) {
if user != "" {
o.user = user
}
}
}
func WithPassword(password string) Option {
return func(o *options) {
if password != "" {
o.password = password
}
}
}
func WithAddressEnv(env string) Option {
return func(o *options) {
if env != "" {
o.addressEnv = env
}
}
}
func WithUserEnv(env string) Option {
return func(o *options) {
if env != "" {
o.userEnv = env
}
}
}
func WithPasswordEnv(env string) Option {
return func(o *options) {
if env != "" {
o.passwordEnv = env
}
}
}
func WithMutexProfileFraction(fraction int) Option {
return func(o *options) {
o.mutexProfileFraction = fraction
}
}
func WithBlockProfileRate(rate int) Option {
return func(o *options) {
o.blockProfileRate = rate
}
}
func WithProfiles(profiles ...pyroscope.ProfileType) Option {
return func(o *options) {
if len(profiles) != 0 {
o.profiles = profiles
}
}
}
type options struct {
address string
user string
password string
addressEnv string
userEnv string
passwordEnv string
mutexProfileFraction int
blockProfileRate int
profiles []pyroscope.ProfileType
}
var defaultOptions = options{
addressEnv: PyroscopeAddressEnv,
userEnv: PyroscopeUserEnv,
passwordEnv: PyroscopePasswordEnv,
mutexProfileFraction: 5,
blockProfileRate: 5,
profiles: []pyroscope.ProfileType{
pyroscope.ProfileCPU,
pyroscope.ProfileInuseObjects,
pyroscope.ProfileAllocObjects,
pyroscope.ProfileInuseSpace,
pyroscope.ProfileAllocSpace,
pyroscope.ProfileGoroutines,
pyroscope.ProfileMutexCount,
pyroscope.ProfileMutexDuration,
pyroscope.ProfileBlockCount,
pyroscope.ProfileBlockDuration,
},
}
func valueOrEnv(value, env string) string {
if value != "" {
return value
}
return os.Getenv(env)
}

38
pprof/pprof.go Normal file
View File

@@ -0,0 +1,38 @@
package pprof
import (
"context"
"runtime"
"github.com/grafana/pyroscope-go"
"go.linka.cloud/grpc-toolkit/logger"
)
func Init(ctx context.Context, app string, opts ...Option) {
if app == "" {
panic("application name is required to start pyroscope profiler")
}
o := defaultOptions
for _, v := range opts {
v(&o)
}
if valueOrEnv(o.address, o.addressEnv) == "" {
return
}
runtime.SetMutexProfileFraction(o.mutexProfileFraction)
runtime.SetBlockProfileRate(o.blockProfileRate)
log := logger.C(ctx).WithFields("service", "pyroscope")
log.Info("starting pyroscope profiler")
_, err := pyroscope.Start(pyroscope.Config{
ApplicationName: app,
ServerAddress: valueOrEnv(o.address, o.addressEnv),
BasicAuthUser: valueOrEnv(o.user, o.userEnv),
BasicAuthPassword: valueOrEnv(o.password, o.passwordEnv),
Logger: log,
ProfileTypes: o.profiles,
})
if err != nil {
log.WithError(err).Error("failed to start pyroscope")
}
}