[core.metrics] Add support for multiple processes in ops metrics & stats API; deprecate process field (#109820) (#112108)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Ahmad Bamieh <ahmadbamieh@gmail.com>
This commit is contained in:
Kibana Machine 2021-09-14 12:13:17 -04:00 committed by GitHub
parent 3f17c9ef86
commit 28b32b0aa1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 846 additions and 405 deletions

View file

@ -25,8 +25,6 @@ import { i18n } from '@kbn/i18n';
async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecationsContext): Promise<DeprecationsDetails[]> {
const deprecations: DeprecationsDetails[] = [];
// Example of an api correctiveAction
const count = await getFooCount(savedObjectsClient);
if (count > 0) {
deprecations.push({
@ -42,12 +40,12 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations
level: 'warning',
correctiveActions: {
manualSteps: [
i18n.translate('xpack.foo.deprecations.manualStepOneMessage', {
defaultMessage: 'Navigate to the Kibana Dashboard and click "Create dashboard".',
}),
i18n.translate('xpack.foo.deprecations.manualStepTwoMessage', {
defaultMessage: 'Select Foo from the "New Visualization" window.',
}),
i18n.translate('xpack.foo.deprecations.manualStepOneMessage', {
defaultMessage: 'Navigate to the Kibana Dashboard and click "Create dashboard".',
}),
i18n.translate('xpack.foo.deprecations.manualStepTwoMessage', {
defaultMessage: 'Select Foo from the "New Visualization" window.',
}),
],
api: {
path: '/internal/security/users/test_dashboard_user',
@ -68,7 +66,6 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations
},
});
}
return deprecations;
}

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) &gt; [(constructor)](./kibana-plugin-core-server.eventloopdelaysmonitor._constructor_.md)
## EventLoopDelaysMonitor.(constructor)
Creating a new instance from EventLoopDelaysMonitor will automatically start tracking event loop delays.
<b>Signature:</b>
```typescript
constructor();
```

View file

@ -0,0 +1,19 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) &gt; [collect](./kibana-plugin-core-server.eventloopdelaysmonitor.collect.md)
## EventLoopDelaysMonitor.collect() method
Collect gathers event loop delays metrics from nodejs perf\_hooks.monitorEventLoopDelay the histogram calculations start from the last time `reset` was called or this EventLoopDelaysMonitor instance was created.
<b>Signature:</b>
```typescript
collect(): IntervalHistogram;
```
<b>Returns:</b>
`IntervalHistogram`
{<!-- -->IntervalHistogram<!-- -->}

View file

@ -0,0 +1,26 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md)
## EventLoopDelaysMonitor class
<b>Signature:</b>
```typescript
export declare class EventLoopDelaysMonitor
```
## Constructors
| Constructor | Modifiers | Description |
| --- | --- | --- |
| [(constructor)()](./kibana-plugin-core-server.eventloopdelaysmonitor._constructor_.md) | | Creating a new instance from EventLoopDelaysMonitor will automatically start tracking event loop delays. |
## Methods
| Method | Modifiers | Description |
| --- | --- | --- |
| [collect()](./kibana-plugin-core-server.eventloopdelaysmonitor.collect.md) | | Collect gathers event loop delays metrics from nodejs perf\_hooks.monitorEventLoopDelay the histogram calculations start from the last time <code>reset</code> was called or this EventLoopDelaysMonitor instance was created. |
| [reset()](./kibana-plugin-core-server.eventloopdelaysmonitor.reset.md) | | Resets the collected histogram data. |
| [stop()](./kibana-plugin-core-server.eventloopdelaysmonitor.stop.md) | | Disables updating the interval timer for collecting new data points. |

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) &gt; [reset](./kibana-plugin-core-server.eventloopdelaysmonitor.reset.md)
## EventLoopDelaysMonitor.reset() method
Resets the collected histogram data.
<b>Signature:</b>
```typescript
reset(): void;
```
<b>Returns:</b>
`void`

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) &gt; [stop](./kibana-plugin-core-server.eventloopdelaysmonitor.stop.md)
## EventLoopDelaysMonitor.stop() method
Disables updating the interval timer for collecting new data points.
<b>Signature:</b>
```typescript
stop(): void;
```
<b>Returns:</b>
`void`

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [exceeds](./kibana-plugin-core-server.intervalhistogram.exceeds.md)
## IntervalHistogram.exceeds property
<b>Signature:</b>
```typescript
exceeds: number;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [fromTimestamp](./kibana-plugin-core-server.intervalhistogram.fromtimestamp.md)
## IntervalHistogram.fromTimestamp property
<b>Signature:</b>
```typescript
fromTimestamp: string;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [lastUpdatedAt](./kibana-plugin-core-server.intervalhistogram.lastupdatedat.md)
## IntervalHistogram.lastUpdatedAt property
<b>Signature:</b>
```typescript
lastUpdatedAt: string;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [max](./kibana-plugin-core-server.intervalhistogram.max.md)
## IntervalHistogram.max property
<b>Signature:</b>
```typescript
max: number;
```

View file

@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md)
## IntervalHistogram interface
an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in nanoseconds.
<b>Signature:</b>
```typescript
export interface IntervalHistogram
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [exceeds](./kibana-plugin-core-server.intervalhistogram.exceeds.md) | <code>number</code> | |
| [fromTimestamp](./kibana-plugin-core-server.intervalhistogram.fromtimestamp.md) | <code>string</code> | |
| [lastUpdatedAt](./kibana-plugin-core-server.intervalhistogram.lastupdatedat.md) | <code>string</code> | |
| [max](./kibana-plugin-core-server.intervalhistogram.max.md) | <code>number</code> | |
| [mean](./kibana-plugin-core-server.intervalhistogram.mean.md) | <code>number</code> | |
| [min](./kibana-plugin-core-server.intervalhistogram.min.md) | <code>number</code> | |
| [percentiles](./kibana-plugin-core-server.intervalhistogram.percentiles.md) | <code>{</code><br/><code> 50: number;</code><br/><code> 75: number;</code><br/><code> 95: number;</code><br/><code> 99: number;</code><br/><code> }</code> | |
| [stddev](./kibana-plugin-core-server.intervalhistogram.stddev.md) | <code>number</code> | |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [mean](./kibana-plugin-core-server.intervalhistogram.mean.md)
## IntervalHistogram.mean property
<b>Signature:</b>
```typescript
mean: number;
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [min](./kibana-plugin-core-server.intervalhistogram.min.md)
## IntervalHistogram.min property
<b>Signature:</b>
```typescript
min: number;
```

View file

@ -0,0 +1,16 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [percentiles](./kibana-plugin-core-server.intervalhistogram.percentiles.md)
## IntervalHistogram.percentiles property
<b>Signature:</b>
```typescript
percentiles: {
50: number;
75: number;
95: number;
99: number;
};
```

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) &gt; [stddev](./kibana-plugin-core-server.intervalhistogram.stddev.md)
## IntervalHistogram.stddev property
<b>Signature:</b>
```typescript
stddev: number;
```

View file

@ -19,6 +19,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [BasePath](./kibana-plugin-core-server.basepath.md) | Access or manipulate the Kibana base path |
| [CspConfig](./kibana-plugin-core-server.cspconfig.md) | CSP configuration for use in Kibana. |
| [ElasticsearchConfig](./kibana-plugin-core-server.elasticsearchconfig.md) | Wrapper of config schema. |
| [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) | |
| [KibanaRequest](./kibana-plugin-core-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. |
| [RouteValidationError](./kibana-plugin-core-server.routevalidationerror.md) | Error to return when the validation is not successful. |
| [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) | |
@ -97,6 +98,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [IExternalUrlPolicy](./kibana-plugin-core-server.iexternalurlpolicy.md) | A policy describing whether access to an external destination is allowed. |
| [IKibanaResponse](./kibana-plugin-core-server.ikibanaresponse.md) | A response data object, expected to returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution |
| [IKibanaSocket](./kibana-plugin-core-server.ikibanasocket.md) | A tiny abstraction for TCP socket. |
| [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) | an IntervalHistogram object that samples and reports the event loop delay over time. The delays will be reported in nanoseconds. |
| [IRenderOptions](./kibana-plugin-core-server.irenderoptions.md) | |
| [IRouter](./kibana-plugin-core-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-core-server.routeconfig.md) and [RequestHandler](./kibana-plugin-core-server.requesthandler.md) for more information about arguments to route registrations. |
| [ISavedObjectsPointInTimeFinder](./kibana-plugin-core-server.isavedobjectspointintimefinder.md) | |

View file

@ -19,7 +19,8 @@ export interface OpsMetrics
| [collected\_at](./kibana-plugin-core-server.opsmetrics.collected_at.md) | <code>Date</code> | Time metrics were recorded at. |
| [concurrent\_connections](./kibana-plugin-core-server.opsmetrics.concurrent_connections.md) | <code>OpsServerMetrics['concurrent_connections']</code> | number of current concurrent connections to the server |
| [os](./kibana-plugin-core-server.opsmetrics.os.md) | <code>OpsOsMetrics</code> | OS related metrics |
| [process](./kibana-plugin-core-server.opsmetrics.process.md) | <code>OpsProcessMetrics</code> | Process related metrics |
| [process](./kibana-plugin-core-server.opsmetrics.process.md) | <code>OpsProcessMetrics</code> | Process related metrics. Deprecated in favor of processes field. |
| [processes](./kibana-plugin-core-server.opsmetrics.processes.md) | <code>OpsProcessMetrics[]</code> | Process related metrics. Reports an array of objects for each kibana pid. |
| [requests](./kibana-plugin-core-server.opsmetrics.requests.md) | <code>OpsServerMetrics['requests']</code> | server requests stats |
| [response\_times](./kibana-plugin-core-server.opsmetrics.response_times.md) | <code>OpsServerMetrics['response_times']</code> | server response time stats |

View file

@ -4,7 +4,11 @@
## OpsMetrics.process property
Process related metrics
> Warning: This API is now obsolete.
>
>
Process related metrics. Deprecated in favor of processes field.
<b>Signature:</b>

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [OpsMetrics](./kibana-plugin-core-server.opsmetrics.md) &gt; [processes](./kibana-plugin-core-server.opsmetrics.processes.md)
## OpsMetrics.processes property
Process related metrics. Reports an array of objects for each kibana pid.
<b>Signature:</b>
```typescript
processes: OpsProcessMetrics[];
```

View file

@ -4,7 +4,7 @@
## OpsProcessMetrics.event\_loop\_delay property
node event loop delay
mean event loop delay since last collection
<b>Signature:</b>

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [OpsProcessMetrics](./kibana-plugin-core-server.opsprocessmetrics.md) &gt; [event\_loop\_delay\_histogram](./kibana-plugin-core-server.opsprocessmetrics.event_loop_delay_histogram.md)
## OpsProcessMetrics.event\_loop\_delay\_histogram property
node event loop delay histogram since last collection
<b>Signature:</b>
```typescript
event_loop_delay_histogram: IntervalHistogram;
```

View file

@ -16,7 +16,8 @@ export interface OpsProcessMetrics
| Property | Type | Description |
| --- | --- | --- |
| [event\_loop\_delay](./kibana-plugin-core-server.opsprocessmetrics.event_loop_delay.md) | <code>number</code> | node event loop delay |
| [event\_loop\_delay\_histogram](./kibana-plugin-core-server.opsprocessmetrics.event_loop_delay_histogram.md) | <code>IntervalHistogram</code> | node event loop delay histogram since last collection |
| [event\_loop\_delay](./kibana-plugin-core-server.opsprocessmetrics.event_loop_delay.md) | <code>number</code> | mean event loop delay since last collection |
| [memory](./kibana-plugin-core-server.opsprocessmetrics.memory.md) | <code>{</code><br/><code> heap: {</code><br/><code> total_in_bytes: number;</code><br/><code> used_in_bytes: number;</code><br/><code> size_limit: number;</code><br/><code> };</code><br/><code> resident_set_size_in_bytes: number;</code><br/><code> }</code> | process memory usage |
| [pid](./kibana-plugin-core-server.opsprocessmetrics.pid.md) | <code>number</code> | pid of the kibana process |
| [uptime\_in\_millis](./kibana-plugin-core-server.opsprocessmetrics.uptime_in_millis.md) | <code>number</code> | uptime of the kibana process |

View file

@ -9,6 +9,7 @@
import { StatusResponse } from '../../../../types/status';
import { httpServiceMock } from '../../../http/http_service.mock';
import { notificationServiceMock } from '../../../notifications/notifications_service.mock';
import { mocked } from '../../../../server/metrics/event_loop_delays/event_loop_delays_monitor.mocks';
import { loadStatus } from './load_status';
const mockedResponse: StatusResponse = {
@ -61,6 +62,7 @@ const mockedResponse: StatusResponse = {
},
},
process: {
pid: 1,
memory: {
heap: {
size_limit: 1000000,
@ -70,9 +72,25 @@ const mockedResponse: StatusResponse = {
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
pid: 1,
event_loop_delay_histogram: mocked.createHistogram(),
uptime_in_millis: 1,
},
processes: [
{
pid: 1,
memory: {
heap: {
size_limit: 1000000,
used_in_bytes: 100,
total_in_bytes: 0,
},
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
event_loop_delay_histogram: mocked.createHistogram(),
uptime_in_millis: 1,
},
],
response_times: {
avg_in_millis: 4000,
max_in_millis: 8000,

View file

@ -39,7 +39,6 @@ import { SavedObjectsClientContract } from '../saved_objects/types';
* const deprecations: DeprecationsDetails[] = [];
* const count = await getFooCount(savedObjectsClient);
* if (count > 0) {
* // Example of a manual correctiveAction
* deprecations.push({
* title: i18n.translate('xpack.foo.deprecations.title', {
* defaultMessage: `Foo's are deprecated`

View file

@ -378,7 +378,9 @@ export type {
OpsProcessMetrics,
MetricsServiceSetup,
MetricsServiceStart,
IntervalHistogram,
} from './metrics';
export { EventLoopDelaysMonitor } from './metrics';
export type { I18nServiceSetup } from './i18n';
export type {

View file

@ -8,8 +8,10 @@
import { MetricsCollector } from './types';
const createCollector = (collectReturnValue: any = {}): jest.Mocked<MetricsCollector<any>> => {
const collector: jest.Mocked<MetricsCollector<any>> = {
const createCollector = <T = any>(
collectReturnValue: any = {}
): jest.Mocked<MetricsCollector<T>> => {
const collector: jest.Mocked<MetricsCollector<T>> = {
collect: jest.fn().mockResolvedValue(collectReturnValue),
reset: jest.fn(),
};

View file

@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
import { MetricsCollector } from './types';
import type { MetricsCollector } from './types';
import { createMockOpsProcessMetrics } from './process.mocks';
const createMock = () => {
const mocked: jest.Mocked<MetricsCollector<any>> = {
@ -21,4 +22,5 @@ const createMock = () => {
export const collectorMock = {
create: createMock,
createOpsProcessMetrics: createMockOpsProcessMetrics,
};

View file

@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { mocked } from '../event_loop_delays/event_loop_delays_monitor.mocks';
import type { OpsProcessMetrics } from './types';
export function createMockOpsProcessMetrics(): OpsProcessMetrics {
const histogram = mocked.createHistogram();
return {
memory: {
heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 },
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
event_loop_delay_histogram: histogram,
pid: 1,
uptime_in_millis: 1,
};
}

View file

@ -9,6 +9,7 @@
import v8, { HeapInfo } from 'v8';
import { ProcessMetricsCollector } from './process';
/* eslint-disable dot-notation */
describe('ProcessMetricsCollector', () => {
let collector: ProcessMetricsCollector;
@ -20,28 +21,34 @@ describe('ProcessMetricsCollector', () => {
jest.restoreAllMocks();
});
it('collects pid from the process', async () => {
const metrics = await collector.collect();
it('collects pid from the process', () => {
const metrics = collector.collect();
expect(metrics.pid).toEqual(process.pid);
expect(metrics).toHaveLength(1);
expect(metrics[0].pid).toEqual(process.pid);
});
it('collects event loop delay', async () => {
const metrics = await collector.collect();
expect(metrics.event_loop_delay).toBeGreaterThan(0);
it('collects event loop delay', () => {
const mockEventLoopDelayMonitor = { collect: jest.fn().mockReturnValue({ mean: 13 }) };
// @ts-expect-error-next-line readonly private method.
collector['eventLoopDelayMonitor'] = mockEventLoopDelayMonitor;
const metrics = collector.collect();
expect(metrics).toHaveLength(1);
expect(metrics[0].event_loop_delay).toBe(13);
expect(mockEventLoopDelayMonitor.collect).toBeCalledTimes(1);
});
it('collects uptime info from the process', async () => {
it('collects uptime info from the process', () => {
const uptime = 58986;
jest.spyOn(process, 'uptime').mockImplementation(() => uptime);
const metrics = await collector.collect();
const metrics = collector.collect();
expect(metrics.uptime_in_millis).toEqual(uptime * 1000);
expect(metrics).toHaveLength(1);
expect(metrics[0].uptime_in_millis).toEqual(uptime * 1000);
});
it('collects memory info from the process', async () => {
it('collects memory info from the process', () => {
const heapTotal = 58986;
const heapUsed = 4688;
const heapSizeLimit = 5788;
@ -61,11 +68,12 @@ describe('ProcessMetricsCollector', () => {
} as HeapInfo)
);
const metrics = await collector.collect();
const metrics = collector.collect();
expect(metrics.memory.heap.total_in_bytes).toEqual(heapTotal);
expect(metrics.memory.heap.used_in_bytes).toEqual(heapUsed);
expect(metrics.memory.heap.size_limit).toEqual(heapSizeLimit);
expect(metrics.memory.resident_set_size_in_bytes).toEqual(rss);
expect(metrics).toHaveLength(1);
expect(metrics[0].memory.heap.total_in_bytes).toEqual(heapTotal);
expect(metrics[0].memory.heap.used_in_bytes).toEqual(heapUsed);
expect(metrics[0].memory.heap.size_limit).toEqual(heapSizeLimit);
expect(metrics[0].memory.resident_set_size_in_bytes).toEqual(rss);
});
});

View file

@ -7,14 +7,26 @@
*/
import v8 from 'v8';
import { Bench } from '@hapi/hoek';
import { OpsProcessMetrics, MetricsCollector } from './types';
import { EventLoopDelaysMonitor } from '../event_loop_delays';
export class ProcessMetricsCollector implements MetricsCollector<OpsProcessMetrics> {
public async collect(): Promise<OpsProcessMetrics> {
export class ProcessMetricsCollector implements MetricsCollector<OpsProcessMetrics[]> {
static getMainThreadMetrics(processes: OpsProcessMetrics[]): undefined | OpsProcessMetrics {
/**
* Currently Kibana does not support multi-processes.
* Once we have multiple processes we can add a `name` field
* and filter on `name === 'server_worker'` to get the main thread.
*/
return processes[0];
}
private readonly eventLoopDelayMonitor = new EventLoopDelaysMonitor();
private getCurrentPidMetrics(): OpsProcessMetrics {
const eventLoopDelayHistogram = this.eventLoopDelayMonitor.collect();
const heapStats = v8.getHeapStatistics();
const memoryUsage = process.memoryUsage();
const [eventLoopDelay] = await Promise.all([getEventLoopDelay()]);
return {
memory: {
heap: {
@ -25,19 +37,17 @@ export class ProcessMetricsCollector implements MetricsCollector<OpsProcessMetri
resident_set_size_in_bytes: memoryUsage.rss,
},
pid: process.pid,
event_loop_delay: eventLoopDelay,
event_loop_delay: eventLoopDelayHistogram.mean,
event_loop_delay_histogram: eventLoopDelayHistogram,
uptime_in_millis: process.uptime() * 1000,
};
}
public reset() {}
}
public collect(): OpsProcessMetrics[] {
return [this.getCurrentPidMetrics()];
}
const getEventLoopDelay = (): Promise<number> => {
const bench = new Bench();
return new Promise((resolve) => {
setImmediate(() => {
return resolve(bench.elapsed());
});
});
};
public reset() {
this.eventLoopDelayMonitor.reset();
}
}

View file

@ -5,11 +5,13 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { MaybePromise } from '@kbn/utility-types';
import type { IntervalHistogram } from '../types';
/** Base interface for all metrics gatherers */
export interface MetricsCollector<T> {
/** collect the data currently gathered by the collector */
collect(): Promise<T>;
collect(): MaybePromise<T>;
/** reset the internal state of the collector */
reset(): void;
}
@ -19,6 +21,8 @@ export interface MetricsCollector<T> {
* @public
*/
export interface OpsProcessMetrics {
/** pid of the kibana process */
pid: number;
/** process memory usage */
memory: {
/** heap memory usage */
@ -33,10 +37,10 @@ export interface OpsProcessMetrics {
/** node rss */
resident_set_size_in_bytes: number;
};
/** node event loop delay */
/** mean event loop delay since last collection*/
event_loop_delay: number;
/** pid of the kibana process */
pid: number;
/** node event loop delay histogram since last collection */
event_loop_delay_histogram: IntervalHistogram;
/** uptime of the kibana process */
uptime_in_millis: number;
}

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { mocked } from '../event_loop_delays_monitor.mocks';
export const monitorEventLoopDelay = jest.fn().mockImplementation(() => {
const mockedHistogram = mocked.createHistogram();
return {
...mockedHistogram,
enable: jest.fn(),
percentile: jest.fn().mockImplementation((percentile: number) => {
return (mockedHistogram.percentiles as Record<string, number | undefined>)[`${percentile}`];
}),
disable: jest.fn(),
reset: jest.fn(),
};
});

View file

@ -6,26 +6,11 @@
* Side Public License, v 1.
*/
import moment from 'moment';
import type { IntervalHistogram } from './event_loop_delays';
export const mockMonitorEnable = jest.fn();
export const mockMonitorPercentile = jest.fn();
export const mockMonitorReset = jest.fn();
export const mockMonitorDisable = jest.fn();
export const monitorEventLoopDelay = jest.fn().mockReturnValue({
enable: mockMonitorEnable,
percentile: mockMonitorPercentile,
disable: mockMonitorDisable,
reset: mockMonitorReset,
...createMockHistogram(),
});
jest.doMock('perf_hooks', () => ({
monitorEventLoopDelay,
}));
import type { EventLoopDelaysMonitor } from './event_loop_delays_monitor';
import type { IntervalHistogram } from '../types';
function createMockHistogram(overwrites: Partial<IntervalHistogram> = {}): IntervalHistogram {
const now = moment();
const now = Date.now();
return {
min: 9093120,
@ -33,8 +18,8 @@ function createMockHistogram(overwrites: Partial<IntervalHistogram> = {}): Inter
mean: 11993238.600747818,
exceeds: 0,
stddev: 1168191.9357543814,
fromTimestamp: now.startOf('day').toISOString(),
lastUpdatedAt: now.toISOString(),
fromTimestamp: moment(now).toISOString(),
lastUpdatedAt: moment(now).toISOString(),
percentiles: {
'50': 12607487,
'75': 12615679,
@ -45,6 +30,22 @@ function createMockHistogram(overwrites: Partial<IntervalHistogram> = {}): Inter
};
}
function createMockEventLoopDelaysMonitor() {
const mockCollect = jest.fn();
const MockEventLoopDelaysMonitor: jest.MockedClass<
typeof EventLoopDelaysMonitor
> = jest.fn().mockReturnValue({
collect: mockCollect,
reset: jest.fn(),
stop: jest.fn(),
});
mockCollect.mockReturnValue(createMockHistogram());
return new MockEventLoopDelaysMonitor();
}
export const mocked = {
createHistogram: createMockHistogram,
createEventLoopDelaysMonitor: createMockEventLoopDelaysMonitor,
};

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
/* eslint-disable dot-notation */
jest.mock('perf_hooks');
import { monitorEventLoopDelay } from 'perf_hooks';
import { EventLoopDelaysMonitor } from './event_loop_delays_monitor';
import { mocked } from './event_loop_delays_monitor.mocks';
describe('EventLoopDelaysMonitor', () => {
beforeAll(() => {
jest.useFakeTimers('modern');
const mockNow = jest.getRealSystemTime();
jest.setSystemTime(mockNow);
});
afterEach(() => jest.clearAllMocks());
afterAll(() => jest.useRealTimers());
test('#constructor enables monitoring', () => {
const eventLoopDelaysMonitor = new EventLoopDelaysMonitor();
expect(monitorEventLoopDelay).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor['loopMonitor'].enable).toBeCalledTimes(1);
});
test('#collect returns event loop delays histogram', () => {
const eventLoopDelaysMonitor = new EventLoopDelaysMonitor();
expect(eventLoopDelaysMonitor['loopMonitor'].disable).toBeCalledTimes(0);
expect(eventLoopDelaysMonitor['loopMonitor'].enable).toBeCalledTimes(1);
const histogramData = eventLoopDelaysMonitor.collect();
expect(eventLoopDelaysMonitor['loopMonitor'].disable).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor['loopMonitor'].enable).toBeCalledTimes(2);
expect(eventLoopDelaysMonitor['loopMonitor'].percentile).toHaveBeenNthCalledWith(1, 50);
expect(eventLoopDelaysMonitor['loopMonitor'].percentile).toHaveBeenNthCalledWith(2, 75);
expect(eventLoopDelaysMonitor['loopMonitor'].percentile).toHaveBeenNthCalledWith(3, 95);
expect(eventLoopDelaysMonitor['loopMonitor'].percentile).toHaveBeenNthCalledWith(4, 99);
// mocked perf_hook returns `mocked.createHistogram()`.
// This ensures that the wiring of the `collect` function is correct.
const mockedHistogram = mocked.createHistogram();
expect(histogramData).toEqual(mockedHistogram);
});
test('#reset resets histogram data', () => {
const eventLoopDelaysMonitor = new EventLoopDelaysMonitor();
eventLoopDelaysMonitor.reset();
expect(eventLoopDelaysMonitor['loopMonitor'].reset).toBeCalledTimes(1);
});
test('#stop disables monitoring event loop delays', () => {
const eventLoopDelaysMonitor = new EventLoopDelaysMonitor();
expect(eventLoopDelaysMonitor['loopMonitor'].disable).toBeCalledTimes(0);
eventLoopDelaysMonitor.stop();
expect(eventLoopDelaysMonitor['loopMonitor'].disable).toBeCalledTimes(1);
});
});

View file

@ -8,48 +8,41 @@
import type { EventLoopDelayMonitor } from 'perf_hooks';
import { monitorEventLoopDelay } from 'perf_hooks';
import { MONITOR_EVENT_LOOP_DELAYS_RESOLUTION } from './constants';
import type { IntervalHistogram } from '../types';
export interface IntervalHistogram {
fromTimestamp: string;
lastUpdatedAt: string;
min: number;
max: number;
mean: number;
exceeds: number;
stddev: number;
percentiles: {
50: number;
75: number;
95: number;
99: number;
};
}
export class EventLoopDelaysCollector {
export class EventLoopDelaysMonitor {
private readonly loopMonitor: EventLoopDelayMonitor;
private fromTimestamp: Date;
/**
* Creating a new instance from EventLoopDelaysMonitor will
* automatically start tracking event loop delays.
*/
constructor() {
const monitor = monitorEventLoopDelay({
resolution: MONITOR_EVENT_LOOP_DELAYS_RESOLUTION,
});
const monitor = monitorEventLoopDelay();
monitor.enable();
this.fromTimestamp = new Date();
this.loopMonitor = monitor;
}
/**
* Collect gathers event loop delays metrics from nodejs perf_hooks.monitorEventLoopDelay
* the histogram calculations start from the last time `reset` was called or this
* EventLoopDelaysMonitor instance was created.
* @returns {IntervalHistogram}
*/
public collect(): IntervalHistogram {
const lastUpdated = new Date();
this.loopMonitor.disable();
const { min, max, mean, exceeds, stddev } = this.loopMonitor;
return {
const collectedData: IntervalHistogram = {
min,
max,
mean,
exceeds,
stddev,
fromTimestamp: this.fromTimestamp.toISOString(),
lastUpdatedAt: new Date().toISOString(),
lastUpdatedAt: lastUpdated.toISOString(),
percentiles: {
50: this.loopMonitor.percentile(50),
75: this.loopMonitor.percentile(75),
@ -57,13 +50,22 @@ export class EventLoopDelaysCollector {
99: this.loopMonitor.percentile(99),
},
};
this.loopMonitor.enable();
return collectedData;
}
/**
* Resets the collected histogram data.
*/
public reset() {
this.loopMonitor.reset();
this.fromTimestamp = new Date();
}
/**
* Disables updating the interval timer for collecting new data points.
*/
public stop() {
this.loopMonitor.disable();
}

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { EventLoopDelaysMonitor } from './event_loop_delays_monitor';

View file

@ -12,8 +12,10 @@ export type {
MetricsServiceSetup,
MetricsServiceStart,
OpsMetrics,
IntervalHistogram,
} from './types';
export type { OpsProcessMetrics, OpsServerMetrics, OpsOsMetrics } from './collectors';
export { MetricsService } from './metrics_service';
export { opsConfig } from './ops_config';
export type { OpsConfigType } from './ops_config';
export { EventLoopDelaysMonitor } from './event_loop_delays';

View file

@ -8,19 +8,15 @@
import { OpsMetrics } from '..';
import { getEcsOpsMetricsLog } from './get_ops_metrics_log';
import { collectorMock } from '../collectors/mocks';
function createBaseOpsMetrics(): OpsMetrics {
const mockProcess = collectorMock.createOpsProcessMetrics();
return {
collected_at: new Date('2020-01-01 01:00:00'),
process: {
memory: {
heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 },
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
pid: 1,
uptime_in_millis: 1,
},
process: mockProcess,
processes: [mockProcess],
os: {
platform: 'darwin' as const,
platformRelease: 'test',

View file

@ -8,8 +8,9 @@
import { BehaviorSubject } from 'rxjs';
import type { PublicMethodsOf } from '@kbn/utility-types';
import type { MetricsService } from './metrics_service';
import { collectorMock } from './collectors/mocks';
import { mocked as eventLoopDelaysMonitorMock } from './event_loop_delays/event_loop_delays_monitor.mocks';
import {
InternalMetricsServiceSetup,
InternalMetricsServiceStart,
@ -22,18 +23,14 @@ const createInternalSetupContractMock = () => {
collectionInterval: 30000,
getOpsMetrics$: jest.fn(),
};
const processMock = collectorMock.createOpsProcessMetrics();
setupContract.getOpsMetrics$.mockReturnValue(
new BehaviorSubject({
collected_at: new Date('2020-01-01 01:00:00'),
process: {
memory: {
heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 },
resident_set_size_in_bytes: 1,
},
event_loop_delay: 1,
pid: 1,
uptime_in_millis: 1,
},
process: processMock,
processes: [processMock],
os: {
platform: 'darwin' as const,
platformRelease: 'test',
@ -81,4 +78,5 @@ export const metricsServiceMock = {
createStartContract: createStartContractMock,
createInternalSetupContract: createInternalSetupContractMock,
createInternalStartContract: createInternalStartContractMock,
createEventLoopDelaysMonitor: eventLoopDelaysMonitorMock.createEventLoopDelaysMonitor,
};

View file

@ -28,7 +28,7 @@ describe('OpsMetricsCollector', () => {
describe('#collect', () => {
it('gathers metrics from the underlying collectors', async () => {
mockOsCollector.collect.mockResolvedValue('osMetrics');
mockProcessCollector.collect.mockResolvedValue('processMetrics');
mockProcessCollector.collect.mockResolvedValue(['processMetrics']);
mockServerCollector.collect.mockResolvedValue({
requests: 'serverRequestsMetrics',
response_times: 'serverTimingMetrics',
@ -43,6 +43,7 @@ describe('OpsMetricsCollector', () => {
expect(metrics).toEqual({
collected_at: expect.any(Date),
process: 'processMetrics',
processes: ['processMetrics'],
os: 'osMetrics',
requests: 'serverRequestsMetrics',
response_times: 'serverTimingMetrics',

View file

@ -28,14 +28,21 @@ export class OpsMetricsCollector implements MetricsCollector<OpsMetrics> {
}
public async collect(): Promise<OpsMetrics> {
const [process, os, server] = await Promise.all([
const [processes, os, server] = await Promise.all([
this.processCollector.collect(),
this.osCollector.collect(),
this.serverCollector.collect(),
]);
return {
collected_at: new Date(),
process,
/**
* Kibana does not yet support multi-process nodes.
* `processes` is just an Array(1) only returning the current process's data
* which is why we can just use processes[0] for `process`
*/
process: processes[0],
processes,
os,
...server,
};

View file

@ -7,7 +7,7 @@
*/
import { Observable } from 'rxjs';
import { OpsProcessMetrics, OpsOsMetrics, OpsServerMetrics } from './collectors';
import type { OpsProcessMetrics, OpsOsMetrics, OpsServerMetrics } from './collectors';
/**
* APIs to retrieves metrics gathered and exposed by the core platform.
@ -51,8 +51,13 @@ export type InternalMetricsServiceStart = MetricsServiceStart;
export interface OpsMetrics {
/** Time metrics were recorded at. */
collected_at: Date;
/** Process related metrics */
/**
* Process related metrics.
* @deprecated use the processes field instead.
*/
process: OpsProcessMetrics;
/** Process related metrics. Reports an array of objects for each kibana pid.*/
processes: OpsProcessMetrics[];
/** OS related metrics */
os: OpsOsMetrics;
/** server response time stats */
@ -62,3 +67,37 @@ export interface OpsMetrics {
/** number of current concurrent connections to the server */
concurrent_connections: OpsServerMetrics['concurrent_connections'];
}
/**
* an IntervalHistogram object that samples and reports the event loop delay over time.
* The delays will be reported in nanoseconds.
*
* @public
*/
export interface IntervalHistogram {
// The first timestamp the interval timer kicked in for collecting data points.
fromTimestamp: string;
// Last timestamp the interval timer kicked in for collecting data points.
lastUpdatedAt: string;
// The minimum recorded event loop delay.
min: number;
// The maximum recorded event loop delay.
max: number;
// The mean of the recorded event loop delays.
mean: number;
// The number of times the event loop delay exceeded the maximum 1 hour event loop delay threshold.
exceeds: number;
// The standard deviation of the recorded event loop delays.
stddev: number;
// An object detailing the accumulated percentile distribution.
percentiles: {
// 50th percentile of delays of the collected data points.
50: number;
// 75th percentile of delays of the collected data points.
75: number;
// 95th percentile of delays of the collected data points.
95: number;
// 99th percentile of delays of the collected data points.
99: number;
};
}

View file

@ -915,6 +915,16 @@ export interface ErrorHttpResponseOptions {
headers?: ResponseHeaders;
}
// Warning: (ae-missing-release-tag) "EventLoopDelaysMonitor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export class EventLoopDelaysMonitor {
constructor();
collect(): IntervalHistogram;
reset(): void;
stop(): void;
}
// @public (undocumented)
export interface ExecutionContextSetup {
withContext<R>(context: KibanaExecutionContext | undefined, fn: (...args: any[]) => R): R;
@ -1148,6 +1158,31 @@ export interface IKibanaSocket {
}): Promise<void>;
}
// @public
export interface IntervalHistogram {
// (undocumented)
exceeds: number;
// (undocumented)
fromTimestamp: string;
// (undocumented)
lastUpdatedAt: string;
// (undocumented)
max: number;
// (undocumented)
mean: number;
// (undocumented)
min: number;
// (undocumented)
percentiles: {
50: number;
75: number;
95: number;
99: number;
};
// (undocumented)
stddev: number;
}
// @public (undocumented)
export interface IRenderOptions {
includeUserSettings?: boolean;
@ -1433,7 +1468,9 @@ export interface OpsMetrics {
collected_at: Date;
concurrent_connections: OpsServerMetrics['concurrent_connections'];
os: OpsOsMetrics;
// @deprecated
process: OpsProcessMetrics;
processes: OpsProcessMetrics[];
requests: OpsServerMetrics['requests'];
response_times: OpsServerMetrics['response_times'];
}
@ -1474,6 +1511,7 @@ export interface OpsOsMetrics {
// @public
export interface OpsProcessMetrics {
event_loop_delay: number;
event_loop_delay_histogram: IntervalHistogram;
memory: {
heap: {
total_in_bytes: number;

View file

@ -106,6 +106,7 @@ export const registerStatusRoute = ({ router, config, metrics, status }: Deps) =
collection_interval_in_millis: metrics.collectionInterval,
os: lastMetrics.os,
process: lastMetrics.process,
processes: lastMetrics.processes,
response_times: lastMetrics.response_times,
concurrent_connections: lastMetrics.concurrent_connections,
requests: {

View file

@ -31,11 +31,6 @@ export const MONITOR_EVENT_LOOP_DELAYS_RESET = 24 * 60 * 60 * 1000;
*/
export const MONITOR_EVENT_LOOP_DELAYS_START = 1 * 60 * 1000;
/**
* Event loop monitoring sampling rate in milliseconds.
*/
export const MONITOR_EVENT_LOOP_DELAYS_RESOLUTION = 10;
/**
* Mean event loop delay threshold for logging a warning.
*/

View file

@ -1,63 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import {
mockMonitorEnable,
mockMonitorPercentile,
monitorEventLoopDelay,
mockMonitorReset,
mockMonitorDisable,
} from './event_loop_delays.mocks';
import { EventLoopDelaysCollector } from './event_loop_delays';
describe('EventLoopDelaysCollector', () => {
jest.useFakeTimers('modern');
const mockNow = jest.getRealSystemTime();
jest.setSystemTime(mockNow);
beforeEach(() => jest.clearAllMocks());
afterAll(() => jest.useRealTimers());
test('#constructor enables monitoring', () => {
new EventLoopDelaysCollector();
expect(monitorEventLoopDelay).toBeCalledWith({ resolution: 10 });
expect(mockMonitorEnable).toBeCalledTimes(1);
});
test('#collect returns event loop delays histogram', () => {
const eventLoopDelaysCollector = new EventLoopDelaysCollector();
const histogramData = eventLoopDelaysCollector.collect();
expect(mockMonitorPercentile).toHaveBeenNthCalledWith(1, 50);
expect(mockMonitorPercentile).toHaveBeenNthCalledWith(2, 75);
expect(mockMonitorPercentile).toHaveBeenNthCalledWith(3, 95);
expect(mockMonitorPercentile).toHaveBeenNthCalledWith(4, 99);
expect(Object.keys(histogramData)).toMatchInlineSnapshot(`
Array [
"min",
"max",
"mean",
"exceeds",
"stddev",
"fromTimestamp",
"lastUpdatedAt",
"percentiles",
]
`);
});
test('#reset resets histogram data', () => {
const eventLoopDelaysCollector = new EventLoopDelaysCollector();
eventLoopDelaysCollector.reset();
expect(mockMonitorReset).toBeCalledTimes(1);
});
test('#stop disables monitoring event loop delays', () => {
const eventLoopDelaysCollector = new EventLoopDelaysCollector();
eventLoopDelaysCollector.stop();
expect(mockMonitorDisable).toBeCalledTimes(1);
});
});

View file

@ -14,7 +14,7 @@ import {
createRootWithCorePlugins,
} from '../../../../../../../core/test_helpers/kbn_server';
import { rollDailyData } from '../daily';
import { mocked } from '../../event_loop_delays.mocks';
import { metricsServiceMock } from '../../../../../../../core/server/mocks';
import {
SAVED_OBJECTS_DAILY_TYPE,
@ -26,18 +26,20 @@ import moment from 'moment';
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
});
const eventLoopDelaysMonitor = metricsServiceMock.createEventLoopDelaysMonitor();
function createRawObject(date: moment.MomentInput) {
const pid = Math.round(Math.random() * 10000);
const instanceUuid = 'mock_instance';
return {
type: SAVED_OBJECTS_DAILY_TYPE,
id: serializeSavedObjectId({ pid, date }),
id: serializeSavedObjectId({ pid, date, instanceUuid }),
attributes: {
...mocked.createHistogram({
fromTimestamp: moment(date).startOf('day').toISOString(),
lastUpdatedAt: moment(date).toISOString(),
}),
...eventLoopDelaysMonitor.collect(),
fromTimestamp: moment(date).startOf('day').toISOString(),
lastUpdatedAt: moment(date).toISOString(),
processId: pid,
instanceUuid,
},
};
}

View file

@ -11,21 +11,19 @@ import {
serializeSavedObjectId,
deleteHistogramSavedObjects,
} from './saved_objects';
import { savedObjectsRepositoryMock } from '../../../../../core/server/mocks';
import { savedObjectsRepositoryMock, metricsServiceMock } from '../../../../../core/server/mocks';
import type { SavedObjectsFindResponse } from '../../../../../core/server/';
import { mocked } from './event_loop_delays.mocks';
describe('serializeSavedObjectId', () => {
it('returns serialized id', () => {
const id = serializeSavedObjectId({ date: 1623233091278, pid: 123 });
expect(id).toBe('123::09062021');
const id = serializeSavedObjectId({ instanceUuid: 'mock_uuid', date: 1623233091278, pid: 123 });
expect(id).toBe('mock_uuid::123::09062021');
});
});
describe('storeHistogram', () => {
const mockHistogram = mocked.createHistogram();
const eventLoopDelaysMonitor = metricsServiceMock.createEventLoopDelaysMonitor();
const mockInternalRepository = savedObjectsRepositoryMock.create();
jest.useFakeTimers('modern');
const mockNow = jest.getRealSystemTime();
jest.setSystemTime(mockNow);
@ -34,13 +32,15 @@ describe('storeHistogram', () => {
afterAll(() => jest.useRealTimers());
it('stores histogram data in a savedObject', async () => {
await storeHistogram(mockHistogram, mockInternalRepository);
const mockHistogram = eventLoopDelaysMonitor.collect();
const instanceUuid = 'mock_uuid';
await storeHistogram(mockHistogram, mockInternalRepository, instanceUuid);
const pid = process.pid;
const id = serializeSavedObjectId({ date: mockNow, pid });
const id = serializeSavedObjectId({ date: mockNow, pid, instanceUuid });
expect(mockInternalRepository.create).toBeCalledWith(
'event_loop_delays_daily',
{ ...mockHistogram, processId: pid },
{ ...mockHistogram, processId: pid, instanceUuid },
{ id, overwrite: true }
);
});

View file

@ -12,12 +12,12 @@ import type {
ISavedObjectsRepository,
} from 'kibana/server';
import moment from 'moment';
import type { IntervalHistogram } from './event_loop_delays';
import type { IntervalHistogram } from 'kibana/server';
export const SAVED_OBJECTS_DAILY_TYPE = 'event_loop_delays_daily';
export interface EventLoopDelaysDaily extends SavedObjectAttributes, IntervalHistogram {
processId: number;
instanceUuid: string;
}
export function registerSavedObjectTypes(registerType: SavedObjectsServiceSetup['registerType']) {
@ -35,10 +35,18 @@ export function registerSavedObjectTypes(registerType: SavedObjectsServiceSetup[
});
}
export function serializeSavedObjectId({ date, pid }: { date: moment.MomentInput; pid: number }) {
export function serializeSavedObjectId({
date,
pid,
instanceUuid,
}: {
date: moment.MomentInput;
pid: number;
instanceUuid: string;
}) {
const formattedDate = moment(date).format('DDMMYYYY');
return `${pid}::${formattedDate}`;
return `${instanceUuid}::${pid}::${formattedDate}`;
}
export async function deleteHistogramSavedObjects(
@ -59,14 +67,15 @@ export async function deleteHistogramSavedObjects(
export async function storeHistogram(
histogram: IntervalHistogram,
internalRepository: ISavedObjectsRepository
internalRepository: ISavedObjectsRepository,
instanceUuid: string
) {
const pid = process.pid;
const id = serializeSavedObjectId({ date: histogram.lastUpdatedAt, pid });
const id = serializeSavedObjectId({ date: histogram.lastUpdatedAt, pid, instanceUuid });
return await internalRepository.create<EventLoopDelaysDaily>(
SAVED_OBJECTS_DAILY_TYPE,
{ ...histogram, processId: pid },
{ ...histogram, processId: pid, instanceUuid },
{ id, overwrite: true }
);
}

View file

@ -11,6 +11,7 @@ import { MakeSchemaFrom } from 'src/plugins/usage_collection/server';
export interface EventLoopDelaysUsageReport {
daily: Array<{
processId: number;
instanceUuid: string;
lastUpdatedAt: string;
fromTimestamp: string;
min: number;
@ -37,6 +38,12 @@ export const eventLoopDelaysUsageSchema: MakeSchemaFrom<EventLoopDelaysUsageRepo
description: 'The process id of the monitored kibana instance.',
},
},
instanceUuid: {
type: 'keyword',
_meta: {
description: 'The uuid of the kibana instance.',
},
},
fromTimestamp: {
type: 'date',
_meta: {

View file

@ -7,43 +7,49 @@
*/
import { Subject } from 'rxjs';
import {
mockMonitorPercentile,
monitorEventLoopDelay,
mockMonitorReset,
mockMonitorDisable,
} from './event_loop_delays.mocks';
import { savedObjectsRepositoryMock } from '../../../../../core/server/mocks';
import { savedObjectsRepositoryMock, metricsServiceMock } from '../../../../../core/server/mocks';
import { startTrackingEventLoopDelaysUsage } from './track_delays';
describe('startTrackingEventLoopDelaysUsage', () => {
const eventLoopDelaysMonitor = metricsServiceMock.createEventLoopDelaysMonitor();
const mockInternalRepository = savedObjectsRepositoryMock.create();
const stopMonitoringEventLoop$ = new Subject<void>();
const instanceUuid = 'mock_uuid';
beforeAll(() => jest.useFakeTimers('modern'));
beforeEach(() => jest.clearAllMocks());
afterEach(() => stopMonitoringEventLoop$.next());
it('initializes EventLoopDelaysCollector and starts timer', () => {
it('collects eventLoopDelaysMonitor metrics after start delay', () => {
const collectionStartDelay = 1000;
startTrackingEventLoopDelaysUsage(mockInternalRepository, stopMonitoringEventLoop$, {
collectionStartDelay,
});
startTrackingEventLoopDelaysUsage(
mockInternalRepository,
instanceUuid,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
collectionStartDelay,
}
);
expect(monitorEventLoopDelay).toBeCalledTimes(1);
expect(mockMonitorPercentile).toBeCalledTimes(0);
expect(eventLoopDelaysMonitor.collect).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionStartDelay);
expect(mockMonitorPercentile).toBeCalled();
expect(eventLoopDelaysMonitor.collect).toBeCalledTimes(1);
});
it('stores event loop delays every collectionInterval duration', () => {
const collectionStartDelay = 100;
const collectionInterval = 1000;
startTrackingEventLoopDelaysUsage(mockInternalRepository, stopMonitoringEventLoop$, {
collectionStartDelay,
collectionInterval,
});
startTrackingEventLoopDelaysUsage(
mockInternalRepository,
instanceUuid,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
collectionStartDelay,
collectionInterval,
}
);
expect(mockInternalRepository.create).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionStartDelay);
@ -54,28 +60,39 @@ describe('startTrackingEventLoopDelaysUsage', () => {
expect(mockInternalRepository.create).toBeCalledTimes(3);
});
it('resets histogram every histogramReset duration', () => {
it('resets eventLoopDelaysMonitor every histogramReset duration', () => {
const collectionStartDelay = 0;
const collectionInterval = 1000;
const histogramReset = 5000;
startTrackingEventLoopDelaysUsage(mockInternalRepository, stopMonitoringEventLoop$, {
collectionStartDelay,
collectionInterval,
histogramReset,
});
expect(mockMonitorReset).toBeCalledTimes(0);
startTrackingEventLoopDelaysUsage(
mockInternalRepository,
instanceUuid,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
collectionStartDelay,
collectionInterval,
histogramReset,
}
);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionInterval * 5);
expect(mockMonitorReset).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(1);
jest.advanceTimersByTime(collectionInterval * 5);
expect(mockMonitorReset).toBeCalledTimes(2);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(2);
});
it('stops monitoring event loop delays once stopMonitoringEventLoop$.next is called', () => {
startTrackingEventLoopDelaysUsage(mockInternalRepository, stopMonitoringEventLoop$);
expect(mockMonitorDisable).toBeCalledTimes(0);
startTrackingEventLoopDelaysUsage(
mockInternalRepository,
instanceUuid,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor
);
expect(eventLoopDelaysMonitor.stop).toBeCalledTimes(0);
stopMonitoringEventLoop$.next();
expect(mockMonitorDisable).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor.stop).toBeCalledTimes(1);
});
});

View file

@ -9,13 +9,13 @@
import { takeUntil, finalize, map } from 'rxjs/operators';
import { Observable, timer } from 'rxjs';
import type { ISavedObjectsRepository } from 'kibana/server';
import type { EventLoopDelaysMonitor } from '../../../../../core/server';
import {
MONITOR_EVENT_LOOP_DELAYS_START,
MONITOR_EVENT_LOOP_DELAYS_INTERVAL,
MONITOR_EVENT_LOOP_DELAYS_RESET,
} from './constants';
import { storeHistogram } from './saved_objects';
import { EventLoopDelaysCollector } from './event_loop_delays';
/**
* The monitoring of the event loop starts immediately.
@ -24,7 +24,9 @@ import { EventLoopDelaysCollector } from './event_loop_delays';
*/
export function startTrackingEventLoopDelaysUsage(
internalRepository: ISavedObjectsRepository,
instanceUuid: string,
stopMonitoringEventLoop$: Observable<void>,
eventLoopDelaysMonitor: EventLoopDelaysMonitor,
configs: {
collectionStartDelay?: number;
collectionInterval?: number;
@ -37,20 +39,19 @@ export function startTrackingEventLoopDelaysUsage(
histogramReset = MONITOR_EVENT_LOOP_DELAYS_RESET,
} = configs;
const eventLoopDelaysCollector = new EventLoopDelaysCollector();
const resetOnCount = Math.ceil(histogramReset / collectionInterval);
timer(collectionStartDelay, collectionInterval)
.pipe(
map((i) => (i + 1) % resetOnCount === 0),
takeUntil(stopMonitoringEventLoop$),
finalize(() => eventLoopDelaysCollector.stop())
finalize(() => eventLoopDelaysMonitor.stop())
)
.subscribe(async (shouldReset) => {
const histogram = eventLoopDelaysCollector.collect();
const histogram = eventLoopDelaysMonitor.collect();
if (shouldReset) {
eventLoopDelaysCollector.reset();
eventLoopDelaysMonitor.reset();
}
await storeHistogram(histogram, internalRepository);
await storeHistogram(histogram, internalRepository, instanceUuid);
});
}

View file

@ -7,13 +7,8 @@
*/
import { Subject } from 'rxjs';
import {
mockMonitorPercentile,
monitorEventLoopDelay,
mockMonitorReset,
} from './event_loop_delays.mocks';
import { loggingSystemMock, metricsServiceMock } from '../../../../../core/server/mocks';
import { startTrackingEventLoopDelaysThreshold } from './track_threshold';
import { loggingSystemMock } from '../../../../../core/server/mocks';
import { usageCountersServiceMock } from '../../../../usage_collection/server/usage_counters/usage_counters_service.mock';
describe('startTrackingEventLoopDelaysThreshold', () => {
@ -21,6 +16,7 @@ describe('startTrackingEventLoopDelaysThreshold', () => {
const stopMonitoringEventLoop$ = new Subject<void>();
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
const mockEventLoopCounter = mockUsageCountersSetup.createUsageCounter('testCounter');
const eventLoopDelaysMonitor = metricsServiceMock.createEventLoopDelaysMonitor();
beforeAll(() => jest.useFakeTimers('modern'));
beforeEach(() => jest.clearAllMocks());
@ -29,15 +25,20 @@ describe('startTrackingEventLoopDelaysThreshold', () => {
it('initializes EventLoopDelaysCollector and starts timer', () => {
const collectionStartDelay = 1000;
const warnThreshold = 1000;
startTrackingEventLoopDelaysThreshold(mockEventLoopCounter, logger, stopMonitoringEventLoop$, {
warnThreshold,
collectionStartDelay,
});
startTrackingEventLoopDelaysThreshold(
mockEventLoopCounter,
logger,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
warnThreshold,
collectionStartDelay,
}
);
expect(monitorEventLoopDelay).toBeCalledTimes(1);
expect(mockMonitorPercentile).toBeCalledTimes(0);
expect(eventLoopDelaysMonitor.collect).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionStartDelay);
expect(mockMonitorPercentile).toBeCalled();
expect(eventLoopDelaysMonitor.collect).toBeCalledTimes(1);
});
it('logs a warning and increments usage counter when the mean delay exceeds the threshold', () => {
@ -45,48 +46,60 @@ describe('startTrackingEventLoopDelaysThreshold', () => {
const collectionInterval = 1000;
const warnThreshold = 10;
startTrackingEventLoopDelaysThreshold(mockEventLoopCounter, logger, stopMonitoringEventLoop$, {
warnThreshold,
collectionStartDelay,
collectionInterval,
});
startTrackingEventLoopDelaysThreshold(
mockEventLoopCounter,
logger,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
warnThreshold,
collectionStartDelay,
collectionInterval,
}
);
expect(logger.warn).toBeCalledTimes(0);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(0);
expect(mockMonitorReset).toBeCalledTimes(0);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionStartDelay);
expect(logger.warn).toBeCalledTimes(1);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(1);
expect(mockMonitorReset).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(1);
jest.advanceTimersByTime(collectionInterval);
expect(logger.warn).toBeCalledTimes(2);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(2);
expect(mockMonitorReset).toBeCalledTimes(2);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(2);
jest.advanceTimersByTime(collectionInterval);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(3);
expect(logger.warn).toBeCalledTimes(3);
expect(mockMonitorReset).toBeCalledTimes(3);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(3);
});
it('does not log warning or increment usage if threshold did not exceed mean delay', () => {
const collectionStartDelay = 100;
const warnThreshold = 15;
startTrackingEventLoopDelaysThreshold(mockEventLoopCounter, logger, stopMonitoringEventLoop$, {
warnThreshold,
collectionStartDelay,
});
startTrackingEventLoopDelaysThreshold(
mockEventLoopCounter,
logger,
stopMonitoringEventLoop$,
eventLoopDelaysMonitor,
{
warnThreshold,
collectionStartDelay,
}
);
expect(logger.warn).toBeCalledTimes(0);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(0);
expect(mockMonitorReset).toBeCalledTimes(0);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(0);
jest.advanceTimersByTime(collectionStartDelay);
expect(logger.warn).toBeCalledTimes(0);
expect(mockEventLoopCounter.incrementCounter).toBeCalledTimes(0);
expect(mockMonitorReset).toBeCalledTimes(1);
expect(eventLoopDelaysMonitor.reset).toBeCalledTimes(1);
});
});

View file

@ -17,7 +17,7 @@ import {
MONITOR_EVENT_LOOP_WARN_THRESHOLD,
ONE_MILLISECOND_AS_NANOSECONDS,
} from './constants';
import { EventLoopDelaysCollector } from './event_loop_delays';
import type { EventLoopDelaysMonitor } from '../../../../../core/server';
/**
* The monitoring of the event loop starts immediately.
@ -29,6 +29,7 @@ export function startTrackingEventLoopDelaysThreshold(
eventLoopCounter: UsageCounter,
logger: Logger,
stopMonitoringEventLoop$: Observable<void>,
eventLoopDelaysMonitor: EventLoopDelaysMonitor,
configs: {
warnThreshold?: number;
collectionStartDelay?: number;
@ -41,14 +42,13 @@ export function startTrackingEventLoopDelaysThreshold(
collectionInterval = MONITOR_EVENT_LOOP_THRESHOLD_INTERVAL,
} = configs;
const eventLoopDelaysCollector = new EventLoopDelaysCollector();
timer(collectionStartDelay, collectionInterval)
.pipe(
takeUntil(stopMonitoringEventLoop$),
finalize(() => eventLoopDelaysCollector.stop())
finalize(() => eventLoopDelaysMonitor.stop())
)
.subscribe(async () => {
const { mean } = eventLoopDelaysCollector.collect();
const { mean } = eventLoopDelaysMonitor.collect();
const meanDurationMs = moment
.duration(mean / ONE_MILLISECOND_AS_NANOSECONDS)
.asMilliseconds();
@ -64,6 +64,6 @@ export function startTrackingEventLoopDelaysThreshold(
});
}
eventLoopDelaysCollector.reset();
eventLoopDelaysMonitor.reset();
});
}

View file

@ -1,43 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`telemetry_ops_stats should return something when there is a metric 1`] = `
Object {
"concurrent_connections": 20,
"os": Object {
"load": Object {
"15m": 3,
"1m": 0.5,
"5m": 1,
},
"memory": Object {
"free_in_bytes": 10,
"total_in_bytes": 10,
"used_in_bytes": 10,
},
"platform": "darwin",
"platformRelease": "test",
"uptime_in_millis": 1000,
},
"process": Object {
"event_loop_delay": 10,
"memory": Object {
"heap": Object {
"size_limit": 0,
"total_in_bytes": 0,
"used_in_bytes": 0,
},
"resident_set_size_in_bytes": 0,
},
"uptime_in_millis": 1000,
},
"requests": Object {
"disconnects": 10,
"total": 100,
},
"response_times": Object {
"average": 100,
"max": 200,
},
"timestamp": Any<String>,
}
`;

View file

@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`telemetry_ops_stats should return something when there is a metric 1`] = `
Object {
"concurrent_connections": 1,
"os": Object {
"load": Object {
"15m": 1,
"1m": 1,
"5m": 1,
},
"memory": Object {
"free_in_bytes": 1,
"total_in_bytes": 1,
"used_in_bytes": 1,
},
"platform": "darwin",
"platformRelease": "test",
"uptime_in_millis": 1,
},
"process": Object {
"event_loop_delay": 1,
"event_loop_delay_histogram": Any<Object>,
"memory": Object {
"heap": Object {
"size_limit": 1,
"total_in_bytes": 1,
"used_in_bytes": 1,
},
"resident_set_size_in_bytes": 1,
},
"uptime_in_millis": 1,
},
"processes": Array [
Object {
"event_loop_delay": 1,
"event_loop_delay_histogram": Any<Object>,
"memory": Object {
"heap": Object {
"size_limit": 1,
"total_in_bytes": 1,
"used_in_bytes": 1,
},
"resident_set_size_in_bytes": 1,
},
"uptime_in_millis": 1,
},
],
"requests": Object {
"disconnects": 1,
"total": 1,
},
"response_times": Object {
"average": 1,
"max": 1,
},
"timestamp": Any<String>,
}
`;

View file

@ -7,15 +7,16 @@
*/
import { Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import {
Collector,
createUsageCollectionSetupMock,
createCollectorFetchContextMock,
} from '../../../../usage_collection/server/mocks';
import { registerOpsStatsCollector } from './';
import { registerOpsStatsCollector } from '.';
import { OpsMetrics } from '../../../../../core/server';
import { loggingSystemMock } from '../../../../../core/server/mocks';
import { loggingSystemMock, metricsServiceMock } from '../../../../../core/server/mocks';
const logger = loggingSystemMock.createLogger();
@ -23,6 +24,8 @@ describe('telemetry_ops_stats', () => {
let collector: Collector<unknown>;
const usageCollectionMock = createUsageCollectionSetupMock();
const metricsServiceSetupMock = metricsServiceMock.createInternalSetupContract();
usageCollectionMock.makeStatsCollector.mockImplementation((config) => {
collector = new Collector(logger, config);
return createUsageCollectionSetupMock().makeStatsCollector(config);
@ -31,45 +34,6 @@ describe('telemetry_ops_stats', () => {
const metrics$ = new Subject<OpsMetrics>();
const mockedFetchContext = createCollectorFetchContextMock();
const metric: OpsMetrics = {
collected_at: new Date('2020-01-01 01:00:00'),
process: {
memory: {
heap: {
total_in_bytes: 0,
used_in_bytes: 0,
size_limit: 0,
},
resident_set_size_in_bytes: 0,
},
event_loop_delay: 10,
pid: 10,
uptime_in_millis: 1000,
},
os: {
platform: 'darwin',
platformRelease: 'test',
load: {
'1m': 0.5,
'5m': 1,
'15m': 3,
},
memory: {
total_in_bytes: 10,
free_in_bytes: 10,
used_in_bytes: 10,
},
uptime_in_millis: 1000,
},
response_times: { avg_in_millis: 100, max_in_millis: 200 },
requests: {
disconnects: 10,
total: 100,
statusCodes: { 200: 100 },
},
concurrent_connections: 20,
};
beforeAll(() => registerOpsStatsCollector(usageCollectionMock, metrics$));
afterAll(() => jest.clearAllTimers());
@ -83,45 +47,18 @@ describe('telemetry_ops_stats', () => {
});
test('should return something when there is a metric', async () => {
metrics$.next(metric);
const opsMetrics = await metricsServiceSetupMock.getOpsMetrics$().pipe(take(1)).toPromise();
metrics$.next(opsMetrics);
expect(collector.isReady()).toBe(true);
expect(await collector.fetch(mockedFetchContext)).toMatchSnapshot({
concurrent_connections: 20,
os: {
load: {
'15m': 3,
'1m': 0.5,
'5m': 1,
},
memory: {
free_in_bytes: 10,
total_in_bytes: 10,
used_in_bytes: 10,
},
platform: 'darwin',
platformRelease: 'test',
uptime_in_millis: 1000,
},
process: {
event_loop_delay: 10,
memory: {
heap: {
size_limit: 0,
total_in_bytes: 0,
used_in_bytes: 0,
},
resident_set_size_in_bytes: 0,
event_loop_delay_histogram: expect.any(Object),
},
processes: [
{
event_loop_delay_histogram: expect.any(Object),
},
uptime_in_millis: 1000,
},
requests: {
disconnects: 10,
total: 100,
},
response_times: {
average: 100,
max: 200,
},
],
timestamp: expect.any(String),
});
});

View file

@ -33,6 +33,11 @@ export function getOpsStatsCollector(
// Ensure we only include the same data that Metricbeat collection would get
// @ts-expect-error
delete metrics.process.pid;
for (const process of metrics.processes) {
// @ts-expect-error
delete process.pid;
}
const responseTimes = {
average: metrics.response_times.avg_in_millis,
max: metrics.response_times.max_in_millis,

View file

@ -21,7 +21,7 @@ import type {
Logger,
CoreUsageDataStart,
} from 'src/core/server';
import { SavedObjectsClient } from '../../../core/server';
import { SavedObjectsClient, EventLoopDelaysMonitor } from '../../../core/server';
import {
startTrackingEventLoopDelaysUsage,
startTrackingEventLoopDelaysThreshold,
@ -56,6 +56,7 @@ type SavedObjectsRegisterType = SavedObjectsServiceSetup['registerType'];
export class KibanaUsageCollectionPlugin implements Plugin {
private readonly logger: Logger;
private readonly legacyConfig$: Observable<SharedGlobalConfig>;
private readonly instanceUuid: string;
private savedObjectsClient?: ISavedObjectsRepository;
private uiSettingsClient?: IUiSettingsClient;
private metric$: Subject<OpsMetrics>;
@ -68,6 +69,7 @@ export class KibanaUsageCollectionPlugin implements Plugin {
this.legacyConfig$ = initializerContext.config.legacy.globalConfig$;
this.metric$ = new Subject<OpsMetrics>();
this.pluginStop$ = new Subject();
this.instanceUuid = initializerContext.env.instanceUuid;
}
public setup(coreSetup: CoreSetup, { usageCollection }: KibanaUsageCollectionPluginsDepsSetup) {
@ -92,11 +94,17 @@ export class KibanaUsageCollectionPlugin implements Plugin {
this.uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
core.metrics.getOpsMetrics$().subscribe(this.metric$);
this.coreUsageData = core.coreUsageData;
startTrackingEventLoopDelaysUsage(this.savedObjectsClient, this.pluginStop$.asObservable());
startTrackingEventLoopDelaysUsage(
this.savedObjectsClient,
this.instanceUuid,
this.pluginStop$.asObservable(),
new EventLoopDelaysMonitor()
);
startTrackingEventLoopDelaysThreshold(
this.eventLoopUsageCounter,
this.logger,
this.pluginStop$.asObservable()
this.pluginStop$.asObservable(),
new EventLoopDelaysMonitor()
);
}

View file

@ -7012,6 +7012,12 @@
"description": "The process id of the monitored kibana instance."
}
},
"instanceUuid": {
"type": "keyword",
"_meta": {
"description": "The uuid of the kibana instance."
}
},
"fromTimestamp": {
"type": "date",
"_meta": {