4 // LoggingResponseWriter
13 // LoggingResponseWriter has anonymous fields ResponseWriter and ResponseBody
14 type LoggingResponseWriter struct {
22 // CloseNotify implements http.CloseNotifier.
23 func (resp *LoggingResponseWriter) CloseNotify() <-chan bool {
24 wrapped, ok := resp.ResponseWriter.(http.CloseNotifier)
26 // If upstream doesn't implement CloseNotifier, we can
27 // satisfy the interface by returning a channel that
28 // never sends anything (the interface doesn't
29 // guarantee that anything will ever be sent on the
30 // channel even if the client disconnects).
33 return wrapped.CloseNotify()
36 // WriteHeader writes header to ResponseWriter
37 func (resp *LoggingResponseWriter) WriteHeader(code int) {
38 if resp.sentHdr == zeroTime {
39 resp.sentHdr = time.Now()
42 resp.ResponseWriter.WriteHeader(code)
45 var zeroTime time.Time
47 func (resp *LoggingResponseWriter) Write(data []byte) (int, error) {
48 if resp.Length == 0 && len(data) > 0 && resp.sentHdr == zeroTime {
49 resp.sentHdr = time.Now()
51 resp.Length += len(data)
52 if resp.Status >= 400 {
53 resp.ResponseBody += string(data)
55 return resp.ResponseWriter.Write(data)
58 // LoggingRESTRouter is used to add logging capabilities to mux.Router
59 type LoggingRESTRouter struct {
63 func (loggingRouter *LoggingRESTRouter) ServeHTTP(wrappedResp http.ResponseWriter, req *http.Request) {
65 resp := LoggingResponseWriter{http.StatusOK, 0, wrappedResp, "", zeroTime}
66 loggingRouter.router.ServeHTTP(&resp, req)
67 statusText := http.StatusText(resp.Status)
68 if resp.Status >= 400 {
69 statusText = strings.Replace(resp.ResponseBody, "\n", "", -1)
73 tLatency := resp.sentHdr.Sub(t0)
74 tResponse := now.Sub(resp.sentHdr)
75 log.Printf("[%s] %s %s %d %.6fs %.6fs %.6fs %d %d \"%s\"", req.RemoteAddr, req.Method, req.URL.Path[1:], req.ContentLength, tTotal.Seconds(), tLatency.Seconds(), tResponse.Seconds(), resp.Status, resp.Length, statusText)