Node.js custom instrumentation

New Relic for Node.js automatically instruments most standard web requests, but sometimes you want expanded instrumentation. With the agent's custom instrumentation API, you can create instrumentation for otherwise unsupported web frameworks, datastores, and message service clients. The Node.js agent's custom instrumentation API also allows you to:

Agent version requirements

The custom instrumentation methods detailed in this document are available as of Node.js agent version 2.0.0. For information on instrumentation using the custom instrumentation API v1.x, see the documentation for legacy Node.js custom instrumentation.

Instrument unsupported web frameworks

Beginning with version 2.0.0 of the Node.js agent, New Relic provides an API to expand instrumentation for additional web frameworks. For more information, including a tutorial, see the documentation for Node.js web framework instrumentation on Github.

Instrument unsupported message service clients

Beginning with version 2.0.0 of the Node.js agent, New Relic provides an API to expand instrumentation for additional message service libraries. For more information, including a tutorial, see the documentation for Node.js message service client instrumentation on Github.

Instrument unsupported datastores

Beginning with version 2.0.0 of the Node.js agent, New Relic provides an API to expand instrumentation for additional datastore libraries. For more information, including a tutorial, see the documentation for Node.js datastore instrumentation on Github.

Instrument web transactions

In order to create custom web transactions, call startWebTransaction to start the transaction. To end the transaction, use any of these options:

Ending the transaction Comments
Promise

If the handler passed to startWebTransaction returns a promise, the agent will end the transaction when the returned promise resolves or rejects.

Manual

If you call getTransaction in the context of the new transaction, this notifies the agent that the transaction will be handled manually.

If getTransaction is called in the handler, then you must end the transaction by calling transaction.end().

Synchronous

If neither of these options are fulfilled, the transaction will be ended when the handler returns synchronously.

Custom web transaction example: Instrument three transactions in socket.io

This example instruments a /websocket/ping transaction, a /websocket/update transaction, and a /websocket/new-message transaction within socket.io. The /ping example is synchronous, while the /new-message and /update examples are asynchronous.

var nr = require('newrelic')
var app = require('http').createServer()
var io = require('socket.io')(app)

io.on('connection', function (socket) {
  socket.on('ping', function (data) {
    nr.createWebTransaction('/websocket/ping', function transactionHandler() {
      // Ended automatically after synchronously returning
      socket.emit('pong')
    })
  })
  socket.on('update', function (data) {
    nr.createWebTransaction('/websocket/update', function transactionHandler() {
      // Using API#getTransaction
      var transaction = nr.getTransaction()
      updateChatWindow(data, function transactionHandler() {
        socket.emit('update-done')
        transaction.end()
      })
    })
  })
  socket.on('new-message', function (data) {
    nr.createWebTransaction('/websocket/new-message', function transactionHandler() {
      // Returning a promise
      return new Promise(function (resolve, reject) {
        addMessageToChat(data, function () {
          socket.emit('message-received')
          resolve()
        })
      })
    })
  })
})

This method only gives basic timing data for the transaction created. To create more intricate timing data and transaction naming for a particular framework, see the Node.js API documentation and the related tutorial on Github.

Instrument background transactions

You can use custom transactions to instrument background tasks; for example:

  • Periodic jobs within your app
  • Work that continues after a request completes

To instrument background tasks, call startBackgroundTransaction in your handler to start a background transaction. To end the transaction, use any of these options:

Ending the transaction Comments
Promise

If the handler passed to startBackgroundTransaction returns a promise, the agent will end the transaction when the returned promise resolves or rejects.

Manual

If you call getTransaction in the context of the new transaction, this notifies the agent that the transaction will be handled manually.

If getTransaction is called in the handler, then you must end the transaction by calling transaction.end().

Synchronous

If neither of these options are fulfilled, the transaction will be ended when the handler returns synchronously.

Custom background transaction example: Instrument within setInterval

This example instruments update:cache within setInterval:

var nr = require('newrelic')
var redis = require('redis').createClient()

// Using API#getTransaction to manage ending the transaction
setInterval(function () {
  nr.createBackgroundTransaction('update:cache', function transactionHandler() {
    var newValue = someDataGenerator()
    var transaction = nr.getTransaction()
 
    redis.set('some:cache:key', newValue, function () {
      transaction.end()
    })
  })
}, 30000) // Every 30s

//Using a promise to manage ending the transaction
setInterval(function () {
  nr.createBackgroundTransaction('flush:cache', function transactionHandler() {
    return new Promise(function(resolve, reject) {
      flushCache(redis, function afterFlush(err) {
        if (err) {
          return reject(err)
        }
        resolve()
      })
    })
  })
}, 60*60*1000)

Expand instrumentation within transactions

You can create instrumentation using the instrumentation registration methods on the API. Writing instrumentation using the instrumentation API allows you to specify metrics and naming in greater detail by "monkey patching" methods (replacing functions) on relevant objects. Other options can offer visibility into web transactions that are already instrumented, or gain insight into databases and other in-transaction work that is not automatically instrumented.

To do this, wrap your callbacks in custom tracers. Custom tracers create and collect specific metrics for an additional segment within an existing transaction, such as a particular function or a database call.

  • To instrument individual callbacks, wrap the callback with createTracer().
  • To instrument a function that is called inside an asynchronous function, wrap both the target function and its parent asynchronous function with createTracer().

These examples must be placed in code that is running under a transaction. The origin of the transaction, custom or automatically created, does not matter.

Example: Instrument a callback

This example wraps a single callback:

// Wrap the callback in a segment
db.createObject(nr.createTracer('db:createObject', function (err, result) {
  // Some error handler that will end the response for us
  if (util.handleError(err, res)) {
    return
  }
  res.write(JSON.stringify(result.rows[0].id))
  res.write('\n')
  res.end()
}))
Example: Instrument an async function

This example wraps both pg.connect and client.query. This is because client.query is called by an asynchronous parent function (pg.connect). Otherwise, you would not get any data from client.query. This allows createTracer() to propagate the active transaction across those asynchronous boundaries.

pg.connect(config.db_string, nr.createTracer('pg:connect', function (err, client, done) {
  if (util.handleError(err, '500', res)) {
    return done()
  }
  client.query('SELECT count(*) FROM test_count', nr.createTracer('pg:query', function (err, result) {
    if (util.handleError(err, '500', res)) {
      return done()
    }

    res.write(result.rows[0].count)
    res.write('\n')
  }))
}))

For more help

Join the discussion about Node.js monitoring in the New Relic Online Technical Community! The Technical Community is a public platform to discuss and troubleshoot your New Relic toolset.

If you need additional help, get support at support.newrelic.com.