From 2d8503cafb9f63ca4cb63f041281987f9a63f16a Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sun, 23 Aug 2026 15:38:08 +0200 Subject: Add on-demand pprof profiling grouped by route Expose pprof at /debug/pprof via -pprof flag, tag each handler with runtime/pprof route label, add handleRoute helper to avoid repeating patterns, and document UI workflow in README. --- web/server.go | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) (limited to 'web') diff --git a/web/server.go b/web/server.go index 198cb44..26dbb06 100644 --- a/web/server.go +++ b/web/server.go @@ -1,8 +1,11 @@ package web import ( + "context" "database/sql" "net/http" + http_pprof "net/http/pprof" + runtime_pprof "runtime/pprof" "sync" "time" @@ -21,6 +24,7 @@ type Server struct { returnToURL string syncMu sync.Mutex syncActive bool + enablePprof bool } type syncClient interface { @@ -32,20 +36,51 @@ func NewServer(db *sql.DB, configPath string) *Server { return &Server{db: db, configPath: configPath} } +func (s *Server) WithPprof(enabled bool) *Server { + s.enablePprof = enabled + return s +} + func (s *Server) Handler() http.Handler { mux := http.NewServeMux() mux.Handle("GET /static/", http.FileServer(http.FS(staticFiles))) - mux.HandleFunc("GET /", s.handleRides) - mux.HandleFunc("GET /rides/{id}", s.handleRide) - mux.HandleFunc("GET /analytics", s.handleAnalytics) - mux.HandleFunc("POST /rides/{id}/official-climbs", s.handleCreateOfficialClimb) - mux.HandleFunc("GET /sync", s.handleSyncForm) - mux.HandleFunc("POST /sync", s.handleSync) - mux.HandleFunc("GET /strava/login", s.handleStravaLogin) - mux.HandleFunc("GET /strava/callback", s.handleStravaCallback) + handleRoute(mux, "GET /", s.handleRides) + handleRoute(mux, "GET /rides/{id}", s.handleRide) + handleRoute(mux, "GET /analytics", s.handleAnalytics) + handleRoute(mux, "POST /rides/{id}/official-climbs", s.handleCreateOfficialClimb) + handleRoute(mux, "GET /sync", s.handleSyncForm) + handleRoute(mux, "POST /sync", s.handleSync) + handleRoute(mux, "GET /strava/login", s.handleStravaLogin) + handleRoute(mux, "GET /strava/callback", s.handleStravaCallback) + if s.enablePprof { + registerPprof(mux) + } return kcore.RecoverMiddleware(mux) } +func handleRoute(mux *http.ServeMux, pattern string, handler http.HandlerFunc) { + mux.HandleFunc(pattern, withPprofLabel(pattern, handler)) +} + +func withPprofLabel(route string, next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + runtime_pprof.Do(r.Context(), runtime_pprof.Labels("route", route), func(ctx context.Context) { + next(w, r.WithContext(ctx)) + }) + } +} + +func registerPprof(mux *http.ServeMux) { + mux.HandleFunc("GET /debug/pprof/", http_pprof.Index) + mux.HandleFunc("GET /debug/pprof/cmdline", http_pprof.Cmdline) + mux.HandleFunc("GET /debug/pprof/profile", http_pprof.Profile) + mux.HandleFunc("GET /debug/pprof/symbol", http_pprof.Symbol) + mux.HandleFunc("GET /debug/pprof/trace", http_pprof.Trace) + for _, name := range []string{"allocs", "block", "goroutine", "heap", "mutex", "threadcreate"} { + mux.Handle("GET /debug/pprof/"+name, http_pprof.Handler(name)) + } +} + func (s *Server) ListenAndServe(addr string) error { return http.ListenAndServe(addr, s.Handler()) } -- cgit v1.3