ibmapm-restclient
Version:
Node.js rest client for ICAM.
692 lines (594 loc) • 20.9 kB
JavaScript
;
var rcutil = require('./util');
var pluginConfig = require('../restclient/config.js').pluginConfig;
var sender = require('../restclient/sender-queue');
var queue;
var fs = require('fs');
var https = require('https');
var http = require('http');
var url = require('url');
var inPath = '/1.0/monitoring/data';
var resource_url = '/OEReceiver/v1/monitoringdata/';
var deepdive_url = '/1.0/monitoring/data/';
var tt_url = inPath + '?type=aar/middleware';
var properties = require('properties');
var EventEmitter = require('events').EventEmitter;
var ready_event = new EventEmitter();
var logger = require('./logutil').getLogger('restclient_BIPlugin.js');
var initialized = false;
module.exports.format = function(task, callback) {
var ret = task;
if (callback) {
callback(null, ret);
}
return ret;
};
module.exports.Name = function() {
return 'BIPlugin';
};
module.exports.checkReadyStatus = function(callback) {
logger.debug('checkReadyStatus');
ready_event.once('bi_initialized', function() {
logger.debug('Got bi_initialized event');
callback(initialized);
});
};
var app_guid;
var apmconns = {};
function mapKey(key) {
var newKey = key;
if (key === 'requestRate') {
newKey = 'REQRATE';
}
if (key === 'averageResponseTime') {
newKey = 'RESP_TIME';
}
if (key === 'slowestResponseTime') {
newKey = 'MAX_RSPTIME';
}
if (key === 'cpuPercentage') {
newKey = 'CPU_P';
}
if (key === 'memoryRssSize') {
newKey = 'MEM_RSS';
}
if (key === 'memoryTotalSize') {
newKey = 'app_memAll';
}
if (key === 'upTime') {
newKey = 'UPTIME';
}
if (key === 'gcDuration') {
newKey = 'gc_duration';
}
if (key === 'scavengeGcCount') {
newKey = 'gc_sCount';
}
if (key === 'markSweepGcCount') {
newKey = 'gc_mCount';
}
if (key === 'processWeakCallbacksGcCount') {
newKey = 'gc_wCount';
}
if (key === 'incrementalMarkingGcCount') {
newKey = 'gc_iCount';
}
if (key === 'usedHeap') {
newKey = 'gc_heapUsed';
}
if (key === 'heapSize') {
newKey = 'gc_heapSize';
}
if (key === 'averageEventLoopLatency') {
newKey = 'eventloop_latencyAvg';
}
if (key === 'minimumEventLoopLatency') {
newKey = 'eventloop_latencyMin';
}
if (key === 'maximumEventLoopLatency') {
newKey = 'eventloop_latencyMax';
}
if (key === 'averageEventLoopTickTime') {
newKey = 'loop_average';
}
if (key === 'maximumEventLoopTickTime') {
newKey = 'loop_maximum';
}
if (key === 'minimumEventLoopTickTime') {
newKey = 'loop_minimum';
}
if (key === 'eventLoopTickCount') {
newKey = 'loop_count';
}
// ignore items from appinfo2
if (key.indexOf('reqSummary_') === 0) {
newKey = undefined;
}
// ignore BIOnly flag
if (key.indexOf('BIOnly') === 0) {
newKey = undefined;
}
return newKey;
}
function mergeObj(obj1, obj2) {
var obj = obj1;
var keys = Object.keys(obj2);
keys.forEach(function(element) {
var newKey = mapKey(element);
if (newKey !== undefined) {
obj[newKey] = obj2[element];
}
}, this);
return obj;
}
function convertTypes(key, value) {
if (key === 'REQRATE') {
return value ? value * 1000 / 60 >>> 0 : value;
}
if (key === 'CPU_P') {
return value ? value * 1000 >>> 0 : value;
}
if (key === 'app_uptime') {
value = value / 1000;
return (value / (24 * 60 * 60) >>> 0) + 'd ' +
(value % (24 * 60 * 60) / (60 * 60) >>> 0) + 'h ' +
(value % (60 * 60) / 60 >>> 0) + 'm ' + (value % 60 >>> 0) + 's';
}
if (key === 'RESP_TIME') {
return Math.round(value);
}
if (key === 'HTTP_REQ') {
return cloneValueFor_HTTP_REQ(value);
}
return value;
};
function cloneValueFor_HTTP_REQ(value) {
var result = [];
for (var i = 0; i < value.length; i++) {
result.push(
{
URL: value[i].URL,
METHOD: value[i].METHOD,
REQ_RESP_TIME: Math.round(value[i].REQ_RESP_TIME),
HIT_COUNT: value[i].HIT_COUNT
}
);
}
return result;
}
function formatMetricPayload(payload) {
var targetPayload = {};
for (var index = 0; index < payload.length; index++) {
if (payload[index].BIOnly) {
mergeObj(targetPayload, payload[index]);
} else if (['loop', 'eventloop', 'requestsSummary']
.indexOf(payload[index].tags._componentType) >= 0) {
continue;
} else {
if (process.env.UA_LWDC_LISTENER_URL) {
mergeObj(targetPayload, payload[index].fields);
} else {
mergeObj(targetPayload, payload[index].metrics);
}
}
}
if (targetPayload.PORT === 'unknown' || targetPayload.PORT == null) {
targetPayload.PORT = targetPayload.APP_PORT;
} else {
try {
targetPayload.PORT = parseInt(targetPayload.PORT);
} catch (e) {
targetPayload.PORT = 0;
}
}
if (!targetPayload.APP_GUID) {
targetPayload.APP_GUID = app_guid;
}
if (!targetPayload.SUBNODE_TYPE) {
targetPayload.SUBNODE_TYPE = 'BNJ';
}
targetPayload = JSON.parse(JSON.stringify(targetPayload, convertTypes));
return targetPayload;
}
function genAARPayloads(task, MSN) {
var ret = [];
if (task.payload instanceof Array) {
for (var index = 0; index < task.payload.length; index++) {
var element = task.payload[index];
element.properties.originID = MSN;
delete element.properties['resourceID'];
ret.push(element);
}
} else {
let element = task.payload;
element.properties.originID = MSN;
delete element.properties['resourceID'];
ret.push(element);
}
return ret;
}
module.exports.send = function(tasks, callback) {
if (!queue) {
queue = sender.getQueue('bam');
}
for (var index = 0; index < tasks.length; index++) {
if (tasks[index].payload) {
sendOne(tasks[index], callback);
}
}
};
function sendOne(task, callback) {
if (!task.addtionalOptions) {
task.addtionalOptions = {};
}
logger.debug('BIPlugin.send ' + task.type);
if (task.type.indexOf('metrics:') === 0) {
task.payload = formatMetricPayload(task.payload);
sendMetrictoAPM(task);
}
if (task.type.indexOf('aar:') === 0) {
sendAARtoAPM(task);
}
if (task.type.indexOf('jso:') === 0) {
sendJSOtoAPM(task);
}
if (callback) {
callback(null, task);
}
return;
};
function sendMetrictoAPM(task) {
logger.debug('BIPlugin.sendMetrictoAPM ' + task.payload);
if (!rcutil.validateBIPayload(task.payload)) {
logger.debug('Payload is not ready for BI Agent');
return;
}
for (var key in apmconns) {
if (apmconns.hasOwnProperty(key)) {
var element = apmconns[key];
if (element.server_url) {
task.url = element.resource_url;
}
if (element.pfx) {
task.addtionalOptions.pfx = element.pfx;
}
if (element.keyfile_password) {
task.addtionalOptions.passphrase = element.keyfile_password;
}
queue.send(task);
}
}
}
function sendJSOtoAPM(task) {
for (var key in apmconns) {
if (apmconns.hasOwnProperty(key)) {
var element = apmconns[key];
if (element.server_url) {
if (element.MSN) {
task.url = element.deepdive_url + '?tenant=34&origin=' + element.MSN +
'&namespace=node.js&type=aar.diagfs';
} else {
getMSN(element);
return;
}
}
if (element.pfx) {
task.addtionalOptions.pfx = element.pfx;
}
if (element.keyfile_password) {
task.addtionalOptions.passphrase = element.keyfile_password;
}
var payloads = JSON.parse(task.payload);
if (Array.isArray(payloads)) {
for (var index = 0; index < payloads.length; index++) {
payloads[index].properties.sourceIPAddress = element.MSN;
payloads[index].properties.ipaddr = element.MSN;
}
} else {
payloads.properties.sourceIPAddress = element.MSN;
payloads.properties.ipaddr = element.MSN;
}
task.payload = JSON.stringify(payloads);
queue.send(task);
}
}
}
function sendAARtoAPM(task) {
for (var key in apmconns) {
if (apmconns.hasOwnProperty(key)) {
var element = apmconns[key];
if (element.server_url) {
if (element.MSN) {
task.url = element.tt_url + '&tenant=34&origin=' + element.MSN;
} else {
getMSN(element);
return;
}
}
var payloads = [];
payloads = genAARPayloads(task, element.MSN);
task.payload = payloads;
if (element.pfx) {
task.addtionalOptions.pfx = element.pfx;
}
if (element.keyfile_password) {
task.addtionalOptions.passphrase = element.keyfile_password;
}
queue.send(task);
}
}
}
init();
logger.debug('Initialized', initialized);
rcutil.setBIEnabled(initialized);
var count = 1;
var intervalObj = setInterval(emitEvent, 5000);
intervalObj.unref();
module.exports.stop = function() {
logger.debug('Stop BIPlugin.');
if (intervalObj) {
clearInterval(intervalObj);
}
};
function emitEvent() {
if (initialized) {
ready_event.emit('bi_initialized');
logger.debug('Emit bi_initialized event.', count, initialized);
if (count > 10) {
clearInterval(intervalObj);
}
count++;
}
}
function initFromEnv() {
var urlbase = '';
var global_obj;
var option;
var got_url = false;
var need_security = false;
if (process.env.IBM_APM_SERVER_URL) {
let isbase64 = isBase64Url(process.env.IBM_APM_SERVER_URL);
if (isbase64) {
urlbase = Buffer.from(process.env.IBM_APM_SERVER_URL, 'base64').toString();
} else {
urlbase = process.env.IBM_APM_SERVER_URL;
}
option = url.parse(urlbase);
got_url = true;
if (option.protocol === 'https:') {
need_security = true;
}
initialized = true; // ready to send payloa
}
// get target server URL done
// get credential
var pfx;
var passphrase;
var got_security = false;
if (need_security) {
if (!process.env.IBM_APM_KEYFILE_PASSWORD) {
logger.debug('IBM_APM_KEYFILE_PASSWORD not set. Use default value instead!!');
process.env.IBM_APM_KEYFILE_PASSWORD = (Buffer.from('ccmR0cKs!')).toString('base64');
}
if (process.env.IBM_APM_KEYFILE) {
try {
let buff;
if (fs.existsSync(global_obj.ICAM_KEYFILE)) {
buff = fs.readFileSync(global_obj.ICAM_KEYFILE);
} else if (fs.existsSync(__dirname + '/../../etc/' + global_obj.APM_KEYFILE)) {
buff = fs.readFileSync(__dirname + '/../../etc/' + global_obj.APM_KEYFILE);
}
apmconns[global_obj.APM_BM_GATEWAY_URL].pfx = buff;
passphrase =
(Buffer.from(process.env.IBM_APM_KEYFILE_PASSWORD, 'base64')).toString();
got_security = true;
} catch (error) {
logger.error('failed to read keyfile from local: ' + error);
}
} else if (process.env.IBM_APM_KEYFILE_URL) {
passphrase =
(Buffer.from(process.env.IBM_APM_KEYFILE_PASSWORD, 'base64')).toString();
var keyfile_options = url.parse(process.env.IBM_APM_KEYFILE_URL);
var sendmethod = (keyfile_options.protocol === 'http:' ? http : https);
rcutil.tlsFix8(keyfile_options);
var req = sendmethod.request(keyfile_options, function(res) {
res.on('data', function(d) {
if (!pfx) {
pfx = d;
} else {
pfx = Buffer.concat([pfx, d], pfx.length + d.length);
}
got_security = true;
});
res.on('error', function(error) {
logger.error('JsonSender response error: ' + error);
});
});
req.on('error', function(error) {
logger.error('JsonSender request error: ' + error);
});
req.end();
}
}
if (got_url && !apmconns[urlbase]) {
apmconns[urlbase] = {
server_url: urlbase,
resource_url: urlbase + resource_url,
tt_url: urlbase + tt_url,
deepdive_url: urlbase + deepdive_url
};
if (process.env.APM_SNI) {
apmconns[urlbase].APM_SNI = process.env.APM_SNI;
}
if (got_security) {
apmconns[urlbase].pfx = pfx;
apmconns[urlbase].keyfile_password = passphrase;
}
}
};
function initFromGlobalEnv() {
let globalenvfile;
if (fs.existsSync('global.environment')) {
globalenvfile = 'global.environment';
} else if (fs.existsSync(__dirname + '/../../etc/global.environment')) {
globalenvfile = __dirname + '/../../etc/global.environment';
} else if (fs.existsSync(__dirname + '/../../../ibmapm/etc/global.environment')) {
globalenvfile = __dirname + '/../../../ibmapm/etc/global.environment';
} else {
globalenvfile = __dirname + '/../../../etc/global.environment';
}
logger.debug('Get the server configure from', globalenvfile);
if (fs.existsSync(globalenvfile)) {
var global_string = fs.readFileSync(globalenvfile);
var global_obj = properties.parse(global_string.toString(), {
separators: '=',
comments: [';', '@', '#']
});
logger.debug(global_obj);
if (global_obj.APM_BM_GATEWAY_URL && isURL(global_obj.APM_BM_GATEWAY_URL) &&
!apmconns[global_obj.APM_BM_GATEWAY_URL]) {
apmconns[global_obj.APM_BM_GATEWAY_URL] = {
server_url: global_obj.APM_BM_GATEWAY_URL,
resource_url: global_obj.APM_BM_GATEWAY_URL + resource_url,
tt_url: global_obj.APM_BM_GATEWAY_URL + tt_url,
deepdive_url: global_obj.APM_BM_GATEWAY_URL + deepdive_url
};
if (global_obj.APM_SNI) {
apmconns[global_obj.APM_BM_GATEWAY_URL].APM_SNI = global_obj.APM_SNI;
}
if (global_obj.APM_KEYFILE && global_obj.APM_BM_GATEWAY_URL.toLowerCase().startsWith('https')) {
try {
let buff;
if (fs.existsSync(global_obj.APM_KEYFILE)) {
buff = fs.readFileSync(global_obj.APM_KEYFILE);
} else if (fs.existsSync(__dirname + '/../../etc/' + global_obj.APM_KEYFILE)) {
buff = fs.readFileSync(__dirname + '/../../etc/' + global_obj.APM_KEYFILE);
} else if (fs.existsSync(__dirname + '/../../../ibmapm/etc/' + global_obj.APM_KEYFILE)) {
buff = fs.readFileSync(__dirname + '/../../../ibmapm/etc/' + global_obj.APM_KEYFILE);
} else if (fs.existsSync(__dirname + '/../../../etc/' + global_obj.APM_KEYFILE)) {
buff = fs.readFileSync(__dirname + '/../../../etc/' + global_obj.APM_KEYFILE);
}
apmconns[global_obj.APM_BM_GATEWAY_URL].pfx = buff;
} catch (error) {
logger.error('failed to read keyfile from local: ' + error);
}
}
if (global_obj.APM_KEYFILE_PSWD) {
apmconns[global_obj.APM_BM_GATEWAY_URL].keyfile_password =
Buffer.from(global_obj.APM_KEYFILE_PSWD, 'base64').toString();
}
initialized = true; // ready to send payload
}
}
}
function initFromPluginConfig() {
if (!(pluginConfig.plugins['APM'] && pluginConfig.plugins['APM'].connections)) {
return;
}
var connections = pluginConfig.plugins['APM'].connections;
for (var index = 0; index < connections.length; index++) {
var element = connections[index];
if (apmconns[element.server_url]) {
logger.debug('The server ' + element.server_url +
' already defined. The definition is duplicated in the plugin config file');
continue;
}
apmconns[element.server_url] = {
server_url: element.server_url,
resource_url: element.server_url + resource_url,
tt_url: element.server_url + tt_url,
deepdive_url: element.server_url + deepdive_url,
keyfile: element.keyfile,
keyfile_password: element.keyfile_password,
proxy: element.proxy
};
if (element.keyfile) {
try {
let keybuff = fs.readFileSync(__dirname + '/../../' + element.keyfile);
apmconns[element.server_url].pfx = keybuff;
} catch (error) {
logger.error('failed to read keyfile from local: ' + error);
}
}
if (element.keyfile_url) {
var keyfile_options = url.parse(element.keyfile_url);
rcutil.tlsFix8(keyfile_options);
var sendmethod = (keyfile_options.protocol === 'http:' ? http : https);
var info = apmconns[element.server_url];
var req = sendmethod.request(keyfile_options, function(res) {
res.on('data', function(d) {
if (!info.pfx) {
info.pfx = d;
} else {
info.pfx = Buffer.concat([info.pfx, d], info.pfx.length + d.length);
}
info.done = true;
});
res.on('error', function(error) {
logger.error('JsonSender response error: ' + error);
});
});
req.on('error', function(error) {
logger.error('JsonSender request error: ' + error);
});
req.end();
}
initialized = true; // ready to send payload
}
}
function init() {
rcutil.init();
app_guid = rcutil.getAppGuid();
initFromGlobalEnv();
initFromEnv();
initFromPluginConfig();
}
function isBase64Url(httpurl) {
if (!isURL(httpurl)) {
return true;
}
return false;
}
function isURL(turl) {
return turl.toLowerCase().startsWith('http');
}
function getMSN(element) {
logger.debug('getMSN ' + element.server_url);
var BM_info = {};
var urlMap = url.parse(element.server_url);
var isHttp = urlMap.protocol === 'http:';
var BM_options = {
hostname: urlMap['hostname'],
host: urlMap['host'],
port: urlMap['port'] ? urlMap['port'] : BM_info.port,
path: urlMap['path'] + 'OEReceiver/getMSNs/' + app_guid,
pfx: element.pfx,
passphrase: element.keyfile_password,
method: 'GET',
rejectUnauthorized: false,
agent: false,
protocol: urlMap.protocol
};
var sendMethod = isHttp ? http : https;
if (!isHttp) {
BM_options.servername = process.env.MONITORING_SERVER_NAME ||
element.APM_SNI || 'default.server';
}
rcutil.tlsFix8(BM_options);
var req = sendMethod.request(BM_options, function(res) {
res.on('data', function(d) {
try {
element.MSN = JSON.parse(d.toString())['MSNs'][0];
logger.debug('The MSN is ' + element.MSN +
', for the server ' + element.server_url);
} catch (e) {
logger.error('error', 'getMSNs cannot be convert to json');
}
});
});
req.on('error', function(e) {
logger.error('error', 'Failed to get MSN from OEReceiver.' + e);
logger.error(e);
});
req.end();
}