@stoplight/moleculer
Version:
Fast & powerful microservices framework for Node.JS
633 lines (580 loc) • 17.4 kB
JavaScript
/*
* moleculer
* Copyright (c) 2019 MoleculerJS (https://github.com/moleculerjs/moleculer)
* MIT Licensed
*/
"use strict";
const os = require("os");
const METRIC = require("./constants");
const cpuUsage = require("../cpu-usage");
let v8, eventLoop;
// Load `v8` module for heap metrics.
try {
v8 = require("v8");
} catch (e) {
// silent
}
/**
* Register common OS, process & Moleculer metrics.
*/
function registerCommonMetrics() {
this.logger.debug("Registering common metrics...");
// --- METRICS SELF METRICS ---
// this.register({ name: METRIC.MOLECULER_METRICS_COMMON_COLLECT_TOTAL, type: METRIC.TYPE_COUNTER, description: "Number of metric collections" });
// this.register({ name: METRIC.MOLECULER_METRICS_COMMON_COLLECT_TIME, type: METRIC.TYPE_GAUGE, description: "Time of collecting metrics", unit: METRIC.UNIT_MILLISECONDS });
// --- PROCESS METRICS ---
const item = this.register({
name: METRIC.PROCESS_ARGUMENTS,
type: METRIC.TYPE_INFO,
labelNames: ["index"],
description: "Process arguments"
});
process.argv.map((arg, index) => item.set(arg, { index }));
this.register({
name: METRIC.PROCESS_PID,
type: METRIC.TYPE_INFO,
description: "Process PID"
}).set(process.pid);
this.register({
name: METRIC.PROCESS_PPID,
type: METRIC.TYPE_INFO,
description: "Process parent PID"
}).set(process.ppid);
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SIZE_USED,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process used heap size"
});
this.register({
name: METRIC.PROCESS_MEMORY_RSS,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process RSS size"
});
this.register({
name: METRIC.PROCESS_MEMORY_EXTERNAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process external memory size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
labelNames: ["space"],
unit: METRIC.UNIT_BYTE,
description: "Process total heap space size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_USED,
type: METRIC.TYPE_GAUGE,
labelNames: ["space"],
unit: METRIC.UNIT_BYTE,
description: "Process used heap space size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_AVAILABLE,
type: METRIC.TYPE_GAUGE,
labelNames: ["space"],
unit: METRIC.UNIT_BYTE,
description: "Process available heap space size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_PHYSICAL,
type: METRIC.TYPE_GAUGE,
labelNames: ["space"],
unit: METRIC.UNIT_BYTE,
description: "Process physical heap space size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_HEAP_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_EXECUTABLE_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat executable size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_PHYSICAL_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat physical size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_AVAILABLE_SIZE_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat available size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_USED_HEAP_SIZE,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat used size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_HEAP_SIZE_LIMIT,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat size limit"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_MALLOCATED_MEMORY,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Process heap stat mallocated size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_PEAK_MALLOCATED_MEMORY,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "Peak of process heap stat mallocated size"
});
this.register({
name: METRIC.PROCESS_MEMORY_HEAP_STAT_ZAP_GARBAGE,
type: METRIC.TYPE_GAUGE,
description: "Process heap stat zap garbage"
});
this.register({
name: METRIC.PROCESS_UPTIME,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_SECONDS,
description: "Process uptime"
});
this.register({
name: METRIC.PROCESS_INTERNAL_ACTIVE_HANDLES,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_HANDLE,
description: "Number of active process handlers"
});
this.register({
name: METRIC.PROCESS_INTERNAL_ACTIVE_REQUESTS,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_REQUEST,
description: "Number of active process requests"
});
this.register({
name: METRIC.PROCESS_VERSIONS_NODE,
type: METRIC.TYPE_INFO,
description: "Node version"
}).set(process.versions.node);
// --- OS METRICS ---
this.register({
name: METRIC.OS_MEMORY_FREE,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "OS free memory size"
});
this.register({
name: METRIC.OS_MEMORY_USED,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "OS used memory size"
});
this.register({
name: METRIC.OS_MEMORY_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_BYTE,
description: "OS total memory size"
});
this.register({
name: METRIC.OS_UPTIME,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_SECONDS,
description: "OS uptime"
});
this.register({ name: METRIC.OS_TYPE, type: METRIC.TYPE_INFO, description: "OS type" }).set(
os.type()
);
this.register({
name: METRIC.OS_RELEASE,
type: METRIC.TYPE_INFO,
description: "OS release"
}).set(os.release());
this.register({
name: METRIC.OS_HOSTNAME,
type: METRIC.TYPE_INFO,
description: "Hostname"
}).set(os.hostname());
this.register({
name: METRIC.OS_ARCH,
type: METRIC.TYPE_INFO,
description: "OS architecture"
}).set(os.arch());
this.register({
name: METRIC.OS_PLATFORM,
type: METRIC.TYPE_INFO,
description: "OS platform"
}).set(os.platform());
const userInfo = getUserInfo();
this.register({ name: METRIC.OS_USER_UID, type: METRIC.TYPE_INFO, description: "UID" }).set(
userInfo.uid
);
this.register({ name: METRIC.OS_USER_GID, type: METRIC.TYPE_INFO, description: "GID" }).set(
userInfo.gid
);
this.register({
name: METRIC.OS_USER_USERNAME,
type: METRIC.TYPE_INFO,
description: "Username"
}).set(userInfo.username);
this.register({
name: METRIC.OS_USER_HOMEDIR,
type: METRIC.TYPE_INFO,
description: "User's home directory"
}).set(userInfo.homedir);
this.register({
name: METRIC.OS_NETWORK_ADDRESS,
type: METRIC.TYPE_INFO,
labelNames: ["interface", "family"],
description: "Network address"
});
this.register({
name: METRIC.OS_NETWORK_MAC,
type: METRIC.TYPE_INFO,
labelNames: ["interface", "family"],
description: "MAC address"
});
this.register({
name: METRIC.OS_DATETIME_UNIX,
type: METRIC.TYPE_GAUGE,
description: "Current datetime in Unix format"
});
this.register({
name: METRIC.OS_DATETIME_ISO,
type: METRIC.TYPE_INFO,
description: "Current datetime in ISO string"
});
this.register({
name: METRIC.OS_DATETIME_UTC,
type: METRIC.TYPE_INFO,
description: "Current UTC datetime"
});
this.register({
name: METRIC.OS_DATETIME_TZ_OFFSET,
type: METRIC.TYPE_GAUGE,
description: "Timezone offset"
});
this.register({
name: METRIC.OS_CPU_LOAD_1,
type: METRIC.TYPE_GAUGE,
description: "CPU load1"
});
this.register({
name: METRIC.OS_CPU_LOAD_5,
type: METRIC.TYPE_GAUGE,
description: "CPU load5"
});
this.register({
name: METRIC.OS_CPU_LOAD_15,
type: METRIC.TYPE_GAUGE,
description: "CPU load15"
});
this.register({
name: METRIC.OS_CPU_UTILIZATION,
type: METRIC.TYPE_GAUGE,
description: "CPU utilization"
});
this.register({
name: METRIC.OS_CPU_USER,
type: METRIC.TYPE_GAUGE,
description: "CPU user time"
});
this.register({
name: METRIC.OS_CPU_SYSTEM,
type: METRIC.TYPE_GAUGE,
description: "CPU system time"
});
this.register({
name: METRIC.OS_CPU_TOTAL,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_CPU,
description: "Number of CPUs"
});
this.register({
name: METRIC.OS_CPU_INFO_MODEL,
type: METRIC.TYPE_INFO,
labelNames: ["index"],
description: "CPU model"
});
this.register({
name: METRIC.OS_CPU_INFO_SPEED,
type: METRIC.TYPE_GAUGE,
labelNames: ["index"],
unit: METRIC.UNIT_GHZ,
description: "CPU speed"
});
this.register({
name: METRIC.OS_CPU_INFO_TIMES_USER,
type: METRIC.TYPE_GAUGE,
labelNames: ["index"],
description: "CPU user time"
});
this.register({
name: METRIC.OS_CPU_INFO_TIMES_SYS,
type: METRIC.TYPE_GAUGE,
labelNames: ["index"],
description: "CPU system time"
});
startGCWatcher.call(this);
startEventLoopStats.call(this);
this.logger.debug(`Registered ${this.store.size} common metrics.`);
}
/**
* Start GC watcher listener.
*/
function startGCWatcher() {
// Load `gc-stats` module for GC metrics.
try {
const gc = require("gc-stats")();
/* istanbul ignore next */
if (gc) {
// --- GARBAGE COLLECTOR METRICS ---
this.register({
name: METRIC.PROCESS_GC_TIME,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_NANOSECONDS,
description: "GC time"
});
this.register({
name: METRIC.PROCESS_GC_TOTAL_TIME,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_MILLISECONDS,
description: "Total time of GC"
});
this.register({
name: METRIC.PROCESS_GC_EXECUTED_TOTAL,
type: METRIC.TYPE_GAUGE,
labelNames: ["type"],
unit: null,
description: "Number of executed GC"
});
gc.on("stats", stats => {
this.set(METRIC.PROCESS_GC_TIME, stats.pause);
this.increment(METRIC.PROCESS_GC_TOTAL_TIME, null, stats.pause / 1e6);
if (stats.gctype == 1)
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "scavenge" });
if (stats.gctype == 2)
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "marksweep" });
if (stats.gctype == 4)
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "incremental" });
if (stats.gctype == 8)
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "weakphantom" });
if (stats.gctype == 15) {
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "scavenge" });
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "marksweep" });
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "incremental" });
this.increment(METRIC.PROCESS_GC_EXECUTED_TOTAL, { type: "weakphantom" });
}
});
}
} catch (e) {
// silent
}
}
function startEventLoopStats() {
// Load `event-loop-stats` metric for Event-loop metrics.
try {
eventLoop = require("event-loop-stats");
if (eventLoop) {
this.register({
name: METRIC.PROCESS_EVENTLOOP_LAG_MIN,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_MILLISECONDS,
description: "Minimum of event loop lag"
});
this.register({
name: METRIC.PROCESS_EVENTLOOP_LAG_AVG,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_MILLISECONDS,
description: "Average of event loop lag"
});
this.register({
name: METRIC.PROCESS_EVENTLOOP_LAG_MAX,
type: METRIC.TYPE_GAUGE,
unit: METRIC.UNIT_MILLISECONDS,
description: "Maximum of event loop lag"
});
this.register({
name: METRIC.PROCESS_EVENTLOOP_LAG_COUNT,
type: METRIC.TYPE_GAUGE,
description: "Number of event loop lag samples."
});
}
} catch (e) {
// silent
}
}
/**
* Update common metric values.
*
* @returns {Promise}
*/
function updateCommonMetrics() {
this.logger.debug("Update common metric values...");
const end = this.timer();
// --- PROCESS METRICS ---
const procMem = process.memoryUsage();
this.set(METRIC.PROCESS_MEMORY_HEAP_SIZE_TOTAL, procMem.heapTotal);
this.set(METRIC.PROCESS_MEMORY_HEAP_SIZE_USED, procMem.heapUsed);
this.set(METRIC.PROCESS_MEMORY_RSS, procMem.rss);
this.set(METRIC.PROCESS_MEMORY_EXTERNAL, procMem.external);
if (v8 && v8.getHeapSpaceStatistics) {
const stat = v8.getHeapSpaceStatistics();
stat.forEach(item => {
const space = item.space_name;
this.set(METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_TOTAL, item.space_size, { space });
this.set(METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_USED, item.space_used_size, { space });
this.set(METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_AVAILABLE, item.space_available_size, {
space
});
this.set(METRIC.PROCESS_MEMORY_HEAP_SPACE_SIZE_PHYSICAL, item.physical_space_size, {
space
});
});
}
if (v8 && v8.getHeapStatistics) {
const stat = v8.getHeapStatistics();
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_HEAP_SIZE_TOTAL, stat.total_heap_size);
this.set(
METRIC.PROCESS_MEMORY_HEAP_STAT_EXECUTABLE_SIZE_TOTAL,
stat.total_heap_size_executable
);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_PHYSICAL_SIZE_TOTAL, stat.total_physical_size);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_AVAILABLE_SIZE_TOTAL, stat.total_available_size);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_USED_HEAP_SIZE, stat.used_heap_size);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_HEAP_SIZE_LIMIT, stat.heap_size_limit);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_MALLOCATED_MEMORY, stat.malloced_memory);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_PEAK_MALLOCATED_MEMORY, stat.peak_malloced_memory);
this.set(METRIC.PROCESS_MEMORY_HEAP_STAT_ZAP_GARBAGE, stat.does_zap_garbage);
}
this.set(METRIC.PROCESS_UPTIME, process.uptime());
this.set(METRIC.PROCESS_INTERNAL_ACTIVE_HANDLES, process._getActiveHandles().length);
this.set(METRIC.PROCESS_INTERNAL_ACTIVE_REQUESTS, process._getActiveRequests().length);
// --- OS METRICS ---
const freeMem = os.freemem();
const totalMem = os.totalmem();
const usedMem = totalMem - freeMem;
this.set(METRIC.OS_MEMORY_FREE, freeMem);
this.set(METRIC.OS_MEMORY_USED, usedMem);
this.set(METRIC.OS_MEMORY_TOTAL, totalMem);
this.set(METRIC.OS_UPTIME, os.uptime());
this.set(METRIC.OS_TYPE, os.type());
this.set(METRIC.OS_RELEASE, os.release());
this.set(METRIC.OS_HOSTNAME, os.hostname());
this.set(METRIC.OS_ARCH, os.arch());
this.set(METRIC.OS_PLATFORM, os.platform());
// --- NETWORK INTERFACES ---
const getNetworkInterfaces = () => {
const list = [];
const ilist = [];
const interfaces = os.networkInterfaces();
for (let iface in interfaces) {
for (let i in interfaces[iface]) {
const f = interfaces[iface][i];
if (f.internal) {
ilist.push({ f, iface });
} else {
list.push({ f, iface });
}
}
}
return list.length > 0 ? list : ilist;
};
const interfaces = getNetworkInterfaces();
for (let { f, iface } of interfaces) {
this.set(METRIC.OS_NETWORK_ADDRESS, f.address, { interface: iface, family: f.family });
this.set(METRIC.OS_NETWORK_MAC, f.mac, { interface: iface, family: f.family });
}
const d = new Date();
this.set(METRIC.OS_DATETIME_UNIX, d.valueOf());
this.set(METRIC.OS_DATETIME_ISO, d.toISOString());
this.set(METRIC.OS_DATETIME_UTC, d.toUTCString());
this.set(METRIC.OS_DATETIME_TZ_OFFSET, d.getTimezoneOffset());
const load = os.loadavg();
this.set(METRIC.OS_CPU_LOAD_1, load[0]);
this.set(METRIC.OS_CPU_LOAD_5, load[1]);
this.set(METRIC.OS_CPU_LOAD_15, load[2]);
if (eventLoop && eventLoop.sense) {
const stat = eventLoop.sense();
this.set(METRIC.PROCESS_EVENTLOOP_LAG_MIN, stat.min);
this.set(METRIC.PROCESS_EVENTLOOP_LAG_AVG, stat.num ? stat.sum / stat.num : 0);
this.set(METRIC.PROCESS_EVENTLOOP_LAG_MAX, stat.max);
this.set(METRIC.PROCESS_EVENTLOOP_LAG_COUNT, stat.num);
}
// this.increment(METRIC.MOLECULER_METRICS_COMMON_COLLECT_TOTAL);
const duration = end();
return this.broker.Promise.resolve()
.then(() =>
cpuUsage().then(res => {
this.set(METRIC.OS_CPU_UTILIZATION, res.avg);
try {
const cpus = os.cpus();
this.set(METRIC.OS_CPU_TOTAL, cpus.length);
this.set(
METRIC.OS_CPU_USER,
cpus.reduce((a, b) => a + b.times.user, 0)
);
this.set(
METRIC.OS_CPU_SYSTEM,
cpus.reduce((a, b) => a + b.times.sys, 0)
);
cpus.forEach((cpu, index) => {
this.set(METRIC.OS_CPU_INFO_MODEL, cpu.model, { index });
this.set(METRIC.OS_CPU_INFO_SPEED, cpu.speed, { index });
this.set(METRIC.OS_CPU_INFO_TIMES_USER, cpu.times.user, { index });
this.set(METRIC.OS_CPU_INFO_TIMES_SYS, cpu.times.sys, { index });
});
} catch (err) {
// silent
}
})
)
.catch(() => {
// silent this.logger.warn("Unable to collect CPU usage metrics.", err);
})
.then(() => {
this.logger.debug(`Collected common metric values in ${duration.toFixed(3)} msec.`);
});
}
/**
* Get OS user info (safe-mode)
*
* @returns
*/
function getUserInfo() {
try {
return os.userInfo();
} catch (e) {
/* istanbul ignore next */
return {};
}
}
/**
* Measure event loop lag.
*
* @returns {Promise<Number>}
*
function measureEventLoopLag() {
return new Promise(resolve => {
const start = process.hrtime();
setImmediate(() => {
const delta = process.hrtime(start);
resolve(delta[0] * 1e9 + delta[1]);
});
});
}*/
module.exports = {
registerCommonMetrics,
updateCommonMetrics
};