[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:
parent
3f17c9ef86
commit
28b32b0aa1
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) > [(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();
|
||||
```
|
|
@ -0,0 +1,19 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) > [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<!-- -->}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [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. |
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) > [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`
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EventLoopDelaysMonitor](./kibana-plugin-core-server.eventloopdelaysmonitor.md) > [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`
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [exceeds](./kibana-plugin-core-server.intervalhistogram.exceeds.md)
|
||||
|
||||
## IntervalHistogram.exceeds property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
exceeds: number;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [fromTimestamp](./kibana-plugin-core-server.intervalhistogram.fromtimestamp.md)
|
||||
|
||||
## IntervalHistogram.fromTimestamp property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
fromTimestamp: string;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [lastUpdatedAt](./kibana-plugin-core-server.intervalhistogram.lastupdatedat.md)
|
||||
|
||||
## IntervalHistogram.lastUpdatedAt property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
lastUpdatedAt: string;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [max](./kibana-plugin-core-server.intervalhistogram.max.md)
|
||||
|
||||
## IntervalHistogram.max property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
max: number;
|
||||
```
|
|
@ -0,0 +1,27 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [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> | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [mean](./kibana-plugin-core-server.intervalhistogram.mean.md)
|
||||
|
||||
## IntervalHistogram.mean property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
mean: number;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [min](./kibana-plugin-core-server.intervalhistogram.min.md)
|
||||
|
||||
## IntervalHistogram.min property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
min: number;
|
||||
```
|
|
@ -0,0 +1,16 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [percentiles](./kibana-plugin-core-server.intervalhistogram.percentiles.md)
|
||||
|
||||
## IntervalHistogram.percentiles property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
percentiles: {
|
||||
50: number;
|
||||
75: number;
|
||||
95: number;
|
||||
99: number;
|
||||
};
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IntervalHistogram](./kibana-plugin-core-server.intervalhistogram.md) > [stddev](./kibana-plugin-core-server.intervalhistogram.stddev.md)
|
||||
|
||||
## IntervalHistogram.stddev property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
stddev: number;
|
||||
```
|
|
@ -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) | |
|
||||
|
|
|
@ -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 |
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [OpsMetrics](./kibana-plugin-core-server.opsmetrics.md) > [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[];
|
||||
```
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## OpsProcessMetrics.event\_loop\_delay property
|
||||
|
||||
node event loop delay
|
||||
mean event loop delay since last collection
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [OpsProcessMetrics](./kibana-plugin-core-server.opsprocessmetrics.md) > [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;
|
||||
```
|
|
@ -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 |
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -378,7 +378,9 @@ export type {
|
|||
OpsProcessMetrics,
|
||||
MetricsServiceSetup,
|
||||
MetricsServiceStart,
|
||||
IntervalHistogram,
|
||||
} from './metrics';
|
||||
export { EventLoopDelaysMonitor } from './metrics';
|
||||
|
||||
export type { I18nServiceSetup } from './i18n';
|
||||
export type {
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
24
src/core/server/metrics/collectors/process.mocks.ts
Normal file
24
src/core/server/metrics/collectors/process.mocks.ts
Normal 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,
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
});
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
}
|
9
src/core/server/metrics/event_loop_delays/index.ts
Normal file
9
src/core/server/metrics/event_loop_delays/index.ts
Normal 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';
|
|
@ -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';
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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 }
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
`;
|
|
@ -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>,
|
||||
}
|
||||
`;
|
|
@ -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),
|
||||
});
|
||||
});
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Reference in a new issue