Skip to content

Commit

Permalink
Improve logging of http errors (#115)
Browse files Browse the repository at this point in the history
* Improve http request logging

When logging an http request, e.g. on 500 error, put all the headers
and response on one line.

Also strip cookies and csrf tokens on security grounds.

* Add a test to generate an http error to check formatting
  • Loading branch information
bboreham authored Aug 20, 2018
1 parent 8f75acd commit c1317dc
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 8 deletions.
28 changes: 20 additions & 8 deletions middleware/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package middleware
import (
"bytes"
"net/http"
"net/http/httputil"
"time"

"github.com/weaveworks/common/logging"
Expand All @@ -27,7 +26,7 @@ func (l Log) Wrap(next http.Handler) http.Handler {
begin := time.Now()
uri := r.RequestURI // capture the URI before running next, as it may get rewritten
// Log headers before running 'next' in case other interceptors change the data.
headers, err := httputil.DumpRequest(r, false)
headers, err := dumpRequest(r)
if err != nil {
headers = nil
l.logWithRequest(r).Errorf("Could not dump request headers: %v", err)
Expand All @@ -39,14 +38,11 @@ func (l Log) Wrap(next http.Handler) http.Handler {
if 100 <= statusCode && statusCode < 500 || statusCode == http.StatusBadGateway || statusCode == http.StatusServiceUnavailable {
l.logWithRequest(r).Debugf("%s %s (%d) %s", r.Method, uri, statusCode, time.Since(begin))
if l.LogRequestHeaders && headers != nil {
l.logWithRequest(r).Debugf("Is websocket request: %v\n%s", IsWSHandshakeRequest(r), string(headers))
l.logWithRequest(r).Debugf("ws: %v; %s", IsWSHandshakeRequest(r), string(headers))
}
} else {
l.logWithRequest(r).Warnf("%s %s (%d) %s", r.Method, uri, statusCode, time.Since(begin))
if headers != nil {
l.logWithRequest(r).Warnf("Is websocket request: %v\n%s", IsWSHandshakeRequest(r), string(headers))
}
l.logWithRequest(r).Warnf("Response: %s", buf.Bytes())
l.logWithRequest(r).Warnf("%s %s (%d) %s Response: %q ws: %v; %s",
r.Method, uri, statusCode, time.Since(begin), buf.Bytes(), IsWSHandshakeRequest(r), headers)
}
})
}
Expand All @@ -56,3 +52,19 @@ func (l Log) Wrap(next http.Handler) http.Handler {
var Logging = Log{
Log: logging.Global(),
}

func dumpRequest(req *http.Request) ([]byte, error) {
var b bytes.Buffer

// Exclude some headers for security, or just that we don't need them when debugging
err := req.Header.WriteSubset(&b, map[string]bool{
"Cookie": true,
"X-Csrf-Token": true,
})
if err != nil {
return nil, err
}

ret := bytes.Replace(b.Bytes(), []byte("\r\n"), []byte("; "), -1)
return ret, nil
}
27 changes: 27 additions & 0 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"github.com/weaveworks/common/httpgrpc"
"github.com/weaveworks/common/logging"
"github.com/weaveworks/common/middleware"
"golang.org/x/net/context"
)

Expand Down Expand Up @@ -131,3 +133,28 @@ func TestRunReturnsError(t *testing.T) {
require.NotNil(t, <-errChan)
})
}

// Test to see what the logging of a 500 error looks like
func TestMiddlewareLogging(t *testing.T) {
var level logging.Level
level.Set("info")
cfg := Config{
HTTPListenPort: 9192,
HTTPMiddleware: []middleware.Interface{middleware.Logging},
MetricsNamespace: "testing_logging",
LogLevel: level,
}
server, err := New(cfg)
require.NoError(t, err)

server.HTTP.HandleFunc("/error500", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
})

go server.Run()
defer server.Shutdown()

req, err := http.NewRequest("GET", "http://127.0.0.1:9192/error500", nil)
require.NoError(t, err)
http.DefaultClient.Do(req)
}

0 comments on commit c1317dc

Please # to comment.