diff --git a/cmd/pgopher/main.go b/cmd/pgopher/main.go index b9af56c..07ad7e5 100644 --- a/cmd/pgopher/main.go +++ b/cmd/pgopher/main.go @@ -2,8 +2,11 @@ package main import ( "context" + "errors" "flag" "log/slog" + "net/http" + "net/http/pprof" "os" "os/signal" "syscall" @@ -16,6 +19,7 @@ var ( logLevel = flag.String("log-level", "INFO", "log level") logJsonFormat = flag.Bool("log-json", false, "log in json format") cfgFile = flag.String("config", "pgopher.yml", "config file") + pprofEnabled = flag.Bool("pprof", false, "enable pprof endpoint") ) func init() { @@ -64,6 +68,21 @@ func main() { slog.Debug("loaded configuration", slog.String("file", *cfgFile), slog.Any("config", cfg)) + if *pprofEnabled { + go func() { + pprofMux := http.NewServeMux() + pprofMux.HandleFunc("/debug/pprof/profile", pprof.Profile) + pprofServer := &http.Server{Addr: cfg.PprofListenAddress, Handler: pprofMux} + + slog.Info("starting pprof server", slog.String("listenAddr", cfg.PprofListenAddress)) + + pprofErr := pprofServer.ListenAndServe() + if pprofErr != nil && !errors.Is(err, http.ErrServerClosed) { + slog.Error("pprof server failed", slog.String("err", pprofErr.Error())) + } + }() + } + err = pgopher.NewServer(cfg).Run(ctx) if err != nil { slog.Error("failed to run server", slog.String("err", err.Error())) diff --git a/internal/pgopher/config.go b/internal/pgopher/config.go index 9a86280..e0539e6 100644 --- a/internal/pgopher/config.go +++ b/internal/pgopher/config.go @@ -3,9 +3,10 @@ package pgopher import "time" type Config struct { - ListenAddress string `yaml:"listenAddress"` - ProfilingTargets []ProfilingTarget `yaml:"profilingTargets"` - Sink Sink `yaml:"sink"` + ListenAddress string `yaml:"listenAddress"` + PprofListenAddress string `yaml:"pprofListenAddress"` + ProfilingTargets []ProfilingTarget `yaml:"profilingTargets"` + Sink Sink `yaml:"sink"` } type ProfilingTarget struct { @@ -26,8 +27,9 @@ type FileSinkOptions struct { func DefaultConfig() Config { return Config{ - ListenAddress: ":8000", - ProfilingTargets: make([]ProfilingTarget, 0), + ListenAddress: ":8000", + PprofListenAddress: "localhost:8010", + ProfilingTargets: make([]ProfilingTarget, 0), Sink: Sink{ Type: "file", FileSinkOptions: FileSinkOptions{ diff --git a/internal/pgopher/pgopher.go b/internal/pgopher/pgopher.go index fdc6ea4..babc53b 100644 --- a/internal/pgopher/pgopher.go +++ b/internal/pgopher/pgopher.go @@ -25,13 +25,15 @@ func (s *Server) Run(ctx context.Context) error { go s.startScheduler(ctx, wg) - http.HandleFunc("/_ready", readinessProbe) - http.HandleFunc("/_live", livenessProbe) - http.HandleFunc("/api/v1/profile/", s.handleProfile) + mux := http.NewServeMux() + + mux.HandleFunc("/_ready", readinessProbe) + mux.HandleFunc("/_live", livenessProbe) + mux.HandleFunc("/api/v1/profile/", s.handleProfile) httpServer := &http.Server{ Addr: s.cfg.ListenAddress, - Handler: http.DefaultServeMux, + Handler: mux, } go func() { diff --git a/pgopher.yml b/pgopher.yml index 4d71d47..cdd3b81 100644 --- a/pgopher.yml +++ b/pgopher.yml @@ -1,6 +1,7 @@ listenAddress: ":8000" +pprofListenAddress: "localhost:8010" profilingTargets: - - name: test + - name: self url: http://localhost:8010/debug/pprof/profile duration: 5s schedule: "* * * * *"