Tracing A Node.js with OCI Application Performance Monitoring and Zipkin

OCI Application Performance Monitoring (APM) provides a comprehensive set of features to monitor applications and diagnose performance issues.

Application Performance Monitoring integrates with open-source tracing system tools (open-source tracers) such as Jaeger and Zipkin and enables you to upload trace data. It also supports context propagation between Application Performance Monitoring agents and open-source tracers.

STEP 1: Configure APM

Go to APM->Administration, click in [Create APM Domain] button and provide the information requested in popup window.

STEP 2: Grab domain details

In the APM domain details you created, get the [Data Upload Endpoint] URL and the auto_generated_public_datakey values, we’ll need them in step 3.

STEP 3: Configure your Node.js app

Follow the steps here to configure Zipkin in your app. Here’s the doc from OCI APM with detailled instructions for the OCI part.

If you want to use an example, use this, as it’s the code we’re going to use for this post. Clone the repo and edit file web/recorder.js according to the instructions below (note the changes we’ll make):

/* eslint-env browser */
const {
  jsonEncoder: {JSON_V2}
} = require('zipkin');
const {HttpLogger} = require('zipkin-transport-http');

Replace the last line with:

const CLSContext = require('zipkin-context-cls');

We continue with:

const debug = 'undefined' !== typeof window
  ?'debug') !== -1
  : process.env.DEBUG;

Of course, the BaseURL will not be a localhost, so remove the line below the comment here:

// Send spans to Zipkin asynchronously over HTTP
const zipkinBaseUrl = 'http://localhost:9411';

// data upload endpoint example is something like

And replace const zipkinBaseUrl = ‘http://localhost:9411;’ with:

const httpLogger = new HttpLogger({
  endpoint: '<domain data upload endpoint in step 2>/20200101/observations/public-span?dataFormat=zipkin&dataFormatVersion=2&dataKey=<public data key in step 2>',
  jsonEncoder: JSON_V2

Here we’ll remove this logger and add a tracer. First remove this:

const httpLogger = new HttpLogger({
  endpoint: `${zipkinBaseUrl}/api/v2/spans`,
  jsonEncoder: JSON_V2

And add this:

// Setup the tracer
const tracer = new Tracer({
  ctxImpl: new CLSContext('zipkin'), // implicit in-process context
  recorder: new BatchRecorder({
    logger: httpLogger
  }), // batched http recorder
  localServiceName: 'mytest', // name of this application
  supportsJoin: false //Span join disable setting

The rest should look like this:

function recorder(serviceName) {
  return debug ? debugRecorder(serviceName) : new BatchRecorder({logger: httpLogger});

function debugRecorder(serviceName) {
  // This is a hack that lets you see the data sent to Zipkin!
  const logger = {
    logSpan: (span) => {
      const json = JSON_V2.encode(span);
      console.log(`${serviceName} reporting: ${json}`);

  const batchRecorder = new BatchRecorder({logger});

  // This is a hack that lets you see which annotations become which spans
  return ({
    record: (rec) => {
      const {spanId, traceId} = rec.traceId;
      console.log(`${serviceName} recording: ${traceId}/${spanId} ${rec.annotation.toString()}`);
module.exports.recorder = recorder; 

STEP 4: Restart your node app


Go to APM Trace explorer and run a query

Traces can be observed in the list!

That’s all, hope it helps!! 🙂

If you’re curious about the goings-on of Oracle Developers in their natural habitat, come join us on our public Slack channel!

And don’t forget our free tier, where you can try out what we just discussed.