跳到主要内容

默认指标

2024年12月25日
柏拉文
越努力,越幸运
提示

Prometheus 推荐了一些默认指标 本身。要收集这些信息,请调用 collectDefaultMetrics。此外,还包括一些特定于 Node.js 的指标,例如事件循环滞后、活动句柄、GCNode.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)时,能够附带当前的追踪 IDspan ID。这样做的目的是在分布式跟踪中能够关联指标和请求的具体操作,从而更好地进行性能分析和问题定位。

  • process.cpuUsage():返回一个对象,表示自进程启动以来的 CPU 使用情况,包括 user(用户 CPU 时间)和 system(系统 CPU 时间),单位是微秒。为了避免重复计算,代码维护了一个 lastCpuUsage 状态变量,记录上一次采样时的 CPU 使用情况,并通过计算本次采样与上次采样的差值来得到增量。

  • Counter:作为 Prometheus 指标的一种类型,Counter 是用来记录一个不断递增的计数器,适合用来监控累积量(如 CPU 使用时间)。每次调用 inc() 方法时,Counter 会增加指定的值。在这段代码中,通过 inc() 方法增加用户和系统 CPU 使用时间的增量。

  • exemplars:该选项用于启用示例数据(Exemplar),它是 OpenTelemetry 中的一个特性,允许将附加的元数据(如 trace IDspan ID)与指标数据关联,方便在追踪和监控系统中进行分析和关联。代码通过 OtelApi.trace.getSpan() 方法获取当前的 tracespan 信息,并将其附加到每个计数器的更新中。

  • 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_bytesNode.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 指标类型,实现了对以下三个内存指标的收集和导出:

  1. nodejs_heap_size_total_bytesNode.js 进程堆的总内存大小。

  2. nodejs_heap_size_used_bytesNode.js 进程堆内存已使用的大小。

  3. nodejs_external_memory_bytesNode.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_bytesNode.js 外部内存的使用量(单位:字节),例如绑定到 C++ 插件或外部库的内存。

代码实现同 nodejs_heap_size_used_bytes

3.3 nodejs_heap_size_total_bytes

nodejs_heap_size_total_bytesNode.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 非常适合用来表示像内存使用量这类会波动的值。

  1. v8.getHeapSpaceStatistics():此 API 提供了对 V8 引擎中堆空间的详细统计,包括堆空间的总大小、已使用大小、可用大小等。该 APINode.js 提供的对 V8 引擎内存管理的深入访问方式。

  2. Gauge 指标类型Gauge 类型用于表示动态变化的数值。这里使用它来表示堆空间的大小,随着堆空间的变化,Gauge 会实时更新其值。

  3. space_namespace_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 指标,定期更新其值,并按需返回句柄的类型和总数。为了进一步增强灵活性,aggregateByObjectNameupdateMetrics 函数被用来分别聚合句柄数据和更新指标。

  • process._getActiveHandles():这是一个 Node.js 内部 API,用于返回当前进程中的所有活跃的 libuv 句柄。不同的句柄类型(如 TCPUDP、计时器、文件描述符等)代表了进程中与外部系统交互的不同操作。通过对这些句柄的监控,可以帮助开发者了解 Node.js 进程的负载和资源使用情况。

  • Gauge 指标GaugePrometheus 监控系统中用来表示和跟踪动态变化的数值的指标类型。通过 Gauge,可以不断更新和获取活动句柄的数量,无论是按类型分组的数量,还是总数量。

  • aggregateByObjectNameupdateMetrics:这两个函数用于对活动句柄进行聚合(按类型分组)并更新相应的指标值。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()(该 APINode.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 来实时跟踪活动请求的数量,适合动态变化的场景。

为什么这样做?

  1. 监控活动请求:在 Node.js 中,I/O 请求是一个关键的性能指标,过多的活动请求可能会导致应用性能瓶颈,特别是在高并发的场景中。通过监控活动请求的数量,能够及时发现潜在的资源阻塞问题。

  2. 按请求类型分组:将请求按类型分组,可以帮助开发者更好地了解不同类型的请求(如文件系统操作、网络连接等)的负载情况。如果某些类型的请求过多,可能指示特定的资源问题或者是应用的某个部分过载。

  3. 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 内置的方法,用于返回当前所有活动的异步资源的信息。每个资源类型会以字符串形式返回(例如 TCPWRAPFSREQWRAP),代表不同类型的异步 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 用于表示动态变化的数值,可以随时间增加或减少。此处用它来监控当前进程打开的文件描述符数量,适合这种资源数量变化的场景。

为什么这样做:

  1. 使用 /proc/self/fd 目录:Linux 系统提供了一个虚拟文件系统 /proc,其中包含了大量有关进程的信息,包括 /proc/self/fd 目录,它列出了进程已打开的所有文件描述符。通过访问该目录,代码可以准确获得进程的文件描述符数量,而无需依赖其他不精确或复杂的方法。

  2. 同步读取文件描述符信息:为了简化操作并确保数据的准确性,代码使用了同步的 fs.readdirSync 方法,这样可以在收集指标时确保读取的数据是最新的,而不必担心异步操作带来的复杂性。

  3. 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 之类的监控系统收集。

为什么这样做?

  1. 读取文件描述符限制:通过读取 /proc/self/limits 文件,能够准确获取到 Node.js 进程的最大文件描述符数,这是通过操作系统提供的信息,而不是依赖于 Node.js 本身的 API。这样可以避免因不同平台或版本的差异导致的获取不准情况。

  2. 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];

该代码使用 PrometheusGauge 类型指标来暴露当前 Node.js 进程的版本信息,包括完整版本号以及版本号的三个组成部分(majorminorpatch)。在 collect 方法中,它将 process.version 字符串拆解为三个部分,并将这些部分与版本信息一同作为标签传递给 Prometheus 监控系统。这使得监控系统可以在运行时获取关于 Node.js 版本的详细信息,方便进行性能监控和版本分析。

  • process.versionNode.js 的内置属性,返回当前运行的 Node.js 版本字符串(如 v16.14.0)。

  • versionSegments:将 process.version 中的版本号部分(如 v16.14.0)进行拆分,并转化为数字数组 [16, 14, 0],分别表示 majorminorpatch 部分。

  • Gauge 指标:用于定义一个可变的指标类型,在此处用来报告当前的 Node.js 版本信息。每次 collect 时,该指标的值被设置为 1,并带有版本信息标签。