github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrecho/nrecho.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  // Package nrecho instruments https://github.com/labstack/echo applications.
     5  //
     6  // Use this package to instrument inbound requests handled by an echo.Echo
     7  // instance.
     8  //
     9  //	e := echo.New()
    10  //	// Add the nrecho middleware before other middlewares or routes:
    11  //	e.Use(nrecho.Middleware(app))
    12  //
    13  // Example: https://github.com/newrelic/go-agent/tree/master/_integrations/nrecho/example/main.go
    14  package nrecho
    15  
    16  import (
    17  	"net/http"
    18  	"reflect"
    19  
    20  	"github.com/labstack/echo"
    21  	newrelic "github.com/newrelic/go-agent"
    22  	"github.com/newrelic/go-agent/internal"
    23  )
    24  
    25  func init() { internal.TrackUsage("integration", "framework", "echo") }
    26  
    27  // FromContext returns the Transaction from the context if present, and nil
    28  // otherwise.
    29  func FromContext(c echo.Context) newrelic.Transaction {
    30  	return newrelic.FromContext(c.Request().Context())
    31  }
    32  
    33  func handlerPointer(handler echo.HandlerFunc) uintptr {
    34  	return reflect.ValueOf(handler).Pointer()
    35  }
    36  
    37  func transactionName(c echo.Context) string {
    38  	ptr := handlerPointer(c.Handler())
    39  	if ptr == handlerPointer(echo.NotFoundHandler) {
    40  		return "NotFoundHandler"
    41  	}
    42  	if ptr == handlerPointer(echo.MethodNotAllowedHandler) {
    43  		return "MethodNotAllowedHandler"
    44  	}
    45  	return c.Path()
    46  }
    47  
    48  // Middleware creates Echo middleware that instruments requests.
    49  //
    50  //	e := echo.New()
    51  //	// Add the nrecho middleware before other middlewares or routes:
    52  //	e.Use(nrecho.Middleware(app))
    53  //
    54  func Middleware(app newrelic.Application) func(echo.HandlerFunc) echo.HandlerFunc {
    55  
    56  	if nil == app {
    57  		return func(next echo.HandlerFunc) echo.HandlerFunc {
    58  			return next
    59  		}
    60  	}
    61  
    62  	return func(next echo.HandlerFunc) echo.HandlerFunc {
    63  		return func(c echo.Context) (err error) {
    64  			rw := c.Response().Writer
    65  			txn := app.StartTransaction(transactionName(c), rw, c.Request())
    66  			defer txn.End()
    67  
    68  			c.Response().Writer = txn
    69  
    70  			// Add txn to c.Request().Context()
    71  			c.SetRequest(c.Request().WithContext(newrelic.NewContext(c.Request().Context(), txn)))
    72  
    73  			err = next(c)
    74  
    75  			// Record the response code. The response headers are not captured
    76  			// in this case because they are set after this middleware returns.
    77  			// Designed to mimic the logic in echo.DefaultHTTPErrorHandler.
    78  			if nil != err && !c.Response().Committed {
    79  
    80  				txn.SetWebResponse(nil)
    81  				c.Response().Writer = rw
    82  
    83  				if httperr, ok := err.(*echo.HTTPError); ok {
    84  					txn.WriteHeader(httperr.Code)
    85  				} else {
    86  					txn.WriteHeader(http.StatusInternalServerError)
    87  				}
    88  			}
    89  
    90  			return
    91  		}
    92  	}
    93  }