Register resident set size and limit bytes metrics in APM (#215458)

## Summary

Resolves https://github.com/elastic/kibana-team/issues/1565

Register resident set size and limit bytes metrics in APM.

<img width="1614" alt="image"
src="https://github.com/user-attachments/assets/d5f1caf5-da4f-4acd-a63f-2b5256a91307"
/>


### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
Jesus Wahrman 2025-03-23 22:22:30 +01:00 committed by GitHub
parent b876e79ae7
commit 04772fcc45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 55 additions and 2 deletions

View file

@ -10,6 +10,7 @@
import v8, { HeapInfo } from 'v8';
import { mockEventLoopDelayMonitor, mockEventLoopUtilizationMonitor } from './process.test.mocks';
import { ProcessMetricsCollector } from './process';
import apm from 'elastic-apm-node';
describe('ProcessMetricsCollector', () => {
let collector: ProcessMetricsCollector;
@ -103,4 +104,24 @@ describe('ProcessMetricsCollector', () => {
expect(mockEventLoopUtilizationMonitor.reset).toBeCalledTimes(1);
});
});
describe('register metrics in apm', () => {
it('calls registerMetric in the constructor', () => {
const apmSpy = jest.spyOn(apm, 'registerMetric');
collector.registerMetrics();
expect(apmSpy).toHaveBeenCalledTimes(2);
expect(apmSpy).toHaveBeenNthCalledWith(
1,
'nodejs.memory.resident_set_size.bytes',
expect.any(Function)
);
expect(apmSpy).toHaveBeenNthCalledWith(
2,
'nodejs.heap.size_limit.bytes',
expect.any(Function)
);
});
});
});

View file

@ -9,6 +9,7 @@
import v8 from 'v8';
import type { OpsProcessMetrics, MetricsCollector } from '@kbn/core-metrics-server';
import apm from 'elastic-apm-node';
import { EventLoopDelaysMonitor } from './event_loop_delays_monitor';
import { EventLoopUtilizationMonitor } from './event_loop_utilization_monitor';
@ -54,6 +55,14 @@ export class ProcessMetricsCollector implements MetricsCollector<OpsProcessMetri
return [this.getCurrentPidMetrics()];
}
public registerMetrics() {
apm.registerMetric('nodejs.memory.resident_set_size.bytes', () => process.memoryUsage().rss);
apm.registerMetric(
'nodejs.heap.size_limit.bytes',
() => v8.getHeapStatistics().heap_size_limit
);
}
public reset() {
this.eventLoopDelayMonitor.reset();
this.eventLoopUtilizationMonitor.reset();

View file

@ -21,7 +21,13 @@ const createMock = () => {
return mocked;
};
const createMockProcessMetricsCollector = () => ({
...createMock(),
registerMetrics: jest.fn().mockResolvedValue({}),
});
export const collectorMock = {
create: createMock,
createOpsProcessMetrics: createMockOpsProcessMetrics,
createProcessMetricsCollector: createMockProcessMetricsCollector,
};

View file

@ -9,7 +9,10 @@
import { collectorMock } from '@kbn/core-metrics-collectors-server-mocks';
export const mockOpsCollector = collectorMock.create();
export const mockOpsCollector = {
...collectorMock.create(),
registerMetrics: jest.fn(),
};
jest.doMock('./ops_metrics_collector', () => ({
OpsMetricsCollector: jest.fn().mockImplementation(() => mockOpsCollector),

View file

@ -78,6 +78,8 @@ export class MetricsService
}
);
this.metricsCollector.registerMetrics();
await this.refreshMetrics();
this.collectInterval = setInterval(() => {

View file

@ -10,7 +10,7 @@
import { collectorMock } from '@kbn/core-metrics-collectors-server-mocks';
export const mockOsCollector = collectorMock.create();
export const mockProcessCollector = collectorMock.create();
export const mockProcessCollector = collectorMock.createProcessMetricsCollector();
export const mockServerCollector = collectorMock.create();
export const mockEsClientCollector = collectorMock.create();

View file

@ -80,4 +80,12 @@ describe('OpsMetricsCollector', () => {
expect(mockServerCollector.reset).toHaveBeenCalledTimes(2);
});
});
describe('#registerMetrics', () => {
it('call registerMetrics on the underlying collectors', () => {
collector.registerMetrics();
expect(mockProcessCollector.registerMetrics).toHaveBeenCalledTimes(1);
});
});
});

View file

@ -64,6 +64,10 @@ export class OpsMetricsCollector implements MetricsCollector<OpsMetrics> {
};
}
public registerMetrics() {
this.processCollector.registerMetrics();
}
public reset() {
this.processCollector.reset();
this.osCollector.reset();