.then((currentInstancesTypes) => { if (toString(currentInstancesTypes) === toString(this.state.lastInstancesTypes)) { return } /* Recreate the main Graph using the latest number of instances */ this.destroyGraph() this.createGraph(currentInstancesTypes) this.setState({ lastInstancesTypes: currentInstancesTypes }) })
/* Records new system service stats to 'responseStats' in a common form based on their response time */ function recordResponseStats (responseString) { const response = utils.JSON_parse(responseString) if (!response || typeof response !== 'object' ) { /* Proper error handling */ log(new TypeError(`1st argument must be JSON {String} but is {${utils.getInstance(responseString)}} ${utils.toString(responseString)}`)) return } /* In order to reduce memory usage, we'll aggregate stats types to the nearest seconds they were respond */ const time = Math.floor(response.timestamp / 1000) /* Combine all services stats in a single stack */ if (!responseStats.all || typeof responseStats.all !== 'object' ) { responseStats.all = {} } if (!responseStats.all[time] || typeof responseStats.all[time] !== 'object' ) { responseStats.all[time] = { total: 0, durations: [] } } responseStats.all[time].total++ responseStats.all[time].durations.push(response.duration) /* Save each service type response stats */ if (!responseStats[response.service.type] || typeof responseStats[response.service.type] !== 'object' ) { responseStats[response.service.type] = {} } if (!responseStats[response.service.type][time] || typeof responseStats[response.service.type][time] !== 'object' ) { responseStats[response.service.type][time] = { total: 0, durations: [] } } responseStats[response.service.type][time].total++ responseStats[response.service.type][time].durations.push(response.duration) }
shavaluator.exec('getResponsesFromTimestamp', [], [timestamp], function onResult (error, responses) { /* TODO: Proper error handler */ if (error) { log(`Unable to execute Redis-Lua script "getResponsesFromTimestamp": ${error instanceof Error ? error.stack : error}`) return } if (!(responses instanceof Array)) { log(`Returned result from Redis-Lua script "getResponsesFromTimestamp" must be an {array} but is {${typeof responses}} ${utils.toString(responses)}`) return } responses.forEach(recordResponseStats) })
;(function validateInput () { if (!instancesTypes || typeof instancesTypes !== 'object' || !Object.keys(instancesTypes).length ) { throw new TypeError(`1st argument "instancesTypes" must be a non-empty {Object} but same is {${getInstance(instancesTypes)}} ${toString(instancesTypes)}`) } Object.keys(instancesTypes) .forEach((type, index) => { const instances = instancesTypes[type] if (!(instances instanceof Array) || !instances.length ) { throw new TypeError(`1st argument "instancesTypes['${type}']" must be a non-empty {Array} but same is {${getInstance(instances)}} ${toString(instances)}`) } instances .forEach((instance, index) => { if (!instance || typeof instance !== 'object' ) { throw new TypeError(`1st argument "instancesTypes['${type}'][${index}]" must be an {Object} but same is {${getInstance(instance)}} ${toString(instance)}`) } if (!instance.name || typeof instance.name !== 'string' ) { throw new TypeError(`1st argument "instancesTypes['${type}'][${index}].name" must be a non-empty {String} but same is {${getInstance(instance.name)}} ${toString(instance.name)}`) } }) }) })()
.catch((error) => log(`Unable to create Grapf from all running Service instances: ${toString(error)}`))
.catch((error) => log(`Unable to sync with the Backend about latest active instance: ${toString(error)}`))
chart.statsUpdaters[type] = function statsUpdater (stats) { if (!stats || typeof stats !== 'object' ) { log(new TypeError(`1st argument "stats" of type "${type}" must be {Object} but same is {${getInstance(stats)}} ${toString(stats)}`)) return } /* Find the exact node that this stats relays to */ that.state.chart.graph.nodes() .some((node) => { if (node.id !== typeRootId) { return } let durationAverage = (stats.duration || {}).average || 0 durationAverage = Math.min(durationAverage, typeNodeMaxDurationAverage) /* As higher the average duration - as more red the node goes */ node.color = getColorFromPercentage(durationAverage / typeNodeMaxDurationAverage * 100) /* As more responses a Service Type resolves - as bigger its size gets */ node.size = Math.min(Math.max(stats.total / 5, typeNodeMinSize), typeNodeMaxSize) chart.refresh() return true }) }
/* Return a HEX color scale where 0 is "green" and 100 is "red" */ function getColorFromPercentage (percentage) { if (typeof percentage !== 'number' || percentage < 0 || percentage > 100 ) { throw new TypeError(`1st argument "percentage" must be a {Number} between [0, 100] (both included) but "percentage" is {${getInstance(percentage)}} ${toString(percentage)}`) } let R = Math.round((255 * percentage) / 100) let G = Math.round((255 * (100 - percentage)) / 100) R = R.toString(16) G = G.toString(16) R = R.length === 1 ? `0${R}` : R G = G.length === 1 ? `0${G}` : G return `#${R}${G}00` }
.forEach((instance, index) => { if (!instance || typeof instance !== 'object' ) { throw new TypeError(`1st argument "instancesTypes['${type}'][${index}]" must be an {Object} but same is {${getInstance(instance)}} ${toString(instance)}`) } if (!instance.name || typeof instance.name !== 'string' ) { throw new TypeError(`1st argument "instancesTypes['${type}'][${index}].name" must be a non-empty {String} but same is {${getInstance(instance.name)}} ${toString(instance.name)}`) } })
barChart.statsUpdater = function statsUpdater (stats) { if (!stats || typeof stats !== 'object' ) { log(new TypeError(`1st argument "stats" of type "${type}" must be {Object} but same is {${getInstace(stats)}} ${toString(stats)}`)) return } barChart.addOneDataItem(stats.total || minValue) barChart.draw() }
.catch((error) => log(`Error: Unable to get all Runing Service Instances: ${toString(error)}`))
lineChart.statsUpdater = function statsUpdater (stats) { if (!stats || typeof stats !== 'object' ) { log(new TypeError(`1st argument "stats" of type "${type}" must be {Object} but same is {${getInstace(stats)}} ${toString(stats)}`)) return } const averageDuasion = (stats.duration || {average: 0}).average || minValue lineChart.addOneDataItem(averageDuasion) lineChart.draw() }