aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-23 15:38:08 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-23 15:38:08 +0200
commit2d8503cafb9f63ca4cb63f041281987f9a63f16a (patch)
treec90cb273473f265b1d0beb1004d9b0e4dfbeee03 /web
parent1125e0b7086dd2b71e0558aaf641c23dea02f200 (diff)
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.
Diffstat (limited to 'web')
-rw-r--r--web/server.go51
1 files changed, 43 insertions, 8 deletions
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())
}