github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/http/logs/logs.go (about) 1 // Package logs provides HTTP request and response logging. 2 package logs 3 4 import ( 5 "net/http" 6 "strconv" 7 "time" 8 9 "github.com/apex/log" 10 11 "github.com/apex/up" 12 "github.com/apex/up/internal/logs" 13 "github.com/apex/up/internal/util" 14 ) 15 16 // TODO: optional verbose mode with req/res header etc? 17 18 // log context. 19 var ctx = logs.Plugin("logs") 20 21 // response wrapper. 22 type response struct { 23 http.ResponseWriter 24 written int 25 code int 26 duration time.Duration 27 } 28 29 // Write implementation. 30 func (r *response) Write(b []byte) (int, error) { 31 n, err := r.ResponseWriter.Write(b) 32 r.written += n 33 return n, err 34 } 35 36 // WriteHeader implementation. 37 func (r *response) WriteHeader(code int) { 38 r.code = code 39 r.ResponseWriter.WriteHeader(code) 40 } 41 42 // New logs handler. 43 func New(c *up.Config, next http.Handler) (http.Handler, error) { 44 if c.Logs.Disable { 45 return next, nil 46 } 47 48 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 49 ctx := logContext(r) 50 logRequest(ctx, r) 51 52 start := time.Now() 53 res := &response{ResponseWriter: w, code: 200} 54 next.ServeHTTP(res, r) 55 res.duration = time.Since(start) 56 57 logResponse(ctx, res, r) 58 }) 59 60 return h, nil 61 } 62 63 // logContext returns the common log context for a request. 64 func logContext(r *http.Request) log.Interface { 65 return ctx.WithFields(log.Fields{ 66 "id": r.Header.Get("X-Request-Id"), 67 "method": r.Method, 68 "path": r.URL.Path, 69 "query": r.URL.Query().Encode(), 70 "ip": r.RemoteAddr, 71 }) 72 } 73 74 // logRequest logs the request. 75 func logRequest(ctx log.Interface, r *http.Request) { 76 if s := r.Header.Get("Content-Length"); s != "" { 77 n, err := strconv.Atoi(s) 78 if err == nil { 79 ctx = ctx.WithField("size", n) 80 } 81 } 82 83 ctx.Info("request") 84 } 85 86 // logResponse logs the response. 87 func logResponse(ctx log.Interface, res *response, r *http.Request) { 88 ctx = ctx.WithFields(log.Fields{ 89 "duration": util.Milliseconds(res.duration), 90 "size": res.written, 91 "status": res.code, 92 }) 93 94 switch { 95 case res.code >= 500: 96 ctx.Error("response") 97 case res.code >= 400: 98 ctx.Warn("response") 99 default: 100 ctx.Info("response") 101 } 102 }