UNPKG

ibmapm-restclient

Version:
692 lines (594 loc) 20.9 kB
'use strict'; 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(); }