From 1f34e2cfc72c3e29cff6fdc221c1fa69414a6820 Mon Sep 17 00:00:00 2001 From: Emilio Garcia Date: Wed, 8 Nov 2023 14:43:45 -0500 Subject: [PATCH 01/16] Error Expected Bug The attribute error.expected should be a boolean, not a string. It is also good practice to use a constant value for the key. --- v3/newrelic/error_events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/newrelic/error_events.go b/v3/newrelic/error_events.go index 07aca4f35..6087865d8 100644 --- a/v3/newrelic/error_events.go +++ b/v3/newrelic/error_events.go @@ -32,7 +32,7 @@ func (e *errorEvent) WriteJSON(buf *bytes.Buffer) { w.stringField("spanId", e.SpanID) } if e.Expect { - w.stringField("error.expected", "true") + w.boolField(expectErrorAttr, true) } sharedTransactionIntrinsics(&e.txnEvent, &w) From 231f2c3c7f1d898b0ac17173b041b1702de1872f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:14:56 -0600 Subject: [PATCH 02/16] Bump google.golang.org/grpc from 1.54.0 to 1.56.3 in /v3/integrations/nrgraphqlgo/example (#811) --------- * Bump google.golang.org/grpc in /v3/integrations/nrgraphqlgo/example Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.54.0 to 1.56.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.54.0...v1.56.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: indirect ... Signed-off-by: dependabot[bot] --------- --- v3/integrations/nrgraphqlgo/example/go.mod | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/v3/integrations/nrgraphqlgo/example/go.mod b/v3/integrations/nrgraphqlgo/example/go.mod index d5a044811..8a0f85ece 100644 --- a/v3/integrations/nrgraphqlgo/example/go.mod +++ b/v3/integrations/nrgraphqlgo/example/go.mod @@ -16,12 +16,12 @@ require ( github.com/klauspost/compress v1.16.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.49.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.54.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.56.3 // indirect + google.golang.org/protobuf v1.30.0 // indirect ) replace github.com/newrelic/go-agent/v3/integrations/nrgraphqlgo => ../ From 4c1206cb5cd64990f9c488830832d34f8b66939c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:15:29 -0600 Subject: [PATCH 03/16] Bump google.golang.org/grpc from 1.54.0 to 1.56.3 in /v3/integrations/nrgrpc (#810) --------- * Bump google.golang.org/grpc in /v3/integrations/nrgrpc Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.54.0 to 1.56.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.54.0...v1.56.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production ... Signed-off-by: dependabot[bot] --------- --- v3/integrations/nrgrpc/go.mod | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/v3/integrations/nrgrpc/go.mod b/v3/integrations/nrgrpc/go.mod index 738fa628f..d52cac659 100644 --- a/v3/integrations/nrgrpc/go.mod +++ b/v3/integrations/nrgrpc/go.mod @@ -9,9 +9,31 @@ require ( github.com/newrelic/go-agent/v3 v3.26.0 github.com/newrelic/go-agent/v3/integrations/nrsecurityagent v1.1.0 // v1.15.0 is the earliest version of grpc using modules. - google.golang.org/grpc v1.54.0 - google.golang.org/protobuf v1.28.1 + google.golang.org/grpc v1.56.3 + google.golang.org/protobuf v1.30.0 ) +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/dlclark/regexp2 v1.9.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b // indirect + github.com/k2io/hookingo v1.0.3 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mackerelio/go-osstat v0.2.4 // indirect + github.com/newrelic/csec-go-agent v0.3.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/struCoder/pidusage v0.2.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.49.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) replace github.com/newrelic/go-agent/v3 => ../.. From ecbc5163916b2170af01f1defe8b7bf43b6ecd58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:15:56 -0600 Subject: [PATCH 04/16] Bump google.golang.org/grpc from 1.54.0 to 1.56.3 in /v3 (#809) --------- * Bump google.golang.org/grpc from 1.54.0 to 1.56.3 in /v3 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.54.0 to 1.56.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.54.0...v1.56.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production ... Signed-off-by: dependabot[bot] --------- --- v3/go.mod | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/v3/go.mod b/v3/go.mod index 02c244f6b..56a33c60b 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -5,7 +5,18 @@ go 1.19 require ( github.com/golang/protobuf v1.5.3 github.com/valyala/fasthttp v1.49.0 - google.golang.org/grpc v1.54.0 + google.golang.org/grpc v1.56.3 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/protobuf v1.30.0 // indirect ) retract v3.22.0 // release process error corrected in v3.22.1 From e92cce9f831e3be947bbd6edb4d934723ede4fbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:07:46 -0600 Subject: [PATCH 05/16] Bump golang.org/x/net from 0.8.0 to 0.17.0 in /v3/integrations/nrgraphqlgo/example (#804) --------- * Bump golang.org/x/net in /v3/integrations/nrgraphqlgo/example Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.8.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] --------- --- v3/integrations/nrgraphqlgo/example/go.mod | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v3/integrations/nrgraphqlgo/example/go.mod b/v3/integrations/nrgraphqlgo/example/go.mod index 8a0f85ece..ac6bc56ad 100644 --- a/v3/integrations/nrgraphqlgo/example/go.mod +++ b/v3/integrations/nrgraphqlgo/example/go.mod @@ -16,9 +16,9 @@ require ( github.com/klauspost/compress v1.16.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.49.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.3 // indirect google.golang.org/protobuf v1.30.0 // indirect From b3ce5df3994fabb8c7d1001970c7994c359c4772 Mon Sep 17 00:00:00 2001 From: aayush-ap <59004877+aayush-ap@users.noreply.github.com> Date: Fri, 17 Nov 2023 00:39:39 +0530 Subject: [PATCH 06/16] Fix for out of memory error with request body (#806) * Release 3.25.0 (#782) * minor fix for complete security disable flag * Create FastHTTP Client Functions * FastHTTP Request Integration * FastHTTP example file * FastHTTP Request Integration * FastHTTP Response file * mod file * update security agent version * supportability metric * Created unit tests and removed extraneous file * Moved FastHTTP to internal instrumentation * Added testing for errors * chore: add logs-in-context example with logrus * chore: move example to specific folder * FastHTTP external segments/Client example * License for Server Example * Added test for external segment/minor fixes * FastHTTP Integration (#774) Added Support For FastHTTP * V3.25.0 Changelog (#781) * V3.25.0 * update version * corrected changelog for 3.25 release * Fixed test not passing * Update segments.go Removed extra function --------- Co-authored-by: aayush-ap Co-authored-by: Steve Willoughby <76975199+nr-swilloughby@users.noreply.github.com> Co-authored-by: Julien Erard Co-authored-by: Emilio Garcia Co-authored-by: Steve Willoughby * fix out of memory issue for req body * Added new config parameter for read request body * update request body buffer * minor fix for dataTruncated * Update readme file * Update csec-go-agent version * Added new wrapper for go-micro stream server * minor fix for GHA * Fix for cpu overhead * backward compatibility * update agent version * minor fix --------- Co-authored-by: Mirac Kara <55501260+mirackara@users.noreply.github.com> Co-authored-by: Steve Willoughby <76975199+nr-swilloughby@users.noreply.github.com> Co-authored-by: Julien Erard Co-authored-by: Emilio Garcia Co-authored-by: Steve Willoughby --- v3/integrations/nrgrpc/go.mod | 24 +------ v3/integrations/nrmicro/nrmicro.go | 72 ++++++++++++++++--- v3/integrations/nrsecurityagent/README.md | 2 + v3/integrations/nrsecurityagent/go.mod | 2 +- .../nrsecurityagent/nrsecurityagent.go | 21 ++++++ v3/newrelic/secure_agent.go | 58 +++++++++++++-- v3/newrelic/transaction.go | 35 ++++----- 7 files changed, 157 insertions(+), 57 deletions(-) diff --git a/v3/integrations/nrgrpc/go.mod b/v3/integrations/nrgrpc/go.mod index d52cac659..642db88de 100644 --- a/v3/integrations/nrgrpc/go.mod +++ b/v3/integrations/nrgrpc/go.mod @@ -13,27 +13,5 @@ require ( google.golang.org/protobuf v1.30.0 ) -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/dlclark/regexp2 v1.9.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b // indirect - github.com/k2io/hookingo v1.0.3 // indirect - github.com/klauspost/compress v1.16.3 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/mackerelio/go-osstat v0.2.4 // indirect - github.com/newrelic/csec-go-agent v0.3.0 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/struCoder/pidusage v0.2.1 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.49.0 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect -) - replace github.com/newrelic/go-agent/v3 => ../.. +replace github.com/newrelic/go-agent/v3/integrations/nrsecurityagent => ../../integrations/nrsecurityagent diff --git a/v3/integrations/nrmicro/nrmicro.go b/v3/integrations/nrmicro/nrmicro.go index 7198a81fb..804e531ff 100644 --- a/v3/integrations/nrmicro/nrmicro.go +++ b/v3/integrations/nrmicro/nrmicro.go @@ -5,6 +5,7 @@ package nrmicro import ( "context" + "io" "net/http" "net/url" "strings" @@ -15,9 +16,11 @@ import ( "github.com/micro/go-micro/registry" "github.com/micro/go-micro/server" + protoV1 "github.com/golang/protobuf/proto" "github.com/newrelic/go-agent/v3/internal" "github.com/newrelic/go-agent/v3/internal/integrationsupport" "github.com/newrelic/go-agent/v3/newrelic" + protoV2 "google.golang.org/protobuf/proto" ) type nrWrapper struct { @@ -162,7 +165,19 @@ func HandlerWrapper(app *newrelic.Application) server.HandlerWrapper { return func(ctx context.Context, req server.Request, rsp interface{}) error { txn := startWebTransaction(ctx, app, req) defer txn.End() - err := fn(newrelic.NewContext(ctx, txn), req, rsp) + if req.Body() != nil && newrelic.IsSecurityAgentPresent() { + messageType, version := getMessageType(req.Body()) + newrelic.GetSecurityAgentInterface().SendEvent("GRPC", req.Body(), messageType, version) + } + + nrrsp := rsp + if req.Stream() && newrelic.IsSecurityAgentPresent() { + if stream, ok := rsp.(server.Stream); ok { + nrrsp = wrappedServerStream{stream} + } + } + + err := fn(newrelic.NewContext(ctx, txn), req, nrrsp) var code int if err != nil { if t, ok := err.(*errors.Error); ok { @@ -227,9 +242,6 @@ func SubscriberWrapper(app *newrelic.Application) server.SubscriberWrapper { func startWebTransaction(ctx context.Context, app *newrelic.Application, req server.Request) *newrelic.Transaction { var hdrs http.Header - var unencodedBody []byte - var err error - if md, ok := metadata.FromContext(ctx); ok { hdrs = make(http.Header, len(md)) for k, v := range md { @@ -242,20 +254,58 @@ func startWebTransaction(ctx context.Context, app *newrelic.Application, req ser Host: req.Service(), Path: req.Endpoint(), } - - if unencodedBody, err = req.Read(); err != nil { - unencodedBody = nil - } - webReq := newrelic.WebRequest{ Header: hdrs, URL: u, Method: req.Method(), Transport: newrelic.TransportHTTP, - Body: unencodedBody, - Type: "HTTP", + Type: "micro", } txn.SetWebRequest(webReq) return txn } + +type wrappedServerStream struct { + stream server.Stream +} + +func (s wrappedServerStream) Context() context.Context { + return s.stream.Context() +} +func (s wrappedServerStream) Request() server.Request { + return s.stream.Request() +} +func (s wrappedServerStream) Send(msg any) error { + return s.stream.Send(msg) +} +func (s wrappedServerStream) Recv(msg any) error { + err := s.stream.Recv(msg) + if err != io.EOF { + messageType, version := getMessageType(msg) + newrelic.GetSecurityAgentInterface().SendEvent("GRPC", msg, messageType, version) + } + return err +} +func (s wrappedServerStream) Error() error { + return s.stream.Error() +} +func (s wrappedServerStream) Close() error { + return s.stream.Close() +} + +func getMessageType(req any) (string, string) { + messageType := "" + version := "v2" + messagev2, ok := req.(protoV2.Message) + if ok { + messageType = string(messagev2.ProtoReflect().Descriptor().FullName()) + } else { + messagev1, ok := req.(protoV1.Message) + if ok { + messageType = string(protoV1.MessageReflect(messagev1).Descriptor().FullName()) + version = "v1" + } + } + return messageType, version +} diff --git a/v3/integrations/nrsecurityagent/README.md b/v3/integrations/nrsecurityagent/README.md index ba77280c3..7ad9fb5b0 100644 --- a/v3/integrations/nrsecurityagent/README.md +++ b/v3/integrations/nrsecurityagent/README.md @@ -54,6 +54,8 @@ validator_service_url: wss://csec.nr-data.net detection: rxss: enabled: true +request: + body_limit:1 ``` * Based on additional packages imported by the user application, add suitable instrumentation package imports. diff --git a/v3/integrations/nrsecurityagent/go.mod b/v3/integrations/nrsecurityagent/go.mod index 4fc26a9ff..c0300761f 100644 --- a/v3/integrations/nrsecurityagent/go.mod +++ b/v3/integrations/nrsecurityagent/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrsecurityagent go 1.19 require ( - github.com/newrelic/csec-go-agent v0.4.0 + github.com/newrelic/csec-go-agent v0.5.1 github.com/newrelic/go-agent/v3 v3.26.0 github.com/newrelic/go-agent/v3/integrations/nrsqlite3 v1.2.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/v3/integrations/nrsecurityagent/nrsecurityagent.go b/v3/integrations/nrsecurityagent/nrsecurityagent.go index c7264d7ad..bb21f6765 100644 --- a/v3/integrations/nrsecurityagent/nrsecurityagent.go +++ b/v3/integrations/nrsecurityagent/nrsecurityagent.go @@ -30,6 +30,7 @@ func defaultSecurityConfig() SecurityConfig { cfg.Security.Mode = "IAST" cfg.Security.Agent.Enabled = true cfg.Security.Detection.Rxss.Enabled = true + cfg.Security.Request.BodyLimit = 300 return cfg } @@ -108,6 +109,8 @@ func ConfigSecurityFromYaml() ConfigOption { // NEW_RELIC_SECURITY_MODE scanning mode: "IAST" for now // NEW_RELIC_SECURITY_AGENT_ENABLED (boolean) // NEW_RELIC_SECURITY_DETECTION_RXSS_ENABLED (boolean) +// NEW_RELIC_SECURITY_REQUEST_BODY_LIMIT (integer) set limit on read request body in kb. By default, this is "300" + func ConfigSecurityFromEnvironment() ConfigOption { return func(cfg *SecurityConfig) { assignBool := func(field *bool, name string) { @@ -125,11 +128,22 @@ func ConfigSecurityFromEnvironment() ConfigOption { } } + assignInt := func(field *int, name string) { + if env := os.Getenv(name); env != "" { + if i, err := strconv.Atoi(env); nil != err { + cfg.Error = fmt.Errorf("invalid %s value: %s", name, env) + } else { + *field = i + } + } + } + assignBool(&cfg.Security.Enabled, "NEW_RELIC_SECURITY_ENABLED") assignString(&cfg.Security.Validator_service_url, "NEW_RELIC_SECURITY_VALIDATOR_SERVICE_URL") assignString(&cfg.Security.Mode, "NEW_RELIC_SECURITY_MODE") assignBool(&cfg.Security.Agent.Enabled, "NEW_RELIC_SECURITY_AGENT_ENABLED") assignBool(&cfg.Security.Detection.Rxss.Enabled, "NEW_RELIC_SECURITY_DETECTION_RXSS_ENABLED") + assignInt(&cfg.Security.Request.BodyLimit, "NEW_RELIC_SECURITY_REQUEST_BODY_LIMIT") } } @@ -160,3 +174,10 @@ func ConfigSecurityEnable(isEnabled bool) ConfigOption { cfg.Security.Enabled = isEnabled } } + +// ConfigSecurityRequestBodyLimit set limit on read request body in kb. By default, this is "300" +func ConfigSecurityRequestBodyLimit(bodyLimit int) ConfigOption { + return func(cfg *SecurityConfig) { + cfg.Security.Request.BodyLimit = bodyLimit + } +} diff --git a/v3/newrelic/secure_agent.go b/v3/newrelic/secure_agent.go index 5011a3e7d..c7f546a80 100644 --- a/v3/newrelic/secure_agent.go +++ b/v3/newrelic/secure_agent.go @@ -4,7 +4,6 @@ import ( "net/http" ) -// // secureAgent is a global interface point for the nrsecureagent's hooks into the go agent. // The default value for this is a noOpSecurityAgent value, which has null definitions for // the methods. The Go compiler is expected to optimize away all the securityAgent method @@ -12,10 +11,8 @@ import ( // // If the nrsecureagent integration was initialized, it will register a real securityAgent // value in the securityAgent varialble instead, thus "activating" the hooks. -// var secureAgent securityAgent = noOpSecurityAgent{} -// // GetSecurityAgentInterface returns the securityAgent value // which provides the working interface to the installed // security agent (or to a no-op interface if none were @@ -26,7 +23,6 @@ var secureAgent securityAgent = noOpSecurityAgent{} // This avoids exposing the variable itself so it's not // writable externally and also sets up for the future if this // ends up not being a global variable later. -// func GetSecurityAgentInterface() securityAgent { return secureAgent } @@ -38,6 +34,7 @@ type securityAgent interface { IsSecurityActive() bool DistributedTraceHeaders(hdrs *http.Request, secureAgentevent any) SendExitEvent(any, error) + RequestBodyReadLimit() int } func (app *Application) RegisterSecurityAgent(s securityAgent) { @@ -88,13 +85,62 @@ func (t noOpSecurityAgent) DistributedTraceHeaders(hdrs *http.Request, secureAge func (t noOpSecurityAgent) SendExitEvent(secureAgentevent any, err error) { } +func (t noOpSecurityAgent) RequestBodyReadLimit() int { + return 300 * 1000 +} -// // IsSecurityAgentPresent returns true if there's an actual security agent hooked in to the // Go APM agent, whether or not it's enabled or operating in any particular mode. It returns // false only if the hook-in interface for those functions is a No-Op will null functionality. -// func IsSecurityAgentPresent() bool { _, isNoOp := secureAgent.(noOpSecurityAgent) return !isNoOp } + +type BodyBuffer struct { + buf []byte + isDataTruncated bool +} + +func (b *BodyBuffer) Write(p []byte) (int, error) { + if l := len(b.buf); len(p) <= secureAgent.RequestBodyReadLimit()-l { + b.buf = append(b.buf, p...) + return len(p), nil + } else if l := len(b.buf); secureAgent.RequestBodyReadLimit()-l > 1 { + end := secureAgent.RequestBodyReadLimit() - l + b.buf = append(b.buf, p[:end-1]...) + return end, nil + } else { + b.isDataTruncated = true + return 0, nil + } +} + +func (b *BodyBuffer) Len() int { + if b == nil { + return 0 + } + return len(b.buf) + +} + +func (b *BodyBuffer) read() []byte { + if b == nil { + return make([]byte, 0) + } + return b.buf +} + +func (b *BodyBuffer) isBodyTruncated() bool { + if b == nil { + return false + } + return b.isDataTruncated +} +func (b *BodyBuffer) String() (string, bool) { + if b == nil { + return "", false + } + return string(b.buf), b.isDataTruncated + +} diff --git a/v3/newrelic/transaction.go b/v3/newrelic/transaction.go index d1d519c63..af360f29a 100644 --- a/v3/newrelic/transaction.go +++ b/v3/newrelic/transaction.go @@ -4,7 +4,6 @@ package newrelic import ( - "bytes" "encoding/json" "fmt" "io" @@ -246,20 +245,14 @@ func serverName(r *http.Request) string { return "" } -func reqBody(req *http.Request) []byte { - var bodyBuffer bytes.Buffer - requestBuffer := make([]byte, 0) - bodyReader := io.TeeReader(req.Body, &bodyBuffer) - - if bodyReader != nil && req.Body != nil { - reqBuffer, err := io.ReadAll(bodyReader) - if err == nil { - requestBuffer = reqBuffer - } - r := io.NopCloser(bytes.NewBuffer(requestBuffer)) - req.Body = r +func reqBody(req *http.Request) *BodyBuffer { + if IsSecurityAgentPresent() { + buf := &BodyBuffer{buf: make([]byte, 0, 100)} + tee := io.TeeReader(req.Body, buf) + req.Body = io.NopCloser(tee) + return buf } - return bytes.TrimRight(requestBuffer, "\x00") + return nil } // SetWebRequest marks the transaction as a web transaction. SetWebRequest @@ -607,7 +600,7 @@ type WebRequest struct { // The following fields are needed for the secure agent's vulnerability // detection features. - Body []byte + Body *BodyBuffer ServerName string Type string RemoteAddress string @@ -634,7 +627,17 @@ func (webrequest WebRequest) GetHost() string { } func (webrequest WebRequest) GetBody() []byte { - return webrequest.Body + if webrequest.Body == nil { + return make([]byte, 0) + } + return webrequest.Body.read() +} + +func (webrequest WebRequest) IsDataTruncated() bool { + if webrequest.Body == nil { + return false + } + return webrequest.Body.isBodyTruncated() } func (webrequest WebRequest) GetServerName() string { From c74d62cf8b6464136e94242a9738171c5e8dc96b Mon Sep 17 00:00:00 2001 From: Emilio Garcia Date: Thu, 16 Nov 2023 14:11:55 -0500 Subject: [PATCH 07/16] move fasthttp out of core library, and into integration package (#808) * move fasthttp out of core library, and into integration package * move examples over * add security agent headers to fasthttp object * fix examples and external segment * add fasthttp tests * cleanup of go mods * fix segment collection * add security agent inbound write capture to wrapped handle func * Update go.mod --- .github/workflows/ci.yaml | 2 + v3/go.mod | 12 --- .../examples/client-fasthttp/go.mod | 11 +++ .../examples/client-fasthttp/main.go | 4 +- .../examples/server-fasthttp/go.mod | 11 +++ .../examples/server-fasthttp/main.go | 7 +- v3/integrations/nrfasthttp/go.mod | 6 +- v3/integrations/nrfasthttp/instrumentation.go | 74 ++++++++++++++ .../nrfasthttp/instrumentation_test.go | 56 +++++++++++ v3/integrations/nrfasthttp/segment.go | 80 +++++++++++++++ v3/integrations/nrfasthttp/segment_test.go | 65 ++++++++++++ v3/newrelic/context.go | 14 --- v3/newrelic/instrumentation.go | 98 +++++-------------- v3/newrelic/internal_17_test.go | 43 -------- v3/newrelic/internal_context_test.go | 25 ----- v3/newrelic/segments.go | 50 ++++------ 16 files changed, 349 insertions(+), 209 deletions(-) create mode 100644 v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod rename v3/{ => integrations/nrfasthttp}/examples/client-fasthttp/main.go (92%) create mode 100644 v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod rename v3/{ => integrations/nrfasthttp}/examples/server-fasthttp/main.go (83%) create mode 100644 v3/integrations/nrfasthttp/instrumentation.go create mode 100644 v3/integrations/nrfasthttp/instrumentation_test.go create mode 100644 v3/integrations/nrfasthttp/segment.go create mode 100644 v3/integrations/nrfasthttp/segment_test.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f35e91398..6b5636a35 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,6 +27,8 @@ jobs: # v3 integrations - go-version: 1.19.x dirs: v3/integrations/nramqp + - go-version: 1.19.x + dirs: v3/integrations/nrfasthttp - go-version: 1.19.x dirs: v3/integrations/nrsarama - go-version: 1.19.x diff --git a/v3/go.mod b/v3/go.mod index 56a33c60b..e064d1ccf 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -4,21 +4,9 @@ go 1.19 require ( github.com/golang/protobuf v1.5.3 - github.com/valyala/fasthttp v1.49.0 google.golang.org/grpc v1.56.3 ) -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/klauspost/compress v1.16.3 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) - retract v3.22.0 // release process error corrected in v3.22.1 retract v3.25.0 // release process error corrected in v3.25.1 diff --git a/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod b/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod new file mode 100644 index 000000000..104505e67 --- /dev/null +++ b/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod @@ -0,0 +1,11 @@ +module client-example + +go 1.19 + +require ( + github.com/newrelic/go-agent/v3 v3.28.0 + github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 + github.com/valyala/fasthttp v1.49.0 +) + +replace github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 => ../../ diff --git a/v3/examples/client-fasthttp/main.go b/v3/integrations/nrfasthttp/examples/client-fasthttp/main.go similarity index 92% rename from v3/examples/client-fasthttp/main.go rename to v3/integrations/nrfasthttp/examples/client-fasthttp/main.go index 7a26b605f..bb958b840 100644 --- a/v3/examples/client-fasthttp/main.go +++ b/v3/integrations/nrfasthttp/examples/client-fasthttp/main.go @@ -7,6 +7,7 @@ import ( "os" "time" + "github.com/newrelic/go-agent/v3/integrations/nrfasthttp" newrelic "github.com/newrelic/go-agent/v3/newrelic" "github.com/valyala/fasthttp" ) @@ -20,8 +21,7 @@ func doRequest(txn *newrelic.Transaction) error { req.SetRequestURI("http://localhost:8080/hello") req.Header.SetMethod("GET") - ctx := &fasthttp.RequestCtx{} - seg := newrelic.StartExternalSegmentFastHTTP(txn, ctx) + seg := nrfasthttp.StartExternalSegment(txn, req) defer seg.End() err := fasthttp.Do(req, resp) diff --git a/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod b/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod new file mode 100644 index 000000000..591d85885 --- /dev/null +++ b/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod @@ -0,0 +1,11 @@ +module server-example + +go 1.19 + +require ( + github.com/newrelic/go-agent/v3 v3.28.0 + github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 + github.com/valyala/fasthttp v1.49.0 +) + +replace github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 => ../../ diff --git a/v3/examples/server-fasthttp/main.go b/v3/integrations/nrfasthttp/examples/server-fasthttp/main.go similarity index 83% rename from v3/examples/server-fasthttp/main.go rename to v3/integrations/nrfasthttp/examples/server-fasthttp/main.go index 8ed532670..bdb642f85 100644 --- a/v3/examples/server-fasthttp/main.go +++ b/v3/integrations/nrfasthttp/examples/server-fasthttp/main.go @@ -9,7 +9,8 @@ import ( "os" "time" - newrelic "github.com/newrelic/go-agent/v3/newrelic" + "github.com/newrelic/go-agent/v3/integrations/nrfasthttp" + "github.com/newrelic/go-agent/v3/newrelic" "github.com/valyala/fasthttp" ) @@ -39,8 +40,8 @@ func main() { if err := app.WaitForConnection(5 * time.Second); nil != err { fmt.Println(err) } - _, helloRoute := newrelic.WrapHandleFuncFastHTTP(app, "/hello", index) - _, errorRoute := newrelic.WrapHandleFuncFastHTTP(app, "/error", noticeError) + _, helloRoute := nrfasthttp.WrapHandleFunc(app, "/hello", index) + _, errorRoute := nrfasthttp.WrapHandleFunc(app, "/error", noticeError) handler := func(ctx *fasthttp.RequestCtx) { path := string(ctx.Path()) method := string(ctx.Method()) diff --git a/v3/integrations/nrfasthttp/go.mod b/v3/integrations/nrfasthttp/go.mod index 7f2b81134..5c97651a0 100644 --- a/v3/integrations/nrfasthttp/go.mod +++ b/v3/integrations/nrfasthttp/go.mod @@ -3,8 +3,6 @@ module github.com/newrelic/go-agent/v3/integrations/nrfasthttp go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 - github.com/stretchr/testify v1.8.4 - github.com/valyala/fasthttp v1.48.0 + github.com/newrelic/go-agent/v3 v3.28.0 + github.com/valyala/fasthttp v1.49.0 ) -replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrfasthttp/instrumentation.go b/v3/integrations/nrfasthttp/instrumentation.go new file mode 100644 index 000000000..9ce64769c --- /dev/null +++ b/v3/integrations/nrfasthttp/instrumentation.go @@ -0,0 +1,74 @@ +package nrfasthttp + +import ( + "net/http" + + "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpadaptor" +) + +type fasthttpWrapperResponse struct { + ctx *fasthttp.RequestCtx +} + +func (rw fasthttpWrapperResponse) Header() http.Header { + hdrs := http.Header{} + rw.ctx.Request.Header.VisitAll(func(key, value []byte) { + hdrs.Add(string(key), string(value)) + }) + return hdrs +} + +func (rw fasthttpWrapperResponse) Write(b []byte) (int, error) { + return rw.ctx.Write(b) +} + +func (rw fasthttpWrapperResponse) WriteHeader(code int) { + rw.ctx.SetStatusCode(code) +} + +func (rw fasthttpWrapperResponse) Body() string { + body := rw.ctx.Response.Body() + return string(body) +} + +// WrapHandleFunc wrapps a fasthttp handler function for automatic instrumentation +func WrapHandleFunc(app *newrelic.Application, pattern string, handler func(*fasthttp.RequestCtx), options ...newrelic.TraceOption) (string, func(*fasthttp.RequestCtx)) { + // add the wrapped function to the trace options as the source code reference point + // (to the beginning of the option list, so that the user can override this) + + p, h := WrapHandle(app, pattern, fasthttp.RequestHandler(handler), options...) + return p, func(ctx *fasthttp.RequestCtx) { h(ctx) } +} + +// WrapHandle wraps a fasthttp request handler for automatic instrumentation +func WrapHandle(app *newrelic.Application, pattern string, handler fasthttp.RequestHandler, options ...newrelic.TraceOption) (string, fasthttp.RequestHandler) { + if app == nil { + return pattern, handler + } + + // add the wrapped function to the trace options as the source code reference point + // (but only if we know we're collecting CLM for this transaction and the user didn't already + // specify a different code location explicitly). + return pattern, func(ctx *fasthttp.RequestCtx) { + cache := newrelic.NewCachedCodeLocation() + txnOptionList := newrelic.AddCodeLevelMetricsTraceOptions(app, options, cache, handler) + method := string(ctx.Method()) + path := string(ctx.Path()) + txn := app.StartTransaction(method+" "+path, txnOptionList...) + ctx.SetUserValue("transaction", txn) + defer txn.End() + r := &http.Request{} + fasthttpadaptor.ConvertRequest(ctx, r, true) + resp := fasthttpWrapperResponse{ctx: ctx} + + txn.SetWebResponse(resp) + txn.SetWebRequestHTTP(r) + + if newrelic.IsSecurityAgentPresent() { + newrelic.GetSecurityAgentInterface().SendEvent("INBOUND_WRITE", resp.Body(), resp.Header()) + } + handler(ctx) + } +} diff --git a/v3/integrations/nrfasthttp/instrumentation_test.go b/v3/integrations/nrfasthttp/instrumentation_test.go new file mode 100644 index 000000000..844c3682b --- /dev/null +++ b/v3/integrations/nrfasthttp/instrumentation_test.go @@ -0,0 +1,56 @@ +package nrfasthttp + +import ( + "testing" + + "github.com/newrelic/go-agent/v3/internal" + "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" +) + +type myError struct{} + +func (e myError) Error() string { return "my msg" } + +func myErrorHandlerFastHTTP(ctx *fasthttp.RequestCtx) { + ctx.WriteString("noticing an error") + txn := ctx.UserValue("transaction").(*newrelic.Transaction) + txn.NoticeError(myError{}) +} + +func TestWrapHandleFastHTTPFunc(t *testing.T) { + singleCount := []float64{1, 0, 0, 0, 0, 0, 0} + app := createTestApp(true) + + _, wrappedHandler := WrapHandleFunc(app.Application, "/hello", myErrorHandlerFastHTTP) + + if wrappedHandler == nil { + t.Error("Error when creating a wrapped handler") + } + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod("GET") + ctx.Request.SetRequestURI("/hello") + wrappedHandler(ctx) + app.ExpectErrors(t, []internal.WantError{{ + TxnName: "WebTransaction/Go/GET /hello", + Msg: "my msg", + Klass: "nrfasthttp.myError", + }}) + + app.ExpectMetrics(t, []internal.WantMetric{ + {Name: "WebTransaction/Go/GET /hello", Scope: "", Forced: true, Data: nil}, + {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, + {Name: "WebTransactionTotalTime/Go/GET /hello", Scope: "", Forced: false, Data: nil}, + {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, + {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, + {Name: "Apdex", Scope: "", Forced: true, Data: nil}, + {Name: "Apdex/Go/GET /hello", Scope: "", Forced: false, Data: nil}, + {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, + {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, + {Name: "Errors/all", Scope: "", Forced: true, Data: singleCount}, + {Name: "Errors/allWeb", Scope: "", Forced: true, Data: singleCount}, + {Name: "Errors/WebTransaction/Go/GET /hello", Scope: "", Forced: true, Data: singleCount}, + {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, + {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, + }) +} diff --git a/v3/integrations/nrfasthttp/segment.go b/v3/integrations/nrfasthttp/segment.go new file mode 100644 index 000000000..aa480f5f0 --- /dev/null +++ b/v3/integrations/nrfasthttp/segment.go @@ -0,0 +1,80 @@ +package nrfasthttp + +import ( + "net/http" + + "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpadaptor" +) + +// StartExternalSegment automatically creates and fills out a New Relic external segment for a given +// fasthttp request object. This function will accept either a fasthttp.Request or a fasthttp.RequestContext +// object as the request argument. +func StartExternalSegment(txn *newrelic.Transaction, request any) *newrelic.ExternalSegment { + var secureAgentEvent any + var ctx *fasthttp.RequestCtx + + switch reqObject := request.(type) { + + case *fasthttp.RequestCtx: + ctx = reqObject + + case *fasthttp.Request: + ctx = &fasthttp.RequestCtx{} + reqObject.CopyTo(&ctx.Request) + + default: + return nil + } + + if nil == txn { + txn = transactionFromRequestContext(ctx) + } + req := &http.Request{} + + fasthttpadaptor.ConvertRequest(ctx, req, true) + s := &newrelic.ExternalSegment{ + StartTime: txn.StartSegmentNow(), + Request: req, + } + + if newrelic.IsSecurityAgentPresent() { + secureAgentEvent = newrelic.GetSecurityAgentInterface().SendEvent("OUTBOUND", request) + s.SetSecureAgentEvent(secureAgentEvent) + } + + if request != nil && req.Header != nil { + for key, values := range s.GetOutboundHeaders() { + for _, value := range values { + req.Header.Set(key, value) + } + } + + if newrelic.IsSecurityAgentPresent() { + newrelic.GetSecurityAgentInterface().DistributedTraceHeaders(req, secureAgentEvent) + } + + for k, values := range req.Header { + for _, value := range values { + ctx.Request.Header.Set(k, value) + } + } + } + + return s +} + +// FromContext extracts a transaction pointer from a fasthttp.RequestContext object +func FromContext(ctx *fasthttp.RequestCtx) *newrelic.Transaction { + return transactionFromRequestContext(ctx) +} + +func transactionFromRequestContext(ctx *fasthttp.RequestCtx) *newrelic.Transaction { + if nil != ctx { + txn := ctx.UserValue("transaction").(*newrelic.Transaction) + return txn + } + + return nil +} diff --git a/v3/integrations/nrfasthttp/segment_test.go b/v3/integrations/nrfasthttp/segment_test.go new file mode 100644 index 000000000..550e39a86 --- /dev/null +++ b/v3/integrations/nrfasthttp/segment_test.go @@ -0,0 +1,65 @@ +package nrfasthttp + +import ( + "testing" + + "github.com/newrelic/go-agent/v3/internal" + "github.com/newrelic/go-agent/v3/internal/integrationsupport" + "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" +) + +func createTestApp(dt bool) integrationsupport.ExpectApp { + return integrationsupport.NewTestApp(replyFn, integrationsupport.ConfigFullTraces, newrelic.ConfigDistributedTracerEnabled(dt)) +} + +var replyFn = func(reply *internal.ConnectReply) { + reply.SetSampleEverything() +} + +func TestExternalSegment(t *testing.T) { + app := createTestApp(false) + txn := app.StartTransaction("myTxn") + + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(resp) + + ctx := &fasthttp.RequestCtx{Request: fasthttp.Request{}} + ctx.Request.SetRequestURI("http://localhost:8080/hello") + ctx.Request.Header.SetMethod("GET") + + seg := StartExternalSegment(txn, ctx) + defer seg.End() + + txn.End() + app.ExpectMetrics(t, []internal.WantMetric{ + {Name: "OtherTransaction/Go/myTxn", Scope: "", Forced: true, Data: nil}, + {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, + {Name: "OtherTransactionTotalTime/Go/myTxn", Scope: "", Forced: false, Data: nil}, + {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, + }) +} + +func TestExternalSegmentRequest(t *testing.T) { + app := createTestApp(false) + txn := app.StartTransaction("myTxn") + + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + req.SetRequestURI("http://localhost:8080/hello") + req.Header.SetMethod("GET") + + seg := StartExternalSegment(txn, req) + defer seg.End() + + txn.End() + app.ExpectMetrics(t, []internal.WantMetric{ + {Name: "OtherTransaction/Go/myTxn", Scope: "", Forced: true, Data: nil}, + {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, + {Name: "OtherTransactionTotalTime/Go/myTxn", Scope: "", Forced: false, Data: nil}, + {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, + }) +} diff --git a/v3/newrelic/context.go b/v3/newrelic/context.go index 731dcb73f..5ce186f3d 100644 --- a/v3/newrelic/context.go +++ b/v3/newrelic/context.go @@ -8,7 +8,6 @@ import ( "net/http" "github.com/newrelic/go-agent/v3/internal" - "github.com/valyala/fasthttp" ) // NewContext returns a new context.Context that carries the provided @@ -53,16 +52,3 @@ func transactionFromRequestContext(req *http.Request) *Transaction { } return txn } - -func transactionFromRequestContextFastHTTP(ctx *fasthttp.RequestCtx) *Transaction { - var txn *Transaction - if nil != ctx { - txn := ctx.UserValue("transaction").(*Transaction) - return txn - } - - if txn != nil { - return txn - } - return nil -} diff --git a/v3/newrelic/instrumentation.go b/v3/newrelic/instrumentation.go index e4351a955..d0ffd7379 100644 --- a/v3/newrelic/instrumentation.go +++ b/v3/newrelic/instrumentation.go @@ -5,31 +5,8 @@ package newrelic import ( "net/http" - - "github.com/valyala/fasthttp" - "github.com/valyala/fasthttp/fasthttpadaptor" ) -type fasthttpWrapperResponse struct { - ctx *fasthttp.RequestCtx -} - -func (rw fasthttpWrapperResponse) Header() http.Header { - hdrs := http.Header{} - rw.ctx.Request.Header.VisitAll(func(key, value []byte) { - hdrs.Add(string(key), string(value)) - }) - return hdrs -} - -func (rw fasthttpWrapperResponse) Write(b []byte) (int, error) { - return rw.ctx.Write(b) -} - -func (rw fasthttpWrapperResponse) WriteHeader(code int) { - rw.ctx.SetStatusCode(code) -} - // instrumentation.go contains helpers built on the lower level api. // WrapHandle instruments http.Handler handlers with Transactions. To @@ -99,54 +76,37 @@ func WrapHandle(app *Application, pattern string, handler http.Handler, options }) } -func WrapHandleFastHTTP(app *Application, pattern string, handler fasthttp.RequestHandler, options ...TraceOption) (string, fasthttp.RequestHandler) { - if app == nil { - return pattern, handler - } - - // add the wrapped function to the trace options as the source code reference point - // (but only if we know we're collecting CLM for this transaction and the user didn't already - // specify a different code location explicitly). - cache := NewCachedCodeLocation() +// AddCodeLevelMetricsTraceOptions adds trace options to an existing slice of TraceOption objects depending on how code level metrics is configured +// in your application. +// Please call cache:=newrelic.NewCachedCodeLocation() before calling this function, and pass the cache to us in order to allow you to optimize the +// performance and accuracy of this function. +func AddCodeLevelMetricsTraceOptions(app *Application, options []TraceOption, cache *CachedCodeLocation, cachedLocations ...interface{}) []TraceOption { + var tOptions *traceOptSet + var txnOptionList []TraceOption - return pattern, func(ctx *fasthttp.RequestCtx) { - var tOptions *traceOptSet - var txnOptionList []TraceOption + if cache == nil { + return options + } - if app.app != nil && app.app.run != nil && app.app.run.Config.CodeLevelMetrics.Enabled { - tOptions = resolveCLMTraceOptions(options) - if tOptions != nil && !tOptions.SuppressCLM && (tOptions.DemandCLM || app.app.run.Config.CodeLevelMetrics.Scope == 0 || (app.app.run.Config.CodeLevelMetrics.Scope&TransactionCLM) != 0) { - // we are for sure collecting CLM here, so go to the trouble of collecting this code location if nothing else has yet. - if tOptions.LocationOverride == nil { - if loc, err := cache.FunctionLocation(handler); err == nil { - WithCodeLocation(loc)(tOptions) - } + if app.app != nil && app.app.run != nil && app.app.run.Config.CodeLevelMetrics.Enabled { + tOptions = resolveCLMTraceOptions(options) + if tOptions != nil && !tOptions.SuppressCLM && (tOptions.DemandCLM || app.app.run.Config.CodeLevelMetrics.Scope == 0 || (app.app.run.Config.CodeLevelMetrics.Scope&TransactionCLM) != 0) { + // we are for sure collecting CLM here, so go to the trouble of collecting this code location if nothing else has yet. + if tOptions.LocationOverride == nil { + if loc, err := cache.FunctionLocation(cachedLocations); err == nil { + WithCodeLocation(loc)(tOptions) } } } - if tOptions == nil { - // we weren't able to curate the options above, so pass whatever we were given downstream - txnOptionList = options - } else { - txnOptionList = append(txnOptionList, withPreparedOptions(tOptions)) - } - - method := string(ctx.Method()) - path := string(ctx.Path()) - txn := app.StartTransaction(method+" "+path, txnOptionList...) - ctx.SetUserValue("transaction", txn) - defer txn.End() - r := &http.Request{} - fasthttpadaptor.ConvertRequest(ctx, r, true) - resp := fasthttpWrapperResponse{ctx: ctx} - - txn.SetWebResponse(resp) - txn.SetWebRequestHTTP(r) - - r = RequestWithTransactionContext(r, txn) - - handler(ctx) } + if tOptions == nil { + // we weren't able to curate the options above, so pass whatever we were given downstream + txnOptionList = options + } else { + txnOptionList = append(txnOptionList, withPreparedOptions(tOptions)) + } + + return txnOptionList } // WrapHandleFunc instruments handler functions using Transactions. To @@ -184,14 +144,6 @@ func WrapHandleFunc(app *Application, pattern string, handler func(http.Response return p, func(w http.ResponseWriter, r *http.Request) { h.ServeHTTP(w, r) } } -func WrapHandleFuncFastHTTP(app *Application, pattern string, handler func(*fasthttp.RequestCtx), options ...TraceOption) (string, func(*fasthttp.RequestCtx)) { - // add the wrapped function to the trace options as the source code reference point - // (to the beginning of the option list, so that the user can override this) - - p, h := WrapHandleFastHTTP(app, pattern, fasthttp.RequestHandler(handler), options...) - return p, func(ctx *fasthttp.RequestCtx) { h(ctx) } -} - // WrapListen wraps an HTTP endpoint reference passed to functions like http.ListenAndServe, // which causes security scanning to be done for that incoming endpoint when vulnerability // scanning is enabled. It returns the endpoint string, so you can replace a call like diff --git a/v3/newrelic/internal_17_test.go b/v3/newrelic/internal_17_test.go index 5ba7b6c7e..82d1dc8f1 100644 --- a/v3/newrelic/internal_17_test.go +++ b/v3/newrelic/internal_17_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/newrelic/go-agent/v3/internal" - "github.com/valyala/fasthttp" ) func myErrorHandler(w http.ResponseWriter, req *http.Request) { @@ -19,48 +18,6 @@ func myErrorHandler(w http.ResponseWriter, req *http.Request) { txn.NoticeError(myError{}) } -func myErrorHandlerFastHTTP(ctx *fasthttp.RequestCtx) { - ctx.WriteString("noticing an error") - txn := ctx.UserValue("transaction").(*Transaction) - txn.NoticeError(myError{}) -} - -func TestWrapHandleFastHTTPFunc(t *testing.T) { - app := testApp(nil, ConfigDistributedTracerEnabled(true), t) - - _, wrappedHandler := WrapHandleFuncFastHTTP(app.Application, "/hello", myErrorHandlerFastHTTP) - - if wrappedHandler == nil { - t.Error("Error when creating a wrapped handler") - } - ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") - ctx.Request.SetRequestURI("/hello") - wrappedHandler(ctx) - app.ExpectErrors(t, []internal.WantError{{ - TxnName: "WebTransaction/Go/GET /hello", - Msg: "my msg", - Klass: "newrelic.myError", - }}) - - app.ExpectMetrics(t, []internal.WantMetric{ - {Name: "WebTransaction/Go/GET /hello", Scope: "", Forced: true, Data: nil}, - {Name: "WebTransaction", Scope: "", Forced: true, Data: nil}, - {Name: "WebTransactionTotalTime/Go/GET /hello", Scope: "", Forced: false, Data: nil}, - {Name: "WebTransactionTotalTime", Scope: "", Forced: true, Data: nil}, - {Name: "HttpDispatcher", Scope: "", Forced: true, Data: nil}, - {Name: "Apdex", Scope: "", Forced: true, Data: nil}, - {Name: "Apdex/Go/GET /hello", Scope: "", Forced: false, Data: nil}, - {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, - {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, - {Name: "Errors/all", Scope: "", Forced: true, Data: singleCount}, - {Name: "Errors/allWeb", Scope: "", Forced: true, Data: singleCount}, - {Name: "Errors/WebTransaction/Go/GET /hello", Scope: "", Forced: true, Data: singleCount}, - {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", Scope: "", Forced: false, Data: nil}, - {Name: "ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", Scope: "", Forced: false, Data: nil}, - }) -} - func TestWrapHandleFunc(t *testing.T) { app := testApp(nil, ConfigDistributedTracerEnabled(false), t) mux := http.NewServeMux() diff --git a/v3/newrelic/internal_context_test.go b/v3/newrelic/internal_context_test.go index 51372d382..1e15e61cd 100644 --- a/v3/newrelic/internal_context_test.go +++ b/v3/newrelic/internal_context_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/newrelic/go-agent/v3/internal" - "github.com/valyala/fasthttp" ) func TestWrapHandlerContext(t *testing.T) { @@ -37,30 +36,6 @@ func TestWrapHandlerContext(t *testing.T) { {Name: "Custom/mySegment", Scope: scope, Forced: false, Data: nil}, }) } -func TestExternalSegmentFastHTTP(t *testing.T) { - app := testApp(nil, ConfigDistributedTracerEnabled(false), t) - txn := app.StartTransaction("myTxn") - - req := fasthttp.AcquireRequest() - resp := fasthttp.AcquireResponse() - defer fasthttp.ReleaseRequest(req) - defer fasthttp.ReleaseResponse(resp) - - req.SetRequestURI("http://localhost:8080/hello") - req.Header.SetMethod("GET") - - ctx := &fasthttp.RequestCtx{} - seg := StartExternalSegmentFastHTTP(txn, ctx) - defer seg.End() - - txn.End() - app.ExpectMetrics(t, []internal.WantMetric{ - {Name: "OtherTransaction/Go/myTxn", Scope: "", Forced: true, Data: nil}, - {Name: "OtherTransaction/all", Scope: "", Forced: true, Data: nil}, - {Name: "OtherTransactionTotalTime/Go/myTxn", Scope: "", Forced: false, Data: nil}, - {Name: "OtherTransactionTotalTime", Scope: "", Forced: true, Data: nil}, - }) -} func TestStartExternalSegmentNilTransaction(t *testing.T) { // Test that StartExternalSegment pulls the transaction from the diff --git a/v3/newrelic/segments.go b/v3/newrelic/segments.go index 65344033a..328db4283 100644 --- a/v3/newrelic/segments.go +++ b/v3/newrelic/segments.go @@ -5,9 +5,6 @@ package newrelic import ( "net/http" - - "github.com/valyala/fasthttp" - "github.com/valyala/fasthttp/fasthttpadaptor" ) // SegmentStartTime is created by Transaction.StartSegmentNow and marks the @@ -289,6 +286,23 @@ func (s *ExternalSegment) outboundHeaders() http.Header { return outboundHeaders(s) } +func (s *ExternalSegment) GetOutboundHeaders() http.Header { + return s.outboundHeaders() +} + +// SetSecureAgentEvent allows integration packages to set the secureAgentEvent +// for this external segment. That field is otherwise unexported and not available +// for other manipulation. +func (s *ExternalSegment) SetSecureAgentEvent(event any) { + s.secureAgentEvent = event +} + +// GetSecureAgentEvent retrieves the secureAgentEvent previously stored by +// a SetSecureAgentEvent method. +func (s *ExternalSegment) GetSecureAgentEvent() any { + return s.secureAgentEvent +} + // StartSegmentNow starts timing a segment. // // Deprecated: StartSegmentNow is deprecated and will be removed in a future @@ -340,36 +354,6 @@ func StartExternalSegment(txn *Transaction, request *http.Request) *ExternalSegm return s } -func StartExternalSegmentFastHTTP(txn *Transaction, ctx *fasthttp.RequestCtx) *ExternalSegment { - if nil == txn { - txn = transactionFromRequestContextFastHTTP(ctx) - } - request := &http.Request{} - - fasthttpadaptor.ConvertRequest(ctx, request, true) - s := &ExternalSegment{ - StartTime: txn.StartSegmentNow(), - Request: request, - } - if IsSecurityAgentPresent() { - s.secureAgentEvent = secureAgent.SendEvent("OUTBOUND", request) - } - - if request != nil && request.Header != nil { - for key, values := range s.outboundHeaders() { - for _, value := range values { - request.Header.Set(key, value) - } - } - - if IsSecurityAgentPresent() { - secureAgent.DistributedTraceHeaders(request, s.secureAgentEvent) - } - } - - return s -} - func addSpanAttr(start SegmentStartTime, key string, val interface{}) { if nil == start.thread { return From d2d2ac49182b473440bf4ce5273b1918aa5b346b Mon Sep 17 00:00:00 2001 From: mirackara Date: Thu, 16 Nov 2023 13:16:07 -0600 Subject: [PATCH 08/16] Update Changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c696edb3..366e2ac6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## 3.28.0 +### Fixed +* Bumped gRPC from 1.54.0 -> 1.56.3 in the following packages /v3/integrations/nrgrpc, /v3/, /v3/integrations/nrgrpc +* Bumped golang.org/x/net from 0.8.0 -> 0.17.0 in package /v3/integrations/nrgraphqlgo +* Fixed issue where nrfasthttp would not properly register security agent headers +* Move fasthttp instrumentation into a new integration package, nrfasthttp +* Fixed issue where usage of io.ReadAll() was causing a memory leak + +### Support statement + +We use the latest version of the Go language. At minimum, you should be using no version of Go older than what is supported by the Go team themselves. + +See the [Go agent EOL Policy](/docs/apm/agents/go-agent/get-started/go-agent-eol-policy) for details about supported versions of the Go agent and third-party components. + + ## 3.27.0 ### Added * Added Support for getting Container ID's from cgroup v2 docker containers From 7b07e07a18ccc63f1fcd3c70c174fb230f2e1569 Mon Sep 17 00:00:00 2001 From: mirackara Date: Thu, 16 Nov 2023 13:17:07 -0600 Subject: [PATCH 09/16] update version.go --- v3/newrelic/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/newrelic/version.go b/v3/newrelic/version.go index 0b8c0fc6f..085452c7a 100644 --- a/v3/newrelic/version.go +++ b/v3/newrelic/version.go @@ -11,7 +11,7 @@ import ( const ( // Version is the full string version of this Go Agent. - Version = "3.27.0" + Version = "3.28.0" ) var ( From df3f664aa219e40631aaff0b68eded9fc58ca688 Mon Sep 17 00:00:00 2001 From: Emilio Garcia Date: Mon, 27 Nov 2023 14:00:14 -0500 Subject: [PATCH 10/16] obfuscate license key --- v3/newrelic/internal_app.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/v3/newrelic/internal_app.go b/v3/newrelic/internal_app.go index d376625d3..62b81d845 100644 --- a/v3/newrelic/internal_app.go +++ b/v3/newrelic/internal_app.go @@ -121,7 +121,7 @@ func (app *app) doHarvest(h *harvest, harvestStart time.Time, run *appRun) { if resp.Err != nil { app.Warn("harvest failure", map[string]interface{}{ "cmd": cmd, - "error": resp.Err.Error(), + "error": obfuscateLicenseKeyCollectorRequest(resp.Err.Error()), "retain_data": resp.ShouldSaveHarvestData(), }) } @@ -132,6 +132,17 @@ func (app *app) doHarvest(h *harvest, harvestStart time.Time, run *appRun) { } } +func obfuscateLicenseKeyCollectorRequest(responseErrorString string) string { + licenseKeyIndex := strings.Index(responseErrorString, "license_key=") + marshalFormatIndex := strings.Index(responseErrorString, "&marshal_format=") + + if licenseKeyIndex == -1 || marshalFormatIndex == -1 { + return responseErrorString + } + + return responseErrorString[0:licenseKeyIndex] + "license_key=**REDACTED**" + responseErrorString[marshalFormatIndex:] +} + func (app *app) connectRoutine() { attempts := 0 for { From 519c68f352975b01e4a788036cb2a787c27d1d6d Mon Sep 17 00:00:00 2001 From: Aayush garg <59004877+aayush-ap@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:53:10 +0530 Subject: [PATCH 11/16] fix for turn nil body into non nil body (#824) --- v3/newrelic/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/newrelic/transaction.go b/v3/newrelic/transaction.go index af360f29a..001ff36e8 100644 --- a/v3/newrelic/transaction.go +++ b/v3/newrelic/transaction.go @@ -246,7 +246,7 @@ func serverName(r *http.Request) string { } func reqBody(req *http.Request) *BodyBuffer { - if IsSecurityAgentPresent() { + if IsSecurityAgentPresent() && req.Body != nil && req.Body != http.NoBody { buf := &BodyBuffer{buf: make([]byte, 0, 100)} tee := io.TeeReader(req.Body, buf) req.Body = io.NopCloser(tee) From 4e2ea8faf9a75d6ed58b2e81050c9bb05b155628 Mon Sep 17 00:00:00 2001 From: Mirac Kara <55501260+mirackara@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:24:22 -0600 Subject: [PATCH 12/16] Add supportability metrics for FastHTTP (#820) --- v3/integrations/nrfasthttp/instrumentation.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v3/integrations/nrfasthttp/instrumentation.go b/v3/integrations/nrfasthttp/instrumentation.go index 9ce64769c..c35b0d2bb 100644 --- a/v3/integrations/nrfasthttp/instrumentation.go +++ b/v3/integrations/nrfasthttp/instrumentation.go @@ -2,12 +2,17 @@ package nrfasthttp import ( "net/http" - + "github.com/newrelic/go-agent/v3/internal" "github.com/newrelic/go-agent/v3/newrelic" "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttpadaptor" ) + +func init() { internal.TrackUsage("integration", "framework", "fasthttp") } + + + type fasthttpWrapperResponse struct { ctx *fasthttp.RequestCtx } From dd53d78e265757acf10d3c9d5a4c7dcc7efbd778 Mon Sep 17 00:00:00 2001 From: Mirac Kara <55501260+mirackara@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:24:37 -0600 Subject: [PATCH 13/16] Always Link Transaction IDs to Traces (#821) --- v3/internal/expect.go | 1 + v3/newrelic/errors_from_internal.go | 2 + v3/newrelic/errors_test.go | 85 ++++++++++++++++++++++++----- v3/newrelic/harvest_test.go | 3 +- v3/newrelic/internal_txn.go | 10 ++-- v3/newrelic/slow_queries_test.go | 1 + v3/newrelic/tracing.go | 11 ++++ v3/newrelic/txn_trace.go | 3 + 8 files changed, 97 insertions(+), 19 deletions(-) diff --git a/v3/internal/expect.go b/v3/internal/expect.go index 2638225fd..edb92bdc2 100644 --- a/v3/internal/expect.go +++ b/v3/internal/expect.go @@ -22,6 +22,7 @@ type WantError struct { TxnName string Msg string Klass string + GUID string UserAttributes map[string]interface{} AgentAttributes map[string]interface{} } diff --git a/v3/newrelic/errors_from_internal.go b/v3/newrelic/errors_from_internal.go index cf8161303..fdb439366 100644 --- a/v3/newrelic/errors_from_internal.go +++ b/v3/newrelic/errors_from_internal.go @@ -124,6 +124,8 @@ func (h *tracedError) WriteJSON(buf *bytes.Buffer) { h.Stack.WriteJSON(buf) } buf.WriteByte('}') + buf.WriteByte(',') + jsonx.AppendString(buf, h.txnEvent.TxnID) buf.WriteByte(']') } diff --git a/v3/newrelic/errors_test.go b/v3/newrelic/errors_test.go index 783306d18..6766276e9 100644 --- a/v3/newrelic/errors_test.go +++ b/v3/newrelic/errors_test.go @@ -27,6 +27,48 @@ func testExpectedJSON(t testing.TB, expect string, actual string) { } } +func TestErrorNoCAT(t *testing.T) { + he := &tracedError{ + errorData: errorData{ + When: time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC), + Stack: emptyStackTrace, + Msg: "my_msg", + Klass: "my_class", + }, + txnEvent: txnEvent{ + FinalName: "my_txn_name", + Attrs: nil, + TxnID: "txn-guid-id", + BetterCAT: betterCAT{ + Enabled: false, + }, + TotalTime: 2 * time.Second, + }, + } + js, err := json.Marshal(he) + if nil != err { + t.Error(err) + } + + expect := ` + [ + 1.41713646e+12, + "my_txn_name", + "my_msg", + "my_class", + { + "agentAttributes":{}, + "userAttributes":{}, + "intrinsics":{ + "totalTime":2 + }, + "stack_trace":[] + }, + "txn-guid-id" + ]` + testExpectedJSON(t, expect, string(js)) +} + func TestErrorTraceMarshal(t *testing.T) { he := &tracedError{ errorData: errorData{ @@ -38,6 +80,7 @@ func TestErrorTraceMarshal(t *testing.T) { txnEvent: txnEvent{ FinalName: "my_txn_name", Attrs: nil, + TxnID: "txn-guid-id", BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", @@ -69,7 +112,8 @@ func TestErrorTraceMarshal(t *testing.T) { "sampled":false }, "stack_trace":[] - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -89,6 +133,7 @@ func TestErrorTraceMarshalOldCAT(t *testing.T) { Enabled: false, }, TotalTime: 2 * time.Second, + TxnID: "txn-guid-id", }, } js, err := json.Marshal(he) @@ -109,7 +154,8 @@ func TestErrorTraceMarshalOldCAT(t *testing.T) { "totalTime":2 }, "stack_trace":[] - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -135,11 +181,13 @@ func TestErrorTraceAttributes(t *testing.T) { txnEvent: txnEvent{ FinalName: "my_txn_name", Attrs: attr, + TxnID: "txn-id", + BetterCAT: betterCAT{ Enabled: true, - TxnID: "txn-id", Priority: 0.5, TraceID: "trace-id", + TxnID: "txn-id", }, TotalTime: 2 * time.Second, }, @@ -164,7 +212,8 @@ func TestErrorTraceAttributes(t *testing.T) { "priority":0.500000, "sampled":false } - } + }, + "txn-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -188,6 +237,7 @@ func TestErrorTraceAttributesOldCAT(t *testing.T) { Klass: "my_class", }, txnEvent: txnEvent{ + TxnID: "txn-guid-id", FinalName: "my_txn_name", Attrs: attr, BetterCAT: betterCAT{ @@ -212,7 +262,8 @@ func TestErrorTraceAttributesOldCAT(t *testing.T) { "intrinsics":{ "totalTime":2 } - } + }, + "txn-guid-id" ]` testExpectedJSON(t, expect, string(js)) } @@ -231,6 +282,8 @@ func TestErrorsLifecycle(t *testing.T) { mergeTxnErrors(&he, ers, txnEvent{ FinalName: "txnName", Attrs: nil, + TxnID: "txn-id", + BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", @@ -257,12 +310,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -274,12 +328,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -291,12 +346,13 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ], [ 1.41713646e+12, @@ -308,17 +364,18 @@ func TestErrorsLifecycle(t *testing.T) { "userAttributes":{}, "intrinsics":{ "totalTime":2, - "guid":"txn-id", + "guid":"txn-id", "traceId":"trace-id", "priority":0.500000, "sampled":false } - } + }, + "txn-id" ] ] ]`) if string(js) != expect { - t.Error(string(js), expect) + t.Error(string(js), "expect: ", expect) } } diff --git a/v3/newrelic/harvest_test.go b/v3/newrelic/harvest_test.go index 58748fab0..73fdd9b71 100644 --- a/v3/newrelic/harvest_test.go +++ b/v3/newrelic/harvest_test.go @@ -415,7 +415,7 @@ func TestHarvestErrorEventsReady(t *testing.T) { }) h.ErrorEvents.Add(&errorEvent{ errorData: errorData{Klass: "klass", Msg: "msg", When: time.Now()}, - txnEvent: txnEvent{FinalName: "finalName", Duration: 1 * time.Second}, + txnEvent: txnEvent{FinalName: "finalName", Duration: 1 * time.Second, TxnID: "txn-guid-id"}, }, 0) ready := h.Ready(now.Add(10 * time.Second)) payloads := ready.Payloads(true) @@ -544,6 +544,7 @@ func TestHarvestMetricsTracesReady(t *testing.T) { TxnName: "finalName", Msg: "msg", Klass: "klass", + GUID: "error-guid-id", }}) expectErrors(t, h.ErrorTraces, []internal.WantError{}) diff --git a/v3/newrelic/internal_txn.go b/v3/newrelic/internal_txn.go index 30fe644d4..b06a8c61c 100644 --- a/v3/newrelic/internal_txn.go +++ b/v3/newrelic/internal_txn.go @@ -118,11 +118,13 @@ func newTxn(app *app, run *appRun, name string, opts ...TraceOption) *thread { if !txnOpts.SuppressCLM && run.Config.CodeLevelMetrics.Enabled && (txnOpts.DemandCLM || run.Config.CodeLevelMetrics.Scope == 0 || (run.Config.CodeLevelMetrics.Scope&TransactionCLM) != 0) { reportCodeLevelMetrics(txnOpts, run, txn.Attrs.Agent.Add) } + txn.TraceIDGenerator = run.Reply.TraceIDGenerator + traceID := txn.TraceIDGenerator.GenerateTraceID() + txn.SetTransactionID(traceID) if run.Config.DistributedTracer.Enabled { txn.BetterCAT.Enabled = true - txn.TraceIDGenerator = run.Reply.TraceIDGenerator - txn.BetterCAT.SetTraceAndTxnIDs(txn.TraceIDGenerator.GenerateTraceID()) + txn.BetterCAT.SetTraceAndTxnIDs(traceID) txn.BetterCAT.Priority = newPriorityFromRandom(txn.TraceIDGenerator.Float32) txn.ShouldCollectSpanEvents = txn.shouldCollectSpanEvents txn.ShouldCreateSpanGUID = txn.shouldCreateSpanGUID @@ -523,7 +525,7 @@ func (thd *thread) End(recovered interface{}) error { // segments occur. for _, evt := range txn.SpanEvents { evt.TraceID = txn.BetterCAT.TraceID - evt.TransactionID = txn.BetterCAT.TxnID + evt.TransactionID = txn.TxnID evt.Sampled = txn.BetterCAT.Sampled evt.Priority = txn.BetterCAT.Priority } @@ -1144,7 +1146,7 @@ func (thd *thread) CreateDistributedTracePayload(hdrs http.Header) { p.Priority = txn.BetterCAT.Priority p.Timestamp.Set(txn.Reply.DistributedTraceTimestampGenerator()) p.TrustedAccountKey = txn.Reply.TrustedAccountKey - p.TransactionID = txn.BetterCAT.TxnID // Set the transaction ID to the transaction guid. + p.TransactionID = txn.TxnID // Set the transaction ID to the transaction guid. if nil != txn.BetterCAT.Inbound { p.NonTrustedTraceState = txn.BetterCAT.Inbound.NonTrustedTraceState p.OriginalTraceState = txn.BetterCAT.Inbound.OriginalTraceState diff --git a/v3/newrelic/slow_queries_test.go b/v3/newrelic/slow_queries_test.go index 3d831f427..9aa112ad6 100644 --- a/v3/newrelic/slow_queries_test.go +++ b/v3/newrelic/slow_queries_test.go @@ -211,6 +211,7 @@ func TestSlowQueriesBetterCAT(t *testing.T) { FinalName: "WebTransaction/Go/hello", Duration: 3 * time.Second, Attrs: attr, + TxnID: "my-txn-id", BetterCAT: betterCAT{ Enabled: true, TxnID: "txn-id", diff --git a/v3/newrelic/tracing.go b/v3/newrelic/tracing.go index a189cef5e..c8fac7ef6 100644 --- a/v3/newrelic/tracing.go +++ b/v3/newrelic/tracing.go @@ -36,6 +36,7 @@ type txnEvent struct { datastoreCallCount uint64 datastoreDuration time.Duration errGroupCallback ErrorGroupCallback + TxnID string } // betterCAT stores the transaction's priority and all fields related @@ -62,6 +63,16 @@ func (bc *betterCAT) SetTraceAndTxnIDs(traceID string) { } } +func (e *txnEvent) SetTransactionID(transactionID string) { + txnLength := 16 + if len(transactionID) <= txnLength { + e.TxnID = transactionID + } else { + e.TxnID = transactionID[:txnLength] + } + +} + // txnData contains the recorded data of a transaction. type txnData struct { IsWeb bool diff --git a/v3/newrelic/txn_trace.go b/v3/newrelic/txn_trace.go index 2e925c5e4..f1d0c99ce 100644 --- a/v3/newrelic/txn_trace.go +++ b/v3/newrelic/txn_trace.go @@ -294,6 +294,9 @@ func (trace *harvestTrace) writeJSON(buf *bytes.Buffer) { jsonx.AppendString(buf, trace.CrossProcess.GUID) } else if trace.BetterCAT.Enabled { jsonx.AppendString(buf, trace.BetterCAT.TraceID) + } else if !trace.BetterCAT.Enabled && trace.CrossProcess.GUID != "" { + jsonx.AppendString(buf, trace.txnEvent.TxnID) + } else { buf.WriteString(`""`) } From 6326b42270209ffbd6023682f9ab5806b3b83ffb Mon Sep 17 00:00:00 2001 From: Emilio Garcia Date: Thu, 30 Nov 2023 14:25:43 -0500 Subject: [PATCH 14/16] more robust solution for obfuscation (#828) * more robust solution for obfuscation * check for licensekey param * fix obfuscation bug * robust obfuscation for all rpm responses * get rid of ioutil * better cleansing and testing for error URL stripping --- v3/newrelic/collector.go | 127 +++++++++++++++++++++++----------- v3/newrelic/collector_test.go | 70 ++++++++++--------- v3/newrelic/internal_app.go | 29 +++----- 3 files changed, 135 insertions(+), 91 deletions(-) diff --git a/v3/newrelic/collector.go b/v3/newrelic/collector.go index 4cbd87c93..791d55f48 100644 --- a/v3/newrelic/collector.go +++ b/v3/newrelic/collector.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "strconv" @@ -64,32 +64,78 @@ type rpmControls struct { // Agent Behavior Summary: // // on connect/preconnect: -// 410 means shutdown -// 200, 202 mean success (start run) -// all other response codes and errors mean try after backoff +// +// 410 means shutdown +// 200, 202 mean success (start run) +// all other response codes and errors mean try after backoff // // on harvest: -// 410 means shutdown -// 401, 409 mean restart run -// 408, 429, 500, 503 mean save data for next harvest -// all other response codes and errors discard the data and continue the current harvest +// +// 410 means shutdown +// 401, 409 mean restart run +// 408, 429, 500, 503 mean save data for next harvest +// all other response codes and errors discard the data and continue the current harvest type rpmResponse struct { statusCode int body []byte // Err indicates whether or not the call was successful: newRPMResponse // should be used to avoid mismatch between statusCode and Err. - Err error + err error disconnectSecurityPolicy bool // forceSaveHarvestData overrides the status code and forces a save of data forceSaveHarvestData bool } -func newRPMResponse(statusCode int) rpmResponse { - var err error +// please create all rpmResponses this way +func newRPMResponse(err error) *rpmResponse { + if err == nil { + return &rpmResponse{} + } + + // remove url from errors to avoid sensitive data leaks + var ue *url.Error + if errors.As(err, &ue) { + ue.URL = "**REDACTED-URL**" + } + + return &rpmResponse{ + err: err, + } +} + +// AddStatusCode adds an http error status code to the rpm response. This can overwrite the error +// string stored in the rpm response if the code is an error code. +func (resp *rpmResponse) AddStatusCode(statusCode int) *rpmResponse { + resp.statusCode = statusCode if statusCode != 200 && statusCode != 202 { - err = fmt.Errorf("response code: %d", statusCode) + resp.err = fmt.Errorf("response code: %d", statusCode) } - return rpmResponse{statusCode: statusCode, Err: err} + + return resp +} + +// SetError overwrites the existing response error +func (resp *rpmResponse) SetError(err error) *rpmResponse { + resp.err = err + return resp +} + +// AddBody adds a byte slice containing an http response body +func (resp *rpmResponse) AddBody(body []byte) *rpmResponse { + resp.body = body + return resp +} + +// ForceSaveHarvestData overrides the status code and forces a save of data +func (resp *rpmResponse) ForceSaveHarvestData() *rpmResponse { + resp.forceSaveHarvestData = true + return resp +} + +// DisconnectSecurityPolicy sets disconnectSecurityPolicy to true in the rpm response +func (resp *rpmResponse) DisconnectSecurityPolicy() *rpmResponse { + resp.disconnectSecurityPolicy = true + return resp } // IsDisconnect indicates that the agent should disconnect. @@ -103,6 +149,10 @@ func (resp rpmResponse) IsRestartException() bool { resp.statusCode == 409 } +func (resp rpmResponse) GetError() error { + return resp.err +} + // ShouldSaveHarvestData indicates that the agent should save the data and try // to send it in the next harvest. func (resp rpmResponse) ShouldSaveHarvestData() bool { @@ -154,19 +204,19 @@ func compress(b []byte, gzipWriterPool *sync.Pool) (*bytes.Buffer, error) { return &buf, nil } -func collectorRequestInternal(url string, cmd rpmCmd, cs rpmControls) rpmResponse { +func collectorRequestInternal(url string, cmd rpmCmd, cs rpmControls) *rpmResponse { compressed, err := compress(cmd.Data, cs.GzipWriterPool) if nil != err { - return rpmResponse{Err: err} + return newRPMResponse(err) } if l := compressed.Len(); l > cmd.MaxPayloadSize { - return rpmResponse{Err: fmt.Errorf("Payload size for %s too large: %d greater than %d", cmd.Name, l, cmd.MaxPayloadSize)} + return newRPMResponse(fmt.Errorf("Payload size for %s too large: %d greater than %d", cmd.Name, l, cmd.MaxPayloadSize)) } req, err := http.NewRequest("POST", url, compressed) if nil != err { - return rpmResponse{Err: err} + return newRPMResponse(err) } req.Header.Add("Accept-Encoding", "identity, deflate") @@ -179,32 +229,28 @@ func collectorRequestInternal(url string, cmd rpmCmd, cs rpmControls) rpmRespons resp, err := cs.Client.Do(req) if err != nil { - return rpmResponse{ - forceSaveHarvestData: true, - Err: err, - } + return newRPMResponse(err).ForceSaveHarvestData() } defer resp.Body.Close() - r := newRPMResponse(resp.StatusCode) + r := newRPMResponse(nil).AddStatusCode(resp.StatusCode) // Read the entire response, rather than using resp.Body as input to json.NewDecoder to // avoid the issue described here: // https://github.com/google/go-github/pull/317 // https://ahmetalpbalkan.com/blog/golang-json-decoder-pitfalls/ // Also, collector JSON responses are expected to be quite small. - body, err := ioutil.ReadAll(resp.Body) - if nil == r.Err { - r.Err = err + body, err := io.ReadAll(resp.Body) + if r.GetError() == nil { + r.SetError(err) } - r.body = body - + r.AddBody(body) return r } // collectorRequest makes a request to New Relic. -func collectorRequest(cmd rpmCmd, cs rpmControls) rpmResponse { +func collectorRequest(cmd rpmCmd, cs rpmControls) *rpmResponse { url := rpmURL(cmd, cs) urlWithoutLicense := removeLicenseFromURL(url) @@ -219,7 +265,7 @@ func collectorRequest(cmd rpmCmd, cs rpmControls) rpmResponse { resp := collectorRequestInternal(url, cmd, cs) if cs.Logger.DebugEnabled() { - if err := resp.Err; err != nil { + if err := resp.GetError(); err != nil { cs.Logger.Debug("rpm failure", map[string]interface{}{ "command": cmd.Name, "url": urlWithoutLicense, @@ -266,13 +312,13 @@ var ( ) // connectAttempt tries to connect an application. -func connectAttempt(config config, cs rpmControls) (*internal.ConnectReply, rpmResponse) { +func connectAttempt(config config, cs rpmControls) (*internal.ConnectReply, *rpmResponse) { preconnectData, err := json.Marshal([]preconnectRequest{{ SecurityPoliciesToken: config.SecurityPoliciesToken, HighSecurity: config.HighSecurity, }}) if nil != err { - return nil, rpmResponse{Err: fmt.Errorf("unable to marshal preconnect data: %v", err)} + return nil, newRPMResponse(fmt.Errorf("unable to marshal preconnect data: %v", err)) } call := rpmCmd{ @@ -283,7 +329,7 @@ func connectAttempt(config config, cs rpmControls) (*internal.ConnectReply, rpmR } resp := collectorRequest(call, cs) - if nil != resp.Err { + if resp.GetError() != nil { return nil, resp } @@ -292,16 +338,17 @@ func connectAttempt(config config, cs rpmControls) (*internal.ConnectReply, rpmR } err = json.Unmarshal(resp.body, &preconnect) if nil != err { - // Certain security policy errors must be treated as a disconnect. - return nil, rpmResponse{ - Err: fmt.Errorf("unable to process preconnect reply: %v", err), - disconnectSecurityPolicy: internal.IsDisconnectSecurityPolicyError(err), + resp := newRPMResponse(fmt.Errorf("unable to process preconnect reply: %v", err)) + if internal.IsDisconnectSecurityPolicyError(err) { + resp.DisconnectSecurityPolicy() } + // Certain security policy errors must be treated as a disconnect. + return nil, resp } js, err := config.createConnectJSON(preconnect.Preconnect.SecurityPolicies.PointerIfPopulated()) if nil != err { - return nil, rpmResponse{Err: fmt.Errorf("unable to create connect data: %v", err)} + return nil, newRPMResponse(fmt.Errorf("unable to create connect data: %v", err)) } call.Collector = preconnect.Preconnect.Collector @@ -309,19 +356,19 @@ func connectAttempt(config config, cs rpmControls) (*internal.ConnectReply, rpmR call.Name = cmdConnect resp = collectorRequest(call, cs) - if nil != resp.Err { + if resp.GetError() != nil { return nil, resp } reply, err := internal.UnmarshalConnectReply(resp.body, preconnect.Preconnect) if nil != err { - return nil, rpmResponse{Err: err} + return nil, newRPMResponse(err) } // Note: This should never happen. It would mean the collector // response is malformed. This exists merely as extra defensiveness. if "" == reply.RunID { - return nil, rpmResponse{Err: errMissingAgentRunID} + return nil, newRPMResponse(errMissingAgentRunID) } return reply, resp diff --git a/v3/newrelic/collector_test.go b/v3/newrelic/collector_test.go index bf31b0183..f553258e9 100644 --- a/v3/newrelic/collector_test.go +++ b/v3/newrelic/collector_test.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" "strings" @@ -21,6 +20,15 @@ import ( "github.com/newrelic/go-agent/v3/internal/logger" ) +func TestURLErrorRedaction(t *testing.T) { + _, err := http.Get("http://notexist.example/sensitive?sensitive=very") + rpm := newRPMResponse(err) + + if strings.Contains(rpm.GetError().Error(), "http://notexist.example/sensitive?sensitive=very") { + t.Error("Sensitive URL should have been removed from the error struct, but were not") + } +} + func TestCollectorResponseCodeError(t *testing.T) { testcases := []struct { code int @@ -60,18 +68,18 @@ func TestCollectorResponseCodeError(t *testing.T) { {code: 999999, success: false, disconnect: false, restart: false, saveHarvestData: false}, } for _, tc := range testcases { - resp := newRPMResponse(tc.code) - if tc.success != (nil == resp.Err) { - t.Error("error", tc.code, tc.success, resp.Err) + resp := newRPMResponse(nil).AddStatusCode(tc.code) + if tc.success != (nil == resp.GetError()) { + t.Error("error", tc.code, tc.success, resp.GetError()) } if tc.disconnect != resp.IsDisconnect() { - t.Error("disconnect", tc.code, tc.disconnect, resp.Err) + t.Error("disconnect", tc.code, tc.disconnect, resp.GetError()) } if tc.restart != resp.IsRestartException() { - t.Error("restart", tc.code, tc.restart, resp.Err) + t.Error("restart", tc.code, tc.restart, resp.GetError()) } if tc.saveHarvestData != resp.ShouldSaveHarvestData() { - t.Error("save harvest data", tc.code, tc.saveHarvestData, resp.Err) + t.Error("save harvest data", tc.code, tc.saveHarvestData, resp.GetError()) } } } @@ -103,7 +111,7 @@ func TestCollectorRequest(t *testing.T) { testField("zip", r.Header.Get("zip"), "zap") return &http.Response{ StatusCode: 200, - Body: ioutil.NopCloser(strings.NewReader("body")), + Body: io.NopCloser(strings.NewReader("body")), }, nil }), }, @@ -115,8 +123,8 @@ func TestCollectorRequest(t *testing.T) { }, } resp := collectorRequest(cmd, cs) - if nil != resp.Err { - t.Error(resp.Err) + if nil != resp.GetError() { + t.Error(resp.GetError()) } } @@ -134,7 +142,7 @@ func TestCollectorBadRequest(t *testing.T) { Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: 200, - Body: ioutil.NopCloser(strings.NewReader("body")), + Body: io.NopCloser(strings.NewReader("body")), }, nil }), }, @@ -147,7 +155,7 @@ func TestCollectorBadRequest(t *testing.T) { } u := ":" // bad url resp := collectorRequestInternal(u, cmd, cs) - if nil == resp.Err { + if nil == resp.GetError() { t.Error("missing expected error") } } @@ -175,7 +183,7 @@ func TestCollectorTimeout(t *testing.T) { } u := "https://example.com" resp := collectorRequestInternal(u, cmd, cs) - if nil == resp.Err { + if nil == resp.GetError() { t.Error("missing expected error") } if !resp.ShouldSaveHarvestData() { @@ -224,7 +232,7 @@ const ( func makeResponse(code int, body string) *http.Response { return &http.Response{ StatusCode: code, - Body: ioutil.NopCloser(strings.NewReader(body)), + Body: io.NopCloser(strings.NewReader(body)), } } @@ -253,7 +261,7 @@ func (m connectMock) RoundTrip(r *http.Request) (*http.Response, error) { func (m connectMock) CancelRequest(req *http.Request) {} -func testConnectHelper(cm connectMock) (*internal.ConnectReply, rpmResponse) { +func testConnectHelper(cm connectMock) (*internal.ConnectReply, *rpmResponse) { cs := rpmControls{ License: "12345", Client: &http.Client{Transport: cm}, @@ -273,8 +281,8 @@ func TestConnectAttemptSuccess(t *testing.T) { redirect: endpointResult{response: makeResponse(200, redirectBody)}, connect: endpointResult{response: makeResponse(200, connectBody)}, }) - if nil == run || nil != resp.Err { - t.Fatal(run, resp.Err) + if nil == run || nil != resp.GetError() { + t.Fatal(run, resp.GetError()) } if run.Collector != "special_collector" { t.Error(run.Collector) @@ -292,7 +300,7 @@ func TestConnectClientError(t *testing.T) { if nil != run { t.Fatal(run) } - if resp.Err == nil { + if resp.GetError() == nil { t.Fatal("missing expected error") } } @@ -305,7 +313,7 @@ func TestConnectAttemptDisconnectOnRedirect(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } if !resp.IsDisconnect() { @@ -321,7 +329,7 @@ func TestConnectAttemptDisconnectOnConnect(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } if !resp.IsDisconnect() { @@ -337,7 +345,7 @@ func TestConnectAttemptBadSecurityPolicies(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } if !resp.IsDisconnect() { @@ -353,7 +361,7 @@ func TestConnectAttemptInvalidJSON(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } } @@ -366,7 +374,7 @@ func TestConnectAttemptCollectorNotString(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } } @@ -379,7 +387,7 @@ func TestConnectAttempt401(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } if !resp.IsRestartException() { @@ -395,7 +403,7 @@ func TestConnectAttemptOtherReturnCode(t *testing.T) { if nil != run { t.Error(run) } - if nil == resp.Err { + if nil == resp.GetError() { t.Fatal("missing error") } } @@ -408,8 +416,8 @@ func TestConnectAttemptMissingRunID(t *testing.T) { if nil != run { t.Error(run) } - if errMissingAgentRunID != resp.Err { - t.Fatal("wrong error", resp.Err) + if errMissingAgentRunID != resp.GetError() { + t.Fatal("wrong error", resp.GetError()) } } @@ -438,7 +446,7 @@ func TestCollectorRequestRespectsMaxPayloadSize(t *testing.T) { }, } resp := collectorRequest(cmd, cs) - if nil == resp.Err { + if nil == resp.GetError() { t.Error("response should have contained error") } if resp.ShouldSaveHarvestData() { @@ -467,7 +475,7 @@ func TestConnectReplyMaxPayloadSize(t *testing.T) { Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: 200, - Body: ioutil.NopCloser(strings.NewReader(replyBody)), + Body: io.NopCloser(strings.NewReader(replyBody)), }, nil }), }, @@ -482,8 +490,8 @@ func TestConnectReplyMaxPayloadSize(t *testing.T) { for _, test := range testcases { reply, resp := connectAttempt(config{}, controls(test.replyBody)) - if nil != resp.Err { - t.Error("resp returned unexpected error:", resp.Err) + if nil != resp.GetError() { + t.Error("resp returned unexpected error:", resp.GetError()) } if test.expectedMaxPayloadSize != reply.MaxPayloadSizeInBytes { t.Errorf("incorrect MaxPayloadSizeInBytes: expected=%d actual=%d", diff --git a/v3/newrelic/internal_app.go b/v3/newrelic/internal_app.go index 62b81d845..3b0d55f39 100644 --- a/v3/newrelic/internal_app.go +++ b/v3/newrelic/internal_app.go @@ -112,16 +112,16 @@ func (app *app) doHarvest(h *harvest, harvestStart time.Time, run *appRun) { if resp.IsDisconnect() || resp.IsRestartException() { select { - case app.collectorErrorChan <- resp: + case app.collectorErrorChan <- *resp: case <-app.shutdownStarted: } return } - if resp.Err != nil { + if resp.GetError() != nil { app.Warn("harvest failure", map[string]interface{}{ "cmd": cmd, - "error": obfuscateLicenseKeyCollectorRequest(resp.Err.Error()), + "error": resp.GetError().Error(), "retain_data": resp.ShouldSaveHarvestData(), }) } @@ -132,17 +132,6 @@ func (app *app) doHarvest(h *harvest, harvestStart time.Time, run *appRun) { } } -func obfuscateLicenseKeyCollectorRequest(responseErrorString string) string { - licenseKeyIndex := strings.Index(responseErrorString, "license_key=") - marshalFormatIndex := strings.Index(responseErrorString, "&marshal_format=") - - if licenseKeyIndex == -1 || marshalFormatIndex == -1 { - return responseErrorString - } - - return responseErrorString[0:licenseKeyIndex] + "license_key=**REDACTED**" + responseErrorString[marshalFormatIndex:] -} - func (app *app) connectRoutine() { attempts := 0 for { @@ -158,15 +147,15 @@ func (app *app) connectRoutine() { if resp.IsDisconnect() { select { - case app.collectorErrorChan <- resp: + case app.collectorErrorChan <- *resp: case <-app.shutdownStarted: } return } - if nil != resp.Err { + if nil != resp.GetError() { app.Warn("application connect failure", map[string]interface{}{ - "error": resp.Err.Error(), + "error": resp.GetError().Error(), }) } @@ -297,7 +286,7 @@ func (app *app) process() { app.setState(nil, nil) if resp.IsDisconnect() { - app.setState(nil, resp.Err) + app.setState(nil, resp.GetError()) app.Error("application disconnected", map[string]interface{}{ "app": app.config.AppName, }) @@ -604,7 +593,7 @@ func (app *app) RecordCustomMetric(name string, value float64) error { if math.IsInf(value, 0) { return errMetricInf } - if "" == name { + if name == "" { return errMetricNameEmpty } run, _ := app.getState() @@ -652,7 +641,7 @@ func (app *app) Consume(id internal.AgentRunID, data harvestable) { return } - if "" == id { + if id == "" { return } From 2b9c3c42c55079ec67bf0092f456fee4d90214d6 Mon Sep 17 00:00:00 2001 From: Steve Willoughby Date: Thu, 30 Nov 2023 11:34:42 -0800 Subject: [PATCH 15/16] release 3.28.1 --- CHANGELOG.md | 12 ++++++++++++ v3/go.mod | 1 + v3/integrations/logcontext-v2/logWriter/go.mod | 2 +- v3/integrations/logcontext-v2/nrlogrus/go.mod | 2 +- v3/integrations/logcontext-v2/nrwriter/go.mod | 2 +- v3/integrations/logcontext-v2/nrzap/go.mod | 2 +- v3/integrations/logcontext-v2/nrzerolog/go.mod | 2 +- .../logcontext-v2/zerologWriter/go.mod | 2 +- v3/integrations/logcontext/nrlogrusplugin/go.mod | 2 +- v3/integrations/nramqp/go.mod | 3 ++- v3/integrations/nrawssdk-v1/go.mod | 2 +- v3/integrations/nrawssdk-v2/go.mod | 2 +- v3/integrations/nrb3/go.mod | 2 +- v3/integrations/nrecho-v3/go.mod | 2 +- v3/integrations/nrecho-v4/go.mod | 2 +- v3/integrations/nrelasticsearch-v7/go.mod | 2 +- .../nrfasthttp/examples/client-fasthttp/go.mod | 3 ++- .../nrfasthttp/examples/server-fasthttp/go.mod | 3 ++- v3/integrations/nrfasthttp/go.mod | 3 ++- v3/integrations/nrgin/go.mod | 2 +- v3/integrations/nrgorilla/go.mod | 2 +- v3/integrations/nrgraphgophers/go.mod | 2 +- v3/integrations/nrgraphqlgo/example/go.mod | 16 +--------------- v3/integrations/nrgraphqlgo/go.mod | 3 ++- v3/integrations/nrgrpc/go.mod | 4 ++-- v3/integrations/nrhttprouter/go.mod | 2 +- v3/integrations/nrlambda/go.mod | 2 +- v3/integrations/nrlogrus/go.mod | 2 +- v3/integrations/nrlogxi/go.mod | 2 +- v3/integrations/nrmicro/go.mod | 2 +- v3/integrations/nrmongo/go.mod | 2 +- v3/integrations/nrmssql/go.mod | 2 +- v3/integrations/nrmysql/go.mod | 2 +- v3/integrations/nrnats/go.mod | 2 +- v3/integrations/nrnats/test/go.mod | 2 +- v3/integrations/nrpgx/example/sqlx/go.mod | 2 +- v3/integrations/nrpgx/go.mod | 2 +- v3/integrations/nrpgx5/go.mod | 2 +- v3/integrations/nrpkgerrors/go.mod | 2 +- v3/integrations/nrpq/example/sqlx/go.mod | 2 +- v3/integrations/nrpq/go.mod | 2 +- v3/integrations/nrredis-v7/go.mod | 2 +- v3/integrations/nrredis-v8/go.mod | 2 +- v3/integrations/nrredis-v9/go.mod | 2 +- v3/integrations/nrsarama/go.mod | 2 +- v3/integrations/nrsecurityagent/go.mod | 2 +- v3/integrations/nrsnowflake/go.mod | 2 +- v3/integrations/nrsqlite3/go.mod | 2 +- v3/integrations/nrstan/examples/go.mod | 2 +- v3/integrations/nrstan/go.mod | 2 +- v3/integrations/nrstan/test/go.mod | 2 +- v3/integrations/nrzap/go.mod | 2 +- v3/newrelic/version.go | 2 +- 53 files changed, 70 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 366e2ac6e..62c28161b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 3.28.1 +### Added +Added Supportability Metrics to `nrfasthttp` (brings `nrfasthttp` version to v1.0.1). +Always Link Transaction IDs to traces regardless of whether Distributed Tracing is enabled or not +### Fixed +Fixed an issue where `nil` `Request.Body` could be set to non-`nil` `request.Body` with zero length when the security agent is enabled +### Security +More Secure URL Redaction +### Support statement +We use the latest version of the Go language. At minimum, you should be using no version of Go older than what is supported by the Go team themselves. +See the [Go agent EOL Policy](/docs/apm/agents/go-agent/get-started/go-agent-eol-policy) for details about supported versions of the Go agent and third-party components. + ## 3.28.0 ### Fixed * Bumped gRPC from 1.54.0 -> 1.56.3 in the following packages /v3/integrations/nrgrpc, /v3/, /v3/integrations/nrgrpc diff --git a/v3/go.mod b/v3/go.mod index e064d1ccf..fda43a82c 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -7,6 +7,7 @@ require ( google.golang.org/grpc v1.56.3 ) + retract v3.22.0 // release process error corrected in v3.22.1 retract v3.25.0 // release process error corrected in v3.25.1 diff --git a/v3/integrations/logcontext-v2/logWriter/go.mod b/v3/integrations/logcontext-v2/logWriter/go.mod index 00b3f09fc..214b032e2 100644 --- a/v3/integrations/logcontext-v2/logWriter/go.mod +++ b/v3/integrations/logcontext-v2/logWriter/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/logWriter go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter v1.0.0 ) diff --git a/v3/integrations/logcontext-v2/nrlogrus/go.mod b/v3/integrations/logcontext-v2/nrlogrus/go.mod index 7d6e2dbea..f5b6977c5 100644 --- a/v3/integrations/logcontext-v2/nrlogrus/go.mod +++ b/v3/integrations/logcontext-v2/nrlogrus/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrlogrus go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/sirupsen/logrus v1.8.1 ) diff --git a/v3/integrations/logcontext-v2/nrwriter/go.mod b/v3/integrations/logcontext-v2/nrwriter/go.mod index bf5a5bcc1..4f3003f68 100644 --- a/v3/integrations/logcontext-v2/nrwriter/go.mod +++ b/v3/integrations/logcontext-v2/nrwriter/go.mod @@ -2,7 +2,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter go 1.19 -require github.com/newrelic/go-agent/v3 v3.26.0 +require github.com/newrelic/go-agent/v3 v3.28.1 replace github.com/newrelic/go-agent/v3 => ../../.. diff --git a/v3/integrations/logcontext-v2/nrzap/go.mod b/v3/integrations/logcontext-v2/nrzap/go.mod index 2ebe79303..178cc204a 100644 --- a/v3/integrations/logcontext-v2/nrzap/go.mod +++ b/v3/integrations/logcontext-v2/nrzap/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrzap go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 go.uber.org/zap v1.24.0 ) diff --git a/v3/integrations/logcontext-v2/nrzerolog/go.mod b/v3/integrations/logcontext-v2/nrzerolog/go.mod index 9cb9cc32e..1c42aad04 100644 --- a/v3/integrations/logcontext-v2/nrzerolog/go.mod +++ b/v3/integrations/logcontext-v2/nrzerolog/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrzerolog go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/rs/zerolog v1.26.1 ) diff --git a/v3/integrations/logcontext-v2/zerologWriter/go.mod b/v3/integrations/logcontext-v2/zerologWriter/go.mod index 1a831bbac..8fd5bbc83 100644 --- a/v3/integrations/logcontext-v2/zerologWriter/go.mod +++ b/v3/integrations/logcontext-v2/zerologWriter/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext-v2/zerologWriter go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter v1.0.0 github.com/rs/zerolog v1.27.0 ) diff --git a/v3/integrations/logcontext/nrlogrusplugin/go.mod b/v3/integrations/logcontext/nrlogrusplugin/go.mod index f882e9e4c..c650a5c85 100644 --- a/v3/integrations/logcontext/nrlogrusplugin/go.mod +++ b/v3/integrations/logcontext/nrlogrusplugin/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/logcontext/nrlogrusplugin go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 // v1.4.0 is required for for the log.WithContext. github.com/sirupsen/logrus v1.4.0 ) diff --git a/v3/integrations/nramqp/go.mod b/v3/integrations/nramqp/go.mod index c099244b4..a8546b821 100644 --- a/v3/integrations/nramqp/go.mod +++ b/v3/integrations/nramqp/go.mod @@ -3,6 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/nramqp go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.27.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/rabbitmq/amqp091-go v1.9.0 ) +replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrawssdk-v1/go.mod b/v3/integrations/nrawssdk-v1/go.mod index 01328d13f..905961ca5 100644 --- a/v3/integrations/nrawssdk-v1/go.mod +++ b/v3/integrations/nrawssdk-v1/go.mod @@ -8,7 +8,7 @@ go 1.19 require ( // v1.15.0 is the first aws-sdk-go version with module support. github.com/aws/aws-sdk-go v1.34.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrawssdk-v2/go.mod b/v3/integrations/nrawssdk-v2/go.mod index b8913611b..ab91cb896 100644 --- a/v3/integrations/nrawssdk-v2/go.mod +++ b/v3/integrations/nrawssdk-v2/go.mod @@ -11,7 +11,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/lambda v1.24.5 github.com/aws/aws-sdk-go-v2/service/s3 v1.27.10 github.com/aws/smithy-go v1.13.3 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrb3/go.mod b/v3/integrations/nrb3/go.mod index dc2a7e3c0..c6f90dff0 100644 --- a/v3/integrations/nrb3/go.mod +++ b/v3/integrations/nrb3/go.mod @@ -2,7 +2,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrb3 go 1.19 -require github.com/newrelic/go-agent/v3 v3.26.0 +require github.com/newrelic/go-agent/v3 v3.28.1 replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrecho-v3/go.mod b/v3/integrations/nrecho-v3/go.mod index 590bcc584..53be4654e 100644 --- a/v3/integrations/nrecho-v3/go.mod +++ b/v3/integrations/nrecho-v3/go.mod @@ -8,7 +8,7 @@ require ( // v3.1.0 is the earliest v3 version of Echo that works with modules due // to the github.com/rsc/letsencrypt import of v3.0.0. github.com/labstack/echo v3.1.0+incompatible - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrecho-v4/go.mod b/v3/integrations/nrecho-v4/go.mod index fd871b086..44bc0bb9c 100644 --- a/v3/integrations/nrecho-v4/go.mod +++ b/v3/integrations/nrecho-v4/go.mod @@ -6,7 +6,7 @@ go 1.19 require ( github.com/labstack/echo/v4 v4.9.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrelasticsearch-v7/go.mod b/v3/integrations/nrelasticsearch-v7/go.mod index 43a3290a6..be403fdf7 100644 --- a/v3/integrations/nrelasticsearch-v7/go.mod +++ b/v3/integrations/nrelasticsearch-v7/go.mod @@ -6,7 +6,7 @@ go 1.19 require ( github.com/elastic/go-elasticsearch/v7 v7.17.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod b/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod index 104505e67..6290b4b27 100644 --- a/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod +++ b/v3/integrations/nrfasthttp/examples/client-fasthttp/go.mod @@ -3,9 +3,10 @@ module client-example go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.28.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 github.com/valyala/fasthttp v1.49.0 ) replace github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 => ../../ +replace github.com/newrelic/go-agent/v3 => ../../../.. diff --git a/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod b/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod index 591d85885..b2a166a20 100644 --- a/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod +++ b/v3/integrations/nrfasthttp/examples/server-fasthttp/go.mod @@ -3,9 +3,10 @@ module server-example go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.28.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 github.com/valyala/fasthttp v1.49.0 ) replace github.com/newrelic/go-agent/v3/integrations/nrfasthttp v1.0.0 => ../../ +replace github.com/newrelic/go-agent/v3 => ../../../.. diff --git a/v3/integrations/nrfasthttp/go.mod b/v3/integrations/nrfasthttp/go.mod index 5c97651a0..7a4ef4f88 100644 --- a/v3/integrations/nrfasthttp/go.mod +++ b/v3/integrations/nrfasthttp/go.mod @@ -3,6 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrfasthttp go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.28.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/valyala/fasthttp v1.49.0 ) +replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrgin/go.mod b/v3/integrations/nrgin/go.mod index 1440cd132..561f0f43d 100644 --- a/v3/integrations/nrgin/go.mod +++ b/v3/integrations/nrgin/go.mod @@ -6,7 +6,7 @@ go 1.19 require ( github.com/gin-gonic/gin v1.9.1 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrgorilla/go.mod b/v3/integrations/nrgorilla/go.mod index 59abf7323..4a70c63e9 100644 --- a/v3/integrations/nrgorilla/go.mod +++ b/v3/integrations/nrgorilla/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( // v1.7.0 is the earliest version of Gorilla using modules. github.com/gorilla/mux v1.7.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrgraphgophers/go.mod b/v3/integrations/nrgraphgophers/go.mod index e75730ad9..7ca579c3c 100644 --- a/v3/integrations/nrgraphgophers/go.mod +++ b/v3/integrations/nrgraphgophers/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( // graphql-go has no tagged releases as of Jan 2020. github.com/graph-gophers/graphql-go v1.3.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrgraphqlgo/example/go.mod b/v3/integrations/nrgraphqlgo/example/go.mod index ac6bc56ad..d99699eef 100644 --- a/v3/integrations/nrgraphqlgo/example/go.mod +++ b/v3/integrations/nrgraphqlgo/example/go.mod @@ -5,24 +5,10 @@ go 1.19 require ( github.com/graphql-go/graphql v0.8.1 github.com/graphql-go/graphql-go-handler v0.2.3 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrgraphqlgo v1.0.0 ) -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/graphql-go/handler v0.2.3 // indirect - github.com/klauspost/compress v1.16.3 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.49.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.30.0 // indirect -) replace github.com/newrelic/go-agent/v3/integrations/nrgraphqlgo => ../ diff --git a/v3/integrations/nrgraphqlgo/go.mod b/v3/integrations/nrgraphqlgo/go.mod index e6eb1f0f6..21f2e55db 100644 --- a/v3/integrations/nrgraphqlgo/go.mod +++ b/v3/integrations/nrgraphqlgo/go.mod @@ -4,5 +4,6 @@ go 1.19 require ( github.com/graphql-go/graphql v0.8.1 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) +replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrgrpc/go.mod b/v3/integrations/nrgrpc/go.mod index 642db88de..eea923f49 100644 --- a/v3/integrations/nrgrpc/go.mod +++ b/v3/integrations/nrgrpc/go.mod @@ -6,12 +6,12 @@ require ( // protobuf v1.3.0 is the earliest version using modules, we use v1.3.1 // because all dependencies were removed in this version. github.com/golang/protobuf v1.5.3 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrsecurityagent v1.1.0 // v1.15.0 is the earliest version of grpc using modules. google.golang.org/grpc v1.56.3 google.golang.org/protobuf v1.30.0 ) -replace github.com/newrelic/go-agent/v3 => ../.. replace github.com/newrelic/go-agent/v3/integrations/nrsecurityagent => ../../integrations/nrsecurityagent +replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrhttprouter/go.mod b/v3/integrations/nrhttprouter/go.mod index f0d35c659..67fa9ed59 100644 --- a/v3/integrations/nrhttprouter/go.mod +++ b/v3/integrations/nrhttprouter/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( // v1.3.0 is the earliest version of httprouter using modules. github.com/julienschmidt/httprouter v1.3.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrlambda/go.mod b/v3/integrations/nrlambda/go.mod index d5742a6e1..caaf942e0 100644 --- a/v3/integrations/nrlambda/go.mod +++ b/v3/integrations/nrlambda/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/aws/aws-lambda-go v1.41.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrlogrus/go.mod b/v3/integrations/nrlogrus/go.mod index d3ae7fb67..6ffdefe1e 100644 --- a/v3/integrations/nrlogrus/go.mod +++ b/v3/integrations/nrlogrus/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrlogrus go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrlogrus v1.0.0 // v1.1.0 is required for the Logger.GetLevel method, and is the earliest // version of logrus using modules. diff --git a/v3/integrations/nrlogxi/go.mod b/v3/integrations/nrlogxi/go.mod index ea0a44d0f..1e616823c 100644 --- a/v3/integrations/nrlogxi/go.mod +++ b/v3/integrations/nrlogxi/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( // 'v1', at commit aebf8a7d67ab, is the only logxi release. github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrmicro/go.mod b/v3/integrations/nrmicro/go.mod index 1f9307f25..3b1d6002f 100644 --- a/v3/integrations/nrmicro/go.mod +++ b/v3/integrations/nrmicro/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/golang/protobuf v1.5.3 github.com/micro/go-micro v1.8.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrmongo/go.mod b/v3/integrations/nrmongo/go.mod index 532268674..1d160f728 100644 --- a/v3/integrations/nrmongo/go.mod +++ b/v3/integrations/nrmongo/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrmongo go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 // mongo-driver does not support modules as of Nov 2019. go.mongodb.org/mongo-driver v1.10.2 ) diff --git a/v3/integrations/nrmssql/go.mod b/v3/integrations/nrmssql/go.mod index 1ff5415ce..e333594be 100644 --- a/v3/integrations/nrmssql/go.mod +++ b/v3/integrations/nrmssql/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/microsoft/go-mssqldb v0.19.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrmysql/go.mod b/v3/integrations/nrmysql/go.mod index 65b0b2844..bcff44a48 100644 --- a/v3/integrations/nrmysql/go.mod +++ b/v3/integrations/nrmysql/go.mod @@ -7,7 +7,7 @@ require ( // v1.5.0 is the first mysql version to support gomod github.com/go-sql-driver/mysql v1.6.0 // v3.3.0 includes the new location of ParseQuery - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrnats/go.mod b/v3/integrations/nrnats/go.mod index 4f191be64..5353a14c5 100644 --- a/v3/integrations/nrnats/go.mod +++ b/v3/integrations/nrnats/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/nats-io/nats-server v1.4.1 github.com/nats-io/nats.go v1.28.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrnats/test/go.mod b/v3/integrations/nrnats/test/go.mod index 0c2807f24..4f0374419 100644 --- a/v3/integrations/nrnats/test/go.mod +++ b/v3/integrations/nrnats/test/go.mod @@ -8,7 +8,7 @@ replace github.com/newrelic/go-agent/v3/integrations/nrnats v1.0.0 => ../ require ( github.com/nats-io/nats-server v1.4.1 github.com/nats-io/nats.go v1.17.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrnats v1.0.0 ) diff --git a/v3/integrations/nrpgx/example/sqlx/go.mod b/v3/integrations/nrpgx/example/sqlx/go.mod index c052f3364..201d17a88 100644 --- a/v3/integrations/nrpgx/example/sqlx/go.mod +++ b/v3/integrations/nrpgx/example/sqlx/go.mod @@ -4,7 +4,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrpgx/example/sqlx go 1.19 require ( github.com/jmoiron/sqlx v1.2.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrpgx v0.0.0 ) replace github.com/newrelic/go-agent/v3/integrations/nrpgx => ../../ diff --git a/v3/integrations/nrpgx/go.mod b/v3/integrations/nrpgx/go.mod index 3b09e3bb5..d2abbb24c 100644 --- a/v3/integrations/nrpgx/go.mod +++ b/v3/integrations/nrpgx/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx/v4 v4.13.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrpgx5/go.mod b/v3/integrations/nrpgx5/go.mod index 006c1f5ac..f77d380a6 100644 --- a/v3/integrations/nrpgx5/go.mod +++ b/v3/integrations/nrpgx5/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/egon12/pgsnap v0.0.0-20221022154027-2847f0124ed8 github.com/jackc/pgx/v5 v5.0.3 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/stretchr/testify v1.8.0 ) diff --git a/v3/integrations/nrpkgerrors/go.mod b/v3/integrations/nrpkgerrors/go.mod index d0a991860..731c91ec0 100644 --- a/v3/integrations/nrpkgerrors/go.mod +++ b/v3/integrations/nrpkgerrors/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrpkgerrors go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 // v0.8.0 was the last release in 2016, and when // major development on pkg/errors stopped. github.com/pkg/errors v0.8.0 diff --git a/v3/integrations/nrpq/example/sqlx/go.mod b/v3/integrations/nrpq/example/sqlx/go.mod index d134e7874..80079a086 100644 --- a/v3/integrations/nrpq/example/sqlx/go.mod +++ b/v3/integrations/nrpq/example/sqlx/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.1.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrpq v0.0.0 ) replace github.com/newrelic/go-agent/v3/integrations/nrpq => ../../ diff --git a/v3/integrations/nrpq/go.mod b/v3/integrations/nrpq/go.mod index c55872d06..0d336ec7b 100644 --- a/v3/integrations/nrpq/go.mod +++ b/v3/integrations/nrpq/go.mod @@ -6,7 +6,7 @@ require ( // NewConnector dsn parsing tests expect v1.1.0 error return behavior. github.com/lib/pq v1.1.0 // v3.3.0 includes the new location of ParseQuery - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrredis-v7/go.mod b/v3/integrations/nrredis-v7/go.mod index 71c502f25..b59fa2752 100644 --- a/v3/integrations/nrredis-v7/go.mod +++ b/v3/integrations/nrredis-v7/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/go-redis/redis/v7 v7.0.0-beta.5 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrredis-v8/go.mod b/v3/integrations/nrredis-v8/go.mod index 8378b9271..a10699607 100644 --- a/v3/integrations/nrredis-v8/go.mod +++ b/v3/integrations/nrredis-v8/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/go-redis/redis/v8 v8.4.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrredis-v9/go.mod b/v3/integrations/nrredis-v9/go.mod index c1a0a2c62..cee95494d 100644 --- a/v3/integrations/nrredis-v9/go.mod +++ b/v3/integrations/nrredis-v9/go.mod @@ -4,7 +4,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrredis-v9 go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/redis/go-redis/v9 v9.0.2 ) diff --git a/v3/integrations/nrsarama/go.mod b/v3/integrations/nrsarama/go.mod index acd484829..99e463697 100644 --- a/v3/integrations/nrsarama/go.mod +++ b/v3/integrations/nrsarama/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/Shopify/sarama v1.38.1 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/stretchr/testify v1.8.1 ) diff --git a/v3/integrations/nrsecurityagent/go.mod b/v3/integrations/nrsecurityagent/go.mod index c0300761f..cd81ce7ff 100644 --- a/v3/integrations/nrsecurityagent/go.mod +++ b/v3/integrations/nrsecurityagent/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/newrelic/csec-go-agent v0.5.1 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrsqlite3 v1.2.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/v3/integrations/nrsnowflake/go.mod b/v3/integrations/nrsnowflake/go.mod index 272453b9c..b458fbad5 100644 --- a/v3/integrations/nrsnowflake/go.mod +++ b/v3/integrations/nrsnowflake/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrsnowflake go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/snowflakedb/gosnowflake v1.6.19 ) diff --git a/v3/integrations/nrsqlite3/go.mod b/v3/integrations/nrsqlite3/go.mod index 38c6e4d6b..dd443a584 100644 --- a/v3/integrations/nrsqlite3/go.mod +++ b/v3/integrations/nrsqlite3/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/mattn/go-sqlite3 v1.0.0 // v3.3.0 includes the new location of ParseQuery - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrstan/examples/go.mod b/v3/integrations/nrstan/examples/go.mod index b69488b45..62ea0ae81 100644 --- a/v3/integrations/nrstan/examples/go.mod +++ b/v3/integrations/nrstan/examples/go.mod @@ -3,7 +3,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrstan/examples go 1.19 require ( github.com/nats-io/stan.go v0.5.0 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrnats v0.0.0 github.com/newrelic/go-agent/v3/integrations/nrstan v0.0.0 ) diff --git a/v3/integrations/nrstan/go.mod b/v3/integrations/nrstan/go.mod index d40cbad24..7c18fa067 100644 --- a/v3/integrations/nrstan/go.mod +++ b/v3/integrations/nrstan/go.mod @@ -6,7 +6,7 @@ go 1.19 require ( github.com/nats-io/stan.go v0.10.4 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 ) diff --git a/v3/integrations/nrstan/test/go.mod b/v3/integrations/nrstan/test/go.mod index 589f16ebf..4c7be2421 100644 --- a/v3/integrations/nrstan/test/go.mod +++ b/v3/integrations/nrstan/test/go.mod @@ -7,7 +7,7 @@ go 1.19 require ( github.com/nats-io/nats-streaming-server v0.25.5 github.com/nats-io/stan.go v0.10.4 - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrstan v0.0.0 ) diff --git a/v3/integrations/nrzap/go.mod b/v3/integrations/nrzap/go.mod index 1dac957b7..840a344c6 100644 --- a/v3/integrations/nrzap/go.mod +++ b/v3/integrations/nrzap/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrzap go 1.19 require ( - github.com/newrelic/go-agent/v3 v3.26.0 + github.com/newrelic/go-agent/v3 v3.28.1 // v1.12.0 is the earliest version of zap using modules. go.uber.org/zap v1.12.0 ) diff --git a/v3/newrelic/version.go b/v3/newrelic/version.go index 085452c7a..af7c8d52c 100644 --- a/v3/newrelic/version.go +++ b/v3/newrelic/version.go @@ -11,7 +11,7 @@ import ( const ( // Version is the full string version of this Go Agent. - Version = "3.28.0" + Version = "3.28.1" ) var ( From 77cfc282f62288e363598dde415d98bd6742db27 Mon Sep 17 00:00:00 2001 From: Steve Willoughby Date: Thu, 30 Nov 2023 11:58:50 -0800 Subject: [PATCH 16/16] updated go.mod files --- v3/integrations/nrgraphqlgo/go.mod | 2 ++ v3/integrations/nrgrpc/go.mod | 2 ++ v3/integrations/nrmicro/go.mod | 1 + v3/integrations/nrstan/test/go.mod | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/v3/integrations/nrgraphqlgo/go.mod b/v3/integrations/nrgraphqlgo/go.mod index 21f2e55db..f953df2e9 100644 --- a/v3/integrations/nrgraphqlgo/go.mod +++ b/v3/integrations/nrgraphqlgo/go.mod @@ -6,4 +6,6 @@ require ( github.com/graphql-go/graphql v0.8.1 github.com/newrelic/go-agent/v3 v3.28.1 ) + + replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrgrpc/go.mod b/v3/integrations/nrgrpc/go.mod index eea923f49..de50413b0 100644 --- a/v3/integrations/nrgrpc/go.mod +++ b/v3/integrations/nrgrpc/go.mod @@ -13,5 +13,7 @@ require ( google.golang.org/protobuf v1.30.0 ) + replace github.com/newrelic/go-agent/v3/integrations/nrsecurityagent => ../../integrations/nrsecurityagent + replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrmicro/go.mod b/v3/integrations/nrmicro/go.mod index 3b1d6002f..fda594e34 100644 --- a/v3/integrations/nrmicro/go.mod +++ b/v3/integrations/nrmicro/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/micro/go-micro v1.8.0 github.com/newrelic/go-agent/v3 v3.28.1 + google.golang.org/protobuf v1.31.0 ) diff --git a/v3/integrations/nrstan/test/go.mod b/v3/integrations/nrstan/test/go.mod index 4c7be2421..6921303fe 100644 --- a/v3/integrations/nrstan/test/go.mod +++ b/v3/integrations/nrstan/test/go.mod @@ -5,7 +5,7 @@ module github.com/newrelic/go-agent/v3/integrations/nrstan/test go 1.19 require ( - github.com/nats-io/nats-streaming-server v0.25.5 + github.com/nats-io/nats-streaming-server v0.25.6 github.com/nats-io/stan.go v0.10.4 github.com/newrelic/go-agent/v3 v3.28.1 github.com/newrelic/go-agent/v3/integrations/nrstan v0.0.0