metrics: add stream api for system info

This commit is contained in:
Girish Ramakrishnan
2025-05-21 17:15:04 +02:00
parent 7e3162d287
commit c0f0084e56
6 changed files with 83 additions and 27 deletions

View File

@@ -150,8 +150,7 @@ exports = module.exports = {
_clear: clear
};
const appstore = require('./appstore.js'),
appTaskManager = require('./apptaskmanager.js'),
const appTaskManager = require('./apptaskmanager.js'),
archives = require('./archives.js'),
assert = require('assert'),
backups = require('./backups.js'),

View File

@@ -2,6 +2,8 @@
exports = module.exports = {
getSystem,
getSystemStream,
getContainers,
sendToGraphite
@@ -16,6 +18,7 @@ const apps = require('./apps.js'),
execSync = require('child_process').execSync,
net = require('net'),
os = require('os'),
{ Readable } = require('stream'),
safe = require('safetydance'),
services = require('./services.js'),
superagent = require('./superagent.js');
@@ -226,7 +229,7 @@ async function getContainers(name, options) {
};
}
async function getSystemStats(options) {
async function readSystemFromGraphite(options) {
assert.strictEqual(typeof options, 'object');
const { fromSecs, intervalSecs, noNullPoints } = options;
@@ -269,7 +272,7 @@ async function getSystemStats(options) {
async function getSystem(options) {
assert.strictEqual(typeof options, 'object');
const systemStats = await getSystemStats(options);
const systemStats = await readSystemFromGraphite(options);
const appStats = {};
for (const app of await apps.list()) {
@@ -289,3 +292,32 @@ async function getSystem(options) {
cpuCount: os.cpus().length
};
}
async function getSystemStream() {
const INTERVAL_SECS = 2;
let intervalId = null, oldCpuMetrics = null;
const metricsStream = new Readable({
read(/*size*/) { /* ignored, we push via interval */ },
destroy(error, callback) {
clearInterval(intervalId);
callback(error);
}
});
intervalId = setInterval(async () => {
const memoryMetrics = await getMemoryMetrics();
const cpuMetrics = await getCpuMetrics();
const cpuPercent = oldCpuMetrics ? (cpuMetrics.userMsecs + cpuMetrics.sysMsecs - oldCpuMetrics.userMsecs - oldCpuMetrics.sysMsecs) * 0.1 / INTERVAL_SECS : null;
oldCpuMetrics = cpuMetrics;
const now = Date.now();
metricsStream.push(JSON.stringify({
cpu: [ cpuPercent, now ],
memory: [ memoryMetrics.used, now ]
}));
}, INTERVAL_SECS*1000);
return metricsStream;
}

View File

@@ -9,6 +9,7 @@ exports = module.exports = {
getLogs,
getLogStream,
getMetrics,
getMetricStream,
getBlockDevices,
getCpus,
};
@@ -130,6 +131,30 @@ async function getMetrics(req, res, next) {
next(new HttpSuccess(200, result));
}
async function getMetricStream(req, res, next) {
if (req.headers.accept !== 'text/event-stream') return next(new HttpError(400, 'This API call requires EventStream'));
const [error, metricStream] = await safe(metrics.getSystemStream());
if (error) return next(BoxError.toHttpError(error));
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no', // disable nginx buffering
'Access-Control-Allow-Origin': '*'
});
res.write('retry: 3000\n');
res.on('close', () => metricStream.destroy());
metricStream.on('data', function (data) {
const obj = JSON.parse(data);
const sse = `data: ${JSON.stringify(obj)}\n\n`;
res.write(sse);
});
metricStream.on('end', res.end.bind(res));
metricStream.on('error', res.end.bind(res, null));
}
async function getBlockDevices(req, res, next) {
const [error, devices] = await safe(system.getBlockDevices());
if (error) return next(new HttpError(500, error));

View File

@@ -114,6 +114,7 @@ async function initializeExpressSync() {
router.get ('/api/v1/system/info', token, authorizeAdmin, routes.system.getInfo); // vendor, product name etc
router.post('/api/v1/system/reboot', json, token, authorizeAdmin, routes.system.reboot);
router.get ('/api/v1/system/metrics', token, authorizeAdmin, routes.system.getMetrics);
router.get ('/api/v1/system/metricstream', token, authorizeAdmin, routes.system.getMetricStream);
router.get ('/api/v1/system/disk_usage', token, authorizeAdmin, routes.system.getDiskUsage);
router.post('/api/v1/system/disk_usage', token, authorizeAdmin, routes.system.updateDiskUsage);
router.get ('/api/v1/system/block_devices', token, authorizeAdmin, routes.system.getBlockDevices);