Node.js custom instrumentation

New Relic for Node.js automatically instruments most standard web requests, but sometimes you want expanded instrumentation. With custom instrumentation, you can instrument web sockets, background jobs, and unsupported databases. Custom instrumentation can also target specific sections of your code for deeper insight.

Custom instrumentation is available in versions 1.10.0 or higher of the Node.js agent.

Instrumenting web transactions

To instrument web transactions, such as websocket requests, create a custom transaction. Creating a custom transaction gives you the same type of visibility into that custom transaction that you would get from a transaction that the agent automatically instruments. To instrument web transactions, wrap the handler you want to instrument with createWebTransaction(). Ensure you call endTransaction() at the end of the transaction.

Example: Instrument two transactions in socket.io

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

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

io.on('connection', function (socket) {
  socket.on('ping', nr.createWebTransaction('/websocket/ping', function (data) {
    socket.emit('pong')
    nr.endTransaction()
  }))
  socket.on('new-message', nr.createWebTransaction('/websocket/new-message', function (data) {
    addMessageToChat(data, function () {
      socket.emit('message-received')
      nr.endTransaction()
    })
  }))
})

Ensure you call endTransaction() at the end of the transaction.

Instrumenting background transactions

You can also use custom transactions to instrument background tasks, such as periodic jobs within your app or work that continues after a request completes. To instrument background tasks, wrap the handler you want to instrument with createBackgroundTransaction(). Ensure you call endTransaction() at the end of the transaction.

Example: Instrument within setInterval

This example instruments update:cache within setInterval:

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

setInterval(nr.createBackgroundTransaction('update:cache', function () {
  var newValue = someDataGenerator()
 
  redis.set('some:cache:key', newValue, function () {
    nr.endTransaction() // End the transaction once redis is done
  })
}), 30000) // Every 30s

Ensure you call endTransaction() at the end of the transaction.

Expanding instrumentation within transactions

You can also use custom instrumentation to provide more visibility into web transactions that are already instrumented, or to 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 callbacks, wrap the callback with createTracer(). If you want to instrument a function that is called inside an asynchronous function, you need to wrap both the target function and its parent asynchronous function with createTracer().

You must place these examples inside an already instrumented transaction. The transaction can be automatically instrumented, or via a custom transaction.

Example: Instrument a callback

This examples 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, we 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

Additional documentation resources include:

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.