Skip to content

Commit 56a6d3c

Browse files
committed
misc performance optimizations
1. Replace chained withFields to use a single WithFields 2. check for debug log enabled before invoking debug logs 3. store request logger in the context to minimize amount of instantiation calls.
1 parent ec431fb commit 56a6d3c

File tree

9 files changed

+90
-23
lines changed

9 files changed

+90
-23
lines changed

ctx/ctx.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const (
5353
CacheOptions
5454
OASDefinition
5555
SelfLooping
56+
RequestLogger
5657
)
5758

5859
func ctxSetSession(r *http.Request, s *user.SessionState, scheduleUpdate bool, hashKey bool) {

gateway/api.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3381,6 +3381,19 @@ func ctxGetRequestStatus(r *http.Request) (stat RequestStatus) {
33813381
return
33823382
}
33833383

3384+
func ctxSetRequestLogger(r *http.Request, logger *logrus.Entry) {
3385+
setCtxValue(r, ctx.RequestLogger, logger)
3386+
}
3387+
3388+
func ctxGetRequestLogger(r *http.Request) *logrus.Entry {
3389+
if v := r.Context().Value(ctx.RequestLogger); v != nil {
3390+
if logger, ok := v.(*logrus.Entry); ok {
3391+
return logger
3392+
}
3393+
}
3394+
return nil
3395+
}
3396+
33843397
var createOauthClientSecret = func() string {
33853398
secret := uuid.New()
33863399
return base64.StdEncoding.EncodeToString([]byte(secret))

gateway/log_helpers.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,18 @@ func (gw *Gateway) getExplicitLogEntryForRequest(logger *logrus.Entry, path stri
6565
}
6666
return logger.WithFields(fields)
6767
}
68+
69+
// getOrCreateRequestLogger returns a cached logger from the request context
70+
// or creates a new one if it doesn't exist. This reduces allocations by
71+
// ensuring the logger is only created once per request instead of once per middleware.
72+
func (gw *Gateway) getOrCreateRequestLogger(r *http.Request, key string) *logrus.Entry {
73+
// Check if logger already exists in context
74+
if logger := ctxGetRequestLogger(r); logger != nil {
75+
return logger
76+
}
77+
78+
// Create new logger and cache it in context
79+
logger := gw.getLogEntryForRequest(logrus.NewEntry(log), r, key, nil)
80+
ctxSetRequestLogger(r, logger)
81+
return logger
82+
}

gateway/middleware.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,12 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h
161161
}
162162

163163
startTime := time.Now()
164-
logger.WithField("ts", startTime.UnixNano()).WithField("mw", mw.Name()).Debug("Started")
164+
if logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
165+
logger.WithFields(logrus.Fields{
166+
"ts": startTime.UnixNano(),
167+
"mw": mw.Name(),
168+
}).Debug("Started")
169+
}
165170

166171
if mw.Base().Spec.CORS.OptionsPassthrough && r.Method == "OPTIONS" {
167172
next.ServeHTTP(w, r)
@@ -189,7 +194,13 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h
189194
job.TimingKv(eventName+".exec_time", finishTime.Nanoseconds(), meta)
190195
}
191196

192-
logger.WithError(err).WithField("code", errCode).WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
197+
if logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
198+
logger.WithFields(logrus.Fields{
199+
"error": err,
200+
"code": errCode,
201+
"ns": finishTime.Nanoseconds(),
202+
}).Debug("Finished")
203+
}
193204
return
194205
}
195206

@@ -200,7 +211,12 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h
200211
job.TimingKv(eventName+".exec_time", finishTime.Nanoseconds(), meta)
201212
}
202213

203-
logger.WithField("code", errCode).WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
214+
if logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
215+
logger.WithFields(logrus.Fields{
216+
"code": errCode,
217+
"ns": finishTime.Nanoseconds(),
218+
}).Debug("Finished")
219+
}
204220

205221
mw.Base().UpdateRequestSession(r)
206222
// Special code, bypasses all other execution
@@ -315,14 +331,8 @@ func (t *BaseMiddleware) Logger() (logger *logrus.Entry) {
315331
}
316332

317333
func (t *BaseMiddleware) SetRequestLogger(r *http.Request) *logrus.Entry {
318-
t.loggerMu.Lock()
319-
defer t.loggerMu.Unlock()
320-
321-
if t.logger == nil {
322-
t.logger = logrus.NewEntry(log)
323-
}
324-
t.logger = t.Gw.getLogEntryForRequest(t.logger, r, ctxGetAuthToken(r), nil)
325-
return t.logger
334+
// Use context-based logger to avoid shared state mutation and reduce allocations
335+
return t.Gw.getOrCreateRequestLogger(r, ctxGetAuthToken(r))
326336
}
327337

328338
func (t *BaseMiddleware) Init() {}

gateway/middleware_decorators.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ func withLogger(
2222
) tykResponseDecorator {
2323

2424
return func(origin TykResponseHandler) TykResponseHandler {
25-
origin.setLogger(logger.WithField("mw", origin.Name()).WithField("type", "response"))
25+
origin.setLogger(logger.WithFields(logrus.Fields{
26+
"mw": origin.Name(),
27+
"type": "response",
28+
}))
2629

2730
return &logDecorator{
2831
TykResponseHandler: origin,
@@ -51,14 +54,19 @@ func (d *logDecorator) HandleResponse(
5154
) error {
5255

5356
start := time.Now()
54-
d.logger().WithField("ts", start.UnixNano()).Debug("Started")
57+
logger := d.logger()
58+
if logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
59+
logger.WithField("ts", start.UnixNano()).Debug("Started")
60+
}
5561

5662
if err := d.TykResponseHandler.HandleResponse(writer, response, request, state); err != nil {
5763
d.logger().WithField("ns", time.Since(start).Nanoseconds()).WithError(err).Error("Failed to process response")
5864
return err
5965
}
6066

61-
d.logger().WithField("ns", time.Since(start).Nanoseconds()).Debug("Finished")
67+
if logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
68+
logger.WithField("ns", time.Since(start).Nanoseconds()).Debug("Finished")
69+
}
6270

6371
return nil
6472
}

gateway/mw_granular_access.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ func (m *GranularAccessMiddleware) ProcessRequest(w http.ResponseWriter, r *http
6969
// if loglevel is set to debug verbosity increases and all requests are logged,
7070
// regardless if an error occured or not.
7171
if gwConfig.LogLevel == "debug" || err != nil {
72-
logger = logger.WithError(err).WithField("pattern", pattern).WithField("match", match)
72+
logger = logger.WithFields(logrus.Fields{
73+
"error": err,
74+
"pattern": pattern,
75+
"match": match,
76+
})
7377
if err != nil {
7478
logger.Error("error matching endpoint")
7579
} else {

gateway/mw_url_rewrite.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,11 @@ func (m *URLRewriteMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Req
536536
}
537537

538538
if err = m.CheckHostRewrite(oldPath, p, r); err != nil {
539-
log.WithError(err).WithField("from", oldPath).WithField("to", p).Error("Checking Host rewrite: error parsing URL")
539+
log.WithFields(logrus.Fields{
540+
"error": err,
541+
"from": oldPath,
542+
"to": p,
543+
}).Error("Checking Host rewrite: error parsing URL")
540544
}
541545

542546
newURL, err := url.Parse(p)

gateway/proxy_muxer.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,10 @@ func (m *proxyMux) setRouter(port int, protocol string, router *mux.Router, conf
206206
func (m *proxyMux) handle404(w http.ResponseWriter, r *http.Request) {
207207
if m.track404Logs {
208208
requestMeta := fmt.Sprintf("%s %s %s", r.Method, r.URL.Path, r.Proto)
209-
log.WithField("request", requestMeta).WithField("origin", r.RemoteAddr).
210-
Error(http.StatusText(http.StatusNotFound))
209+
log.WithFields(logrus.Fields{
210+
"request": requestMeta,
211+
"origin": r.RemoteAddr,
212+
}).Error(http.StatusText(http.StatusNotFound))
211213
}
212214

213215
w.WriteHeader(http.StatusNotFound)

gateway/reverse_proxy.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -516,12 +516,16 @@ var hopHeaders = []string{
516516

517517
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) ProxyResponse {
518518
startTime := time.Now()
519-
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
519+
if p.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
520+
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
521+
}
520522

521523
resp := p.WrappedServeHTTP(rw, req, recordDetail(req, p.TykAPISpec))
522524

523525
finishTime := time.Since(startTime)
524-
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
526+
if p.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
527+
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
528+
}
525529

526530
// make response body to be nopCloser and re-readable before serve it through chain of middlewares
527531
nopCloseResponseBody(resp.Response)
@@ -531,12 +535,16 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) Prox
531535

532536
func (p *ReverseProxy) ServeHTTPForCache(rw http.ResponseWriter, req *http.Request) ProxyResponse {
533537
startTime := time.Now()
534-
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
538+
if p.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
539+
p.logger.WithField("ts", startTime.UnixNano()).Debug("Started")
540+
}
535541

536542
resp := p.WrappedServeHTTP(rw, req, true)
537543
nopCloseResponseBody(resp.Response)
538544
finishTime := time.Since(startTime)
539-
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
545+
if p.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
546+
p.logger.WithField("ns", finishTime.Nanoseconds()).Debug("Finished")
547+
}
540548

541549
return resp
542550
}
@@ -831,7 +839,9 @@ func (rt *TykRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
831839
return nil, errors.New("handler could")
832840
}
833841

834-
rt.logger.WithField("looping_url", "tyk://"+r.Host).Debug("Executing request on internal route")
842+
if rt.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
843+
rt.logger.WithField("looping_url", "tyk://"+r.Host).Debug("Executing request on internal route")
844+
}
835845

836846
return handleInMemoryLoop(handler, r)
837847
}

0 commit comments

Comments
 (0)