package infra import ( "bufio" "fmt" "io" "net" "net/http" "time" ) // responseWriter captura el status code escrito al ResponseWriter. // Implementa http.Hijacker y http.Flusher delegando al writer subyacente // para preservar WebSocket upgrade y SSE. type responseWriter struct { http.ResponseWriter status int } func (rw *responseWriter) WriteHeader(status int) { rw.status = status rw.ResponseWriter.WriteHeader(status) } // Hijack delega en el ResponseWriter subyacente si lo soporta. Necesario // para que WebSocket Upgrade funcione a traves del logger middleware. func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { if h, ok := rw.ResponseWriter.(http.Hijacker); ok { return h.Hijack() } return nil, nil, http.ErrNotSupported } // Flush delega en el ResponseWriter subyacente si lo soporta. Necesario // para SSE y respuestas streaming. func (rw *responseWriter) Flush() { if f, ok := rw.ResponseWriter.(http.Flusher); ok { f.Flush() } } // HTTPLoggerMiddleware retorna un Middleware que loguea metodo, path, status y duracion de cada request. // El formato de cada linea es: METHOD /path STATUS DURACIONms func HTTPLoggerMiddleware(logger io.Writer) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() rw := &responseWriter{ResponseWriter: w, status: http.StatusOK} next.ServeHTTP(rw, r) duration := time.Since(start) fmt.Fprintf(logger, "%s %s %d %dms\n", r.Method, r.URL.Path, rw.status, duration.Milliseconds()) }) } }