feat: Add 'local service' monitor type

This adds a new monitor type to check local services by executing a shell command. It also includes fixes for Prometheus errors when adding new tags and for the UI not updating when tags are changed.
This commit is contained in:
iotux
2025-12-14 16:39:00 +01:00
parent 883083f5c3
commit 7461bd296f
8 changed files with 225 additions and 39 deletions
+62
View File
@@ -0,0 +1,62 @@
const { MonitorType } = require("./monitor-type");
const { exec } = require("child_process");
const { DOWN, UP, log, evaluateJsonQuery } = require("../../src/util");
class LocalServiceMonitorType extends MonitorType {
name = "local-service";
description = "Checks if a local service is running by executing a command.";
async check(monitor, heartbeat, server) {
return new Promise((resolve, reject) => {
exec(monitor.local_service_command, async (error, stdout, stderr) => {
if (error) {
heartbeat.status = DOWN;
heartbeat.msg = `Error executing command: ${error.message}`;
reject(new Error(heartbeat.msg));
return;
}
const output = stdout.trim();
if (monitor.local_service_check_type === "keyword") {
if (monitor.local_service_expected_output) {
if (output.includes(monitor.local_service_expected_output)) {
heartbeat.status = UP;
heartbeat.msg = `OK - Output contains "${monitor.local_service_expected_output}"`;
resolve();
} else {
heartbeat.status = DOWN;
heartbeat.msg = `Output did not contain "${monitor.local_service_expected_output}"`;
reject(new Error(heartbeat.msg));
}
} else {
heartbeat.status = UP;
heartbeat.msg = "OK - Command executed successfully";
resolve();
}
} else if (monitor.local_service_check_type === "json-query") {
try {
const data = JSON.parse(output);
const { status, response } = await evaluateJsonQuery(data, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
if (status) {
heartbeat.status = UP;
heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`;
resolve();
} else {
throw new Error(`JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`);
}
} catch (e) {
heartbeat.status = DOWN;
heartbeat.msg = e.message;
reject(e);
}
}
});
});
}
}
module.exports = {
LocalServiceMonitorType,
};