package server import ( "fmt" "log" "net/http" "net/http/httputil" "os" "time" ) type loggedRoundTripper struct { rt http.RoundTripper log HTTPLogger } func (c *loggedRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { c.log.LogRequest(request) startTime := time.Now() response, err := c.rt.RoundTrip(request) duration := time.Since(startTime) c.log.LogResponse(request, response, err, duration) return response, err } // NewLoggedTransport takes an http.RoundTripper and returns a new one that logs requests and responses func NewLoggedTransport(rt http.RoundTripper, log HTTPLogger) http.RoundTripper { return &loggedRoundTripper{rt: rt, log: log} } // HTTPLogger defines the interface to log http request and responses type HTTPLogger interface { LogRequest(*http.Request) LogResponse(*http.Request, *http.Response, error, time.Duration) } // DefaultLogger is an http logger that will use the standard logger in the log package to provide basic information about http responses type DefaultLogger struct { Dump bool } // LogRequest doens't do anything since we'll be logging replies only func (dl DefaultLogger) LogRequest(r *http.Request) { log.Printf("--> %s %s", r.Method, r.URL.String()) if dl.Dump { if data, err := httputil.DumpRequestOut(r, false); err == nil { os.Stderr.Write(data) fmt.Fprintf(os.Stderr, "\n\n") } } } // LogResponse logs path, host, status code and duration in milliseconds func (dl DefaultLogger) LogResponse(req *http.Request, res *http.Response, err error, duration time.Duration) { duration /= time.Millisecond if err != nil { log.Printf("<-- %s %s status=error durationMs=%d error=%q", req.Method, req.URL.String(), duration, err.Error()) } else { log.Printf("<-- %s %s status=%d durationMs=%d", req.Method, req.URL.String(), res.StatusCode, duration) if dl.Dump { if data, err := httputil.DumpResponse(res, false); err == nil { os.Stderr.Write(data) fmt.Fprintf(os.Stderr, "\n\n") } } } } // DefaultLoggedTransport wraps http.DefaultTransport to log using DefaultLogger var DefaultLoggedTransport = NewLoggedTransport(http.DefaultTransport, DefaultLogger{})