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