feat: add pyroscope

This commit is contained in:
Kevin Franklin Kim 2025-09-30 10:23:10 +02:00
parent e975d78b01
commit 8e56aabc6f
No known key found for this signature in database
7 changed files with 111 additions and 5 deletions

View File

@ -6,6 +6,7 @@ import (
"time"
"github.com/foomo/keel/service"
"github.com/grafana/pyroscope-go"
"github.com/spf13/viper"
"go.uber.org/zap"
@ -146,6 +147,48 @@ func WithPrometheusMeter(enabled bool) Option {
}
}
// WithPyroscopeService option with default value
func WithPyroscopeService(enabled bool) Option {
return func(inst *Server) {
if config.GetBool(inst.Config(), "otel.enabled", enabled)() {
tags := map[string]string{}
if v := os.Getenv("HOSTNAME"); v != "" {
tags["pod"] = v
}
if v := config.GetString(inst.Config(), "otel.service.git.ref", "")(); v != "" {
tags["service_git_ref"] = v
}
if v := config.GetString(inst.Config(), "otel.service.repository", "")(); v != "" {
tags["service_repository"] = v
}
if v := config.GetString(inst.Config(), "otel.service.root_path", "")(); v != "" {
tags["service_root_path"] = v
}
svs := service.NewPyroscope(inst.Logger(), pyroscope.Config{
ApplicationName: config.GetString(inst.Config(), "otel.service.name", telemetry.ServiceName)(),
Tags: tags,
Logger: telemetry.NewPyroscopeLogger(inst.l),
ProfileTypes: []pyroscope.ProfileType{
// Default
pyroscope.ProfileCPU,
pyroscope.ProfileAllocObjects,
pyroscope.ProfileAllocSpace,
pyroscope.ProfileInuseObjects,
pyroscope.ProfileInuseSpace,
// Optional
pyroscope.ProfileGoroutines,
pyroscope.ProfileMutexCount,
pyroscope.ProfileMutexDuration,
pyroscope.ProfileBlockCount,
pyroscope.ProfileBlockDuration,
},
})
inst.initServices = append(inst.initServices, svs)
}
}
}
// WithHTTPPrometheusService option with default value
func WithHTTPPrometheusService(enabled bool) Option {
return func(inst *Server) {

View File

@ -7,6 +7,7 @@ import (
"net/http"
"net/http/pprof"
pyroscope_pprof "github.com/grafana/pyroscope-go/http/pprof"
"go.uber.org/zap"
)
@ -20,7 +21,7 @@ func NewHTTPPProf(l *zap.Logger, name, addr, path string) *HTTP {
handler := http.NewServeMux()
handler.HandleFunc(path+"/", pprof.Index)
handler.HandleFunc(path+"/cmdline", pprof.Cmdline)
handler.HandleFunc(path+"/profile", pprof.Profile)
handler.HandleFunc(path+"/profile", pyroscope_pprof.Profile)
handler.HandleFunc(path+"/symbol", pprof.Symbol)
handler.HandleFunc(path+"/trace", pprof.Trace)
return NewHTTP(l, name, addr, handler)

35
service/pyroscope.go Normal file
View File

@ -0,0 +1,35 @@
//go:build !pprof
package service
import (
"context"
"sync/atomic"
otelpyroscope "github.com/grafana/otel-profiling-go"
"github.com/grafana/pyroscope-go"
"go.opentelemetry.io/otel"
"go.uber.org/zap"
)
type Pyroscope struct {
l *zap.Logger
name string
cfg pyroscope.Config
running atomic.Bool
profiler *pyroscope.Profiler
}
func NewPyroscope(l *zap.Logger, cfg pyroscope.Config) *GoRoutine {
otel.SetTracerProvider(otelpyroscope.NewTracerProvider(otel.GetTracerProvider()))
return NewGoRoutine(l, "pyroscope", func(ctx context.Context, l *zap.Logger) error {
p, err := pyroscope.Start(cfg)
if err != nil {
return err
}
select {
case <-ctx.Done():
return p.Stop()
}
})
}

View File

@ -2,10 +2,10 @@ package telemetry
const (
DefaultServiceName = "service"
DefaultTracerName = "github.com/foomo/keel/telemetry"
DefaultName = "github.com/foomo/keel/telemetry"
)
var (
TracerName = DefaultTracerName
Name = DefaultName
ServiceName = DefaultServiceName
)

View File

@ -22,7 +22,7 @@ var (
)
func Meter() metric.Meter {
return otel.Meter("")
return otel.Meter(Name)
}
func NewNoopMeterProvider() (metric.MeterProvider, error) {

27
telemetry/profile.go Normal file
View File

@ -0,0 +1,27 @@
package telemetry
import (
"fmt"
"go.uber.org/zap"
)
type PyroscopeLogger struct {
l *zap.Logger
}
func NewPyroscopeLogger(l *zap.Logger) *PyroscopeLogger {
return &PyroscopeLogger{l: l.Named("pyroscope")}
}
func (l *PyroscopeLogger) Infof(format string, a ...interface{}) {
l.l.Info(fmt.Sprintf(format, a...))
}
func (l *PyroscopeLogger) Debugf(format string, a ...interface{}) {
l.l.Debug(fmt.Sprintf(format, a...))
}
func (l *PyroscopeLogger) Errorf(format string, a ...interface{}) {
l.l.Error(fmt.Sprintf(format, a...))
}

View File

@ -17,7 +17,7 @@ import (
)
func Tracer() trace.Tracer {
return TraceProvider().Tracer(TracerName)
return TraceProvider().Tracer(Name)
}
func TraceProvider() trace.TracerProvider {