NewRelic (NR) is a tool that you can use for monitoring your applications. Once your code is instrumented with NewRelic, you can see your transactions on their dashboard and gain valuable insights like latency, throughput etc for your APIs and also things like DB calls, calls to external subsystems etc.
If your system is written using Go, you would need to use their Go Agent. Here are some ways in which you can use it effectively.
Using NR to instrument calls to your API
Before you can leverage NR, you will have to create a NR Transaction (txn). The best place to create this txn will be in your middleware i.e. when the request is coming in. So on startup of your server, first create a NR app:
func createNRApp(licenseKey string) (newrelic.Application, error) {
cfg := newrelic.NewConfig(”YourNRAppName”, licenseKey)
app, err := newrelic.NewApplication(cfg)
if err != nil {
return nil, err
}
return app, nil
}
Now that we have a NR app, we can send this app into our middleware to crate a txn for every request that comes through:
type NewRelicContextKey string
var NRKey = NewRelicContextKey(“NewRelicTxn”)
func newRelicMiddleware(app newrelic.Application) negroni.Handler {
return negroni.HandlerFunc(func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
txn := app.StartTransaction(”txnName”, w, req)
defer txn.End()
ctx := context.WithValue(req.Context(), NRKey, txn)
req = req.WithContext(ctx)
next(txn, req)
}
}
Now every request context will have NR txn embedded within it. Strictly speaking we did not have to embed it into the context, if all we wanted to do was instrument calls to our APIs. However, by placing the txn into the context and propagating the request context down your call chain, you can use NR to instrument other aspects of your code, as you will see in the next section.
Using NR to instrument calls to your DB
NR has support to instrument calls to your DB, which they call Datastore Segments. Let’s assume you have propagated the request context down to the models which access your DB. If you have followed along thus far, you now also have a NR txn embedded in your request context. We will now write a helper function to start a NR Datastore Segment:
func NRDS(txn newrelic.Transaction, tableName, operation string) newrelic.DatastoreSegment {
s := newrelic.DatastoreSegment{
Product: newrelic.DatastoreMySQL,
Collection: tableName,
Operation: operation,
}
s.StartTime = newrelic.StartSegmentNow(txn)
return s
}
With the above helper function at hand, adding instrumentation in your models becomes trivial:
func myModelFunc(ctx context.Context, {remaining args of your model}) {
txn, _ := ctx.Value(NRKey).(newrelic.Transaction)
s := NRDS(txn, “MyTableName”, “SELECT”)
defer s.End()
// your model code…
}
You will see your DB calls times in the “Databases” tab of the NR panel.
Using NR to instrument external calls
NR can also be used for instrumenting calls to external sub-sytems. Let’s assume you have propagated the request context to the function making the external calls. We will first write a helper function to make our lives easier:
func NRES(txn newrelic.Transaction, url string) newrelic.ExternalSegment {
return newrelic.ExternalSegment{
StartTime: newrelic.StartSegmentNow(txn),
URL: url,
}
}
With the above in place, instrumenting external calls becomes trivial:
func myExternalFunc(ctx context.Context, url string) {
txn, _ := ctx.Value(NRKey).(newrelic.Transaction)
seg := NRES(txn, url)
defer seg.End()
// your external call code here
}
Using NR to instrument background workers
Many times we don’t have a request/response type scenario. e.g. maybe we have a backend worker that is just polling or performs a workflow only on an event. In those cases we would need to create a new context and a new txn. Embed that txn into the context and propagate this context down the worker call chain. Here is a helper function that lets you do this:
func NRMakeCtxAndTxn(app *newrelic.Application, txnName string) (newrelic.Transaction, context.Context) {
if app == nil {
return nil, context.Background()
}
txn := (*app).StartTransaction(txnName, nil, nil)
ctx := context.WithValue(context.Background(), NRKey, txn)
return txn, ctx
}
func NREndTxn(txn newrelic.Transaction) {
if txn != nil {
txn.End()
}
}
Then in your backend workers, before kicking off a task do:
func myWorkerTask(nrApp *newrelic.Application) {
txn, _ := NRMakeCtxAndTxn(nrApp, “taskName”)
defer NREndTxn(txn)
// task code goes here…
}
Hopefully these code snippets help you.