Skip to content

Commit 42b50e4

Browse files
[7.x] Add more robust error handling to OsCgroupMetricsCollector (#78213) (#78435)
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 91f863c commit 42b50e4

File tree

6 files changed

+55
-13
lines changed

6 files changed

+55
-13
lines changed

src/core/server/metrics/collectors/cgroup.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
import mockFs from 'mock-fs';
21+
import { loggerMock } from '@kbn/logging/target/mocks';
2122
import { OsCgroupMetricsCollector } from './cgroup';
2223

2324
describe('OsCgroupMetricsCollector', () => {
@@ -30,8 +31,10 @@ describe('OsCgroupMetricsCollector', () => {
3031
},
3132
});
3233

33-
const collector = new OsCgroupMetricsCollector({});
34+
const logger = loggerMock.create();
35+
const collector = new OsCgroupMetricsCollector({ logger });
3436
expect(await collector.collect()).toEqual({});
37+
expect(logger.error).not.toHaveBeenCalled();
3538
});
3639

3740
it('collects default cgroup data', async () => {
@@ -51,7 +54,7 @@ throttled_time 666
5154
`,
5255
});
5356

54-
const collector = new OsCgroupMetricsCollector({});
57+
const collector = new OsCgroupMetricsCollector({ logger: loggerMock.create() });
5558
expect(await collector.collect()).toMatchInlineSnapshot(`
5659
Object {
5760
"cpu": Object {
@@ -90,6 +93,7 @@ throttled_time 666
9093
});
9194

9295
const collector = new OsCgroupMetricsCollector({
96+
logger: loggerMock.create(),
9397
cpuAcctPath: 'xxcustomcpuacctxx',
9498
cpuPath: 'xxcustomcpuxx',
9599
});
@@ -112,4 +116,23 @@ throttled_time 666
112116
}
113117
`);
114118
});
119+
120+
it('returns empty object and logs error on an EACCES error', async () => {
121+
mockFs({
122+
'/proc/self/cgroup': `
123+
123:memory:/groupname
124+
123:cpu:/groupname
125+
123:cpuacct:/groupname
126+
`,
127+
'/sys/fs/cgroup': mockFs.directory({ mode: parseInt('0000', 8) }),
128+
});
129+
130+
const logger = loggerMock.create();
131+
132+
const collector = new OsCgroupMetricsCollector({ logger });
133+
expect(await collector.collect()).toEqual({});
134+
expect(logger.error).toHaveBeenCalledWith(
135+
"cgroup metrics could not be read due to error: [Error: EACCES, permission denied '/sys/fs/cgroup/cpuacct/groupname/cpuacct.usage']"
136+
);
137+
});
115138
});

src/core/server/metrics/collectors/cgroup.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919

2020
import fs from 'fs';
2121
import { join as joinPath } from 'path';
22+
import { Logger } from '@kbn/logging';
2223
import { MetricsCollector, OpsOsMetrics } from './types';
2324

2425
type OsCgroupMetrics = Pick<OpsOsMetrics, 'cpu' | 'cpuacct'>;
2526

2627
interface OsCgroupMetricsCollectorOptions {
28+
logger: Logger;
2729
cpuPath?: string;
2830
cpuAcctPath?: string;
2931
}
@@ -38,8 +40,12 @@ export class OsCgroupMetricsCollector implements MetricsCollector<OsCgroupMetric
3840

3941
public async collect(): Promise<OsCgroupMetrics> {
4042
try {
43+
if (this.noCgroupPresent) {
44+
return {};
45+
}
46+
4147
await this.initializePaths();
42-
if (this.noCgroupPresent || !this.cpuAcctPath || !this.cpuPath) {
48+
if (!this.cpuAcctPath || !this.cpuPath) {
4349
return {};
4450
}
4551

@@ -64,12 +70,15 @@ export class OsCgroupMetricsCollector implements MetricsCollector<OsCgroupMetric
6470
},
6571
};
6672
} catch (err) {
67-
if (err.code === 'ENOENT') {
68-
this.noCgroupPresent = true;
69-
return {};
70-
} else {
71-
throw err;
73+
this.noCgroupPresent = true;
74+
75+
if (err.code !== 'ENOENT') {
76+
this.options.logger.error(
77+
`cgroup metrics could not be read due to error: [${err.toString()}]`
78+
);
7279
}
80+
81+
return {};
7382
}
7483
}
7584

src/core/server/metrics/collectors/os.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
jest.mock('getos', () => (cb: Function) => cb(null, { dist: 'distrib', release: 'release' }));
2121

22+
import { loggerMock } from '@kbn/logging/target/mocks';
2223
import os from 'os';
2324
import { cgroupCollectorMock } from './os.test.mocks';
2425
import { OsMetricsCollector } from './os';
@@ -27,7 +28,7 @@ describe('OsMetricsCollector', () => {
2728
let collector: OsMetricsCollector;
2829

2930
beforeEach(() => {
30-
collector = new OsMetricsCollector();
31+
collector = new OsMetricsCollector({ logger: loggerMock.create() });
3132
cgroupCollectorMock.collect.mockReset();
3233
cgroupCollectorMock.reset.mockReset();
3334
});

src/core/server/metrics/collectors/os.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,26 @@
2020
import os from 'os';
2121
import getosAsync, { LinuxOs } from 'getos';
2222
import { promisify } from 'util';
23+
import { Logger } from '@kbn/logging';
2324
import { OpsOsMetrics, MetricsCollector } from './types';
2425
import { OsCgroupMetricsCollector } from './cgroup';
2526

2627
const getos = promisify(getosAsync);
2728

2829
export interface OpsMetricsCollectorOptions {
30+
logger: Logger;
2931
cpuPath?: string;
3032
cpuAcctPath?: string;
3133
}
3234

3335
export class OsMetricsCollector implements MetricsCollector<OpsOsMetrics> {
3436
private readonly cgroupCollector: OsCgroupMetricsCollector;
3537

36-
constructor(options: OpsMetricsCollectorOptions = {}) {
37-
this.cgroupCollector = new OsCgroupMetricsCollector(options);
38+
constructor(options: OpsMetricsCollectorOptions) {
39+
this.cgroupCollector = new OsCgroupMetricsCollector({
40+
...options,
41+
logger: options.logger.get('cgroup'),
42+
});
3843
}
3944

4045
public async collect(): Promise<OpsOsMetrics> {

src/core/server/metrics/metrics_service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ export class MetricsService
5050
.pipe(first())
5151
.toPromise();
5252

53-
this.metricsCollector = new OpsMetricsCollector(http.server, config.cGroupOverrides);
53+
this.metricsCollector = new OpsMetricsCollector(http.server, {
54+
logger: this.logger,
55+
...config.cGroupOverrides,
56+
});
5457

5558
await this.refreshMetrics();
5659

src/core/server/metrics/ops_metrics_collector.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* under the License.
1818
*/
1919

20+
import { loggerMock } from '@kbn/logging/target/mocks';
2021
import {
2122
mockOsCollector,
2223
mockProcessCollector,
@@ -30,7 +31,7 @@ describe('OpsMetricsCollector', () => {
3031

3132
beforeEach(() => {
3233
const hapiServer = httpServiceMock.createInternalSetupContract().server;
33-
collector = new OpsMetricsCollector(hapiServer, {});
34+
collector = new OpsMetricsCollector(hapiServer, { logger: loggerMock.create() });
3435

3536
mockOsCollector.collect.mockResolvedValue('osMetrics');
3637
});

0 commit comments

Comments
 (0)