Commit 9c11b550 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: reuse request logger for static files, 404s, 405s

parent fb75773a
......@@ -291,7 +291,7 @@ func (s *Server) HandleProtocolMessage(
var start time.Time
if s.conf.Verbose >= 2 {
start = time.Now()
server.LogRequest(method, path, "", "", http.Header(headers), message)
server.LogRequest("client", method, path, "", "", http.Header(headers), message)
}
status, output, result := s.handleProtocolMessage(path, method, headers, message)
......
......@@ -397,11 +397,14 @@ func LogWarning(err error) error {
return log(logrus.WarnLevel, err)
}
func LogRequest(method, url, proto, from string, headers http.Header, message []byte) {
func LogRequest(typ, method, url, proto, from string, headers http.Header, message []byte) {
fields := logrus.Fields{
"method": method,
"url": url,
"headers": ToJson(headers),
"type": typ,
"method": method,
"url": url,
}
if len(headers) > 0 {
fields["headers"] = headers
}
if len(message) > 0 {
fields["message"] = string(message)
......@@ -416,11 +419,19 @@ func LogRequest(method, url, proto, from string, headers http.Header, message []
}
func LogResponse(status int, duration time.Duration, response []byte) {
Logger.WithFields(logrus.Fields{
fields := logrus.Fields{
"status": status,
"duration": duration.String(),
"response": string(response),
}).Tracef("<= response")
}
if len(response) > 0 {
fields["response"] = string(response)
}
l := Logger.WithFields(fields)
if status < 400 {
l.Trace("<= response")
} else {
l.Warn("<= response")
}
}
func ToJson(o interface{}) string {
......
......@@ -13,7 +13,6 @@ import (
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
......@@ -187,12 +186,15 @@ func (s *Server) Handler() http.Handler {
}
}
router.NotFound(s.logHandler("requestor", false, true, true)(router.NotFoundHandler()).ServeHTTP)
router.MethodNotAllowed(s.logHandler("requestor", false, true, true)(router.MethodNotAllowedHandler()).ServeHTTP)
// Group main API endpoints, so we can attach our request/response logger to it
// while not adding it to the endpoints already added above (which do their own logging).
router.Group(func(r chi.Router) {
r.Use(cors.New(corsOptions).Handler)
if s.conf.Verbose >= 2 {
r.Use(s.logHandler)
r.Use(s.logHandler("requestor", true, true, true))
}
// Server routes
......@@ -213,38 +215,52 @@ func (s *Server) Handler() http.Handler {
}
// logHandler is middleware for logging HTTP requests and responses.
func (s *Server) logHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var message []byte
var err error
// Read r.Body, and then replace with a fresh ReadCloser for the next handler
if message, err = ioutil.ReadAll(r.Body); err != nil {
message = []byte("<failed to read body: " + err.Error() + ">")
}
_ = r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(message))
server.LogRequest(r.Method, r.URL.String(), r.Proto, r.RemoteAddr, r.Header, message)
// copy output of HTTP handler to our buffer for later logging
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
respBuf := new(bytes.Buffer)
ww.Tee(respBuf)
func (s *Server) logHandler(typ string, logResponse, logHeaders, logFrom bool) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var message []byte
var err error
// Read r.Body, and then replace with a fresh ReadCloser for the next handler
if message, err = ioutil.ReadAll(r.Body); err != nil {
message = []byte("<failed to read body: " + err.Error() + ">")
}
_ = r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(message))
// print response after HTTP is served
start := time.Now()
defer func() {
var resp []byte
if ww.BytesWritten() > 0 {
resp = respBuf.Bytes()
var headers http.Header
var from string
if logHeaders {
headers = r.Header
}
if logFrom {
from = r.RemoteAddr
}
server.LogRequest(typ, r.Method, r.URL.String(), r.Proto, from, headers, message)
// copy output of HTTP handler to our buffer for later logging
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
var buf *bytes.Buffer
if logResponse {
buf = new(bytes.Buffer)
ww.Tee(buf)
}
server.LogResponse(ww.Status(), time.Since(start), resp)
}()
next.ServeHTTP(ww, r)
return
})
// print response afterwards
var resp []byte
var start time.Time
defer func() {
if logResponse && ww.BytesWritten() > 0 {
resp = buf.Bytes()
}
server.LogResponse(ww.Status(), time.Since(start), resp)
}()
// start timer and preform request
start = time.Now()
next.ServeHTTP(ww, r)
})
}
}
func (s *Server) StaticFilesHandler() http.Handler {
......@@ -254,12 +270,9 @@ func (s *Server) StaticFilesHandler() http.Handler {
} else { // URL not known, don't log it but otherwise continue
s.conf.Logger.Infof("Hosting files at %s", s.conf.StaticPath)
}
// Hook up chi middleware logger with our own logger
middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{
Logger: log.New(s.conf.Logger.WriterLevel(logrus.TraceLevel), "static: ", 0),
NoColor: true,
})
return http.StripPrefix(s.conf.StaticPrefix, middleware.Logger(http.FileServer(http.Dir(s.conf.StaticPath))))
return http.StripPrefix(s.conf.StaticPrefix, s.logHandler("static", false, false, false)(
http.FileServer(http.Dir(s.conf.StaticPath))),
)
}
func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment