commit f19370a8028e4d8ca1682864b2e7f512b75ab7bb Author: WeeXnes Date: Mon Jun 16 01:11:24 2025 +0200 initial commit diff --git a/package.json b/package.json new file mode 100644 index 0000000..e756b41 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..20c92eb --- /dev/null +++ b/src/config.ts @@ -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() to set the url endpoint.'); + process.exit(1); + } +} + diff --git a/src/cpu.ts b/src/cpu.ts new file mode 100644 index 0000000..47ac45d --- /dev/null +++ b/src/cpu.ts @@ -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} The number of logical CPUs, or null if unavailable. + */ + static async getLogicalCpuCount(): Promise { + 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} CPU load percentage (0–100), or null if unavailable. + */ + static async getCpuLoad(): Promise { + 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} The average CPU temperature in °C, or null if unavailable. + */ + static async getAverageTemp(): Promise { + 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} The average minimum CPU clock in GHz, or null if unavailable. + */ + static async getMinCpuClockGHz(): Promise { + 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} The average maximum CPU clock in GHz, or null if unavailable. + */ + static async getMaxCpuClockGHz(): Promise { + 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} The average current CPU clock in GHz, or null if unavailable. + */ + static async getCurrentCpuClockGHz(): Promise { + 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; + } + } + + + +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..617dfc9 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +export { System } from './system' +export { CPU } from './cpu' +export { RAM } from './ram' +export { setUrlEndpoint } from './config'; \ No newline at end of file diff --git a/src/ram.ts b/src/ram.ts new file mode 100644 index 0000000..4534be0 --- /dev/null +++ b/src/ram.ts @@ -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} Total memory in GB, or null if unavailable. + */ + static async getTotalMemory(): Promise { + 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} Used memory in GB, or null if unavailable. + */ + static async getUsedMemory(): Promise { + 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; + } + } + + + +} diff --git a/src/system.ts b/src/system.ts new file mode 100644 index 0000000..e21a4d4 --- /dev/null +++ b/src/system.ts @@ -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} OS name (e.g., "Linux", "Windows_NT") or null if unavailable. + */ + static async getOsName(): Promise { + 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} Kernel version string or null if unavailable. + */ + static async getKernelVersion(): Promise { + 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} Kernel build version string or null if unavailable. + */ + static async getKernelBuildVersion(): Promise { + 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} Machine architecture (e.g., "x86_64") or null if unavailable. + */ + static async getMachineArchitecture(): Promise { + 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} Uptime in seconds, or null if unavailable. + */ + static async getUptimeSeconds(): Promise { + 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; + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..df5c8a6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true + }, + "include": ["src"] +} \ No newline at end of file