From d0568110d7f159ff177d823b8498367931a2a0c5 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 27 Mar 2026 13:12:46 -0700 Subject: [PATCH] Add request logging middleware and improve startup diagnostics - Request logger middleware logs method, path, status, duration for all non-static/health requests (those go to debug level) - Initial fetch complete log now includes repo and doc counts - Helps diagnose deployment and routing issues Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/server/server.go | 45 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/internal/server/server.go b/internal/server/server.go index b64e57b..4d3ce6a 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -93,6 +93,7 @@ func New(cfg Config) (*Server, error) { // Handler returns the chi router with all routes mounted. func (s *Server) Handler() http.Handler { r := chi.NewRouter() + r.Use(s.requestLogger) staticFS, err := fs.Sub(web.Content, "static") if err != nil { @@ -327,6 +328,43 @@ func (s *Server) renderError(w http.ResponseWriter, r *http.Request, code int, m s.render(w, r, "error.html", data, code) } +type statusWriter struct { + http.ResponseWriter + code int +} + +func (w *statusWriter) WriteHeader(code int) { + w.code = code + w.ResponseWriter.WriteHeader(code) +} + +func (s *Server) requestLogger(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + sw := &statusWriter{ResponseWriter: w, code: http.StatusOK} + next.ServeHTTP(sw, r) + duration := time.Since(start) + + // Skip static assets and health checks at info level + if strings.HasPrefix(r.URL.Path, "/static/") || r.URL.Path == "/health" { + s.log.Debug("request", + "method", r.Method, + "path", r.URL.Path, + "status", sw.code, + "duration", duration, + ) + return + } + + s.log.Info("request", + "method", r.Method, + "path", r.URL.Path, + "status", sw.code, + "duration", duration, + ) + }) +} + func verifyHMAC(body []byte, signature, secret string) bool { if signature == "" { return false @@ -393,7 +431,12 @@ func StartBackgroundFetch(ctx context.Context, cfg BackgroundConfig) { } } cfg.Cache.SetReady() - log.Info("initial fetch complete") + repos := cfg.Cache.ListRepos() + totalDocs := 0 + for _, r := range repos { + totalDocs += len(r.Docs) + } + log.Info("initial fetch complete", "repos", len(repos), "docs", totalDocs) break }