Skip to content

Instantly share code, notes, and snippets.

@renehamburger
Last active April 4, 2020 20:20
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 renehamburger/14448d4a2b3c72aa66cdec0314da546e to your computer and use it in GitHub Desktop.
Save renehamburger/14448d4a2b3c72aa66cdec0314da546e to your computer and use it in GitHub Desktop.
Send user_beancounters metrics to CloudRadar
#!/usr/bin/env node
// How to use:
// 1. Create a custom check on CloudRadar called 'beancounters', which should be updated every 5 minutes.
// 2. Insert the custom check token below.
// 3. Execute this script every 5 minutes with root privileges,
// e.g. by calling `sudo crontab -e` and adding `*/5 * * * * /usr/bin/node /<Path to script>/check-user-beancounters.js`
const axios = require('axios').default;
const { exec } = require('child_process');
const { promisify } = require('util');
// Unfortunately, CloudRadar only allows 12 values, so they need to be hand picked
const REPORTED_METRICS = ['numproc', 'numtcpsock', 'numothersock'];
const REPORTED_FIELDS = ['held', 'maxheld', 'failcnt'];
const WARNING_LEVEL = 0.9;
const url = 'https://hub.cloudradar.io/cct/';
const options = {
headers: {
'X-CustomCheck-Token': '<Enter the custom check token here>'
}
};
/**
* @param {string} cmd
* @return {Promise<string>} as
*/
async function executeShellCommand(cmd) {
const results = await promisify(exec)(cmd);
if (results.stderr) {
throw results.stderr;
}
return results.stdout;
}
async function main() {
const textContent = await executeShellCommand('cat /proc/user_beancounters');
const textContentWithoutUserId = textContent.replace(/\n *uid/, '\n').replace(/\n *\d+:/, '\n');
const rows = textContentWithoutUserId.trim().split('\n');
const header = rows[1].trim().split(/\s+/);
const metrics = rows.slice(2)
.map(row => {
const columns = row.trim().split(/\s+/);
return columns.map((column, index) => index ? parseFloat(column) : column);
})
.filter(columns => columns[0] !== 'dummy');
const failedMetrics = metrics.filter(metric => metric[5] !== 0);
const metricsAboveWarningLevel = metrics.filter(metric => metric[2] >= WARNING_LEVEL * metric[4]);
const payload = {
'beancounters.success': failedMetrics.length ? 0 : 1,
'beancounters.table': `${header}\n${metrics.join('\n')}`
};
if (failedMetrics.length) {
const failedMetricsString = failedMetrics.map(metric => metric[0]).join(', ');
payload['beancounters.alert'] = `Failed metrics: ${failedMetricsString})}`;
} else if (metricsAboveWarningLevel.length) {
const warningString = metricsAboveWarningLevel.map(metric => `${metric[0]}: ${metric[2]} of ${metric[4]}`).join(', ');
payload['beancounters.warning'] = `Metrics closer to failure: ${warningString})}`;
}
metrics.forEach((columns) => {
const metricLabel = columns[0];
columns.slice(1).forEach((value, index) => {
const columnLabel = header[index + 1];
if (REPORTED_METRICS.includes(metricLabel) && REPORTED_FIELDS.includes(columnLabel)) {
payload[`beancounters.${metricLabel}.${columnLabel}`] = value;
}
});
});
await axios.post(url, payload, options);
}
main().catch((err) => {
if (err.response) {
console.error(`CloudRadar request failed: ${err.response.status} - ${err.response.statusText} - ${err.response.data.error}`);
} else {
console.error(err);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment