Skip to content

Commit

Permalink
feat: web handler supports to log request body
Browse files Browse the repository at this point in the history
  • Loading branch information
tsingsun committed Jan 24, 2025
1 parent 98039fc commit 396a19f
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 11 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.25.0
golang.org/x/net v0.33.0
golang.org/x/oauth2 v0.20.0
golang.org/x/sync v0.10.0
golang.org/x/sys v0.28.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
Expand Down
2 changes: 1 addition & 1 deletion pkg/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ func TestMarshalFunc(t *testing.T) {
err := DefaultUnmarshalFunc(bt, &want)
require.NoError(t, err)
assert.Equal(t, data, want)
assert.NotSame(t, bt, want)
assert.NotSame(t, &bt, &want)
},
},
{
Expand Down
33 changes: 28 additions & 5 deletions web/handler/logger.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package handler

import (
"bytes"
"github.com/gin-gonic/gin"
"github.com/tsingsun/woocoo/pkg/conf"
"github.com/tsingsun/woocoo/pkg/log"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"io"
"strings"
"time"
)
Expand All @@ -24,7 +26,7 @@ const (
var (
AccessLogComponentName = "web.accessLog"
defaultLoggerFormat = "id,remoteIp,host,method,uri,userAgent,status,error," +
"latency,bytesIn,bytesOut"
"latency,bodyIn,bytesIn,bytesOut"
LoggerFieldSkip = zap.Skip()
)

Expand Down Expand Up @@ -52,6 +54,7 @@ type LoggerConfig struct {
// - error
// - latency (In nanoseconds)
// - latencyHuman (Human readable)
// - bodyIn (request body)
// - bytesIn (Bytes received)
// - bytesOut (Bytes sent)
// - header:<NAME>
Expand All @@ -61,9 +64,11 @@ type LoggerConfig struct {
//
//
// Optional. Default value DefaultLoggerConfig.Format.
Format string `json:"format" yaml:"format"`
Level zapcore.Level `json:"level" yaml:"level"`
Tags []loggerTag
Format string `json:"format" yaml:"format"`
// if true log request body
logBodyIn bool
Level zapcore.Level `json:"level" yaml:"level"`
Tags []loggerTag
}

func (h *LoggerConfig) BuildTag(format string) {
Expand All @@ -72,6 +77,8 @@ func (h *LoggerConfig) BuildTag(format string) {
for _, tag := range ts {
tag = strings.TrimSpace(tag)
switch {
case tag == "bodyIn":
h.logBodyIn = true
case strings.HasPrefix(tag, "header:"):
tags = append(tags, loggerTag{FullKey: tag, typ: loggerTagTypeHeader, key: tag[7:]})
case strings.HasPrefix(tag, "query:"):
Expand All @@ -89,7 +96,9 @@ func (h *LoggerConfig) BuildTag(format string) {
h.Tags = tags
}

// LoggerMiddleware is a middleware that logs each request.
// LoggerMiddleware is a middleware that logs https requests.
//
// Default Skipper will not skip error request whatever you set exclude paths.
type LoggerMiddleware struct {
config LoggerConfig
logger log.ComponentLogger
Expand Down Expand Up @@ -150,14 +159,25 @@ func (h *LoggerMiddleware) ApplyFunc(cfg *conf.Configuration) gin.HandlerFunc {
h.config.Skipper = DefaultSkipper
}
traceIDKey := h.logger.Logger().TraceIDKey
logBodyIn := h.config.logBodyIn
return func(c *gin.Context) {
start := time.Now()
logCarrier := log.NewCarrier()
c.Set(AccessLogComponentName, logCarrier)

var body []byte
if logBodyIn {
if c.Request.Body != nil {
body, _ = io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewReader(body))
}
}

c.Next()
if h.config.Skipper(c) {
return
}

req := c.Request
res := c.Writer
latency := time.Now().Sub(start)
Expand Down Expand Up @@ -235,6 +255,9 @@ func (h *LoggerMiddleware) ApplyFunc(cfg *conf.Configuration) gin.HandlerFunc {
if fc := GetLogCarrierFromGinContext(c); fc != nil && len(fc.Fields) > 0 {
fields = append(fields, fc.Fields...)
}
if len(body) > 0 {
fields = append(fields, zap.String("bodyIn", string(body)))
}
clog := h.logger.Ctx(c)
if privateErr {
clog.Error("", fields...)
Expand Down
55 changes: 51 additions & 4 deletions web/handler/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func TestLoggerMiddleware(t *testing.T) {
gin.SetMode(gin.ReleaseMode)
type args struct {
cfg *conf.Configuration
request *http.Request
handler gin.HandlerFunc
}
tests := []struct {
Expand Down Expand Up @@ -183,12 +184,55 @@ func TestLoggerMiddleware(t *testing.T) {
return true
},
},
{
name: "log body in",
args: args{
cfg: conf.NewFromStringMap(map[string]any{
"format": "bodyIn",
}),
request: httptest.NewRequest("POST", "/?query=1", strings.NewReader(`
{
"username": "testuser",
"password": "testpass",
"age": 28,
"hobbies": ["reading", "swimming", "coding"]
}`)),
handler: func(c *gin.Context) {
// get body correct
var user struct {
Username string `json:"username"`
Password string `json:"password"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
if err := c.ShouldBindJSON(&user); err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
}
assert.Equal(t, "testuser", user.Username)
},
},
want: func() any {
logdata := wctest.InitBuffWriteSyncer()
return logdata
},
wantErr: func(t assert.TestingT, err error, i ...any) bool {
r := i[1].(*httptest.ResponseRecorder)
assert.Equal(t, http.StatusOK, r.Code)
ss := i[0].(*logtest.Buffer)
all := ss.String()
assert.Contains(t, all, `testuser`)
return true
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest("GET", "/?query=1", nil)
r.Header.Set("accept", "*")
r.AddCookie(&http.Cookie{Name: "c1", Domain: "localhost", Value: "cookievalue"})
r := tt.args.request
if r == nil {
r = httptest.NewRequest("GET", "/?query=1", nil)
r.Header.Set("accept", "*")
r.AddCookie(&http.Cookie{Name: "c1", Domain: "localhost", Value: "cookievalue"})
}
w := httptest.NewRecorder()
want := tt.want()
accessLog := AccessLog()
Expand All @@ -199,8 +243,11 @@ func TestLoggerMiddleware(t *testing.T) {
srv.GET("/", func(c *gin.Context) {
tt.args.handler(c)
})
srv.POST("/", func(c *gin.Context) {
tt.args.handler(c)
})
srv.ServeHTTP(w, r)
if !tt.wantErr(t, nil, want) {
if !tt.wantErr(t, nil, want, w) {
return
}
})
Expand Down

0 comments on commit 396a19f

Please sign in to comment.