From f999c5786071f8d5f08c481818b7cd706630b8cb Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:09:40 -0500 Subject: [PATCH 01/13] Create FastHTTP Client Functions --- .../nrfasthttp/nrfasthttp_client.go | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 v3/integrations/nrfasthttp/nrfasthttp_client.go diff --git a/v3/integrations/nrfasthttp/nrfasthttp_client.go b/v3/integrations/nrfasthttp/nrfasthttp_client.go new file mode 100644 index 000000000..98960f5fc --- /dev/null +++ b/v3/integrations/nrfasthttp/nrfasthttp_client.go @@ -0,0 +1,21 @@ +package nrfasthttp + +import ( + newrelic "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" +) + +type FastHTTPClient interface { + Do(req *fasthttp.Request, resp *fasthttp.Response) error +} + +func Do(client FastHTTPClient, txn *newrelic.Transaction, req *fasthttp.Request, res *fasthttp.Response) error { + seg := txn.StartSegment("fasthttp-do") + err := client.Do(req, res) + if err != nil { + txn.NoticeError(err) + } + seg.End() + + return err +} From 79146fb6763e76a872c4b0c78ca225d9bd458758 Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:09:51 -0500 Subject: [PATCH 02/13] FastHTTP Request Integration --- .../nrfasthttp/nrfasthttp_request.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 v3/integrations/nrfasthttp/nrfasthttp_request.go diff --git a/v3/integrations/nrfasthttp/nrfasthttp_request.go b/v3/integrations/nrfasthttp/nrfasthttp_request.go new file mode 100644 index 000000000..66ae112f6 --- /dev/null +++ b/v3/integrations/nrfasthttp/nrfasthttp_request.go @@ -0,0 +1,52 @@ +package nrfasthttp + +import ( + "net/http" + + newrelic "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpadaptor" +) + +func GetTransaction(ctx *fasthttp.RequestCtx) *newrelic.Transaction { + txn := ctx.UserValue("transaction") + + if txn == nil { + return nil + } + + return txn.(*newrelic.Transaction) +} + +func NRHandler(app *newrelic.Application, original fasthttp.RequestHandler) fasthttp.RequestHandler { + return func(ctx *fasthttp.RequestCtx) { + // Ignore reporting transaction for browser requesting .ico files + if string(ctx.Path()) == "/favicon.ico" { + original(ctx) + return + } + + txn := app.StartTransaction(string(ctx.Path())) + defer txn.End() + ctx.SetUserValue("transaction", txn) + + segRequest := txn.StartSegment("fasthttp-set-request") + // Set transaction attributes + txn.AddAttribute("method", string(ctx.Method())) + txn.AddAttribute("path", string(ctx.Path())) + // convert fasthttp request to http request + r := &http.Request{} + fasthttpadaptor.ConvertRequest(ctx, r, true) + + txn.SetWebRequestHTTP(r) + txn.InsertDistributedTraceHeaders(r.Header) + segRequest.End() + + original(ctx) + // Set Web Response + seg := txn.StartSegment("fasthttp-set-response") + resp := fasthttpWrapperResponse{ctx: ctx, txn: txn} + txn.SetWebResponse(resp) + seg.End() + } +} From a36c2134459f9e3c0f39c3e9ec5a3bc0bca2d90d Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:10:13 -0500 Subject: [PATCH 03/13] FastHTTP example file --- v3/integrations/nrfasthttp/example/main.go | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 v3/integrations/nrfasthttp/example/main.go diff --git a/v3/integrations/nrfasthttp/example/main.go b/v3/integrations/nrfasthttp/example/main.go new file mode 100644 index 000000000..68ed289c7 --- /dev/null +++ b/v3/integrations/nrfasthttp/example/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "os" + "time" + + "github.com/newrelic/go-agent/v3/integrations/nrfasthttp" + + newrelic "github.com/newrelic/go-agent/v3/newrelic" + + "github.com/valyala/fasthttp" +) + +func main() { + // Initialize New Relic + app, err := newrelic.NewApplication( + newrelic.ConfigAppName("httprouter App"), + newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")), + newrelic.ConfigDebugLogger(os.Stdout), + newrelic.ConfigDistributedTracerEnabled(true), + ) + if err != nil { + fmt.Println(err) + return + } + if err := app.WaitForConnection(5 * time.Second); nil != err { + fmt.Println(err) + } + + // Define your handler + handler := nrfasthttp.NRHandler(app, func(ctx *fasthttp.RequestCtx) { + txn := nrfasthttp.GetTransaction(ctx) + client := &fasthttp.Client{} + + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) + req.SetRequestURI("http://example.com") + + res := fasthttp.AcquireResponse() + defer fasthttp.ReleaseResponse(res) + + // Call nrfasthttp.Do instead of fasthttp.Do + err := nrfasthttp.Do(client, txn, req, res) + + if err != nil { + fmt.Println("Request failed: ", err) + return + } + // Your handler logic here... + ctx.WriteString("Hello World") + }) + + // Start the server with the instrumented handler + fasthttp.ListenAndServe(":8080", handler) +} From dd7d252df9a6370461905f3f3babf04e9ada3461 Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:11:20 -0500 Subject: [PATCH 04/13] FastHTTP Request Integration --- v3/integrations/nrfasthttp/nrfasthttp_request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/integrations/nrfasthttp/nrfasthttp_request.go b/v3/integrations/nrfasthttp/nrfasthttp_request.go index 66ae112f6..a21c79acd 100644 --- a/v3/integrations/nrfasthttp/nrfasthttp_request.go +++ b/v3/integrations/nrfasthttp/nrfasthttp_request.go @@ -45,7 +45,7 @@ func NRHandler(app *newrelic.Application, original fasthttp.RequestHandler) fast original(ctx) // Set Web Response seg := txn.StartSegment("fasthttp-set-response") - resp := fasthttpWrapperResponse{ctx: ctx, txn: txn} + resp := fasthttpWrapperResponse{ctx: ctx} txn.SetWebResponse(resp) seg.End() } From fc0b173749f473dee41b3eedf5bd9b3c68bef8c8 Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:11:29 -0500 Subject: [PATCH 05/13] FastHTTP Response file --- .../nrfasthttp/nrfasthttp_response.go | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 v3/integrations/nrfasthttp/nrfasthttp_response.go diff --git a/v3/integrations/nrfasthttp/nrfasthttp_response.go b/v3/integrations/nrfasthttp/nrfasthttp_response.go new file mode 100644 index 000000000..36882bd3e --- /dev/null +++ b/v3/integrations/nrfasthttp/nrfasthttp_response.go @@ -0,0 +1,27 @@ +package nrfasthttp + +import ( + "net/http" + + "github.com/valyala/fasthttp" +) + +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) +} From 3108963562097f7b6c777658c70f689ee9e8a3e8 Mon Sep 17 00:00:00 2001 From: mirackara Date: Fri, 25 Aug 2023 14:11:35 -0500 Subject: [PATCH 06/13] mod file --- v3/integrations/nrfasthttp/go.mod | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 v3/integrations/nrfasthttp/go.mod diff --git a/v3/integrations/nrfasthttp/go.mod b/v3/integrations/nrfasthttp/go.mod new file mode 100644 index 000000000..3a36ce9a8 --- /dev/null +++ b/v3/integrations/nrfasthttp/go.mod @@ -0,0 +1,23 @@ +module github.com/newrelic/go-agent/v3/integrations/nrfasthttp + +go 1.19 + +require ( + github.com/newrelic/go-agent/v3 v3.23.1 + github.com/valyala/fasthttp v1.48.0 +) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/valyala/bytebufferpool v1.0.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 +) + +replace github.com/newrelic/go-agent/v3 => ../.. From 55af84ad489ee084c67939e6c35d7053e12aa565 Mon Sep 17 00:00:00 2001 From: mirackara Date: Mon, 28 Aug 2023 15:18:58 -0500 Subject: [PATCH 07/13] supportability metric --- v3/integrations/nrfasthttp/nrfasthttp_request.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/v3/integrations/nrfasthttp/nrfasthttp_request.go b/v3/integrations/nrfasthttp/nrfasthttp_request.go index a21c79acd..5a300c224 100644 --- a/v3/integrations/nrfasthttp/nrfasthttp_request.go +++ b/v3/integrations/nrfasthttp/nrfasthttp_request.go @@ -3,11 +3,14 @@ package nrfasthttp import ( "net/http" + internal "github.com/newrelic/go-agent/v3/internal" newrelic "github.com/newrelic/go-agent/v3/newrelic" "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttpadaptor" ) +func init() { internal.TrackUsage("integration", "framework", "fasthttp") } + func GetTransaction(ctx *fasthttp.RequestCtx) *newrelic.Transaction { txn := ctx.UserValue("transaction") From 349fe30940ca3a8d9b610fb939aa1aacf0a1a6ab Mon Sep 17 00:00:00 2001 From: mirackara Date: Mon, 28 Aug 2023 17:51:56 -0500 Subject: [PATCH 08/13] Created unit tests and removed extraneous file --- v3/integrations/nrfasthttp/go.mod | 16 +-- .../nrfasthttp/nrfasthttp_request.go | 20 ++++ .../nrfasthttp/nrfasthttp_response.go | 27 ----- v3/integrations/nrfasthttp/nrfasthttp_test.go | 101 ++++++++++++++++++ 4 files changed, 122 insertions(+), 42 deletions(-) delete mode 100644 v3/integrations/nrfasthttp/nrfasthttp_response.go create mode 100644 v3/integrations/nrfasthttp/nrfasthttp_test.go diff --git a/v3/integrations/nrfasthttp/go.mod b/v3/integrations/nrfasthttp/go.mod index 3a36ce9a8..d4e207230 100644 --- a/v3/integrations/nrfasthttp/go.mod +++ b/v3/integrations/nrfasthttp/go.mod @@ -4,20 +4,6 @@ go 1.19 require ( github.com/newrelic/go-agent/v3 v3.23.1 + github.com/stretchr/testify v1.8.4 github.com/valyala/fasthttp v1.48.0 ) - -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/valyala/bytebufferpool v1.0.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 -) - -replace github.com/newrelic/go-agent/v3 => ../.. diff --git a/v3/integrations/nrfasthttp/nrfasthttp_request.go b/v3/integrations/nrfasthttp/nrfasthttp_request.go index 5a300c224..1b61dc6eb 100644 --- a/v3/integrations/nrfasthttp/nrfasthttp_request.go +++ b/v3/integrations/nrfasthttp/nrfasthttp_request.go @@ -11,6 +11,26 @@ import ( func init() { internal.TrackUsage("integration", "framework", "fasthttp") } +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 GetTransaction(ctx *fasthttp.RequestCtx) *newrelic.Transaction { txn := ctx.UserValue("transaction") diff --git a/v3/integrations/nrfasthttp/nrfasthttp_response.go b/v3/integrations/nrfasthttp/nrfasthttp_response.go deleted file mode 100644 index 36882bd3e..000000000 --- a/v3/integrations/nrfasthttp/nrfasthttp_response.go +++ /dev/null @@ -1,27 +0,0 @@ -package nrfasthttp - -import ( - "net/http" - - "github.com/valyala/fasthttp" -) - -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) -} diff --git a/v3/integrations/nrfasthttp/nrfasthttp_test.go b/v3/integrations/nrfasthttp/nrfasthttp_test.go new file mode 100644 index 000000000..5554e72fb --- /dev/null +++ b/v3/integrations/nrfasthttp/nrfasthttp_test.go @@ -0,0 +1,101 @@ +package nrfasthttp + +import ( + "github.com/newrelic/go-agent/v3/internal" + "github.com/newrelic/go-agent/v3/internal/integrationsupport" + newrelic "github.com/newrelic/go-agent/v3/newrelic" + + "errors" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/valyala/fasthttp" +) + +type mockFastHTTPClient struct { + err error +} + +func (m *mockFastHTTPClient) Do(req *fasthttp.Request, resp *fasthttp.Response) error { + + if m.err != nil { + return m.err + } + return nil +} + +func TestFastHTTPWrapperResponseHeader(t *testing.T) { + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.Set("X-Test-Header", "test") + wrapper := fasthttpWrapperResponse{ctx: ctx} + hdrs := wrapper.Header() + assert.Equal(t, "test", hdrs.Get("X-Test-Header")) +} + +func TestFastHTTPWrapperResponseWrite(t *testing.T) { + ctx := &fasthttp.RequestCtx{} + wrapper := fasthttpWrapperResponse{ctx: ctx} + + w, err := wrapper.Write([]byte("Hello World!")) + assert.Nil(t, err) + assert.Equal(t, 12, w) + assert.Equal(t, "Hello World!", string(ctx.Response.Body())) +} + +func TestFastHTTPWrapperResponseWriteHeader(t *testing.T) { + ctx := &fasthttp.RequestCtx{} + wrapper := fasthttpWrapperResponse{ctx: ctx} + wrapper.WriteHeader(http.StatusForbidden) + assert.Equal(t, http.StatusForbidden, ctx.Response.StatusCode()) +} + +func TestGetTransaction(t *testing.T) { + ctx := &fasthttp.RequestCtx{} + txn := &newrelic.Transaction{} + ctx.SetUserValue("transaction", txn) + assert.Equal(t, txn, GetTransaction(ctx)) +} + +func TestNRHandler(t *testing.T) { + app := integrationsupport.NewBasicTestApp() + original := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("Hello World!") + } + nrhandler := NRHandler(app.Application, original) + ctx := &fasthttp.RequestCtx{} + nrhandler(ctx) + assert.Equal(t, "Hello World!", string(ctx.Response.Body())) + app.ExpectMetrics(t, []internal.WantMetric{ + {Name: "WebTransaction"}, + {Name: "Apdex/Go/"}, + {Name: "Custom/fasthttp-set-response"}, + {Name: "WebTransactionTotalTime/Go/"}, + {Name: "Custom/fasthttp-set-request"}, + {Name: "WebTransaction/Go/"}, + {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, + {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb"}, + {Name: "WebTransactionTotalTime"}, + {Name: "Apdex"}, + {Name: "Custom/fasthttp-set-request", Scope: "WebTransaction/Go/", Forced: false, Data: nil}, + {Name: "Custom/fasthttp-set-response", Scope: "WebTransaction/Go/", Forced: false, Data: nil}, + {Name: "HttpDispatcher"}, + }) +} + +func TestDo(t *testing.T) { + client := &mockFastHTTPClient{} + txn := &newrelic.Transaction{} + req := &fasthttp.Request{} + resp := &fasthttp.Response{} + + // check for no error + err := Do(client, txn, req, resp) + assert.NoError(t, err) + + // check for error + client.err = errors.New("ahh!!") + err = Do(client, txn, req, resp) + + assert.Error(t, err) +} From 7f56709de4c4a9c8a15689b5a981f5666734c60e Mon Sep 17 00:00:00 2001 From: mirackara Date: Wed, 6 Sep 2023 13:30:04 -0500 Subject: [PATCH 09/13] Moved FastHTTP to internal instrumentation --- v3/examples/server-fasthttp/main.go | 55 ++++++++++ v3/go.mod | 15 +++ v3/integrations/nrfasthttp/example/main.go | 56 ---------- .../nrfasthttp/nrfasthttp_client.go | 21 ---- .../nrfasthttp/nrfasthttp_request.go | 75 ------------- v3/integrations/nrfasthttp/nrfasthttp_test.go | 101 ------------------ v3/newrelic/instrumentation.go | 91 +++++++++++++++- v3/newrelic/internal_17_test.go | 32 ++++++ 8 files changed, 188 insertions(+), 258 deletions(-) create mode 100644 v3/examples/server-fasthttp/main.go delete mode 100644 v3/integrations/nrfasthttp/example/main.go delete mode 100644 v3/integrations/nrfasthttp/nrfasthttp_client.go delete mode 100644 v3/integrations/nrfasthttp/nrfasthttp_request.go delete mode 100644 v3/integrations/nrfasthttp/nrfasthttp_test.go diff --git a/v3/examples/server-fasthttp/main.go b/v3/examples/server-fasthttp/main.go new file mode 100644 index 000000000..cf3f3557f --- /dev/null +++ b/v3/examples/server-fasthttp/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "errors" + "fmt" + "os" + "time" + + newrelic "github.com/newrelic/go-agent/v3/newrelic" + + "github.com/valyala/fasthttp" +) + +func index(ctx *fasthttp.RequestCtx) { + ctx.WriteString("Hello World") +} + +func noticeError(ctx *fasthttp.RequestCtx) { + ctx.WriteString("noticing an error") + txn := ctx.UserValue("transaction").(*newrelic.Transaction) + txn.NoticeError(errors.New("my error message")) +} + +func main() { + // Initialize New Relic + app, err := newrelic.NewApplication( + newrelic.ConfigAppName("FastHTTP App"), + newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")), + newrelic.ConfigDebugLogger(os.Stdout), + newrelic.ConfigDistributedTracerEnabled(true), + ) + if err != nil { + fmt.Println(err) + return + } + if err := app.WaitForConnection(5 * time.Second); nil != err { + fmt.Println(err) + } + _, helloRoute := newrelic.WrapHandleFuncFastHTTP(app, "/hello", index) + _, errorRoute := newrelic.WrapHandleFuncFastHTTP(app, "/error", noticeError) + handler := func(ctx *fasthttp.RequestCtx) { + path := string(ctx.Path()) + method := string(ctx.Method()) + + switch { + case method == "GET" && path == "/hello": + helloRoute(ctx) + case method == "GET" && path == "/error": + errorRoute(ctx) + } + } + + // Start the server with the instrumented handler + fasthttp.ListenAndServe(":8080", handler) +} diff --git a/v3/go.mod b/v3/go.mod index 01b5abf73..48f2396f4 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -1,7 +1,22 @@ module github.com/newrelic/go-agent/v3 + go 1.18 + require ( github.com/golang/protobuf v1.5.3 + github.com/valyala/fasthttp v1.49.0 google.golang.org/grpc v1.54.0 ) + +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.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/protobuf v1.28.1 // indirect +) + retract v3.22.0 // release process error corrected in v3.22.1 diff --git a/v3/integrations/nrfasthttp/example/main.go b/v3/integrations/nrfasthttp/example/main.go deleted file mode 100644 index 68ed289c7..000000000 --- a/v3/integrations/nrfasthttp/example/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "fmt" - "os" - "time" - - "github.com/newrelic/go-agent/v3/integrations/nrfasthttp" - - newrelic "github.com/newrelic/go-agent/v3/newrelic" - - "github.com/valyala/fasthttp" -) - -func main() { - // Initialize New Relic - app, err := newrelic.NewApplication( - newrelic.ConfigAppName("httprouter App"), - newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")), - newrelic.ConfigDebugLogger(os.Stdout), - newrelic.ConfigDistributedTracerEnabled(true), - ) - if err != nil { - fmt.Println(err) - return - } - if err := app.WaitForConnection(5 * time.Second); nil != err { - fmt.Println(err) - } - - // Define your handler - handler := nrfasthttp.NRHandler(app, func(ctx *fasthttp.RequestCtx) { - txn := nrfasthttp.GetTransaction(ctx) - client := &fasthttp.Client{} - - req := fasthttp.AcquireRequest() - defer fasthttp.ReleaseRequest(req) - req.SetRequestURI("http://example.com") - - res := fasthttp.AcquireResponse() - defer fasthttp.ReleaseResponse(res) - - // Call nrfasthttp.Do instead of fasthttp.Do - err := nrfasthttp.Do(client, txn, req, res) - - if err != nil { - fmt.Println("Request failed: ", err) - return - } - // Your handler logic here... - ctx.WriteString("Hello World") - }) - - // Start the server with the instrumented handler - fasthttp.ListenAndServe(":8080", handler) -} diff --git a/v3/integrations/nrfasthttp/nrfasthttp_client.go b/v3/integrations/nrfasthttp/nrfasthttp_client.go deleted file mode 100644 index 98960f5fc..000000000 --- a/v3/integrations/nrfasthttp/nrfasthttp_client.go +++ /dev/null @@ -1,21 +0,0 @@ -package nrfasthttp - -import ( - newrelic "github.com/newrelic/go-agent/v3/newrelic" - "github.com/valyala/fasthttp" -) - -type FastHTTPClient interface { - Do(req *fasthttp.Request, resp *fasthttp.Response) error -} - -func Do(client FastHTTPClient, txn *newrelic.Transaction, req *fasthttp.Request, res *fasthttp.Response) error { - seg := txn.StartSegment("fasthttp-do") - err := client.Do(req, res) - if err != nil { - txn.NoticeError(err) - } - seg.End() - - return err -} diff --git a/v3/integrations/nrfasthttp/nrfasthttp_request.go b/v3/integrations/nrfasthttp/nrfasthttp_request.go deleted file mode 100644 index 1b61dc6eb..000000000 --- a/v3/integrations/nrfasthttp/nrfasthttp_request.go +++ /dev/null @@ -1,75 +0,0 @@ -package nrfasthttp - -import ( - "net/http" - - internal "github.com/newrelic/go-agent/v3/internal" - newrelic "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 -} - -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 GetTransaction(ctx *fasthttp.RequestCtx) *newrelic.Transaction { - txn := ctx.UserValue("transaction") - - if txn == nil { - return nil - } - - return txn.(*newrelic.Transaction) -} - -func NRHandler(app *newrelic.Application, original fasthttp.RequestHandler) fasthttp.RequestHandler { - return func(ctx *fasthttp.RequestCtx) { - // Ignore reporting transaction for browser requesting .ico files - if string(ctx.Path()) == "/favicon.ico" { - original(ctx) - return - } - - txn := app.StartTransaction(string(ctx.Path())) - defer txn.End() - ctx.SetUserValue("transaction", txn) - - segRequest := txn.StartSegment("fasthttp-set-request") - // Set transaction attributes - txn.AddAttribute("method", string(ctx.Method())) - txn.AddAttribute("path", string(ctx.Path())) - // convert fasthttp request to http request - r := &http.Request{} - fasthttpadaptor.ConvertRequest(ctx, r, true) - - txn.SetWebRequestHTTP(r) - txn.InsertDistributedTraceHeaders(r.Header) - segRequest.End() - - original(ctx) - // Set Web Response - seg := txn.StartSegment("fasthttp-set-response") - resp := fasthttpWrapperResponse{ctx: ctx} - txn.SetWebResponse(resp) - seg.End() - } -} diff --git a/v3/integrations/nrfasthttp/nrfasthttp_test.go b/v3/integrations/nrfasthttp/nrfasthttp_test.go deleted file mode 100644 index 5554e72fb..000000000 --- a/v3/integrations/nrfasthttp/nrfasthttp_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package nrfasthttp - -import ( - "github.com/newrelic/go-agent/v3/internal" - "github.com/newrelic/go-agent/v3/internal/integrationsupport" - newrelic "github.com/newrelic/go-agent/v3/newrelic" - - "errors" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/valyala/fasthttp" -) - -type mockFastHTTPClient struct { - err error -} - -func (m *mockFastHTTPClient) Do(req *fasthttp.Request, resp *fasthttp.Response) error { - - if m.err != nil { - return m.err - } - return nil -} - -func TestFastHTTPWrapperResponseHeader(t *testing.T) { - ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.Set("X-Test-Header", "test") - wrapper := fasthttpWrapperResponse{ctx: ctx} - hdrs := wrapper.Header() - assert.Equal(t, "test", hdrs.Get("X-Test-Header")) -} - -func TestFastHTTPWrapperResponseWrite(t *testing.T) { - ctx := &fasthttp.RequestCtx{} - wrapper := fasthttpWrapperResponse{ctx: ctx} - - w, err := wrapper.Write([]byte("Hello World!")) - assert.Nil(t, err) - assert.Equal(t, 12, w) - assert.Equal(t, "Hello World!", string(ctx.Response.Body())) -} - -func TestFastHTTPWrapperResponseWriteHeader(t *testing.T) { - ctx := &fasthttp.RequestCtx{} - wrapper := fasthttpWrapperResponse{ctx: ctx} - wrapper.WriteHeader(http.StatusForbidden) - assert.Equal(t, http.StatusForbidden, ctx.Response.StatusCode()) -} - -func TestGetTransaction(t *testing.T) { - ctx := &fasthttp.RequestCtx{} - txn := &newrelic.Transaction{} - ctx.SetUserValue("transaction", txn) - assert.Equal(t, txn, GetTransaction(ctx)) -} - -func TestNRHandler(t *testing.T) { - app := integrationsupport.NewBasicTestApp() - original := func(ctx *fasthttp.RequestCtx) { - ctx.WriteString("Hello World!") - } - nrhandler := NRHandler(app.Application, original) - ctx := &fasthttp.RequestCtx{} - nrhandler(ctx) - assert.Equal(t, "Hello World!", string(ctx.Response.Body())) - app.ExpectMetrics(t, []internal.WantMetric{ - {Name: "WebTransaction"}, - {Name: "Apdex/Go/"}, - {Name: "Custom/fasthttp-set-response"}, - {Name: "WebTransactionTotalTime/Go/"}, - {Name: "Custom/fasthttp-set-request"}, - {Name: "WebTransaction/Go/"}, - {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, - {Name: "DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb"}, - {Name: "WebTransactionTotalTime"}, - {Name: "Apdex"}, - {Name: "Custom/fasthttp-set-request", Scope: "WebTransaction/Go/", Forced: false, Data: nil}, - {Name: "Custom/fasthttp-set-response", Scope: "WebTransaction/Go/", Forced: false, Data: nil}, - {Name: "HttpDispatcher"}, - }) -} - -func TestDo(t *testing.T) { - client := &mockFastHTTPClient{} - txn := &newrelic.Transaction{} - req := &fasthttp.Request{} - resp := &fasthttp.Response{} - - // check for no error - err := Do(client, txn, req, resp) - assert.NoError(t, err) - - // check for error - client.err = errors.New("ahh!!") - err = Do(client, txn, req, resp) - - assert.Error(t, err) -} diff --git a/v3/newrelic/instrumentation.go b/v3/newrelic/instrumentation.go index a118aef9f..dc5bf9a45 100644 --- a/v3/newrelic/instrumentation.go +++ b/v3/newrelic/instrumentation.go @@ -5,18 +5,41 @@ 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 // instrument this code: // -// http.Handle("/foo", myHandler) +// http.Handle("/foo", myHandler) // // Perform this replacement: // -// http.Handle(newrelic.WrapHandle(app, "/foo", myHandler)) +// http.Handle(newrelic.WrapHandle(app, "/foo", myHandler)) // // WrapHandle adds the Transaction to the request's context. Access it using // FromContext to add attributes, create segments, or notice errors: @@ -76,6 +99,56 @@ 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() + + return pattern, func(ctx *fasthttp.RequestCtx) { + var tOptions *traceOptSet + var txnOptionList []TraceOption + + 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 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) + } +} + // WrapHandleFunc instruments handler functions using Transactions. To // instrument this code: // @@ -111,15 +184,23 @@ 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 // -// http.ListenAndServe(":8000", nil) +// http.ListenAndServe(":8000", nil) +// // with -// http.ListenAndServe(newrelic.WrapListen(":8000"), nil) // +// http.ListenAndServe(newrelic.WrapListen(":8000"), nil) func WrapListen(endpoint string) string { secureAgent.SendEvent("APP_INFO", endpoint) return endpoint diff --git a/v3/newrelic/internal_17_test.go b/v3/newrelic/internal_17_test.go index 82d1dc8f1..58b9bfd3d 100644 --- a/v3/newrelic/internal_17_test.go +++ b/v3/newrelic/internal_17_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/newrelic/go-agent/v3/internal" + "github.com/valyala/fasthttp" ) func myErrorHandler(w http.ResponseWriter, req *http.Request) { @@ -18,6 +19,37 @@ func myErrorHandler(w http.ResponseWriter, req *http.Request) { txn.NoticeError(myError{}) } +func TestWrapHandleFastHTTPFunc(t *testing.T) { + app := testApp(nil, ConfigDistributedTracerEnabled(true), t) + + // Define your handler + handler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("Hello World") + } + + _, wrappedHandler := WrapHandleFuncFastHTTP(app.Application, "/hello", handler) + + 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.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}, + }) +} + func TestWrapHandleFunc(t *testing.T) { app := testApp(nil, ConfigDistributedTracerEnabled(false), t) mux := http.NewServeMux() From de5ca156ce16a1581ece240ba318f952a65374e1 Mon Sep 17 00:00:00 2001 From: mirackara Date: Thu, 7 Sep 2023 14:40:14 -0500 Subject: [PATCH 10/13] Added testing for errors --- v3/newrelic/internal_17_test.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/v3/newrelic/internal_17_test.go b/v3/newrelic/internal_17_test.go index 58b9bfd3d..5ba7b6c7e 100644 --- a/v3/newrelic/internal_17_test.go +++ b/v3/newrelic/internal_17_test.go @@ -19,15 +19,16 @@ 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) - // Define your handler - handler := func(ctx *fasthttp.RequestCtx) { - ctx.WriteString("Hello World") - } - - _, wrappedHandler := WrapHandleFuncFastHTTP(app.Application, "/hello", handler) + _, wrappedHandler := WrapHandleFuncFastHTTP(app.Application, "/hello", myErrorHandlerFastHTTP) if wrappedHandler == nil { t.Error("Error when creating a wrapped handler") @@ -36,6 +37,11 @@ func TestWrapHandleFastHTTPFunc(t *testing.T) { 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}, @@ -47,6 +53,11 @@ func TestWrapHandleFastHTTPFunc(t *testing.T) { {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}, }) } From f1750def36f854ecb68d3db62aec68b12866d9cc Mon Sep 17 00:00:00 2001 From: mirackara Date: Mon, 11 Sep 2023 19:19:43 -0500 Subject: [PATCH 11/13] FastHTTP external segments/Client example --- v3/examples/client-fasthttp/main.go | 62 +++++++++++++++++++++++++++++ v3/newrelic/context.go | 10 +++++ v3/newrelic/segments.go | 27 +++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 v3/examples/client-fasthttp/main.go diff --git a/v3/examples/client-fasthttp/main.go b/v3/examples/client-fasthttp/main.go new file mode 100644 index 000000000..7a26b605f --- /dev/null +++ b/v3/examples/client-fasthttp/main.go @@ -0,0 +1,62 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +package main + +import ( + "fmt" + "os" + "time" + + newrelic "github.com/newrelic/go-agent/v3/newrelic" + "github.com/valyala/fasthttp" +) + +func doRequest(txn *newrelic.Transaction) error { + 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 := newrelic.StartExternalSegmentFastHTTP(txn, ctx) + defer seg.End() + + err := fasthttp.Do(req, resp) + if err != nil { + return err + } + + fmt.Println("Response Code is ", resp.StatusCode()) + return nil + +} + +func main() { + app, err := newrelic.NewApplication( + newrelic.ConfigAppName("Client App"), + newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")), + newrelic.ConfigDebugLogger(os.Stdout), + newrelic.ConfigDistributedTracerEnabled(true), + ) + + if err := app.WaitForConnection(5 * time.Second); nil != err { + fmt.Println(err) + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + txn := app.StartTransaction("client-txn") + err = doRequest(txn) + if err != nil { + txn.NoticeError(err) + } + txn.End() + + // Shut down the application to flush data to New Relic. + app.Shutdown(10 * time.Second) +} diff --git a/v3/newrelic/context.go b/v3/newrelic/context.go index 5ce186f3d..991feb64b 100644 --- a/v3/newrelic/context.go +++ b/v3/newrelic/context.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/newrelic/go-agent/v3/internal" + "github.com/valyala/fasthttp" ) // NewContext returns a new context.Context that carries the provided @@ -52,3 +53,12 @@ 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 + } + return txn +} diff --git a/v3/newrelic/segments.go b/v3/newrelic/segments.go index a9c96d52c..f45735687 100644 --- a/v3/newrelic/segments.go +++ b/v3/newrelic/segments.go @@ -5,6 +5,9 @@ package newrelic import ( "net/http" + + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpadaptor" ) // SegmentStartTime is created by Transaction.StartSegmentNow and marks the @@ -334,6 +337,30 @@ 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, + } + 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) + } + } + secureAgent.DistributedTraceHeaders(request, s.secureAgentEvent) + } + + return s +} + func addSpanAttr(start SegmentStartTime, key string, val interface{}) { if nil == start.thread { return From ef22a97ead73e5b5f57ec2bc58832875e626765f Mon Sep 17 00:00:00 2001 From: mirackara Date: Mon, 11 Sep 2023 19:19:57 -0500 Subject: [PATCH 12/13] License for Server Example --- v3/examples/server-fasthttp/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/v3/examples/server-fasthttp/main.go b/v3/examples/server-fasthttp/main.go index cf3f3557f..8ed532670 100644 --- a/v3/examples/server-fasthttp/main.go +++ b/v3/examples/server-fasthttp/main.go @@ -1,3 +1,6 @@ +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + package main import ( From aed3dde33888a9a01f08d68af7e5619c6483e910 Mon Sep 17 00:00:00 2001 From: mirackara Date: Thu, 14 Sep 2023 12:04:22 -0500 Subject: [PATCH 13/13] Added test for external segment/minor fixes --- v3/newrelic/context.go | 6 +++++- v3/newrelic/internal_context_test.go | 29 ++++++++++++++++++++++++++++ v3/newrelic/segments.go | 10 ++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/v3/newrelic/context.go b/v3/newrelic/context.go index 991feb64b..731dcb73f 100644 --- a/v3/newrelic/context.go +++ b/v3/newrelic/context.go @@ -60,5 +60,9 @@ func transactionFromRequestContextFastHTTP(ctx *fasthttp.RequestCtx) *Transactio txn := ctx.UserValue("transaction").(*Transaction) return txn } - return txn + + if txn != nil { + return txn + } + return nil } diff --git a/v3/newrelic/internal_context_test.go b/v3/newrelic/internal_context_test.go index 1e15e61cd..64b73c08f 100644 --- a/v3/newrelic/internal_context_test.go +++ b/v3/newrelic/internal_context_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/newrelic/go-agent/v3/internal" + "github.com/valyala/fasthttp" ) func TestWrapHandlerContext(t *testing.T) { @@ -36,6 +37,34 @@ 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() + + err := fasthttp.Do(req, resp) + 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}, + }) + if err != nil { + t.Error(err) + } +} 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 f45735687..9869cc402 100644 --- a/v3/newrelic/segments.go +++ b/v3/newrelic/segments.go @@ -348,14 +348,20 @@ func StartExternalSegmentFastHTTP(txn *Transaction, ctx *fasthttp.RequestCtx) *E StartTime: txn.StartSegmentNow(), Request: request, } - s.secureAgentEvent = secureAgent.SendEvent("OUTBOUND", 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) } } - secureAgent.DistributedTraceHeaders(request, s.secureAgentEvent) + + if IsSecurityAgentPresent() { + secureAgent.DistributedTraceHeaders(request, s.secureAgentEvent) + } } return s