diff --git a/cmd/go-httpbin/main.go b/cmd/go-httpbin/main.go index f49403ed765e14a2569b7afaee2b51617d34d219..5f0a873724a43f004082f3ee289d125725d7997e 100644 --- a/cmd/go-httpbin/main.go +++ b/cmd/go-httpbin/main.go @@ -27,6 +27,8 @@ func main() { flag.DurationVar(&maxDuration, "max-duration", httpbin.DefaultMaxDuration, "Maximum duration a response may take") flag.Parse() + log.SetFlags(log.Ldate | log.Lmicroseconds | log.LUTC) + // Command line flags take precedence over environment vars, so we only // check for environment vars if we have default values for our command // line flags. @@ -62,6 +64,6 @@ func main() { }) listenAddr := net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) - log.Printf("listening on port %s", listenAddr) + log.Printf("addr=%s", listenAddr) log.Fatal(http.ListenAndServe(listenAddr, h.Handler())) } diff --git a/httpbin/middleware.go b/httpbin/middleware.go index 5e896a08de26efb473acf9d2aaed07878e0af363..d4a56afb657a982d6e302904dd8dc5ca8fd63ae8 100644 --- a/httpbin/middleware.go +++ b/httpbin/middleware.go @@ -4,15 +4,9 @@ import ( "fmt" "log" "net/http" + "time" ) -func logger(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.ServeHTTP(w, r) - log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL) - }) -} - func cors(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") @@ -56,3 +50,52 @@ func limitRequestSize(maxSize int64, h http.Handler) http.Handler { h.ServeHTTP(w, r) }) } + +// metaResponseWriter implements is an http.ResponseWriter and http.Flusher +// that records its status code and body size for logging purposes. +type metaResponseWriter struct { + w http.ResponseWriter + status int + size int +} + +func (mw *metaResponseWriter) Write(b []byte) (int, error) { + size, err := mw.w.Write(b) + mw.size += size + return size, err +} + +func (mw *metaResponseWriter) WriteHeader(s int) { + mw.w.WriteHeader(s) + mw.status = s +} + +func (mw *metaResponseWriter) Flush() { + f := mw.w.(http.Flusher) + f.Flush() +} + +func (mw *metaResponseWriter) Header() http.Header { + return mw.w.Header() +} + +func (mw *metaResponseWriter) Status() int { + if mw.status == 0 { + return http.StatusOK + } + return mw.status +} + +func (mw *metaResponseWriter) Size() int { + return mw.size +} + +func logger(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mw := &metaResponseWriter{w: w} + t := time.Now() + h.ServeHTTP(mw, r) + duration := time.Now().Sub(t) + log.Printf("status=%d method=%s uri=%q size=%d duration=%s", mw.Status(), r.Method, r.URL.RequestURI(), mw.Size(), duration) + }) +}