From 01ce816579b3f79dccc792a251e9ef65668a505d Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 23 Dec 2024 13:17:55 +0100 Subject: [PATCH 1/9] Bump newrelic --- gateway/middleware.go | 8 ++- gateway/newrelic.go | 100 -------------------------- gateway/proxy_muxer.go | 11 ++- gateway/server.go | 33 ++++++++- go.mod | 3 +- go.sum | 6 +- internal/service/newrelic/newrelic.go | 95 ++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 110 deletions(-) delete mode 100644 gateway/newrelic.go create mode 100644 internal/service/newrelic/newrelic.go diff --git a/gateway/middleware.go b/gateway/middleware.go index bcef9fe6ecf..6323179e67f 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -15,7 +15,6 @@ import ( "github.com/gocraft/health" "github.com/justinas/alice" - newrelic "github.com/newrelic/go-agent" "github.com/paulbellamy/ratecounter" "github.com/sirupsen/logrus" "golang.org/x/sync/singleflight" @@ -24,9 +23,11 @@ import ( "github.com/TykTechnologies/tyk/header" "github.com/TykTechnologies/tyk/internal/cache" "github.com/TykTechnologies/tyk/internal/event" + "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/TykTechnologies/tyk/internal/middleware" "github.com/TykTechnologies/tyk/internal/otel" "github.com/TykTechnologies/tyk/internal/policy" + "github.com/TykTechnologies/tyk/internal/service/newrelic" "github.com/TykTechnologies/tyk/request" "github.com/TykTechnologies/tyk/rpc" "github.com/TykTechnologies/tyk/storage" @@ -138,8 +139,9 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h logger := mw.Base().SetRequestLogger(r) if gw.GetConfig().NewRelic.AppName != "" { - if txn, ok := w.(newrelic.Transaction); ok { - defer newrelic.StartSegment(txn, mw.Name()).End() + ctxtxn := httpctx.NewValue[*newrelic.Transaction]("internal:new-relic-transaction") + if txn := ctxtxn.Get(r); txn != nil { + defer txn.StartSegment(mw.Name()).End() } } diff --git a/gateway/newrelic.go b/gateway/newrelic.go deleted file mode 100644 index 513ddd577a3..00000000000 --- a/gateway/newrelic.go +++ /dev/null @@ -1,100 +0,0 @@ -package gateway - -import ( - "fmt" - "strconv" - - "github.com/gocraft/health" - "github.com/gorilla/mux" - newrelic "github.com/newrelic/go-agent" - "github.com/newrelic/go-agent/_integrations/nrgorilla/v1" - "github.com/sirupsen/logrus" -) - -// SetupNewRelic creates new newrelic.Application instance -func (gw *Gateway) SetupNewRelic() (app newrelic.Application) { - var ( - err error - gwConfig = gw.GetConfig() - ) - - logger := log.WithFields(logrus.Fields{"prefix": "newrelic"}) - - logger.Info("Initializing NewRelic...") - - cfg := newrelic.NewConfig(gwConfig.NewRelic.AppName, gwConfig.NewRelic.LicenseKey) - if gwConfig.NewRelic.AppName != "" { - cfg.Enabled = true - } - cfg.DistributedTracer.Enabled = gwConfig.NewRelic.EnableDistributedTracing - - cfg.Logger = &newRelicLogger{logger} - - if app, err = newrelic.NewApplication(cfg); err != nil { - logger.Warn("Error initializing NewRelic, skipping... ", err) - return - } - - instrument.AddSink(&newRelicSink{relic: app}) - logger.Info("NewRelic initialized") - - return -} - -// AddNewRelicInstrumentation adds NewRelic instrumentation to the router -func AddNewRelicInstrumentation(app newrelic.Application, r *mux.Router) { - if app != nil { - nrgorilla.InstrumentRoutes(r, app) - } -} - -type newRelicLogger struct{ *logrus.Entry } - -func (l *newRelicLogger) Error(msg string, c map[string]interface{}) { - l.WithFields(c).Error(msg) -} -func (l *newRelicLogger) Warn(msg string, c map[string]interface{}) { - l.WithFields(c).Warn(msg) -} -func (l *newRelicLogger) Info(msg string, c map[string]interface{}) { - l.WithFields(c).Info(msg) -} -func (l *newRelicLogger) Debug(msg string, c map[string]interface{}) { - l.WithFields(c).Debug(msg) -} -func (l *newRelicLogger) DebugEnabled() bool { - return l.Level >= logrus.DebugLevel -} - -type newRelicSink struct { - relic newrelic.Application - health.Sink -} - -func (s *newRelicSink) EmitEvent(job string, event string, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event, makeParams(kvs)) -} - -func (s *newRelicSink) EmitEventErr(job string, event string, err error, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":msg:"+err.Error(), makeParams(kvs)) -} - -func (s *newRelicSink) EmitTiming(job string, event string, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) -} - -func (s *newRelicSink) EmitComplete(job string, status health.CompletionStatus, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":health:"+status.String()+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) -} - -func (s *newRelicSink) EmitGauge(job string, event string, value float64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":value:"+fmt.Sprintf("%.2f", value), makeParams(kvs)) -} - -func makeParams(kvs map[string]string) (params map[string]interface{}) { - params = make(map[string]interface{}, len(kvs)) - for k, v := range kvs { - params[k] = v - } - return -} diff --git a/gateway/proxy_muxer.go b/gateway/proxy_muxer.go index 89b23f6d4ad..cc4abde97e0 100644 --- a/gateway/proxy_muxer.go +++ b/gateway/proxy_muxer.go @@ -20,7 +20,9 @@ import ( "github.com/TykTechnologies/again" "github.com/TykTechnologies/tyk/config" + "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/TykTechnologies/tyk/internal/httputil" + "github.com/TykTechnologies/tyk/internal/service/newrelic" "github.com/TykTechnologies/tyk/tcp" "github.com/gorilla/mux" @@ -96,9 +98,14 @@ func (h *handleWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if NewRelicApplication != nil { - txn := NewRelicApplication.StartTransaction(r.URL.Path, w, r) + txn := NewRelicApplication.StartTransaction(r.URL.Path) + w = txn.SetWebResponse(w) + + ctxtxn := httpctx.NewValue[*newrelic.Transaction]("internal:new-relic-transaction") + ctxtxn.Set(r, txn) + defer txn.End() - h.router.ServeHTTP(txn, r) + h.router.ServeHTTP(w, r) return } h.router.ServeHTTP(w, r) diff --git a/gateway/server.go b/gateway/server.go index 745b9dabafb..6473a8dd694 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -36,7 +36,6 @@ import ( grayloghook "github.com/gemnasium/logrus-graylog-hook" "github.com/gorilla/mux" "github.com/lonelycode/osin" - newrelic "github.com/newrelic/go-agent" "github.com/sirupsen/logrus" logrussyslog "github.com/sirupsen/logrus/hooks/syslog" @@ -67,6 +66,7 @@ import ( "github.com/TykTechnologies/tyk/internal/cache" "github.com/TykTechnologies/tyk/internal/model" "github.com/TykTechnologies/tyk/internal/netutil" + "github.com/TykTechnologies/tyk/internal/service/newrelic" ) var ( @@ -78,7 +78,7 @@ var ( rawLog = logger.GetRaw() memProfFile *os.File - NewRelicApplication newrelic.Application + NewRelicApplication *newrelic.Application // confPaths is the series of paths to try to use as config files. The // first one to exist will be used. If none exists, a default config @@ -256,6 +256,35 @@ func NewGateway(config config.Config, ctx context.Context) *Gateway { return gw } +// SetupNewRelic creates new newrelic.Application instance +func (gw *Gateway) SetupNewRelic() (app *newrelic.Application) { + var ( + err error + gwConfig = gw.GetConfig() + ) + + logger := log.WithFields(logrus.Fields{"prefix": "newrelic"}) + logger.Info("Initializing NewRelic...") + + cfg := []newrelic.ConfigOption{ + newrelic.ConfigAppName(gwConfig.NewRelic.AppName), + newrelic.ConfigLicense(gwConfig.NewRelic.LicenseKey), + newrelic.ConfigEnabled(gwConfig.NewRelic.AppName != ""), + newrelic.ConfigDistributedTracerEnabled(gwConfig.NewRelic.EnableDistributedTracing), + newrelic.ConfigLogger(newrelic.NewLogger(logger)), + } + + if app, err = newrelic.NewApplication(cfg...); err != nil { + logger.Warn("Error initializing NewRelic, skipping... ", err) + return + } + + instrument.AddSink(newrelic.NewSink(app)) + logger.Info("NewRelic initialized") + + return +} + func (gw *Gateway) UnmarshalJSON(data []byte) error { return nil } diff --git a/go.mod b/go.mod index 3c10e2b4e2b..e20de39d9b2 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,8 @@ require ( github.com/goccy/go-json v0.10.4 github.com/google/go-cmp v0.6.0 github.com/nats-io/nats.go v1.38.0 - github.com/newrelic/go-agent v2.13.0+incompatible + github.com/newrelic/go-agent/v3 v3.35.1 + github.com/newrelic/go-agent/v3/integrations/nrgorilla v1.2.2 github.com/testcontainers/testcontainers-go v0.34.0 github.com/testcontainers/testcontainers-go/modules/kafka v0.33.0 github.com/testcontainers/testcontainers-go/modules/nats v0.33.0 diff --git a/go.sum b/go.sum index 26a12714560..374644330f6 100644 --- a/go.sum +++ b/go.sum @@ -604,8 +604,10 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/stan.go v0.10.4 h1:19GS/eD1SeQJaVkeM9EkvEYattnvnWrZ3wkSWSw4uXw= github.com/nats-io/stan.go v0.10.4/go.mod h1:3XJXH8GagrGqajoO/9+HgPyKV5MWsv7S5ccdda+pc6k= -github.com/newrelic/go-agent v2.13.0+incompatible h1:Dl6m75MHAzfB0kicv9GiLxzQatRjTLUAdrnYyoT8s4M= -github.com/newrelic/go-agent v2.13.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= +github.com/newrelic/go-agent/v3 v3.35.1 h1:N43qBNDILmnwLDCSfnE1yy6adyoVEU95nAOtdUgG4vA= +github.com/newrelic/go-agent/v3 v3.35.1/go.mod h1:GNTda53CohAhkgsc7/gqSsJhDZjj8vaky5u+vKz7wqM= +github.com/newrelic/go-agent/v3/integrations/nrgorilla v1.2.2 h1:YaFf6tmxSKNVgS9ZHx6O8HSpckWiyNSBZQKwaXfG1fQ= +github.com/newrelic/go-agent/v3/integrations/nrgorilla v1.2.2/go.mod h1:NlYWXdP4WVAg8v7ZM0FRWulv0OtssOS3l4R6pYlWGf0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go new file mode 100644 index 00000000000..de323f5bc1b --- /dev/null +++ b/internal/service/newrelic/newrelic.go @@ -0,0 +1,95 @@ +package newrelic + +import ( + "fmt" + "strconv" + + "github.com/newrelic/go-agent/v3/integrations/nrgorilla" + "github.com/newrelic/go-agent/v3/newrelic" + + "github.com/gocraft/health" + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" +) + +type ( + Application = newrelic.Application + Transaction = newrelic.Transaction + ConfigOption = newrelic.ConfigOption +) + +var ( + NewApplication = newrelic.NewApplication + + ConfigLogger = newrelic.ConfigLogger + ConfigEnabled = newrelic.ConfigEnabled + ConfigAppName = newrelic.ConfigAppName + ConfigLicense = newrelic.ConfigLicense + ConfigDistributedTracerEnabled = newrelic.ConfigDistributedTracerEnabled +) + +// AddNewRelicInstrumentation adds NewRelic instrumentation to the router +func AddNewRelicInstrumentation(app *newrelic.Application, r *mux.Router) { + nrgorilla.InstrumentRoutes(r, app) +} + +type Logger struct{ *logrus.Entry } + +func NewLogger(e *logrus.Entry) *Logger { + return &Logger{e} +} + +func (l *Logger) Error(msg string, c map[string]interface{}) { + l.WithFields(c).Error(msg) +} +func (l *Logger) Warn(msg string, c map[string]interface{}) { + l.WithFields(c).Warn(msg) +} +func (l *Logger) Info(msg string, c map[string]interface{}) { + l.WithFields(c).Info(msg) +} +func (l *Logger) Debug(msg string, c map[string]interface{}) { + l.WithFields(c).Debug(msg) +} +func (l *Logger) DebugEnabled() bool { + return l.Level >= logrus.DebugLevel +} + +type Sink struct { + relic *newrelic.Application + health.Sink +} + +func NewSink(relic *newrelic.Application) *Sink { + return &Sink{ + relic: relic, + } +} + +func (s *Sink) EmitEvent(job string, event string, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event, makeParams(kvs)) +} + +func (s *Sink) EmitEventErr(job string, event string, err error, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":msg:"+err.Error(), makeParams(kvs)) +} + +func (s *Sink) EmitTiming(job string, event string, nanoseconds int64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) +} + +func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanoseconds int64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":health:"+status.String()+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) +} + +func (s *Sink) EmitGauge(job string, event string, value float64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":value:"+fmt.Sprintf("%.2f", value), makeParams(kvs)) +} + +func makeParams(kvs map[string]string) (params map[string]interface{}) { + params = make(map[string]interface{}, len(kvs)) + for k, v := range kvs { + params[k] = v + } + return +} From 7fc0f64fcdea19d2bd083c8243d29b8bcf798702 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 23 Dec 2024 13:30:00 +0100 Subject: [PATCH 2/9] Move context into newrelic service pkg --- gateway/middleware.go | 4 +--- gateway/proxy_muxer.go | 4 +--- internal/service/newrelic/newrelic.go | 3 +++ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/gateway/middleware.go b/gateway/middleware.go index 6323179e67f..875d67581da 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -23,7 +23,6 @@ import ( "github.com/TykTechnologies/tyk/header" "github.com/TykTechnologies/tyk/internal/cache" "github.com/TykTechnologies/tyk/internal/event" - "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/TykTechnologies/tyk/internal/middleware" "github.com/TykTechnologies/tyk/internal/otel" "github.com/TykTechnologies/tyk/internal/policy" @@ -139,8 +138,7 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h logger := mw.Base().SetRequestLogger(r) if gw.GetConfig().NewRelic.AppName != "" { - ctxtxn := httpctx.NewValue[*newrelic.Transaction]("internal:new-relic-transaction") - if txn := ctxtxn.Get(r); txn != nil { + if txn := newrelic.Context.Get(r); txn != nil { defer txn.StartSegment(mw.Name()).End() } } diff --git a/gateway/proxy_muxer.go b/gateway/proxy_muxer.go index cc4abde97e0..ccf42f00252 100644 --- a/gateway/proxy_muxer.go +++ b/gateway/proxy_muxer.go @@ -20,7 +20,6 @@ import ( "github.com/TykTechnologies/again" "github.com/TykTechnologies/tyk/config" - "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/TykTechnologies/tyk/internal/httputil" "github.com/TykTechnologies/tyk/internal/service/newrelic" "github.com/TykTechnologies/tyk/tcp" @@ -101,8 +100,7 @@ func (h *handleWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { txn := NewRelicApplication.StartTransaction(r.URL.Path) w = txn.SetWebResponse(w) - ctxtxn := httpctx.NewValue[*newrelic.Transaction]("internal:new-relic-transaction") - ctxtxn.Set(r, txn) + newrelic.Context.Set(r, txn) defer txn.End() h.router.ServeHTTP(w, r) diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index de323f5bc1b..79690dbb43f 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/newrelic/go-agent/v3/integrations/nrgorilla" "github.com/newrelic/go-agent/v3/newrelic" @@ -21,6 +22,8 @@ type ( var ( NewApplication = newrelic.NewApplication + Context = httpctx.NewValue[*Transaction]("internal:new-relic-transaction") + ConfigLogger = newrelic.ConfigLogger ConfigEnabled = newrelic.ConfigEnabled ConfigAppName = newrelic.ConfigAppName From ac02dcd402622d3083a28335e73dd79e43b2f37a Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 23 Dec 2024 13:46:53 +0100 Subject: [PATCH 3/9] Clean up golangci-lint warnings, shadowing logger, deprecated InstrumentRoutes --- gateway/server.go | 12 ++++++------ internal/service/newrelic/newrelic.go | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/gateway/server.go b/gateway/server.go index 6473a8dd694..b6dd5ba7f99 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -256,31 +256,31 @@ func NewGateway(config config.Config, ctx context.Context) *Gateway { return gw } -// SetupNewRelic creates new newrelic.Application instance +// SetupNewRelic creates new newrelic.Application instance. func (gw *Gateway) SetupNewRelic() (app *newrelic.Application) { var ( err error gwConfig = gw.GetConfig() ) - logger := log.WithFields(logrus.Fields{"prefix": "newrelic"}) - logger.Info("Initializing NewRelic...") + log := log.WithFields(logrus.Fields{"prefix": "newrelic"}) + log.Info("Initializing NewRelic...") cfg := []newrelic.ConfigOption{ newrelic.ConfigAppName(gwConfig.NewRelic.AppName), newrelic.ConfigLicense(gwConfig.NewRelic.LicenseKey), newrelic.ConfigEnabled(gwConfig.NewRelic.AppName != ""), newrelic.ConfigDistributedTracerEnabled(gwConfig.NewRelic.EnableDistributedTracing), - newrelic.ConfigLogger(newrelic.NewLogger(logger)), + newrelic.ConfigLogger(newrelic.NewLogger(log)), } if app, err = newrelic.NewApplication(cfg...); err != nil { - logger.Warn("Error initializing NewRelic, skipping... ", err) + log.Warn("Error initializing NewRelic, skipping... ", err) return } instrument.AddSink(newrelic.NewSink(app)) - logger.Info("NewRelic initialized") + log.Info("NewRelic initialized") return } diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index 79690dbb43f..5d1ed0a01e2 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -4,13 +4,14 @@ import ( "fmt" "strconv" - "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/newrelic/go-agent/v3/integrations/nrgorilla" "github.com/newrelic/go-agent/v3/newrelic" "github.com/gocraft/health" "github.com/gorilla/mux" "github.com/sirupsen/logrus" + + "github.com/TykTechnologies/tyk/internal/httpctx" ) type ( @@ -33,7 +34,7 @@ var ( // AddNewRelicInstrumentation adds NewRelic instrumentation to the router func AddNewRelicInstrumentation(app *newrelic.Application, r *mux.Router) { - nrgorilla.InstrumentRoutes(r, app) + r.Use(nrgorilla.Middleware(app)) } type Logger struct{ *logrus.Entry } From a57f2bb2375e21864e1d29540a75fcc6bfd33746 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 23 Dec 2024 15:59:47 +0100 Subject: [PATCH 4/9] Fix invalid duration_ns, optimize logging --- gateway/server.go | 2 -- gateway/testutil.go | 2 ++ internal/service/newrelic/newrelic.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gateway/server.go b/gateway/server.go index b6dd5ba7f99..419faf81582 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -264,7 +264,6 @@ func (gw *Gateway) SetupNewRelic() (app *newrelic.Application) { ) log := log.WithFields(logrus.Fields{"prefix": "newrelic"}) - log.Info("Initializing NewRelic...") cfg := []newrelic.ConfigOption{ newrelic.ConfigAppName(gwConfig.NewRelic.AppName), @@ -280,7 +279,6 @@ func (gw *Gateway) SetupNewRelic() (app *newrelic.Application) { } instrument.AddSink(newrelic.NewSink(app)) - log.Info("NewRelic initialized") return } diff --git a/gateway/testutil.go b/gateway/testutil.go index 267f28d1cd2..5b2de1a5db2 100644 --- a/gateway/testutil.go +++ b/gateway/testutil.go @@ -1161,6 +1161,8 @@ func (s *Test) newGateway(genConf func(globalConf *config.Config)) *Gateway { gwConfig.BundleBaseURL = testHttpBundles gwConfig.MiddlewarePath = testMiddlewarePath + config.FillEnv(&gwConfig) + // force ipv4 for now, to work around the docker bug affecting // Go 1.8 and earlier gwConfig.ListenAddress = "127.0.0.1" diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index 5d1ed0a01e2..1bfe9371b2a 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -79,11 +79,11 @@ func (s *Sink) EmitEventErr(job string, event string, err error, kvs map[string] } func (s *Sink) EmitTiming(job string, event string, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) + s.relic.RecordCustomEvent(job+":"+event+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) } func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":health:"+status.String()+":dur(ns):"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) + s.relic.RecordCustomEvent(job+":health:"+status.String()+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) } func (s *Sink) EmitGauge(job string, event string, value float64, kvs map[string]string) { From 51cc31cb92bc8c770724ce842a6615b4c2925591 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Mon, 23 Dec 2024 16:03:15 +0100 Subject: [PATCH 5/9] Add error check for golangci-lint --- gateway/testutil.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gateway/testutil.go b/gateway/testutil.go index 5b2de1a5db2..8e2825fe187 100644 --- a/gateway/testutil.go +++ b/gateway/testutil.go @@ -1161,7 +1161,9 @@ func (s *Test) newGateway(genConf func(globalConf *config.Config)) *Gateway { gwConfig.BundleBaseURL = testHttpBundles gwConfig.MiddlewarePath = testMiddlewarePath - config.FillEnv(&gwConfig) + if err := config.FillEnv(&gwConfig); err != nil { + log.WithError(err).Error("error filling test config from env") + } // force ipv4 for now, to work around the docker bug affecting // Go 1.8 and earlier From c473b175fc1979f60cc6d96039cdbd9509e920d7 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Jan 2025 09:45:21 +0100 Subject: [PATCH 6/9] Move out logger/sink in newrelic --- internal/service/newrelic/logger.go | 33 +++++++++++++ internal/service/newrelic/newrelic.go | 67 +-------------------------- internal/service/newrelic/sink.go | 49 ++++++++++++++++++++ 3 files changed, 83 insertions(+), 66 deletions(-) create mode 100644 internal/service/newrelic/logger.go create mode 100644 internal/service/newrelic/sink.go diff --git a/internal/service/newrelic/logger.go b/internal/service/newrelic/logger.go new file mode 100644 index 00000000000..7fac94c20f1 --- /dev/null +++ b/internal/service/newrelic/logger.go @@ -0,0 +1,33 @@ +package newrelic + +import ( + "github.com/newrelic/go-agent/v3/newrelic" + + "github.com/sirupsen/logrus" +) + +type Logger struct { + *logrus.Entry +} + +var _ newrelic.Logger = &Logger{} + +func NewLogger(e *logrus.Entry) *Logger { + return &Logger{e} +} + +func (l *Logger) Error(msg string, c map[string]interface{}) { + l.WithFields(c).Error(msg) +} +func (l *Logger) Warn(msg string, c map[string]interface{}) { + l.WithFields(c).Warn(msg) +} +func (l *Logger) Info(msg string, c map[string]interface{}) { + l.WithFields(c).Info(msg) +} +func (l *Logger) Debug(msg string, c map[string]interface{}) { + l.WithFields(c).Debug(msg) +} +func (l *Logger) DebugEnabled() bool { + return l.Level >= logrus.DebugLevel +} diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index 1bfe9371b2a..e3e446b2329 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -1,15 +1,10 @@ package newrelic import ( - "fmt" - "strconv" - "github.com/newrelic/go-agent/v3/integrations/nrgorilla" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/gocraft/health" "github.com/gorilla/mux" - "github.com/sirupsen/logrus" "github.com/TykTechnologies/tyk/internal/httpctx" ) @@ -23,6 +18,7 @@ type ( var ( NewApplication = newrelic.NewApplication + // Context exposes a repository for the newrelic *Transaction. Context = httpctx.NewValue[*Transaction]("internal:new-relic-transaction") ConfigLogger = newrelic.ConfigLogger @@ -36,64 +32,3 @@ var ( func AddNewRelicInstrumentation(app *newrelic.Application, r *mux.Router) { r.Use(nrgorilla.Middleware(app)) } - -type Logger struct{ *logrus.Entry } - -func NewLogger(e *logrus.Entry) *Logger { - return &Logger{e} -} - -func (l *Logger) Error(msg string, c map[string]interface{}) { - l.WithFields(c).Error(msg) -} -func (l *Logger) Warn(msg string, c map[string]interface{}) { - l.WithFields(c).Warn(msg) -} -func (l *Logger) Info(msg string, c map[string]interface{}) { - l.WithFields(c).Info(msg) -} -func (l *Logger) Debug(msg string, c map[string]interface{}) { - l.WithFields(c).Debug(msg) -} -func (l *Logger) DebugEnabled() bool { - return l.Level >= logrus.DebugLevel -} - -type Sink struct { - relic *newrelic.Application - health.Sink -} - -func NewSink(relic *newrelic.Application) *Sink { - return &Sink{ - relic: relic, - } -} - -func (s *Sink) EmitEvent(job string, event string, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event, makeParams(kvs)) -} - -func (s *Sink) EmitEventErr(job string, event string, err error, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":msg:"+err.Error(), makeParams(kvs)) -} - -func (s *Sink) EmitTiming(job string, event string, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) -} - -func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanoseconds int64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":health:"+status.String()+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) -} - -func (s *Sink) EmitGauge(job string, event string, value float64, kvs map[string]string) { - s.relic.RecordCustomEvent(job+":"+event+":value:"+fmt.Sprintf("%.2f", value), makeParams(kvs)) -} - -func makeParams(kvs map[string]string) (params map[string]interface{}) { - params = make(map[string]interface{}, len(kvs)) - for k, v := range kvs { - params[k] = v - } - return -} diff --git a/internal/service/newrelic/sink.go b/internal/service/newrelic/sink.go new file mode 100644 index 00000000000..51c13ca6f9e --- /dev/null +++ b/internal/service/newrelic/sink.go @@ -0,0 +1,49 @@ +package newrelic + +import ( + "fmt" + "strconv" + + "github.com/newrelic/go-agent/v3/newrelic" + + "github.com/gocraft/health" +) + +type Sink struct { + relic *newrelic.Application + health.Sink +} + +func NewSink(relic *newrelic.Application) *Sink { + return &Sink{ + relic: relic, + } +} + +func (s *Sink) EmitEvent(job string, event string, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event, makeParams(kvs)) +} + +func (s *Sink) EmitEventErr(job string, event string, err error, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":msg:"+err.Error(), makeParams(kvs)) +} + +func (s *Sink) EmitTiming(job string, event string, nanoseconds int64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) +} + +func (s *Sink) EmitComplete(job string, status health.CompletionStatus, nanoseconds int64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":health:"+status.String()+":duration_ns:"+strconv.FormatInt(nanoseconds, 10), makeParams(kvs)) +} + +func (s *Sink) EmitGauge(job string, event string, value float64, kvs map[string]string) { + s.relic.RecordCustomEvent(job+":"+event+":value:"+fmt.Sprintf("%.2f", value), makeParams(kvs)) +} + +func makeParams(kvs map[string]string) (params map[string]interface{}) { + params = make(map[string]interface{}, len(kvs)) + for k, v := range kvs { + params[k] = v + } + return +} From 05be6f9c55e94d0d6da5960fe7828c5ce7036fd9 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Jan 2025 09:58:33 +0100 Subject: [PATCH 7/9] Adjust godoc a bit --- internal/service/newrelic/newrelic.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index e3e446b2329..4478c5a3e18 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -9,18 +9,17 @@ import ( "github.com/TykTechnologies/tyk/internal/httpctx" ) +// Type aliases used from newrelic pkg. type ( Application = newrelic.Application Transaction = newrelic.Transaction ConfigOption = newrelic.ConfigOption ) +// Variable aliases used from newrelic pkg. var ( NewApplication = newrelic.NewApplication - // Context exposes a repository for the newrelic *Transaction. - Context = httpctx.NewValue[*Transaction]("internal:new-relic-transaction") - ConfigLogger = newrelic.ConfigLogger ConfigEnabled = newrelic.ConfigEnabled ConfigAppName = newrelic.ConfigAppName @@ -28,7 +27,12 @@ var ( ConfigDistributedTracerEnabled = newrelic.ConfigDistributedTracerEnabled ) -// AddNewRelicInstrumentation adds NewRelic instrumentation to the router +var ( + // Context exposes a repository for the newrelic *Transaction on request context. + Context = httpctx.NewValue[*Transaction]("internal:new-relic-transaction") +) + +// AddNewRelicInstrumentation adds NewRelic instrumentation to the router. func AddNewRelicInstrumentation(app *newrelic.Application, r *mux.Router) { r.Use(nrgorilla.Middleware(app)) } From 93d11f9fd18687c1219cf270c254b77cf66aa8d4 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Jan 2025 15:00:25 +0100 Subject: [PATCH 8/9] Clean up newrelic a bit --- gateway/api_loader.go | 3 +++ gateway/middleware.go | 6 ++---- gateway/proxy_muxer.go | 11 ----------- gateway/server.go | 6 +++--- gateway/testutil.go | 1 + internal/service/newrelic/newrelic.go | 21 ++++++++++----------- 6 files changed, 19 insertions(+), 29 deletions(-) diff --git a/gateway/api_loader.go b/gateway/api_loader.go index 3a2b9c01984..07f75d48613 100644 --- a/gateway/api_loader.go +++ b/gateway/api_loader.go @@ -29,6 +29,7 @@ import ( "github.com/TykTechnologies/tyk/internal/httpctx" "github.com/TykTechnologies/tyk/internal/httputil" "github.com/TykTechnologies/tyk/internal/otel" + "github.com/TykTechnologies/tyk/internal/service/newrelic" ) const ( @@ -769,6 +770,8 @@ func (gw *Gateway) loadHTTPService(spec *APISpec, apisByListen map[string]int, g router := muxer.router(port, spec.Protocol, gwConfig) if router == nil { router = mux.NewRouter() + newrelic.Mount(router, gw.NewRelicApplication, logrus.NewEntry(log)) + muxer.setRouter(port, spec.Protocol, router, gwConfig) } diff --git a/gateway/middleware.go b/gateway/middleware.go index 875d67581da..52c1b9cf840 100644 --- a/gateway/middleware.go +++ b/gateway/middleware.go @@ -137,10 +137,8 @@ func (gw *Gateway) createMiddleware(actualMW TykMiddleware) func(http.Handler) h return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger := mw.Base().SetRequestLogger(r) - if gw.GetConfig().NewRelic.AppName != "" { - if txn := newrelic.Context.Get(r); txn != nil { - defer txn.StartSegment(mw.Name()).End() - } + if txn := newrelic.FromContext(r.Context()); txn != nil { + defer txn.StartSegment(mw.Name()).End() } job := instrument.NewJob("MiddlewareCall") diff --git a/gateway/proxy_muxer.go b/gateway/proxy_muxer.go index ccf42f00252..5a6e27870da 100644 --- a/gateway/proxy_muxer.go +++ b/gateway/proxy_muxer.go @@ -21,7 +21,6 @@ import ( "github.com/TykTechnologies/again" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/internal/httputil" - "github.com/TykTechnologies/tyk/internal/service/newrelic" "github.com/TykTechnologies/tyk/tcp" "github.com/gorilla/mux" @@ -96,16 +95,6 @@ func (h *handleWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - if NewRelicApplication != nil { - txn := NewRelicApplication.StartTransaction(r.URL.Path) - w = txn.SetWebResponse(w) - - newrelic.Context.Set(r, txn) - - defer txn.End() - h.router.ServeHTTP(w, r) - return - } h.router.ServeHTTP(w, r) } diff --git a/gateway/server.go b/gateway/server.go index 419faf81582..2b206c70214 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -77,8 +77,7 @@ var ( pubSubLog = log.WithField("prefix", "pub-sub") rawLog = logger.GetRaw() - memProfFile *os.File - NewRelicApplication *newrelic.Application + memProfFile *os.File // confPaths is the series of paths to try to use as config files. The // first one to exist will be used. If none exists, a default config @@ -125,6 +124,7 @@ type Gateway struct { HostCheckTicker chan struct{} HostCheckerClient *http.Client TracerProvider otel.TracerProvider + NewRelicApplication *newrelic.Application keyGen DefaultKeyGenerator @@ -467,7 +467,7 @@ func (gw *Gateway) setupGlobals() { } if gw.GetConfig().NewRelic.AppName != "" { - NewRelicApplication = gw.SetupNewRelic() + gw.NewRelicApplication = gw.SetupNewRelic() } gw.readGraphqlPlaygroundTemplate() diff --git a/gateway/testutil.go b/gateway/testutil.go index 8e2825fe187..106e9fa3266 100644 --- a/gateway/testutil.go +++ b/gateway/testutil.go @@ -1313,6 +1313,7 @@ func (s *Test) Close() { s.Gw.Analytics.Stop() s.Gw.ReloadTestCase.StopTicker() s.Gw.GlobalHostChecker.StopPoller() + s.Gw.NewRelicApplication.Shutdown(5 * time.Second) err = s.RemoveApis() if err != nil { diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index 4478c5a3e18..6b11308585b 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -1,12 +1,10 @@ package newrelic import ( + "github.com/gorilla/mux" "github.com/newrelic/go-agent/v3/integrations/nrgorilla" "github.com/newrelic/go-agent/v3/newrelic" - - "github.com/gorilla/mux" - - "github.com/TykTechnologies/tyk/internal/httpctx" + "github.com/sirupsen/logrus" ) // Type aliases used from newrelic pkg. @@ -19,6 +17,7 @@ type ( // Variable aliases used from newrelic pkg. var ( NewApplication = newrelic.NewApplication + FromContext = newrelic.FromContext ConfigLogger = newrelic.ConfigLogger ConfigEnabled = newrelic.ConfigEnabled @@ -27,12 +26,12 @@ var ( ConfigDistributedTracerEnabled = newrelic.ConfigDistributedTracerEnabled ) -var ( - // Context exposes a repository for the newrelic *Transaction on request context. - Context = httpctx.NewValue[*Transaction]("internal:new-relic-transaction") -) +// Mount adds the nrgorilla middleware to the router. The application is added to the request context. +// If app is nil, nothing will be done and the function will return. +func Mount(router *mux.Router, app *Application, logger *logrus.Entry) { + if app == nil { + return + } -// AddNewRelicInstrumentation adds NewRelic instrumentation to the router. -func AddNewRelicInstrumentation(app *newrelic.Application, r *mux.Router) { - r.Use(nrgorilla.Middleware(app)) + router.Use(nrgorilla.Middleware(app)) } From 26e84d8dcc053af4c6c40580717c054cacf251d8 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Jan 2025 15:07:03 +0100 Subject: [PATCH 9/9] Drop unused logger --- gateway/api_loader.go | 2 +- internal/service/newrelic/newrelic.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gateway/api_loader.go b/gateway/api_loader.go index 07f75d48613..90f954c9ca2 100644 --- a/gateway/api_loader.go +++ b/gateway/api_loader.go @@ -770,7 +770,7 @@ func (gw *Gateway) loadHTTPService(spec *APISpec, apisByListen map[string]int, g router := muxer.router(port, spec.Protocol, gwConfig) if router == nil { router = mux.NewRouter() - newrelic.Mount(router, gw.NewRelicApplication, logrus.NewEntry(log)) + newrelic.Mount(router, gw.NewRelicApplication) muxer.setRouter(port, spec.Protocol, router, gwConfig) } diff --git a/internal/service/newrelic/newrelic.go b/internal/service/newrelic/newrelic.go index 6b11308585b..775ff650c32 100644 --- a/internal/service/newrelic/newrelic.go +++ b/internal/service/newrelic/newrelic.go @@ -4,7 +4,6 @@ import ( "github.com/gorilla/mux" "github.com/newrelic/go-agent/v3/integrations/nrgorilla" "github.com/newrelic/go-agent/v3/newrelic" - "github.com/sirupsen/logrus" ) // Type aliases used from newrelic pkg. @@ -28,7 +27,7 @@ var ( // Mount adds the nrgorilla middleware to the router. The application is added to the request context. // If app is nil, nothing will be done and the function will return. -func Mount(router *mux.Router, app *Application, logger *logrus.Entry) { +func Mount(router *mux.Router, app *Application) { if app == nil { return }