默认指标
Prometheus
推荐了一些默认指标 本身。要收集这些信息,请调用 collectDefaultMetrics
。此外,还包括一些特定于 Node.js
的指标,例如事件循环滞后、活动句柄、GC
和 Node.js
版本。
一、GC 指标
1.1 nodejs_gc_duration_seconds
nodejs_gc_duration_seconds
记录垃圾回收(GC
)的持续时间。
const Histogram = require('../histogram');
let perf_hooks;
try {
// eslint-disable-next-line
perf_hooks = require('perf_hooks');
} catch {
// node version is too old
}
const NODEJS_GC_DURATION_SECONDS = 'nodejs_gc_duration_seconds';
const DEFAULT_GC_DURATION_BUCKETS = [0.001, 0.01, 0.1, 1, 2, 5];
const kinds = [];
if (perf_hooks && perf_hooks.constants) {
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR] = 'major';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR] = 'minor';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL] = 'incremental';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB] = 'weakcb';
}
module.exports = (registry, config = {}) => {
if (!perf_hooks) {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const buckets = config.gcDurationBuckets
? config.gcDurationBuckets
: DEFAULT_GC_DURATION_BUCKETS;
const gcHistogram = new Histogram({
name: namePrefix + NODEJS_GC_DURATION_SECONDS,
help: 'Garbage collection duration by kind, one of major, minor, incremental or weakcb.',
labelNames: ['kind', ...labelNames],
enableExemplars: false,
buckets,
registers: registry ? [registry] : undefined,
});
const obs = new perf_hooks.PerformanceObserver(list => {
const entry = list.getEntries()[0];
// Node < 16 uses entry.kind
// Node >= 16 uses entry.detail.kind
// See: https://nodejs.org/docs/latest-v16.x/api/deprecations.html#deprecations_dep0152_extension_performanceentry_properties
const kind = entry.detail ? kinds[entry.detail.kind] : kinds[entry.kind];
// Convert duration from milliseconds to seconds
gcHistogram.observe(Object.assign({ kind }, labels), entry.duration / 1000);
});
obs.observe({ entryTypes: ['gc'] });
};
module.exports.metricNames = [NODEJS_GC_DURATION_SECONDS];
这段代码实现了一个用于监控 Node.js
垃圾回收(GC
)时长的 Prometheus
指标导出模块。它通过 Histogram
指标类型,收集并导出不同类型垃圾回收操作的时长数据。具体来说,代码监控了四种垃圾回收类型:major
(全量GC)、minor
(增量GC)、incremental
(增量GC)和 weakcb
(弱引用GC)。该模块首先通过 perf_hooks
模块获取垃圾回收的性能数据,并使用 PerformanceObserver
监听垃圾回收事件。通过 Histogram
指标,垃圾回收的时长数据会根据不同的类型(kind
)和额外的标签(labels
)进行分类和收集。时长数据以秒为单位进行采集,并通过 Prometheus
提供的 Histogram
类型进行导出,支持配置自定义的桶(buckets
)以控制时长数据的分布。选择使用 Histogram
是因为垃圾回收时长通常是一个连续的数值,Histogram
可以有效地记录并分析这些数值的分布情况,帮助开发人员监控和优化垃圾回收性能,及时发现潜在的性能瓶颈。
二、CPU 指标
2.1 process_cpu_seconds_total
process_cpu_seconds_total
(总 CPU
时间): 这个指标将 用户 CPU
时间 和 系统 CPU
时间 相加,表示 Node.js
进程在用户态和系统态的总 CPU
使用时间,单位为秒。
const OtelApi = require('@opentelemetry/api');
const Counter = require('../counter');
const PROCESS_CPU_USER_SECONDS = 'process_cpu_user_seconds_total';
const PROCESS_CPU_SYSTEM_SECONDS = 'process_cpu_system_seconds_total';
const PROCESS_CPU_SECONDS = 'process_cpu_seconds_total';
module.exports = (registry, config = {}) => {
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const exemplars = config.enableExemplars ? config.enableExemplars : false;
const labelNames = Object.keys(labels);
let lastCpuUsage = process.cpuUsage();
const cpuUserUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_USER_SECONDS,
help: 'Total user CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.
collect() {
const cpuUsage = process.cpuUsage();
const userUsageMicros = cpuUsage.user - lastCpuUsage.user;
const systemUsageMicros = cpuUsage.system - lastCpuUsage.system;
lastCpuUsage = cpuUsage;
if (this.enableExemplars) {
let exemplarLabels = {};
const currentSpan = OtelApi.trace.getSpan(OtelApi.context.active());
if (currentSpan) {
exemplarLabels = {
traceId: currentSpan.spanContext().traceId,
spanId: currentSpan.spanContext().spanId,
};
}
cpuUserUsageCounter.inc({
labels,
value: userUsageMicros / 1e6,
exemplarLabels,
});
cpuSystemUsageCounter.inc({
labels,
value: systemUsageMicros / 1e6,
exemplarLabels,
});
cpuUsageCounter.inc({
labels,
value: (userUsageMicros + systemUsageMicros) / 1e6,
exemplarLabels,
});
} else {
cpuUserUsageCounter.inc(labels, userUsageMicros / 1e6);
cpuSystemUsageCounter.inc(labels, systemUsageMicros / 1e6);
cpuUsageCounter.inc(
labels,
(userUsageMicros + systemUsageMicros) / 1e6,
);
}
},
});
const cpuSystemUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_SYSTEM_SECONDS,
help: 'Total system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
});
const cpuUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_SECONDS,
help: 'Total user and system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
});
};
module.exports.metricNames = [
PROCESS_CPU_USER_SECONDS,
PROCESS_CPU_SYSTEM_SECONDS,
PROCESS_CPU_SECONDS,
];
这段代码使用 Counter
指标类型,定期收集并报告 Node.js
进程的 用户 CPU
时间、系统 CPU
时间 和 总 CPU
时间。通过调用 process.cpuUsage()
获取进程的当前 CPU
使用情况,并计算自上次调用以来的 CPU
使用增量(以微秒为单位)。这些增量值被转换为秒,并通过 Counter
指标更新。为了支持跟踪和诊断,代码还集成了 OpenTelemetry(otel-api)
的功能,当启用了示例(exemplars
)时,能够附带当前的追踪 ID
和 span ID
。这样做的目的是在分布式跟踪中能够关联指标和请求的具体操作,从而更好地进行性能分析和问题定位。
-
process.cpuUsage()
:返回一个对象,表示自进程启动以来的CPU
使用情况,包括user
(用户CPU
时间)和system
(系统CPU
时间),单位是微秒。为了避免重复计算,代码维护了一个lastCpuUsage
状态变量,记录上一次采样时的CPU
使用情况,并通过计算本次采样与上次采样的差值来得到增量。 -
Counter
:作为Prometheus
指标的一种类型,Counter
是用来记录一个不断递增的计数器,适合用来监控累积量(如CPU
使用时间)。每次调用inc()
方法时,Counter
会增加指定的值。在这段代码中,通过inc()
方法增加用户和系统CPU
使用时间的增量。 -
exemplars
:该选项用于启用示例数据(Exemplar
),它是OpenTelemetry
中的一个特性,允许将附加的元数据(如trace ID
和span ID
)与指标数据关联,方便在追踪和监控系统中进行分析和关联。代码通过OtelApi.trace.getSpan()
方法获取当前的trace
和span
信息,并将其附加到每个计数器的更新中。 -
collect()
:该方法在每次指标收集时被调用,它计算当前的CPU
使用增量,并更新相应的指标值。通过定期调用collect()
,能够确保持续跟踪和报告CPU
使用情况。
2.2 process_cpu_user_seconds_total
process_cpu_user_seconds_total
(用户 CPU 时间): 这个指标用于跟踪 Node.js
进程在用户态(用户空间)花费的总 CPU
时间,单位为秒。用户态是指进程执行应用程序代码的时间。
实现代码同 process_cpu_user_seconds_total
2.3 process_cpu_system_seconds_total
process_cpu_system_seconds_total
(系统 CPU 时间): 该指标用于跟踪 Node.js
进程在内核态(操作系统内核空间)花费的总 CPU
时间,单位为秒。系统态时间通常用于处理系统调用(如文件读写、网络请求等)和其他内核任务。
实现代码同 process_cpu_user_seconds_total
三、内存指标
3.1 nodejs_heap_size_used_bytes
nodejs_heap_size_used_bytes
:Node.js
进程堆已使用的内存大小(单位:字节)。
const Gauge = require('../gauge');
const safeMemoryUsage = require('./helpers/safeMemoryUsage');
const NODEJS_HEAP_SIZE_TOTAL = 'nodejs_heap_size_total_bytes';
const NODEJS_HEAP_SIZE_USED = 'nodejs_heap_size_used_bytes';
const NODEJS_EXTERNAL_MEMORY = 'nodejs_external_memory_bytes';
module.exports = (registry, config = {}) => {
if (typeof process.memoryUsage !== 'function') {
return;
}
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const collect = () => {
const memUsage = safeMemoryUsage();
if (memUsage) {
heapSizeTotal.set(labels, memUsage.heapTotal);
heapSizeUsed.set(labels, memUsage.heapUsed);
if (memUsage.external !== undefined) {
externalMemUsed.set(labels, memUsage.external);
}
}
};
const heapSizeTotal = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE_TOTAL,
help: 'Process heap size from Node.js in bytes.',
registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.
collect,
});
const heapSizeUsed = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE_USED,
help: 'Process heap size used from Node.js in bytes.',
registers,
labelNames,
});
const externalMemUsed = new Gauge({
name: namePrefix + NODEJS_EXTERNAL_MEMORY,
help: 'Node.js external memory size in bytes.',
registers,
labelNames,
});
};
module.exports.metricNames = [
NODEJS_HEAP_SIZE_TOTAL,
NODEJS_HEAP_SIZE_USED,
NODEJS_EXTERNAL_MEMORY,
];
这段代码实现了一个用于监控 Node.js
进程内存使用情况的 Prometheus
指标导出模块。它通过 Gauge
指标类型,实现了对以下三个内存指标的收集和导出:
-
nodejs_heap_size_total_bytes
:Node.js
进程堆的总内存大小。 -
nodejs_heap_size_used_bytes
:Node.js
进程堆内存已使用的大小。 -
nodejs_external_memory_bytes
:Node.js
外部内存(例如与C++
插件或外部库相关的内存)的使用量。
具体实现中,首先检查了 process.memoryUsage()
是否可用,用于获取内存使用数据。然后,通过 safeMemoryUsage
函数封装 process.memoryUsage
的调用,确保能够稳定获取内存数据。接着,创建了三个 Gauge
指标,分别用于监控堆总内存、已使用堆内存和外部内存,并通过 collect
方法定期更新这些指标的值。collect
方法会每次调用时通过 set
更新这些 Gauge
指标的具体数值。最后,支持通过传入 config.labels
配置自定义标签,便于根据不同的环境或实例进行区分。
选择使用 Gauge
指标是因为内存使用情况是一个实时变化的值,Gauge
适合表示这样的可以增减的数值。通过这种方式,可以方便地将内存使用情况暴露给 Prometheus
进行监控和报警。
3.2 nodejs_external_memory_bytes
nodejs_external_memory_bytes
:Node.js
外部内存的使用量(单位:字节),例如绑定到 C++
插件或外部库的内存。
代码实现同 nodejs_heap_size_used_bytes
3.3 nodejs_heap_size_total_bytes
nodejs_heap_size_total_bytes
:Node.js
进程堆的总大小(单位:字节)。
代码实现同 nodejs_heap_size_used_bytes
3.4 process_resident_memory_bytes
process_resident_memory_bytes
:此指标表示进程常驻内存的大小(RSS
),单位是字节。常驻内存是进程占用的物理内存的实际大小,包含了所有进程在运行时所需的内存部分(如代码、堆、栈、共享库等)。通过这个指标,可以监控进程的内存使用情况,帮助开发者理解应用的内存消耗,并进行优化或问题排查。
const Gauge = require('../gauge');
const linuxVariant = require('./osMemoryHeapLinux');
const safeMemoryUsage = require('./helpers/safeMemoryUsage');
const PROCESS_RESIDENT_MEMORY = 'process_resident_memory_bytes';
function notLinuxVariant(registry, config = {}) {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_RESIDENT_MEMORY,
help: 'Resident memory size in bytes.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const memUsage = safeMemoryUsage();
// I don't think the other things returned from `process.memoryUsage()` is relevant to a standard export
if (memUsage) {
this.set(labels, memUsage.rss);
}
},
});
}
module.exports = (registry, config) =>
process.platform === 'linux'
? linuxVariant(registry, config)
: notLinuxVariant(registry, config);
module.exports.metricNames =
process.platform === 'linux'
? linuxVariant.metricNames
: [PROCESS_RESIDENT_MEMORY];
这段代码使用 Gauge
指标类型,针对 Node.js
进程的 常驻内存(RSS
)大小进行了监控。对于 Linux
系统,它会调用 linuxVariant
方法来处理操作系统特有的内存数据获取逻辑;对于其他平台,它则使用标准的 process.memoryUsage().rss
数据来报告常驻内存大小。通过这种方式,代码能够适应不同的操作系统,提供一致的内存监控功能。之所以选择 Gauge
指标类型,是因为常驻内存会动态变化,Gauge
非常适合表示这种会波动的数值。
-
safeMemoryUsage()
:这个辅助函数被用来安全地获取内存使用情况,防止在某些环境下(比如限制的沙箱环境)无法访问process.memoryUsage()
时发生错误。 -
linuxVariant()
:这个函数专门处理Linux
系统上的内存监控,可能会根据Linux
系统的特点做一些特别的处理(虽然在这个代码中没有给出具体实现)。这表明代码在跨平台兼容性方面做了处理,Linux
平台采用不同的实现方式,其他平台则直接使用process.memoryUsage()
。 -
process.memoryUsage().rss
:这是Node.js
内置的API
,用于获取进程的内存使用情况。在这个代码中,它用于获取和暴露常驻内存的大小。
3.5 nodejs_heap_space_size_total_bytes
nodejs_heap_space_size_total_bytes
:这个指标记录了 Node.js
中各个堆空间的总大小,单位是字节。它反映了 Node.js
进程在内存中为堆分配的总体空间大小。
const Gauge = require('../gauge');
const v8 = require('v8');
const METRICS = ['total', 'used', 'available'];
const NODEJS_HEAP_SIZE = {};
METRICS.forEach(metricType => {
NODEJS_HEAP_SIZE[metricType] = `nodejs_heap_space_size_${metricType}_bytes`;
});
module.exports = (registry, config = {}) => {
try {
v8.getHeapSpaceStatistics();
} catch (e) {
if (e.code === 'ERR_NOT_IMPLEMENTED') {
return; // Bun
}
throw e;
}
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = ['space', ...Object.keys(labels)];
const gauges = {};
METRICS.forEach(metricType => {
gauges[metricType] = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE[metricType],
help: `Process heap space size ${metricType} from Node.js in bytes.`,
labelNames,
registers,
});
});
// Use this one metric's `collect` to set all metrics' values.
gauges.total.collect = () => {
for (const space of v8.getHeapSpaceStatistics()) {
const spaceName = space.space_name.substr(
0,
space.space_name.indexOf('_space'),
);
gauges.total.set({ space: spaceName, ...labels }, space.space_size);
gauges.used.set({ space: spaceName, ...labels }, space.space_used_size);
gauges.available.set(
{ space: spaceName, ...labels },
space.space_available_size,
);
}
};
};
module.exports.metricNames = Object.values(NODEJS_HEAP_SIZE);
使用 Gauge
指标类型,代码实现了对 Node.js
进程堆空间的内存使用情况的监控。通过 v8.getHeapSpaceStatistics() API
获取各个堆空间的总大小、已使用的内存和可用内存的实时数据,代码将这些数据通过 Prometheus
指标暴露给监控系统。通过这个实现,开发者可以实时获取 Node.js
中堆内存的具体使用情况,为内存泄漏检测和性能优化提供数据支持。之所以选择使用 Gauge
指标,是因为这些内存数据是动态变化的,Gauge
非常适合用来表示像内存使用量这类会波动的值。
-
v8.getHeapSpaceStatistics()
:此API
提供了对V8
引擎中堆空间的详细统计,包括堆空间的总大小、已使用大小、可用大小等。该API
是Node.js
提供的对V8
引擎内存管理的深入访问方式。 -
Gauge
指标类型:Gauge
类型用于表示动态变化的数值。这里使用它来表示堆空间的大小,随着堆空间的变化,Gauge
会实时更新其值。 -
space_name
和space_used_size
:代码通过遍历每个堆空间的名称和使用情况,将不同堆空间的数据设置到相应的指标中,进行具体的内存统计。
3.6 nodejs_heap_space_used_total_bytes
nodejs_heap_space_size_used_bytes
:这个指标记录了 Node.js
中各个堆空间已使用的内存量,单位是字节。它用于显示各个堆空间当前使用的内存,能够帮助开发者评估内存分配和使用情况。
实现代码同 nodejs_heap_space_size_total_bytes
3.7 nodejs_heap_space_available_total_bytes
nodejs_heap_space_size_available_bytes
:这个指标记录了 Node.js
中各个堆空间中尚未使用的可用内存量,单位是字节。通过这个指标,开发者可以了解各个堆空间剩余的可用内存。
实现代码同 nodejs_heap_space_size_total_bytes
四、进程指标
4.1 process_start_time_seconds
process_start_time_seconds
:表示 Node.js
进程的启动时间(以秒为单位),从 Unix
纪元(1970年1月1日)开始的时间戳。此指标的作用是在监控系统中能够清楚地看到某个进程何时启动,对于长时间运行的应用尤其有帮助。
'use strict';
const Gauge = require('../gauge');
const startInSeconds = Math.round(Date.now() / 1000 - process.uptime());
const PROCESS_START_TIME = 'process_start_time_seconds';
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_START_TIME,
help: 'Start time of the process since unix epoch in seconds.',
registers: registry ? [registry] : undefined,
labelNames,
aggregator: 'omit',
collect() {
this.set(labels, startInSeconds);
},
});
};
module.exports.metricNames = [PROCESS_START_TIME];
该代码通过暴露 process_start_time_seconds
指标,向监控系统提供了 Node.js
进程启动时间的信息。它通过计算当前时间与进程运行时间 (process.uptime()
) 的差值来得出启动时间,进而创建了一个 Gauge
指标来报告该信息。
-
process.uptime()
:返回当前Node.js
进程的运行时间(以秒为单位)。通过将当前时间减去process.uptime()
,我们可以获得进程的启动时间,单位为秒。 -
Gauge
指标:Gauge
是一种表示瞬时值(当前值)的Prometheus
指标类型。此处用于报告进程的启动时间,它的值不会随着时间流逝而自动改变,而是每次collect
时重新计算并设置。 -
startInSeconds
:该值通过Date.now()
获取当前时间的时间戳(单位为毫秒),然后通过减去process.uptime()
(表示进程运行的秒数)得到进程的启动时间戳(单位为秒)。
五、句柄指标
5.1 nodejs_active_handles
nodejs_active_handles
(按类型分组的活动句柄数): 该指标用于跟踪 Node.js
进程中的活跃 libuv
句柄的数量,按句柄类型(每种类型是 C++
类名)进行分组。process._getActiveHandles()
返回的每个句柄代表了 Node.js
与底层系统进行异步操作的一个接口,如 TCP
连接、文件描述符等。通过统计这些句柄的类型和数量,开发者可以了解进程中进行的异步操作的负载情况。
const { aggregateByObjectName } = require('./helpers/processMetricsHelpers');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const Gauge = require('../gauge');
const NODEJS_ACTIVE_HANDLES = 'nodejs_active_handles';
const NODEJS_ACTIVE_HANDLES_TOTAL = 'nodejs_active_handles_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function is removed in later nodes (exists in node@6-12...)
if (typeof process._getActiveHandles !== 'function') {
return;
}
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_HANDLES,
help: 'Number of active libuv handles grouped by handle type. Every handle type is C++ class name.',
labelNames: ['type', ...labelNames],
registers,
collect() {
const handles = process._getActiveHandles();
updateMetrics(this, aggregateByObjectName(handles), labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_HANDLES_TOTAL,
help: 'Total number of active handles.',
registers,
labelNames,
collect() {
const handles = process._getActiveHandles();
this.set(labels, handles.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_HANDLES,
NODEJS_ACTIVE_HANDLES_TOTAL,
];
这段代码通过使用 Gauge
指标类型,监控并报告 Node.js
进程中 活跃的 libuv
句柄数量,并按照句柄的类型(例如 TCP
、文件、计时器等)对其进行分组。具体而言,process._getActiveHandles()
函数返回当前所有活动的句柄,代码通过调用该函数来收集相关数据。为了确保在使用时不出错,还检查了 process._getActiveHandles()
是否在当前版本的 Node.js
中有效(在较新版本的 Node.js
中该方法可能已被移除)。对于每一种句柄类型,代码都会为其创建一个 Gauge
指标,定期更新其值,并按需返回句柄的类型和总数。为了进一步增强灵活性,aggregateByObjectName
和 updateMetrics
函数被用来分别聚合句柄数据和更新指标。
-
process._getActiveHandles()
:这是一个Node.js
内部API
,用于返回当前进程中的所有活跃的libuv
句柄。不同的句柄类型(如TCP
、UDP
、计时器、文件描述符等)代表了进程中与外部系统交互的不同操作。通过对这些句柄的监控,可以帮助开发者了解Node.js
进程的负载和资源使用情况。 -
Gauge
指标:Gauge
是Prometheus
监控系统中用来表示和跟踪动态变化的数值的指标类型。通过Gauge
,可以不断更新和获取活动句柄的数量,无论是按类型分组的数量,还是总数量。 -
aggregateByObjectName
和updateMetrics
:这两个函数用于对活动句柄进行聚合(按类型分组)并更新相应的指标值。aggregateByObjectName
根据句柄的对象名称(即句柄类型)对其进行分组,updateMetrics
则用于更新每个Gauge
指标的值。
5.2 nodejs_active_handles_total
nodejs_active_handles_total
(总的活动句柄数): 该指标用于统计进程中总共有多少个活跃的 libuv
句柄(不按类型分组)。这是一个简单的计数器,表示 Node.js
正在进行多少个异步操作或与底层系统进行交互。
实现代码同 nodejs_active_handles
六、请求指标
6.1 nodejs_active_requests
nodejs_active_requests
:这个指标用于按请求类型分组监控当前所有活动请求的数量。每个请求的类型由 C++
类名表示。libuv
在处理 I/O
操作时,会为每种 I/O
类型(如 TCP
连接、DNS
查询等)创建不同的请求类型。通过这个指标,我们能够获得有关不同类型活动请求的数量信息。
'use strict';
const Gauge = require('../gauge');
const { aggregateByObjectName } = require('./helpers/processMetricsHelpers');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const NODEJS_ACTIVE_REQUESTS = 'nodejs_active_requests';
const NODEJS_ACTIVE_REQUESTS_TOTAL = 'nodejs_active_requests_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function is removed in later nodes (exists in node@6)
if (typeof process._getActiveRequests !== 'function') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_REQUESTS,
help: 'Number of active libuv requests grouped by request type. Every request type is C++ class name.',
labelNames: ['type', ...labelNames],
registers: registry ? [registry] : undefined,
collect() {
const requests = process._getActiveRequests();
updateMetrics(this, aggregateByObjectName(requests), labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_REQUESTS_TOTAL,
help: 'Total number of active requests.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const requests = process._getActiveRequests();
this.set(labels, requests.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_REQUESTS,
NODEJS_ACTIVE_REQUESTS_TOTAL,
];
该代码通过监控 Node.js
进程中的活动请求,实现了对活动请求的跟踪与监控。代码首先检查 Node.js
是否支持 process._getActiveRequests()
(该 API
在 Node.js 6.x
版本及以上有效),然后创建两个 Gauge
类型的指标:
-
nodejs_active_requests
:记录当前活动请求的数量,并按请求类型(由libuv
提供的C++
类名表示)进行分组。 -
nodejs_active_requests_total
:记录当前所有活动请求的总数量。
通过调用 process._getActiveRequests()
获取活动请求列表,并使用 aggregateByObjectName
函数按请求类型聚合这些请求。之后,这些请求数量将通过 Gauge
指标暴露出来,供 Prometheus
等监控系统采集。
-
process._getActiveRequests()
:这是一个Node.js
内置的私有方法,用于返回当前进程中所有活动的libuv
请求。每个请求都是一个对象,包含有关I/O
操作的信息。该方法仅在Node.js 6.x
或以上版本中有效,因此代码首先检查此方法是否存在。 -
aggregateByObjectName(requests)
:这个辅助函数将请求按其类型(通常是C++
类名)进行分组。这样可以确保同类型的请求被汇总在一起,便于对不同类型的I/O
活动进行更详细的监控。 -
updateMetrics(this, aggregatedData, labels)
:这是另一个辅助函数,负责将聚合后的请求数据更新到Gauge
指标中。这些数据最终会被Prometheus
收集并用于监控。 -
Gauge
指标:Gauge
是一种用于表示当前数值的指标类型,可以随时增加、减少。此处用Gauge
来实时跟踪活动请求的数量,适合动态变化的场景。
为什么这样做?
-
监控活动请求:在
Node.js
中,I/O
请求是一个关键的性能指标,过多的活动请求可能会导致应用性能瓶颈,特别是在高并发的场景中。通过监控活动请求的数量,能够及时发现潜在的资源阻塞问题。 -
按请求类型分组:将请求按类型分组,可以帮助开发者更好地了解不同类型的请求(如文件系统操作、网络连接等)的负载情况。如果某些类型的请求过多,可能指示特定的资源问题或者是应用的某个部分过载。
-
Prometheus
监控:通过暴露这两个Gauge
指标,应用程序能够与Prometheus
集成,从而实现高效的实时监控。这种方式能让开发者对Node.js
应用的I/O
负载进行深入了解,并及时采取优化措施。
6.2 nodejs_active_requests_total
nodejs_active_requests_total
:这个指标用于监控当前进程中 所有活动请求的总数,即不按类型分组,只显示一个汇总的活动请求数量。这是一个简单的计数指标,记录了当前正在进行的所有请求的总数。
实现代码同 nodejs_active_requests
七、资源指标
7.1 nodejs_active_resources
nodejs_active_resources
:此指标用于按资源类型分组监控当前活动资源的数量。每个资源类型代表一种异步操作(如文件操作、网络请求等),它们正在阻止事件循环退出。该指标将这些资源按照类型分组,以便开发者可以查看每种类型的资源数量,帮助诊断是否某种类型的异步操作过多,导致事件循环被阻塞。
const Gauge = require('../gauge');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const NODEJS_ACTIVE_RESOURCES = 'nodejs_active_resources';
const NODEJS_ACTIVE_RESOURCES_TOTAL = 'nodejs_active_resources_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function does not exist in previous nodes (exists in node@17.3.0)
if (typeof process.getActiveResourcesInfo !== 'function') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_RESOURCES,
help: 'Number of active resources that are currently keeping the event loop alive, grouped by async resource type.',
labelNames: ['type', ...labelNames],
registers: registry ? [registry] : undefined,
collect() {
const resources = process.getActiveResourcesInfo();
const data = {};
for (let i = 0; i < resources.length; i++) {
const resource = resources[i];
if (Object.hasOwn(data, resource)) {
data[resource] += 1;
} else {
data[resource] = 1;
}
}
updateMetrics(this, data, labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_RESOURCES_TOTAL,
help: 'Total number of active resources.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const resources = process.getActiveResourcesInfo();
this.set(labels, resources.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_RESOURCES,
NODEJS_ACTIVE_RESOURCES_TOTAL,
];
该代码通过暴露 活动资源的监控指标,实现了对 Node.js
异步资源的跟踪。它首先检查 Node.js
是否支持 process.getActiveResourcesInfo()
方法(此方法从 Node.js 17.3.0
版本开始可用)。然后,代码创建了两个 Gauge
指标:
-
nodejs_active_resources
:记录当前活动资源的数量,并按资源类型(如文件I/O
、网络请求等)进行分组。 -
nodejs_active_resources_total
:记录当前所有活动资源的总数量。
代码通过调用 process.getActiveResourcesInfo()
获取所有活动资源信息,并根据资源类型聚合数据,然后通过 Gauge
指标将数据暴露出来供监控系统采集。
-
process.getActiveResourcesInfo()
:这是一个Node.js
内置的方法,用于返回当前所有活动的异步资源的信息。每个资源类型会以字符串形式返回(例如TCPWRAP
或FSREQWRAP
),代表不同类型的异步I/O
操作。该方法从Node.js 17.3.0
版本开始提供,因此在代码中首先检查该方法是否存在,以确保向下兼容。 -
按资源类型分组:使用
data[resource]
作为计数器,将相同类型的资源进行聚合,从而为每种类型的资源提供单独的计数。这样可以帮助开发者识别哪些类型的异步操作占用了最多的资源,可能会导致事件循环阻塞。 -
updateMetrics(this, data, labels)
:这是一个辅助函数,用来更新Gauge
指标,将聚合后的资源数据与标签一起传递给Prometheus
或其他监控工具。 -
Gauge
指标:Gauge
是一种用来表示实时值的指标类型。在本代码中,Gauge
用于动态跟踪当前活动资源的数量,并按资源类型进行分组。它会随着事件循环的变化而实时更新。
为什么要这样做
-
监控活动资源:
Node.js
中的异步操作会将事件循环阻塞,可能导致应用响应变慢或出现性能瓶颈。通过监控活动资源的数量和类型,开发者可以了解哪些资源正在阻塞事件循环,从而更有效地诊断性能问题。 -
按资源类型分组:将资源按类型分组,可以让开发者深入了解哪些类型的异步操作占用了较多的资源。这对于优化应用的
I/O
操作、避免资源泄漏或过度阻塞事件循环非常有帮助。 -
Prometheus
监控:通过暴露这两个Gauge
指标,应用能够与Prometheus
等监控工具集成,实现对活动资源的实时监控。这种方式能够帮助开发者持续监控应用的资源消耗,提前识别潜在的性能问题。
7.2 nodejs_active_resources_total
nodejs_active_resources_total
:此指标用于监控当前活动资源的 总数量,不按类型进行分组。它反映了事件循环当前被多少个异步操作阻塞。此指标有助于提供一个简洁的总览,显示有多少个资源正在活动中。
代码实现同 nodejs_active_resources
八、描述符指标
8.1 process_open_fds
process_open_fds
指标用于监控 Node.js
进程中已打开的文件描述符数。文件描述符(File Descriptors
,简称 FDs
)是操作系统用于表示和管理打开的文件、套接字等资源的整数。当一个进程打开一个文件、网络连接或者其他 I/O
资源时,操作系统会为每个打开的文件分配一个文件描述符。在 Linux
系统中,这些文件描述符可以通过 /proc/self/fd
目录来查看,该目录包含了所有已打开文件的符号链接。process_open_fds
指标的功能是:定期读取 /proc/self/fd
目录,并计算当前进程已打开的文件描述符数。每次收集时,都会记录并提供该进程正在使用的文件描述符的数量,这对于监控文件句柄泄漏或资源瓶颈非常有用。
const Gauge = require('../gauge');
const fs = require('fs');
const process = require('process');
const PROCESS_OPEN_FDS = 'process_open_fds';
module.exports = (registry, config = {}) => {
if (process.platform !== 'linux') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_OPEN_FDS,
help: 'Number of open file descriptors.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
try {
const fds = fs.readdirSync('/proc/self/fd');
// Minus 1 to not count the fd that was used by readdirSync(),
// it's now closed.
this.set(labels, fds.length - 1);
} catch {
// noop
}
},
});
};
module.exports.metricNames = [PROCESS_OPEN_FDS];
该代码通过监控 Linux
系统中的文件描述符数,实现了对进程打开文件数量的实时跟踪,并将此数据暴露为 Prometheus
指标以供监控。具体来说,代码首先检查当前操作系统是否为 Linux
(该功能仅适用于 Linux
),然后通过读取 /proc/self/fd
目录来获取当前进程已打开的文件描述符。该目录包含了所有已打开文件的符号链接,代码会通过计算该目录下文件的数量来得出当前进程打开的文件数。每次收集时,都会将该值暴露为一个 Gauge
指标,供 Prometheus
等监控系统收集和使用。
-
fs.readdirSync('/proc/self/fd')
:该函数用于同步读取/proc/self/fd
目录。这个目录包含当前进程打开的所有文件描述符(以符号链接的形式列出)。每个文件描述符都被表示为一个文件或套接字,fs.readdirSync()
返回的是该目录下所有文件描述符符号链接的文件名列表。fds.length 即为当前进程打开的文件描述符数。 -
减去1的原因:由于
fs.readdirSync()
本身会打开文件描述符以读取目录,因此在获取目录内容时,fs.readdirSync()
会额外创建一个文件描述符,导致最终返回的文件描述符数多了一些。为了纠正这一点,代码通过fds.length - 1
来排除这个额外的文件描述符。 -
Gauge
指标:Gauge
用于表示动态变化的数值,可以随时间增加或减少。此处用它来监控当前进程打开的文件描述符数量,适合这种资源数量变化的场景。
为什么这样做:
-
使用
/proc/self/fd
目录:Linux
系统提供了一个虚拟文件系统/proc
,其中包含了大量有关进程的信息,包括/proc/self/fd
目录,它列出了进程已打开的所有文件描述符。通过访问该目录,代码可以准确获得进程的文件描述符数量,而无需依赖其他不精确或复杂的方法。 -
同步读取文件描述符信息:为了简化操作并确保数据的准确性,代码使用了同步的
fs.readdirSync
方法,这样可以在收集指标时确保读取的数据是最新的,而不必担心异步操作带来的复杂性。 -
Prometheus
监控支持:使用Gauge
指标类型,使得该指标可以被Prometheus
采集,并对进程的文件描述符使用情况进行实时监控。如果文件描述符数异常增多(例如因资源泄漏),可以及时发现并进行优化。
8.2 process_max_fds
process_max_fds
: 该指标用于跟踪当前 Node.js
进程的最大可打开文件描述符数(也称为最大文件句柄数)。文件描述符是操作系统用来标识和操作文件、套接字等资源的整数。在操作系统中,每个进程都有一个文件描述符限制,决定了该进程能够同时打开多少个文件或其他 I/O
资源。通过监控该指标,开发者可以了解进程的资源使用上限,判断是否有潜在的资源瓶颈或是否需要调整文件描述符的限制。
'use strict';
const Gauge = require('../gauge');
const fs = require('fs');
const PROCESS_MAX_FDS = 'process_max_fds';
let maxFds;
module.exports = (registry, config = {}) => {
if (maxFds === undefined) {
// This will fail if a linux-like procfs is not available.
try {
const limits = fs.readFileSync('/proc/self/limits', 'utf8');
const lines = limits.split('\n');
for (const line of lines) {
if (line.startsWith('Max open files')) {
const parts = line.split(/ +/);
maxFds = Number(parts[1]);
break;
}
}
} catch {
return;
}
}
if (maxFds === undefined) return;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_MAX_FDS,
help: 'Maximum number of open file descriptors.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
if (maxFds !== undefined) this.set(labels, maxFds);
},
});
};
module.exports.metricNames = [PROCESS_MAX_FDS];
该代码通过读取 Linux
系统中的 /proc/self/limits
文件,获取当前进程的最大文件描述符数,并通过 Gauge
指标类型将其暴露为可被 Prometheus
监控的指标。具体而言,代码首先尝试读取 /proc/self/limits
文件,其中包含当前进程的资源限制信息,找到其中的 Max open files
行来获取最大文件描述符数。如果读取成功并获取到文件描述符限制,代码会创建一个 Gauge
指标,并将该值定期更新。最终,这个指标可以被监控系统(如 Prometheus
)用来跟踪进程的文件描述符使用情况。
-
/proc/self/limits
:这是一个在Linux
系统中可以用来查询进程资源限制的虚拟文件,包含了进程的最大文件描述符数、最大内存等信息。Max open files
行的值即为该进程允许打开的最大文件数量,通常在系统负载较高时可能需要调整。 -
fs.readFileSync
:该函数用于同步读取文件内容,这里用来读取/proc/self/limits
文件。由于这是一个简单的文件操作,并且没有涉及到大的I/O
操作,使用同步读取是合理的。 -
Gauge
指标:Gauge
是一种用于度量值动态变化的指标类型,适合用来监控像文件描述符数这类可以上下波动的数值。每次监控时,Gauge
指标会更新其值(在此例中就是更新Max open files
的值),并可以被Prometheus
之类的监控系统收集。
为什么这样做?
-
读取文件描述符限制:通过读取
/proc/self/limits
文件,能够准确获取到Node.js
进程的最大文件描述符数,这是通过操作系统提供的信息,而不是依赖于Node.js
本身的API
。这样可以避免因不同平台或版本的差异导致的获取不准情况。 -
Gauge
用于动态变化的资源限制:Gauge
是一种可以增减的指标类型,适合表示可以波动的资源限制或消耗。process_max_fds
值可以被动态监控,帮助开发者了解进程的资源限制,并在资源压力较大时及时作出调整。
九、事件循环指标
9.1 nodejs_eventloop_lag_seconds
nodejs_eventloop_lag_seconds
:这个指标记录了事件循环的延迟(lag
)时间,单位是秒。它是一个总览性的指标,反映了事件循环延迟的基本情况,帮助开发者快速了解事件循环的当前表现。
const Gauge = require('../gauge');
// Check if perf_hooks module is available
let perf_hooks;
try {
perf_hooks = require('perf_hooks');
} catch {
// node version is too old
}
// Reported always.
const NODEJS_EVENTLOOP_LAG = 'nodejs_eventloop_lag_seconds';
// Reported only when perf_hooks is available.
const NODEJS_EVENTLOOP_LAG_MIN = 'nodejs_eventloop_lag_min_seconds';
const NODEJS_EVENTLOOP_LAG_MAX = 'nodejs_eventloop_lag_max_seconds';
const NODEJS_EVENTLOOP_LAG_MEAN = 'nodejs_eventloop_lag_mean_seconds';
const NODEJS_EVENTLOOP_LAG_STDDEV = 'nodejs_eventloop_lag_stddev_seconds';
const NODEJS_EVENTLOOP_LAG_P50 = 'nodejs_eventloop_lag_p50_seconds';
const NODEJS_EVENTLOOP_LAG_P90 = 'nodejs_eventloop_lag_p90_seconds';
const NODEJS_EVENTLOOP_LAG_P99 = 'nodejs_eventloop_lag_p99_seconds';
function reportEventloopLag(start, gauge, labels) {
const delta = process.hrtime(start);
const nanosec = delta[0] * 1e9 + delta[1];
const seconds = nanosec / 1e9;
gauge.set(labels, seconds);
}
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const registers = registry ? [registry] : undefined;
let collect = () => {
const start = process.hrtime();
setImmediate(reportEventloopLag, start, lag, labels);
};
if (perf_hooks && perf_hooks.monitorEventLoopDelay) {
try {
const histogram = perf_hooks.monitorEventLoopDelay({
resolution: config.eventLoopMonitoringPrecision,
});
histogram.enable();
collect = () => {
const start = process.hrtime();
setImmediate(reportEventloopLag, start, lag, labels);
lagMin.set(labels, histogram.min / 1e9);
lagMax.set(labels, histogram.max / 1e9);
lagMean.set(labels, histogram.mean / 1e9);
lagStddev.set(labels, histogram.stddev / 1e9);
lagP50.set(labels, histogram.percentile(50) / 1e9);
lagP90.set(labels, histogram.percentile(90) / 1e9);
lagP99.set(labels, histogram.percentile(99) / 1e9);
histogram.reset();
};
} catch (e) {
if (e.code === 'ERR_NOT_IMPLEMENTED') {
return; // Bun
}
throw e;
}
}
const lag = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG,
help: 'Lag of event loop in seconds.',
registers,
labelNames,
aggregator: 'average',
// Use this one metric's `collect` to set all metrics' values.
collect,
});
const lagMin = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MIN,
help: 'The minimum recorded event loop delay.',
registers,
labelNames,
aggregator: 'min',
});
const lagMax = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MAX,
help: 'The maximum recorded event loop delay.',
registers,
labelNames,
aggregator: 'max',
});
const lagMean = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MEAN,
help: 'The mean of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagStddev = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_STDDEV,
help: 'The standard deviation of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP50 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P50,
help: 'The 50th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP90 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P90,
help: 'The 90th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP99 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P99,
help: 'The 99th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
};
module.exports.metricNames = [
NODEJS_EVENTLOOP_LAG,
NODEJS_EVENTLOOP_LAG_MIN,
NODEJS_EVENTLOOP_LAG_MAX,
NODEJS_EVENTLOOP_LAG_MEAN,
NODEJS_EVENTLOOP_LAG_STDDEV,
NODEJS_EVENTLOOP_LAG_P50,
NODEJS_EVENTLOOP_LAG_P90,
NODEJS_EVENTLOOP_LAG_P99,
];
使用 Gauge
指标类型,代码实现了对 Node.js
事件循环延迟的多维度监控,通过不同的统计指标(如最小、最大、均值、标准差和百分位)提供了丰富的延迟数据。首先,代码尝试使用 perf_hooks
模块的 monitorEventLoopDelay
API
获取精确的事件循环延迟数据,并计算事件循环的各种统计信息。若 perf_hooks
不可用(如在较旧的 Node.js
版本中),代码会退回使用 process.hrtime
来手动计算延迟。通过这些统计数据,开发者可以实时监控 Node.js
应用的性能,尤其是在高负载情况下事件循环的表现,帮助及时发现性能瓶颈。选择 Gauge
类型是因为事件循环延迟是动态变化的,Gauge
适用于实时监控和捕捉这种变化。
-
perf_hooks.monitorEventLoopDelay
:提供了高精度的事件循环延迟监控,能够记录最小、最大、均值等多种统计数据。 -
process.hrtime()
:用于在perf_hooks
不可用时,手动测量事件循环的延迟。 -
setImmediate()
:用于非阻塞地异步执行事件循环延迟的报告函数,确保事件循环延迟数据能够实时记录。
代码实现同 nodejs_eventloop_lag_seconds
9.2 nodejs_eventloop_lag_p50_seconds
nodejs_eventloop_lag_p50_seconds
:这些指标分别记录了事件循环延迟的 50th
百分位值。它们用于捕捉不同范围内的延迟表现,帮助开发者了解事件循环延迟在不同负载下的分布情况,尤其是99th 百分位可以揭示系统中的极端性能波动。
代码实现同 nodejs_eventloop_lag_seconds
9.3 nodejs_eventloop_lag_p90_seconds
nodejs_eventloop_lag_p90_seconds
:这些指标分别记录了事件循环延迟的 90th
百分位值。它们用于捕捉不同范围内的延迟表现,帮助开发者了解事件循环延迟在不同负载下的分布情况,尤其是99th 百分位可以揭示系统中的极端性能波动。
代码实现同 nodejs_eventloop_lag_seconds
9.4 nodejs_eventloop_lag_p99_seconds
nodejs_eventloop_lag_p99_seconds
:这些指标分别记录了事件循环延迟的 99th
百分位值。它们用于捕捉不同范围内的延迟表现,帮助开发者了解事件循环延迟在不同负载下的分布情况,尤其是99th 百分位可以揭示系统中的极端性能波动。
代码实现同 nodejs_eventloop_lag_seconds
9.5 nodejs_eventloop_lag_min_seconds
nodejs_eventloop_lag_min_seconds
:记录事件循环延迟的最小值。通过该指标,可以观察到事件循环延迟的最优表现,即事件循环延迟最短的时刻。
代码实现同 nodejs_eventloop_lag_seconds
9.6 nodejs_eventloop_lag_max_seconds
nodejs_eventloop_lag_max_seconds
:记录事件循环延迟的最大值。这个指标可以帮助开发者找到系统性能瓶颈,尤其是在延迟较高时。
代码实现同 nodejs_eventloop_lag_seconds
9.7 nodejs_eventloop_lag_mean_seconds
nodejs_eventloop_lag_mean_seconds
:记录事件循环延迟的平均值。这是衡量事件循环延迟普遍水平的一个指标,能反映系统整体的性能趋势。
代码实现同 nodejs_eventloop_lag_seconds
9.8 nodejs_eventloop_lag_stddev_seconds
nodejs_eventloop_lag_stddev_seconds
:记录事件循环延迟的标准差,反映延迟波动的幅度。较高的标准差说明事件循环的延迟波动较大,可能存在性能不稳定的问题。
代码实现同 nodejs_eventloop_lag_seconds
十、Node.js 版本号
10.1 nodejs_version_info
nodejs_version_info
的指标,它表示当前 Node.js
进程的版本信息。
'use strict';
const Gauge = require('../gauge');
const version = process.version;
const versionSegments = version.slice(1).split('.').map(Number);
const NODE_VERSION_INFO = 'nodejs_version_info';
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODE_VERSION_INFO,
help: 'Node.js version info.',
labelNames: ['version', 'major', 'minor', 'patch', ...labelNames],
registers: registry ? [registry] : undefined,
aggregator: 'first',
collect() {
// Needs to be in collect() so value is present even if reg is reset
this.labels(
version,
versionSegments[0],
versionSegments[1],
versionSegments[2],
...Object.values(labels),
).set(1);
},
});
};
module.exports.metricNames = [NODE_VERSION_INFO];
该代码使用 Prometheus
的 Gauge
类型指标来暴露当前 Node.js
进程的版本信息,包括完整版本号以及版本号的三个组成部分(major
、minor
、patch
)。在 collect
方法中,它将 process.version
字符串拆解为三个部分,并将这些部分与版本信息一同作为标签传递给 Prometheus
监控系统。这使得监控系统可以在运行时获取关于 Node.js
版本的详细信息,方便进行性能监控和版本分析。
-
process.version
:Node.js
的内置属性,返回当前运行的Node.js
版本字符串(如 v16.14.0)。 -
versionSegments
:将process.version
中的版本号部分(如v16.14.0
)进行拆分,并转化为数字数组[16, 14, 0]
,分别表示major
、minor
和patch
部分。 -
Gauge
指标:用于定义一个可变的指标类型,在此处用来报告当前的Node.js
版本信息。每次collect
时,该指标的值被设置为1
,并带有版本信息标签。