initial commit
This commit is contained in:
commit
f19370a802
7 changed files with 430 additions and 0 deletions
25
package.json
Normal file
25
package.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "prometheus-node-exporter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": ["dist"],
|
||||||
|
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/dev.js",
|
||||||
|
"dev": "ts-node src/dev.ts"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.1",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
16
src/config.ts
Normal file
16
src/config.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export let urlEndpoint: string;
|
||||||
|
export let urlPath: string = "/api/v1/query";
|
||||||
|
|
||||||
|
|
||||||
|
export function setUrlEndpoint(url: string) {
|
||||||
|
urlEndpoint = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function checkEndpoint() {
|
||||||
|
if(!urlEndpoint){
|
||||||
|
console.log('URL endpoint isnt set, use setUrlEndpoint(<url>) to set the url endpoint.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
178
src/cpu.ts
Normal file
178
src/cpu.ts
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import {checkEndpoint, urlEndpoint, urlPath} from "./config";
|
||||||
|
|
||||||
|
|
||||||
|
export class CPU {
|
||||||
|
/**
|
||||||
|
* Retrieves the number of logical CPU cores on the system.
|
||||||
|
*
|
||||||
|
* Uses the `node_cpu_seconds_total` metric grouped by `cpu` label.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} The number of logical CPUs, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getLogicalCpuCount(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const query = 'count(count by (cpu) (node_cpu_seconds_total))';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, { params: { query } });
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (!result || result.length === 0) return null;
|
||||||
|
return parseInt(result[0].value[1], 10);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch logical CPU count:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the average CPU load (usage) as a percentage over a 1-minute rate window.
|
||||||
|
*
|
||||||
|
* Uses the `node_cpu_seconds_total` metric, subtracting idle time.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} CPU load percentage (0–100), or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getCpuLoad(): Promise<number | null> {
|
||||||
|
checkEndpoint()
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: {
|
||||||
|
query: `100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[1m])) * 100)`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (result.length === 0) return null;
|
||||||
|
const load = parseFloat(result[0].value[1]);
|
||||||
|
return load;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error querying Prometheus:', (err as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the average CPU temperature in Celsius.
|
||||||
|
*
|
||||||
|
* Uses the `node_hwmon_temp_celsius` metric.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} The average CPU temperature in °C, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getAverageTemp(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const promQuery = 'avg(node_hwmon_temp_celsius)';
|
||||||
|
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: { query: promQuery }
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No CPU temperature data found in Prometheus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const avgTempStr = result[0].value[1];
|
||||||
|
const avgTemp = parseFloat(avgTempStr);
|
||||||
|
|
||||||
|
if (isNaN(avgTemp)) return null;
|
||||||
|
|
||||||
|
return parseFloat(avgTemp.toFixed(1));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch CPU temperatures from Prometheus:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the average minimum CPU clock speed in GHz.
|
||||||
|
*
|
||||||
|
* Uses the `node_cpu_scaling_frequency_min_hertz` metric.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} The average minimum CPU clock in GHz, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getMinCpuClockGHz(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const query = 'avg(node_cpu_scaling_frequency_min_hertz)';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: { query }
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No min CPU frequency data found in Prometheus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const freqGHz = parseFloat(result[0].value[1]) / 1_000_000_000;
|
||||||
|
return parseFloat(freqGHz.toFixed(2));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch min CPU frequency from Prometheus:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves the average maximum CPU clock speed in GHz.
|
||||||
|
*
|
||||||
|
* Uses the `node_cpu_scaling_frequency_max_hertz` metric.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} The average maximum CPU clock in GHz, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getMaxCpuClockGHz(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const query = 'avg(node_cpu_scaling_frequency_max_hertz)';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: { query }
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No max CPU frequency data found in Prometheus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const freqGHz = parseFloat(result[0].value[1]) / 1_000_000_000;
|
||||||
|
return parseFloat(freqGHz.toFixed(2));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch max CPU frequency from Prometheus:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the average current CPU clock speed in GHz.
|
||||||
|
*
|
||||||
|
* Uses the `node_cpu_scaling_frequency_hertz` metric.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} The average current CPU clock in GHz, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getCurrentCpuClockGHz(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const query = 'avg(node_cpu_scaling_frequency_hertz)';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: { query }
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No current CPU frequency data found in Prometheus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const freqGHz = parseFloat(result[0].value[1]) / 1_000_000_000;
|
||||||
|
return parseFloat(freqGHz.toFixed(2));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch current CPU frequency from Prometheus:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
4
src/index.ts
Normal file
4
src/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export { System } from './system'
|
||||||
|
export { CPU } from './cpu'
|
||||||
|
export { RAM } from './ram'
|
||||||
|
export { setUrlEndpoint } from './config';
|
71
src/ram.ts
Normal file
71
src/ram.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import {urlEndpoint, checkEndpoint, urlPath} from './config';
|
||||||
|
|
||||||
|
export class RAM {
|
||||||
|
/**
|
||||||
|
* Retrieves the total system memory in gigabytes.
|
||||||
|
*
|
||||||
|
* Queries Prometheus metric `node_memory_MemTotal_bytes` and converts bytes to GB.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} Total memory in GB, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getTotalMemory(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fullUrl = `${urlEndpoint}${urlPath}`;
|
||||||
|
const query = 'node_memory_MemTotal_bytes';
|
||||||
|
|
||||||
|
const response = await axios.get(fullUrl, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No data for node_memory_MemTotal_bytes');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalBytes = parseFloat(result[0].value[1]);
|
||||||
|
if (isNaN(totalBytes)) return null;
|
||||||
|
|
||||||
|
return totalBytes / (1024 ** 3);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching total memory from Prometheus:', (err as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves the used system memory in gigabytes.
|
||||||
|
*
|
||||||
|
* Calculates used memory by subtracting `node_memory_MemAvailable_bytes`
|
||||||
|
* from `node_memory_MemTotal_bytes` and converts bytes to GB.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} Used memory in GB, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getUsedMemory(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fullUrl = `${urlEndpoint}/api/v1/query`;
|
||||||
|
const query = 'node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes';
|
||||||
|
|
||||||
|
const response = await axios.get(fullUrl, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No data for used memory query');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedBytes = parseFloat(result[0].value[1]);
|
||||||
|
if (isNaN(usedBytes)) return null;
|
||||||
|
|
||||||
|
return usedBytes / (1024 ** 3);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching used memory from Prometheus:', (err as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
124
src/system.ts
Normal file
124
src/system.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { checkEndpoint, urlEndpoint, urlPath } from './config';
|
||||||
|
|
||||||
|
export class System {
|
||||||
|
/**
|
||||||
|
* Fetches the OS name (sysname) from node_exporter metrics.
|
||||||
|
*
|
||||||
|
* @returns {Promise<string | null>} OS name (e.g., "Linux", "Windows_NT") or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getOsName(): Promise<string | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = 'node_uname_info';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) return null;
|
||||||
|
|
||||||
|
return result[0].metric.sysname ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch OS name:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the OS kernel version (release) from node_exporter metrics.
|
||||||
|
*
|
||||||
|
* @returns {Promise<string | null>} Kernel version string or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getKernelVersion(): Promise<string | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = 'node_uname_info';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) return null;
|
||||||
|
|
||||||
|
return result[0].metric.release ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch kernel version:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the OS kernel build version info (version) from node_exporter metrics.
|
||||||
|
*
|
||||||
|
* @returns {Promise<string | null>} Kernel build version string or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getKernelBuildVersion(): Promise<string | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = 'node_uname_info';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) return null;
|
||||||
|
|
||||||
|
return result[0].metric.version ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch kernel build version:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the machine architecture from node_exporter metrics.
|
||||||
|
*
|
||||||
|
* @returns {Promise<string | null>} Machine architecture (e.g., "x86_64") or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getMachineArchitecture(): Promise<string | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = 'node_uname_info';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, { params: { query } });
|
||||||
|
const result = response.data?.data?.result;
|
||||||
|
|
||||||
|
if (!result || result.length === 0) return null;
|
||||||
|
|
||||||
|
return result[0].metric.machine ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch machine architecture:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves the system uptime in seconds.
|
||||||
|
*
|
||||||
|
* Calculated as the difference between
|
||||||
|
* `node_time_seconds` and `node_boot_time_seconds` metrics.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number | null>} Uptime in seconds, or null if unavailable.
|
||||||
|
*/
|
||||||
|
static async getUptimeSeconds(): Promise<number | null> {
|
||||||
|
checkEndpoint();
|
||||||
|
try {
|
||||||
|
const query = 'node_time_seconds - node_boot_time_seconds';
|
||||||
|
const response = await axios.get(`${urlEndpoint}${urlPath}`, {
|
||||||
|
params: { query }
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = response.data.data.result;
|
||||||
|
if (!result || result.length === 0) {
|
||||||
|
console.warn('No uptime data found in Prometheus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uptimeStr = result[0].value[1];
|
||||||
|
const uptime = parseFloat(uptimeStr);
|
||||||
|
if (isNaN(uptime)) return null;
|
||||||
|
|
||||||
|
return uptime;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch system uptime from Prometheus:', (error as Error).message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue