Tracing a Node.js micro-service with OCI Application Performance Monitoring (APM) AND Zipkin

OCI Application Performance Monitoring 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 steps here to configure Zipkin in your app. Here the doco from OCI APM with detailled instructions for the OCI part. If you want to use an example use this, it is the code we are going to use for this post. Clone the repo and edit file web/recorder.js by adding the code in bold and removing the text

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

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

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

// data upload endpoint example is something like

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

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

// 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

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!! 🙂


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.