Skip to content

Instantly share code, notes, and snippets.

@peterholditch
Created October 30, 2015 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterholditch/c0eb5ab3d8a7f35cee10 to your computer and use it in GitHub Desktop.
Save peterholditch/c0eb5ab3d8a7f35cee10 to your computer and use it in GitHub Desktop.
example for custom correlation with AppDynamics in Node.js
var argv = process.argv.slice(2);
var appd = require('appdynamics');
if (!(argv[0] === '-entry' || argv[0] === '-service')) {
console.error('Please specify an -entry or -service role, e.g. `node example -entry`')
process.exit(1);
}
/* Initialize App Dynamics */
appd.profile({
debug: false,
controllerHostName: 'localhost',
controllerPort: 8090,
applicationName: 'microservices',
tierName: 'tier' + argv[0],
nodeName: 'node' + argv[0], // Node names must be unique.
controllerSslEnabled: false // Optional - use if connecting to controller via SSL
});
var EventEmitter = require('events').EventEmitter;
var redis = require('redis');
var publisherClient;
var subscriberClient;
function publish(channel, message, cb) {
var client = publisherClient || (publisherClient = redis.createClient());
client.publish(channel, JSON.stringify(message), cb);
}
function subscribe(channel) {
var client = subscriberClient || (subscriberClient = redis.createClient());
var emitter = new EventEmitter();
client.on('message', function(messageChannel, message) {
if (channel !== messageChannel) return;
emitter.emit('message', JSON.parse(message));
});
client.subscribe(channel);
return emitter;
}
function createEntry() {
var publisherTopic = 'entry';
var subscriberTopic = 'entry:response';
var subscriber = subscribe(subscriberTopic);
require('http').createServer(function(req, res) {
/* Take next message */
subscriber.once('message', function(message) {
res.writeHead(message.statusCode);
res.end(JSON.stringify(message));
});
var request = {
at: new Date()
};
publish(publisherTopic, request, function(err) {
if (err) console.error(err);
console.log('requested');
});
}).listen(8080, function() {
console.log('example listening on 8080');
});
}
function createService() {
var publisherTopic = 'entry:response';
var subscriberTopic = 'entry';
var subscriber = subscribe(subscriberTopic);
subscriber.on('message', function(message) {
console.log('received', message);
var response = {
statusCode: 200,
dates: [message.at, new Date()]
};
publish(publisherTopic, response, function(err) {
if (err) console.error(err);
console.log('responded');
});
});
}
if (argv[0] === '-entry') createEntry();
else if (argv[0] === '-service') createService();
function noop(){}
var argv = process.argv.slice(2);
var appd = require('appdynamics');
if (!(argv[0] === '-entry' || argv[0] === '-service')) {
console.error('Please specify an -entry or -service role, e.g. `node example -entry`')
process.exit(1);
}
/* Initialize App Dynamics */
appd.profile({
debug: false,
controllerHostName: 'localhost',
controllerPort: 8090,
applicationName: 'microservices',
tierName: 'tier' + argv[0],
nodeName: 'node' + argv[0], // Node names must be unique.
controllerSslEnabled: false // Optional - use if connecting to controller via SSL
});
var EventEmitter = require('events').EventEmitter;
var redis = require('redis');
var publisherClient;
var subscriberClient;
function publish(channel, message, cb) {
var client = publisherClient || (publisherClient = redis.createClient());
client.publish(channel, JSON.stringify(message), cb);
}
function subscribe(channel) {
var client = subscriberClient || (subscriberClient = redis.createClient());
var emitter = new EventEmitter();
client.on('message', function(messageChannel, message) {
if (channel !== messageChannel) return;
emitter.emit('message', JSON.parse(message));
});
client.subscribe(channel);
return emitter;
}
function createEntry() {
var publisherTopic = 'entry';
var subscriberTopic = 'entry:response';
var subscriber = subscribe(subscriberTopic);
require('http').createServer(function(req, res) {
var trx = appd.startTransaction(req);
trx.beforeExitCall = function getExitInfo(exitCall) { //Suppress default Redis exit handling
if (exitCall.backendName == 'Redis')
return;
else
return exitCall;
}
/* Take next message */
subscriber.once('message', function(message) {
if (message.ci) {
var nci = appd.parseCorrelationInfo(message.ci);
var trx = appd.startTransaction(nci);
}
res.writeHead(message.statusCode);
res.end(JSON.stringify(message));
if (message.ci) trx.end();
});
var exit = trx.startExitCall({ // Inform AppD of upcoming exit call
exitType: 'EXIT_CACHE',
label: 'Event Bus - ' + publisherTopic,
identifyingProperties: { // together, properties must uniquely identify the message destination
bus: "local_redis",
topic: publisherTopic
}
});
var request = {
ci: trx.createCorrelationInfo(exit), // put the AppD correlation header into the message payload
at: new Date()
};
publish(publisherTopic, request, function(err) {
if (err) console.error(err);
console.log('requested');
trx.endExitCall(exit); // Inform AppD that exit call has completed
trx.end();
});
}).listen(8080, function() {
console.log('example listening on 8080');
});
}
function createService() {
var publisherTopic = 'entry:response';
var subscriberTopic = 'entry';
var subscriber = subscribe(subscriberTopic);
subscriber.on('message', function(message) {
console.log('received', message);
if (!message.ci) return publish(publisherTopic, { statusCode: 400, info: 'CorrelationInfo is empty.' }, noop);
var nci = appd.parseCorrelationInfo(message.ci);
var trx = appd.startTransaction(nci); // Associate transaction as downstream component of txn defined in ci field (upstream correlato header)
trx.beforeExitCall = function getExitInfo(exitCall) { // Supress default Redis exit handling
if (exitCall.backendName == 'Redis')
return;
else
return exitCall;
}
var exit = trx.startExitCall({
exitType: 'EXIT_CACHE',
label: 'Event Bus - ' + publisherTopic,
identifyingProperties: { // together, properties must identify the message destination
bus: "local_redis",
topic: publisherTopic
}
});
var response = {
ci: trx.createCorrelationInfo(exit), // ci is correlation header for reply message
statusCode: 200,
dates: [message.at, new Date()]
};
publish(publisherTopic, response, function(err) {
if (err) console.error(err);
console.log('responded');
trx.endExitCall(exit); // Inform AppD that exit call is over
trx.end();
});
});
}
if (argv[0] === '-entry') createEntry();
else if (argv[0] === '-service') createService();
function noop(){}
The 2 other files in this gist show a Redis request/response example (example-original.js)
and the same file modified for instrumentation with AppDynamics (example.js)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment