Search code examples
gonewrelic

How to create generic or global context in golang for new relic (Golang New relic integration)?


I am creating new relic transaction in main.go and have to pass it along to handler and then to controller and so on. Is there a way i can define this globally and then can be accessed in any handler, controller or and db transaction?

import(
    "github.com/gin-gonic/gin"
    newrelic "github.com/newrelic/go-agent"
)

// main.go
func newrelicHandler() (gin.HandlerFunc, newrelic.Application) {
    newrelicConfig := newrelic.NewConfig("NEW_RELIC_APP_NAME", "NEW_RELIC_APP_KEY")
    app, err := newrelic.NewApplication(newrelicConfig)
    if err != nil {
        return func(c *gin.Context) {
            c.Next()
        }, app
    }
    return func(c *gin.Context) {
        txn := app.StartTransaction(c.Request.URL.Path, c.Writer, c.Request)
        c.Set("newRelicTransaction", txn)
        apm, ok := c.Get("newRelicTransaction")
        defer txn.End()
        c.Next()
    }, app
}

func main() {
    r := gin.New()
    x, app := newrelicHandler()
    r.Use(x)
}


// test/handler.go

func (t *TestHandler) Display(c *gin.Context) {
    apmTxn, ok := c.Get("newRelicTransaction")

    test, err := t.testCtrl.Display(apmTxn)
    if err != nil {
        return err
    }

    c.JSON(http.StatusOK, gin.H{"message": "success"})
}


// test/controller.go

func (t *TestCtrl) Display(txn interface{}) {
    apmTxn, ok := txn.(newrelic.Transaction)
    if !ok {
        fmt.Println("ERR")
    }
    segment := newrelic.StartSegment(apmTxn, "SomeSegmentName")
    // get data
    segment.End()
    return 
}



Solution

  • Avoid using a global context, rather create one at the entrypoint and then just pass it as an argument to any function that needs it.

    You can make use of the nrgin package provided by the Gin framework.

    And in the main() function

    • Create an instance of newrelic - newrelic.NewApplication(cfg)
    • Call the - nrgin.Middleware(app) function passing in the newrelic instance. This will add the Gin transaction context key - newRelicTransaction to the context.
    • Register the function in step 2 as a middleware for all your routes - router.Use(nrgin.Middleware(app))

    You can then pass this same context object to your other functions that can accept a parameter of type context.Context since gin.Context is simply implementing the context interface of Go.

    Example code

    import "github.com/newrelic/go-agent/_integrations/nrgin/v1"
    
    func main() {
        cfg := newrelic.NewConfig("Gin App", mustGetEnv("NEW_RELIC_LICENSE_KEY"))
        
        app, err := newrelic.NewApplication(cfg)
        if nil != err {
            fmt.Println(err)
        }
    
        router := gin.Default()
        router.Use(nrgin.Middleware(app))
    
        router.GET("/example-get", GetController)
    
        router.Run(":80")
    }
    
    func GetController(c *gin.Context) {
        if txn := nrgin.Transaction(c); nil != txn {
            txn.SetName("custom-name")
        }
    
        databaseGet(c)
    
        c.Writer.WriteString("example response...")
    }
    
    func databaseGet(c context.Context) {
        if txn := nrgin.Transaction(c); nil != txn {
            txn.SetName("custom-name")
        }
    
        c.Writer.WriteString("example response...")
    }