diff --git a/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.js b/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts similarity index 74% rename from x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.js rename to x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts index 37e739d0066a0..fc103959381bc 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.js +++ b/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts @@ -4,9 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +// @ts-ignore import { createApmQuery } from './create_apm_query'; +// @ts-ignore import { ApmClusterMetric } from '../metrics'; +import { LegacyRequest, ElasticsearchResponse } from '../../types'; export async function getTimeOfLastEvent({ req, @@ -15,6 +17,13 @@ export async function getTimeOfLastEvent({ start, end, clusterUuid, +}: { + req: LegacyRequest; + callWithRequest: (_req: any, endpoint: string, params: any) => Promise; + apmIndexPattern: string; + start: number; + end: number; + clusterUuid: string; }) { const params = { index: apmIndexPattern, @@ -49,5 +58,5 @@ export async function getTimeOfLastEvent({ }; const response = await callWithRequest(req, 'search', params); - return get(response, 'hits.hits[0]._source.timestamp'); + return response.hits?.hits.length ? response.hits?.hits[0]._source.timestamp : undefined; } diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.js b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts similarity index 65% rename from x-pack/plugins/monitoring/server/lib/apm/get_apm_info.js rename to x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts index ea37ff7783ad7..4ca708e9d2832 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.js +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts @@ -4,39 +4,49 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, upperFirst } from 'lodash'; +import { upperFirst } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createQuery } from '../create_query'; +// @ts-ignore import { getDiffCalculation } from '../beats/_beats_stats'; +// @ts-ignore import { ApmMetric } from '../metrics'; import { getTimeOfLastEvent } from './_get_time_of_last_event'; +import { LegacyRequest, ElasticsearchResponse } from '../../types'; -export function handleResponse(response, apmUuid) { - const firstStats = get( - response, - 'hits.hits[0].inner_hits.first_hit.hits.hits[0]._source.beats_stats' - ); - const stats = get(response, 'hits.hits[0]._source.beats_stats'); +export function handleResponse(response: ElasticsearchResponse, apmUuid: string) { + if (!response.hits || response.hits.hits.length === 0) { + return {}; + } - const eventsTotalFirst = get(firstStats, 'metrics.libbeat.pipeline.events.total', null); - const eventsEmittedFirst = get(firstStats, 'metrics.libbeat.pipeline.events.published', null); - const eventsDroppedFirst = get(firstStats, 'metrics.libbeat.pipeline.events.dropped', null); - const bytesWrittenFirst = get(firstStats, 'metrics.libbeat.output.write.bytes', null); + const firstStats = response.hits.hits[0].inner_hits.first_hit.hits.hits[0]._source.beats_stats; + const stats = response.hits.hits[0]._source.beats_stats; - const eventsTotalLast = get(stats, 'metrics.libbeat.pipeline.events.total', null); - const eventsEmittedLast = get(stats, 'metrics.libbeat.pipeline.events.published', null); - const eventsDroppedLast = get(stats, 'metrics.libbeat.pipeline.events.dropped', null); - const bytesWrittenLast = get(stats, 'metrics.libbeat.output.write.bytes', null); + if (!firstStats || !stats) { + return {}; + } + + const eventsTotalFirst = firstStats.metrics?.libbeat?.pipeline?.events?.total; + const eventsEmittedFirst = firstStats.metrics?.libbeat?.pipeline?.events?.published; + const eventsDroppedFirst = firstStats.metrics?.libbeat?.pipeline?.events?.dropped; + const bytesWrittenFirst = firstStats.metrics?.libbeat?.output?.write?.bytes; + + const eventsTotalLast = stats.metrics?.libbeat?.pipeline?.events?.total; + const eventsEmittedLast = stats.metrics?.libbeat?.pipeline?.events?.published; + const eventsDroppedLast = stats.metrics?.libbeat?.pipeline?.events?.dropped; + const bytesWrittenLast = stats.metrics?.libbeat?.output?.write?.bytes; return { uuid: apmUuid, - transportAddress: get(stats, 'beat.host', null), - version: get(stats, 'beat.version', null), - name: get(stats, 'beat.name', null), - type: upperFirst(get(stats, 'beat.type')) || null, - output: upperFirst(get(stats, 'metrics.libbeat.output.type')) || null, - configReloads: get(stats, 'metrics.libbeat.config.reloads', null), - uptime: get(stats, 'metrics.beat.info.uptime.ms', null), + transportAddress: stats.beat?.host, + version: stats.beat?.version, + name: stats.beat?.name, + type: upperFirst(stats.beat?.type) || null, + output: upperFirst(stats.metrics?.libbeat?.output?.type) || null, + configReloads: stats.metrics?.libbeat?.config?.reloads, + uptime: stats.metrics?.beat?.info?.uptime?.ms, eventsTotal: getDiffCalculation(eventsTotalLast, eventsTotalFirst), eventsEmitted: getDiffCalculation(eventsEmittedLast, eventsEmittedFirst), eventsDropped: getDiffCalculation(eventsDroppedLast, eventsDroppedFirst), @@ -44,7 +54,21 @@ export function handleResponse(response, apmUuid) { }; } -export async function getApmInfo(req, apmIndexPattern, { clusterUuid, apmUuid, start, end }) { +export async function getApmInfo( + req: LegacyRequest, + apmIndexPattern: string, + { + clusterUuid, + apmUuid, + start, + end, + }: { + clusterUuid: string; + apmUuid: string; + start: number; + end: number; + } +) { checkParam(apmIndexPattern, 'apmIndexPattern in beats/getBeatSummary'); const filters = [ diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apms.js b/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts similarity index 69% rename from x-pack/plugins/monitoring/server/lib/apm/get_apms.js rename to x-pack/plugins/monitoring/server/lib/apm/get_apms.ts index 2d59bfea72eb2..f6df94f8de138 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apms.js +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts @@ -5,68 +5,79 @@ */ import moment from 'moment'; -import { upperFirst, get } from 'lodash'; +import { upperFirst } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createApmQuery } from './create_apm_query'; +// @ts-ignore import { calculateRate } from '../calculate_rate'; +// @ts-ignore import { getDiffCalculation } from './_apm_stats'; +import { LegacyRequest, ElasticsearchResponse, ElasticsearchResponseHit } from '../../types'; -export function handleResponse(response, start, end) { - const hits = get(response, 'hits.hits', []); +export function handleResponse(response: ElasticsearchResponse, start: number, end: number) { const initial = { ids: new Set(), beats: [] }; - const { beats } = hits.reduce((accum, hit) => { - const stats = get(hit, '_source.beats_stats'); - const uuid = get(stats, 'beat.uuid'); + const { beats } = response.hits?.hits.reduce((accum: any, hit: ElasticsearchResponseHit) => { + const stats = hit._source.beats_stats; + if (!stats) { + return accum; + } + + const earliestStats = hit.inner_hits.earliest.hits.hits[0]._source.beats_stats; + if (!earliestStats) { + return accum; + } + + const uuid = stats?.beat?.uuid; // skip this duplicated beat, newer one was already added if (accum.ids.has(uuid)) { return accum; } - // add another beat summary accum.ids.add(uuid); - const earliestStats = get(hit, 'inner_hits.earliest.hits.hits[0]._source.beats_stats'); // add the beat const rateOptions = { - hitTimestamp: get(stats, 'timestamp'), - earliestHitTimestamp: get(earliestStats, 'timestamp'), + hitTimestamp: stats.timestamp, + earliestHitTimestamp: earliestStats.timestamp, timeWindowMin: start, timeWindowMax: end, }; const { rate: bytesSentRate } = calculateRate({ - latestTotal: get(stats, 'metrics.libbeat.output.write.bytes'), - earliestTotal: get(earliestStats, 'metrics.libbeat.output.write.bytes'), + latestTotal: stats.metrics?.libbeat?.output?.write?.bytes, + earliestTotal: earliestStats?.metrics?.libbeat?.output?.write?.bytes, ...rateOptions, }); const { rate: totalEventsRate } = calculateRate({ - latestTotal: get(stats, 'metrics.libbeat.pipeline.events.total'), - earliestTotal: get(earliestStats, 'metrics.libbeat.pipeline.events.total'), + latestTotal: stats.metrics?.libbeat?.pipeline?.events?.total, + earliestTotal: earliestStats.metrics?.libbeat?.pipeline?.events?.total, ...rateOptions, }); - const errorsWrittenLatest = get(stats, 'metrics.libbeat.output.write.errors'); - const errorsWrittenEarliest = get(earliestStats, 'metrics.libbeat.output.write.errors'); - const errorsReadLatest = get(stats, 'metrics.libbeat.output.read.errors'); - const errorsReadEarliest = get(earliestStats, 'metrics.libbeat.output.read.errors'); + const errorsWrittenLatest = stats.metrics?.libbeat?.output?.write?.errors ?? 0; + const errorsWrittenEarliest = earliestStats.metrics?.libbeat?.output?.write?.errors ?? 0; + const errorsReadLatest = stats.metrics?.libbeat?.output?.read?.errors ?? 0; + const errorsReadEarliest = earliestStats.metrics?.libbeat?.output?.read?.errors ?? 0; const errors = getDiffCalculation( errorsWrittenLatest + errorsReadLatest, errorsWrittenEarliest + errorsReadEarliest ); accum.beats.push({ - uuid: get(stats, 'beat.uuid'), - name: get(stats, 'beat.name'), - type: upperFirst(get(stats, 'beat.type')), - output: upperFirst(get(stats, 'metrics.libbeat.output.type')), + uuid: stats.beat?.uuid, + name: stats.beat?.name, + type: upperFirst(stats.beat?.type), + output: upperFirst(stats.metrics?.libbeat?.output?.type), total_events_rate: totalEventsRate, bytes_sent_rate: bytesSentRate, errors, - memory: get(stats, 'metrics.beat.memstats.memory_alloc'), - version: get(stats, 'beat.version'), - time_of_last_event: get(hit, '_source.timestamp'), + memory: stats.metrics?.beat?.memstats?.memory_alloc, + version: stats.beat?.version, + time_of_last_event: hit._source.timestamp, }); return accum; @@ -75,7 +86,7 @@ export function handleResponse(response, start, end) { return beats; } -export async function getApms(req, apmIndexPattern, clusterUuid) { +export async function getApms(req: LegacyRequest, apmIndexPattern: string, clusterUuid: string) { checkParam(apmIndexPattern, 'apmIndexPattern in getBeats'); const config = req.server.config(); diff --git a/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.js b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts similarity index 60% rename from x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.js rename to x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts index 5d6c38e19bef2..57325673a131a 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.js +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts @@ -4,52 +4,62 @@ * you may not use this file except in compliance with the Elastic License. */ -import { upperFirst, get } from 'lodash'; +import { upperFirst } from 'lodash'; +import { LegacyRequest, ElasticsearchResponse } from '../../types'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createBeatsQuery } from './create_beats_query.js'; +// @ts-ignore import { getDiffCalculation } from './_beats_stats'; -export function handleResponse(response, beatUuid) { - const firstStats = get( - response, - 'hits.hits[0].inner_hits.first_hit.hits.hits[0]._source.beats_stats' - ); - const stats = get(response, 'hits.hits[0]._source.beats_stats'); +export function handleResponse(response: ElasticsearchResponse, beatUuid: string) { + if (!response.hits || response.hits.hits.length === 0) { + return {}; + } - const eventsTotalFirst = get(firstStats, 'metrics.libbeat.pipeline.events.total', null); - const eventsEmittedFirst = get(firstStats, 'metrics.libbeat.pipeline.events.published', null); - const eventsDroppedFirst = get(firstStats, 'metrics.libbeat.pipeline.events.dropped', null); - const bytesWrittenFirst = get(firstStats, 'metrics.libbeat.output.write.bytes', null); + const firstStats = response.hits.hits[0].inner_hits.first_hit.hits.hits[0]._source.beats_stats; + const stats = response.hits.hits[0]._source.beats_stats; - const eventsTotalLast = get(stats, 'metrics.libbeat.pipeline.events.total', null); - const eventsEmittedLast = get(stats, 'metrics.libbeat.pipeline.events.published', null); - const eventsDroppedLast = get(stats, 'metrics.libbeat.pipeline.events.dropped', null); - const bytesWrittenLast = get(stats, 'metrics.libbeat.output.write.bytes', null); - const handlesHardLimit = get(stats, 'metrics.beat.handles.limit.hard', null); - const handlesSoftLimit = get(stats, 'metrics.beat.handles.limit.soft', null); + const eventsTotalFirst = firstStats?.metrics?.libbeat?.pipeline?.events?.total ?? null; + const eventsEmittedFirst = firstStats?.metrics?.libbeat?.pipeline?.events?.published ?? null; + const eventsDroppedFirst = firstStats?.metrics?.libbeat?.pipeline?.events?.dropped ?? null; + const bytesWrittenFirst = firstStats?.metrics?.libbeat?.output?.write?.bytes ?? null; + + const eventsTotalLast = stats?.metrics?.libbeat?.pipeline?.events?.total ?? null; + const eventsEmittedLast = stats?.metrics?.libbeat?.pipeline?.events?.published ?? null; + const eventsDroppedLast = stats?.metrics?.libbeat?.pipeline?.events?.dropped ?? null; + const bytesWrittenLast = stats?.metrics?.libbeat?.output?.write?.bytes ?? null; + const handlesHardLimit = stats?.metrics?.beat?.handles?.limit?.hard ?? null; + const handlesSoftLimit = stats?.metrics?.beat?.handles?.limit?.soft ?? null; return { uuid: beatUuid, - transportAddress: get(stats, 'beat.host', null), - version: get(stats, 'beat.version', null), - name: get(stats, 'beat.name', null), - type: upperFirst(get(stats, 'beat.type')) || null, - output: upperFirst(get(stats, 'metrics.libbeat.output.type')) || null, - configReloads: get(stats, 'metrics.libbeat.config.reloads', null), - uptime: get(stats, 'metrics.beat.info.uptime.ms', null), - eventsTotal: getDiffCalculation(eventsTotalLast, eventsTotalFirst), - eventsEmitted: getDiffCalculation(eventsEmittedLast, eventsEmittedFirst), - eventsDropped: getDiffCalculation(eventsDroppedLast, eventsDroppedFirst), - bytesWritten: getDiffCalculation(bytesWrittenLast, bytesWrittenFirst), + transportAddress: stats?.beat?.host ?? null, + version: stats?.beat?.version ?? null, + name: stats?.beat?.name ?? null, + type: upperFirst(stats?.beat?.type) ?? null, + output: upperFirst(stats?.metrics?.libbeat?.output?.type) ?? null, + configReloads: stats?.metrics?.libbeat?.config?.reloads ?? null, + uptime: stats?.metrics?.beat?.info?.uptime?.ms ?? null, + eventsTotal: getDiffCalculation(eventsTotalLast, eventsTotalFirst) ?? null, + eventsEmitted: getDiffCalculation(eventsEmittedLast, eventsEmittedFirst) ?? null, + eventsDropped: getDiffCalculation(eventsDroppedLast, eventsDroppedFirst) ?? null, + bytesWritten: getDiffCalculation(bytesWrittenLast, bytesWrittenFirst) ?? null, handlesHardLimit, handlesSoftLimit, }; } export async function getBeatSummary( - req, - beatsIndexPattern, - { clusterUuid, beatUuid, start, end } + req: LegacyRequest, + beatsIndexPattern: string, + { + clusterUuid, + beatUuid, + start, + end, + }: { clusterUuid: string; beatUuid: string; start: number; end: number } ) { checkParam(beatsIndexPattern, 'beatsIndexPattern in beats/getBeatSummary'); diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index a5d7051105797..73eea99467c59 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -78,7 +78,9 @@ export interface IBulkUploader { export interface LegacyRequest { logger: Logger; getLogger: (...scopes: string[]) => Logger; - payload: unknown; + payload: { + [key: string]: any; + }; getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any; @@ -107,3 +109,80 @@ export interface LegacyRequest { }; }; } + +export interface ElasticsearchResponse { + hits?: { + hits: ElasticsearchResponseHit[]; + total: { + value: number; + }; + }; +} + +export interface ElasticsearchResponseHit { + _source: ElasticsearchSource; + inner_hits: { + [field: string]: { + hits: { + hits: ElasticsearchResponseHit[]; + total: { + value: number; + }; + }; + }; + }; +} + +export interface ElasticsearchSource { + timestamp: string; + beats_stats?: { + timestamp?: string; + beat?: { + uuid?: string; + name?: string; + type?: string; + version?: string; + host?: string; + }; + metrics?: { + beat?: { + memstats?: { + memory_alloc?: number; + }; + info?: { + uptime?: { + ms?: number; + }; + }; + handles?: { + limit?: { + hard?: number; + soft?: number; + }; + }; + }; + libbeat?: { + config?: { + reloads?: number; + }; + output?: { + type?: string; + write?: { + bytes?: number; + errors?: number; + }; + read?: { + errors?: number; + }; + }; + pipeline?: { + events?: { + total?: number; + published?: number; + dropped?: number; + }; + }; + }; + }; + }; +}