[ARVADOS] created: 1.3.0-1786-g7db3ceda1
Git user
git at public.curoverse.com
Wed Oct 23 20:01:14 UTC 2019
at 7db3ceda16742b65d73ebbc05d02351a5e0496bd (commit)
commit 7db3ceda16742b65d73ebbc05d02351a5e0496bd
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Oct 23 15:57:12 2019 -0400
15713: Include 1K of response body when logging error responses.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/sdk/go/httpserver/logger.go b/sdk/go/httpserver/logger.go
index f64708454..50191eb3f 100644
--- a/sdk/go/httpserver/logger.go
+++ b/sdk/go/httpserver/logger.go
@@ -78,11 +78,15 @@ func logResponse(w *responseTimer, req *http.Request, lgr *logrus.Entry) {
if respCode == 0 {
respCode = http.StatusOK
}
- lgr.WithFields(logrus.Fields{
+ fields := logrus.Fields{
"respStatusCode": respCode,
"respStatus": http.StatusText(respCode),
"respBytes": w.WroteBodyBytes(),
- }).Info("response")
+ }
+ if respCode >= 400 {
+ fields["respBody"] = string(w.Sniffed())
+ }
+ lgr.WithFields(fields).Info("response")
}
type responseTimer struct {
diff --git a/sdk/go/httpserver/logger_test.go b/sdk/go/httpserver/logger_test.go
index 3b2bc7758..eb71fcd81 100644
--- a/sdk/go/httpserver/logger_test.go
+++ b/sdk/go/httpserver/logger_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"encoding/json"
+ "fmt"
"net/http"
"net/http/httptest"
"testing"
@@ -24,17 +25,23 @@ func Test(t *testing.T) {
var _ = check.Suite(&Suite{})
-type Suite struct{}
+type Suite struct {
+ ctx context.Context
+ log *logrus.Logger
+ logdata *bytes.Buffer
+}
-func (s *Suite) TestLogRequests(c *check.C) {
- captured := &bytes.Buffer{}
- log := logrus.New()
- log.Out = captured
- log.Formatter = &logrus.JSONFormatter{
+func (s *Suite) SetUpTest(c *check.C) {
+ s.logdata = bytes.NewBuffer(nil)
+ s.log = logrus.New()
+ s.log.Out = s.logdata
+ s.log.Formatter = &logrus.JSONFormatter{
TimestampFormat: time.RFC3339Nano,
}
- ctx := ctxlog.Context(context.Background(), log)
+ s.ctx = ctxlog.Context(context.Background(), s.log)
+}
+func (s *Suite) TestLogRequests(c *check.C) {
h := AddRequestIDs(LogRequests(
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello world"))
@@ -45,9 +52,9 @@ func (s *Suite) TestLogRequests(c *check.C) {
c.Assert(err, check.IsNil)
resp := httptest.NewRecorder()
- HandlerWithContext(ctx, h).ServeHTTP(resp, req)
+ HandlerWithContext(s.ctx, h).ServeHTTP(resp, req)
- dec := json.NewDecoder(captured)
+ dec := json.NewDecoder(s.logdata)
gotReq := make(map[string]interface{})
err = dec.Decode(&gotReq)
@@ -72,3 +79,46 @@ func (s *Suite) TestLogRequests(c *check.C) {
c.Check(gotResp[key].(float64), check.Not(check.Equals), float64(0))
}
}
+
+func (s *Suite) TestLogErrorBody(c *check.C) {
+ dec := json.NewDecoder(s.logdata)
+
+ for _, trial := range []struct {
+ label string
+ statusCode int
+ sentBody string
+ expectLog bool
+ expectBody string
+ }{
+ {"ok", 200, "hello world", false, ""},
+ {"redir", 302, "<a href='http://foo.example/baz'>redir</a>", false, ""},
+ {"4xx short body", 400, "oops", true, "oops"},
+ {"4xx long body", 400, fmt.Sprintf("%0*d", sniffBytes*2, 1), true, fmt.Sprintf("%0*d", sniffBytes, 0)},
+ {"5xx empty body", 500, "", true, ""},
+ } {
+ comment := check.Commentf("in trial: %q", trial.label)
+
+ req, err := http.NewRequest("GET", "https://foo.example/bar", nil)
+ c.Assert(err, check.IsNil)
+ resp := httptest.NewRecorder()
+
+ HandlerWithContext(s.ctx, LogRequests(
+ http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ w.WriteHeader(trial.statusCode)
+ w.Write([]byte(trial.sentBody))
+ }),
+ )).ServeHTTP(resp, req)
+
+ gotReq := make(map[string]interface{})
+ err = dec.Decode(&gotReq)
+ c.Logf("%#v", gotReq)
+ gotResp := make(map[string]interface{})
+ err = dec.Decode(&gotResp)
+ c.Logf("%#v", gotResp)
+ if trial.expectLog {
+ c.Check(gotResp["respBody"], check.Equals, trial.expectBody, comment)
+ } else {
+ c.Check(gotResp["respBody"], check.IsNil, comment)
+ }
+ }
+}
diff --git a/sdk/go/httpserver/responsewriter.go b/sdk/go/httpserver/responsewriter.go
index 8dea759cc..26aee65c5 100644
--- a/sdk/go/httpserver/responsewriter.go
+++ b/sdk/go/httpserver/responsewriter.go
@@ -8,10 +8,13 @@ import (
"net/http"
)
+const sniffBytes = 1024
+
type ResponseWriter interface {
http.ResponseWriter
WroteStatus() int
WroteBodyBytes() int
+ Sniffed() []byte
}
// responseWriter wraps http.ResponseWriter and exposes the status
@@ -22,6 +25,7 @@ type responseWriter struct {
wroteStatus int // Last status given to WriteHeader()
wroteBodyBytes int // Bytes successfully written
err error // Last error returned from Write()
+ sniffed []byte
}
func WrapResponseWriter(orig http.ResponseWriter) ResponseWriter {
@@ -44,6 +48,7 @@ func (w *responseWriter) Write(data []byte) (n int, err error) {
if w.wroteStatus == 0 {
w.WriteHeader(http.StatusOK)
}
+ w.sniff(data)
n, err = w.ResponseWriter.Write(data)
w.wroteBodyBytes += n
w.err = err
@@ -61,3 +66,17 @@ func (w *responseWriter) WroteBodyBytes() int {
func (w *responseWriter) Err() error {
return w.err
}
+
+func (w *responseWriter) sniff(data []byte) {
+ max := sniffBytes - len(w.sniffed)
+ if max <= 0 {
+ return
+ } else if max < len(data) {
+ data = data[:max]
+ }
+ w.sniffed = append(w.sniffed, data...)
+}
+
+func (w *responseWriter) Sniffed() []byte {
+ return w.sniffed
+}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list