update: limit logBrief http body size (#3498)
Co-authored-by: 常公征 <changgz@yealink.com>
This commit is contained in:
@@ -28,6 +28,16 @@ func DupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
|
|||||||
return io.NopCloser(tee), io.NopCloser(&buf)
|
return io.NopCloser(tee), io.NopCloser(&buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LimitDupReadCloser returns two io.ReadCloser that read from the first will be written to the second.
|
||||||
|
// But the second io.ReadCloser is limited to up to n bytes.
|
||||||
|
// The first returned reader needs to be read first, because the content
|
||||||
|
// read from it will be written to the underlying buffer of the second reader.
|
||||||
|
func LimitDupReadCloser(reader io.ReadCloser, n int64) (io.ReadCloser, io.ReadCloser) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
tee := LimitTeeReader(reader, &buf, n)
|
||||||
|
return io.NopCloser(tee), io.NopCloser(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
// KeepSpace customizes the reading functions to keep leading and tailing spaces.
|
// KeepSpace customizes the reading functions to keep leading and tailing spaces.
|
||||||
func KeepSpace() TextReadOption {
|
func KeepSpace() TextReadOption {
|
||||||
return func(o *textReadOptions) {
|
return func(o *textReadOptions) {
|
||||||
|
|||||||
@@ -108,6 +108,29 @@ func TestDupReadCloser(t *testing.T) {
|
|||||||
verify(r2)
|
verify(r2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLimitDupReadCloser(t *testing.T) {
|
||||||
|
input := "hello world"
|
||||||
|
limitBytes := int64(4)
|
||||||
|
reader := io.NopCloser(bytes.NewBufferString(input))
|
||||||
|
r1, r2 := LimitDupReadCloser(reader, limitBytes)
|
||||||
|
verify := func(r io.Reader) {
|
||||||
|
output, err := io.ReadAll(r)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, input, string(output))
|
||||||
|
}
|
||||||
|
verifyLimit := func(r io.Reader, limit int64) {
|
||||||
|
output, err := io.ReadAll(r)
|
||||||
|
if limit < int64(len(input)) {
|
||||||
|
input = input[:limit]
|
||||||
|
}
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, input, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(r1)
|
||||||
|
verifyLimit(r2, limitBytes)
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadBytes(t *testing.T) {
|
func TestReadBytes(t *testing.T) {
|
||||||
reader := io.NopCloser(bytes.NewBufferString("helloworld"))
|
reader := io.NopCloser(bytes.NewBufferString("helloworld"))
|
||||||
buf := make([]byte, 5)
|
buf := make([]byte, 5)
|
||||||
|
|||||||
33
core/iox/tee.go
Normal file
33
core/iox/tee.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package iox
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// LimitTeeReader returns a Reader that writes up to n bytes to w what it reads from r.
|
||||||
|
// First n bytes reads from r performed through it are matched with
|
||||||
|
// corresponding writes to w. There is no internal buffering -
|
||||||
|
// the write must complete before the first n bytes read completes.
|
||||||
|
// Any error encountered while writing is reported as a read error.
|
||||||
|
func LimitTeeReader(r io.Reader, w io.Writer, n int64) io.Reader {
|
||||||
|
return &limitTeeReader{r, w, n}
|
||||||
|
}
|
||||||
|
|
||||||
|
type limitTeeReader struct {
|
||||||
|
r io.Reader
|
||||||
|
w io.Writer
|
||||||
|
n int64 // limit bytes remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *limitTeeReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = t.r.Read(p)
|
||||||
|
if n > 0 && t.n > 0 {
|
||||||
|
limit := int64(n)
|
||||||
|
if limit > t.n {
|
||||||
|
limit = t.n
|
||||||
|
}
|
||||||
|
if n, err := t.w.Write(p[:limit]); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
t.n -= limit
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
35
core/iox/tee_test.go
Normal file
35
core/iox/tee_test.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package iox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLimitTeeReader(t *testing.T) {
|
||||||
|
limit := int64(4)
|
||||||
|
src := []byte("hello, world")
|
||||||
|
dst := make([]byte, len(src))
|
||||||
|
rb := bytes.NewBuffer(src)
|
||||||
|
wb := new(bytes.Buffer)
|
||||||
|
r := LimitTeeReader(rb, wb, limit)
|
||||||
|
if n, err := io.ReadFull(r, dst); err != nil || n != len(src) {
|
||||||
|
t.Fatalf("ReadFull(r, dst) = %d, %v; want %d, nil", n, err, len(src))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(dst, src) {
|
||||||
|
t.Errorf("bytes read = %q want %q", dst, src)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(wb.Bytes(), src[:limit]) {
|
||||||
|
t.Errorf("bytes written = %q want %q", wb.Bytes(), src)
|
||||||
|
}
|
||||||
|
if n, err := r.Read(dst); n != 0 || err != io.EOF {
|
||||||
|
t.Errorf("r.Read at EOF = %d, %v want 0, EOF", n, err)
|
||||||
|
}
|
||||||
|
rb = bytes.NewBuffer(src)
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
pr.Close()
|
||||||
|
r = LimitTeeReader(rb, pw, limit)
|
||||||
|
if n, err := io.ReadFull(r, dst); n != 0 || err != io.ErrClosedPipe {
|
||||||
|
t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/color"
|
"github.com/zeromicro/go-zero/core/color"
|
||||||
@@ -39,7 +38,7 @@ func LogHandler(next http.Handler) http.Handler {
|
|||||||
lrw := response.NewWithCodeResponseWriter(w)
|
lrw := response.NewWithCodeResponseWriter(w)
|
||||||
|
|
||||||
var dup io.ReadCloser
|
var dup io.ReadCloser
|
||||||
r.Body, dup = iox.DupReadCloser(r.Body)
|
r.Body, dup = iox.LimitDupReadCloser(r.Body, limitBodyBytes)
|
||||||
next.ServeHTTP(lrw, r.WithContext(internal.WithLogCollector(r.Context(), logs)))
|
next.ServeHTTP(lrw, r.WithContext(internal.WithLogCollector(r.Context(), logs)))
|
||||||
r.Body = dup
|
r.Body = dup
|
||||||
logBrief(r, lrw.Code, timer, logs)
|
logBrief(r, lrw.Code, timer, logs)
|
||||||
@@ -136,14 +135,7 @@ func logBrief(r *http.Request, code int, timer *utils.ElapsedTimer, logs *intern
|
|||||||
|
|
||||||
ok := isOkResponse(code)
|
ok := isOkResponse(code)
|
||||||
if !ok {
|
if !ok {
|
||||||
fullReq := dumpRequest(r)
|
buf.WriteString(fmt.Sprintf("\n%s", dumpRequest(r)))
|
||||||
limitReader := io.LimitReader(strings.NewReader(fullReq), limitBodyBytes)
|
|
||||||
body, err := io.ReadAll(limitReader)
|
|
||||||
if err != nil {
|
|
||||||
buf.WriteString(fmt.Sprintf("\n%s", fullReq))
|
|
||||||
} else {
|
|
||||||
buf.WriteString(fmt.Sprintf("\n%s", string(body)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body := logs.Flush()
|
body := logs.Flush()
|
||||||
|
|||||||
Reference in New Issue
Block a user