Skip to content

Commit 5a50b5f

Browse files
fix(leak): don't mutate logger, just make events from it (#69)
* fix: don't race poking logger variable, just make events from it fixes: #68 * fix: need a concurrent buffer to run the current logging test * fix: correct a data race
1 parent 676ffb8 commit 5a50b5f

File tree

2 files changed

+65
-25
lines changed

2 files changed

+65
-25
lines changed

logger.go

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ func SetLogger(opts ...Option) gin.HandlerFunc {
7676
Timestamp().
7777
Logger()
7878
return func(c *gin.Context) {
79+
rl := l
7980
if cfg.logger != nil {
80-
l = cfg.logger(c, l)
81+
rl = cfg.logger(c, l)
8182
}
8283

8384
start := time.Now()
@@ -112,43 +113,33 @@ func SetLogger(opts ...Option) gin.HandlerFunc {
112113
}
113114
latency := end.Sub(start)
114115

115-
l = l.With().
116-
Int("status", c.Writer.Status()).
117-
Str("method", c.Request.Method).
118-
Str("path", path).
119-
Str("ip", c.ClientIP()).
120-
Dur("latency", latency).
121-
Str("user_agent", c.Request.UserAgent()).
122-
Int("body_size", c.Writer.Size()).
123-
Logger()
124-
125116
msg := "Request"
126117
if len(c.Errors) > 0 {
127118
msg = c.Errors.String()
128119
}
129120

121+
var evt *zerolog.Event
130122
level, hasLevel := cfg.pathLevels[path]
131123

132124
switch {
133125
case c.Writer.Status() >= http.StatusBadRequest && c.Writer.Status() < http.StatusInternalServerError:
134-
{
135-
l.WithLevel(cfg.clientErrorLevel).
136-
Msg(msg)
137-
}
126+
evt = rl.WithLevel(cfg.clientErrorLevel)
138127
case c.Writer.Status() >= http.StatusInternalServerError:
139-
{
140-
l.WithLevel(cfg.serverErrorLevel).
141-
Msg(msg)
142-
}
128+
evt = rl.WithLevel(cfg.serverErrorLevel)
143129
case hasLevel:
144-
{
145-
l.WithLevel(level).
146-
Msg(msg)
147-
}
130+
evt = rl.WithLevel(level)
148131
default:
149-
l.WithLevel(cfg.defaultLevel).
150-
Msg(msg)
132+
evt = rl.WithLevel(cfg.defaultLevel)
151133
}
134+
evt.
135+
Int("status", c.Writer.Status()).
136+
Str("method", c.Request.Method).
137+
Str("path", path).
138+
Str("ip", c.ClientIP()).
139+
Dur("latency", latency).
140+
Str("user_agent", c.Request.UserAgent()).
141+
Int("body_size", c.Writer.Size()).
142+
Msg(msg)
152143
}
153144
}
154145
}

logger_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package logger
33
import (
44
"bytes"
55
"context"
6+
"fmt"
67
"net/http"
78
"net/http/httptest"
89
"regexp"
10+
"strings"
11+
"sync"
912
"testing"
1013

1114
"github.com/gin-gonic/gin"
@@ -151,6 +154,52 @@ func TestLoggerWithLevels(t *testing.T) {
151154
assert.Contains(t, buffer.String(), "FTL")
152155
}
153156

157+
type concurrentBuffer struct {
158+
mu sync.Mutex
159+
b bytes.Buffer
160+
}
161+
162+
func (b *concurrentBuffer) Write(p []byte) (n int, err error) {
163+
b.mu.Lock()
164+
defer b.mu.Unlock()
165+
return b.b.Write(p)
166+
}
167+
168+
func TestCustomLoggerIssue68(t *testing.T) {
169+
buffer := new(concurrentBuffer)
170+
gin.SetMode(gin.ReleaseMode)
171+
r := gin.New()
172+
l := zerolog.New(buffer)
173+
r.Use(SetLogger(
174+
WithLogger(func(*gin.Context, zerolog.Logger) zerolog.Logger { return l }),
175+
WithDefaultLevel(zerolog.DebugLevel),
176+
WithClientErrorLevel(zerolog.ErrorLevel),
177+
WithServerErrorLevel(zerolog.FatalLevel),
178+
))
179+
r.GET("/example", func(c *gin.Context) {})
180+
181+
// concurrent requests should only have their info logged once
182+
var wg sync.WaitGroup
183+
for i := 0; i < 10; i++ {
184+
wg.Add(1)
185+
req := fmt.Sprintf("/example?a=%d", i)
186+
go func() {
187+
defer wg.Done()
188+
performRequest(r, "GET", req)
189+
}()
190+
}
191+
wg.Wait()
192+
193+
bs := buffer.b.String()
194+
for i := 0; i < 10; i++ {
195+
// should contain each request log exactly once
196+
msg := fmt.Sprintf("/example?a=%d", i)
197+
if assert.Contains(t, bs, msg) {
198+
assert.Equal(t, strings.Index(bs, msg), strings.LastIndex(bs, msg))
199+
}
200+
}
201+
}
202+
154203
func TestLoggerParseLevel(t *testing.T) {
155204
type args struct {
156205
levelStr string

0 commit comments

Comments
 (0)