mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Inventory] Add k8s fields to Service entity type (#195407)](https://github.com/elastic/kibana/pull/195407) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Cauê Marcondes","email":"55978943+cauemarcondes@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-09T13:44:12Z","message":"[Inventory] Add k8s fields to Service entity type (#195407)\n\ncloses https://github.com/elastic/kibana/issues/195244\r\n\r\n- Removed metrics definition from service, host and container\r\n- Removed `metrics-apm` index patterns from the service definition\r\nbecause k8s fields are not available on that scope.\r\n- Added `traces-apm*` index pattern on the service definition\r\n\r\n\r\nhttps://github.com/user-attachments/assets/6c6b4fd6-817a-494e-8649-e2d76a8e98e3","sha":"cc7fdba1422f2717984b958509be13abc820b15b","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:cloud-deploy","ci:project-deploy-observability","Team:obs-ux-infra_services","v8.16.0","Feature:EEM"],"title":"[Inventory] Add k8s fields to Service entity type","number":195407,"url":"https://github.com/elastic/kibana/pull/195407","mergeCommit":{"message":"[Inventory] Add k8s fields to Service entity type (#195407)\n\ncloses https://github.com/elastic/kibana/issues/195244\r\n\r\n- Removed metrics definition from service, host and container\r\n- Removed `metrics-apm` index patterns from the service definition\r\nbecause k8s fields are not available on that scope.\r\n- Added `traces-apm*` index pattern on the service definition\r\n\r\n\r\nhttps://github.com/user-attachments/assets/6c6b4fd6-817a-494e-8649-e2d76a8e98e3","sha":"cc7fdba1422f2717984b958509be13abc820b15b"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195407","number":195407,"mergeCommit":{"message":"[Inventory] Add k8s fields to Service entity type (#195407)\n\ncloses https://github.com/elastic/kibana/issues/195244\r\n\r\n- Removed metrics definition from service, host and container\r\n- Removed `metrics-apm` index patterns from the service definition\r\nbecause k8s fields are not available on that scope.\r\n- Added `traces-apm*` index pattern on the service definition\r\n\r\n\r\nhttps://github.com/user-attachments/assets/6c6b4fd6-817a-494e-8649-e2d76a8e98e3","sha":"cc7fdba1422f2717984b958509be13abc820b15b"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
This commit is contained in:
parent
062ea5726f
commit
ef6f776b12
16 changed files with 12 additions and 1161 deletions
|
@ -12,7 +12,7 @@ export const builtInContainersFromEcsEntityDefinition: EntityDefinition =
|
||||||
entityDefinitionSchema.parse({
|
entityDefinitionSchema.parse({
|
||||||
id: `${BUILT_IN_ID_PREFIX}containers_from_ecs_data`,
|
id: `${BUILT_IN_ID_PREFIX}containers_from_ecs_data`,
|
||||||
managed: true,
|
managed: true,
|
||||||
version: '1.0.0',
|
version: '0.1.0',
|
||||||
name: 'Containers from ECS data',
|
name: 'Containers from ECS data',
|
||||||
description:
|
description:
|
||||||
'This definition extracts container entities from common data streams by looking for the ECS field container.id',
|
'This definition extracts container entities from common data streams by looking for the ECS field container.id',
|
||||||
|
@ -65,94 +65,4 @@ export const builtInContainersFromEcsEntityDefinition: EntityDefinition =
|
||||||
'agent.type',
|
'agent.type',
|
||||||
'agent.ephemeral_id',
|
'agent.ephemeral_id',
|
||||||
],
|
],
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'log_rate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter: 'log.level: * OR error.log.level: *',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'error_log_rate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter: '(log.level: "error" OR "ERROR") OR (error.log.level: "error" OR "ERROR")',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cpu_usage_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.cpu.total.pct',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_usage_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.memory.usage.pct',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'network_in_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.network.in.bytes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'network_out_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.network.out.bytes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'disk_read_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.diskio.read.ops',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'disk_write_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'docker.diskio.write.ops',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { BUILT_IN_ID_PREFIX } from './constants';
|
||||||
export const builtInHostsFromEcsEntityDefinition: EntityDefinition = entityDefinitionSchema.parse({
|
export const builtInHostsFromEcsEntityDefinition: EntityDefinition = entityDefinitionSchema.parse({
|
||||||
id: `${BUILT_IN_ID_PREFIX}hosts_from_ecs_data`,
|
id: `${BUILT_IN_ID_PREFIX}hosts_from_ecs_data`,
|
||||||
managed: true,
|
managed: true,
|
||||||
version: '1.0.0',
|
version: '0.1.0',
|
||||||
name: 'Hosts from ECS data',
|
name: 'Hosts from ECS data',
|
||||||
description:
|
description:
|
||||||
'This definition extracts host entities from common data streams by looking for the ECS field host.name',
|
'This definition extracts host entities from common data streams by looking for the ECS field host.name',
|
||||||
|
@ -65,115 +65,4 @@ export const builtInHostsFromEcsEntityDefinition: EntityDefinition = entityDefin
|
||||||
'agent.type',
|
'agent.type',
|
||||||
'agent.version',
|
'agent.version',
|
||||||
],
|
],
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'log_rate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter: 'log.level: * OR error.log.level: *',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'error_log_rate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter: '(log.level: "error" OR "ERROR") OR (error.log.level: "error" OR "ERROR")',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cpu_usage_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'system.cpu.total.norm.pct',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'normalized_load_avg',
|
|
||||||
equation: 'A / B',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'system.load.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'B',
|
|
||||||
aggregation: 'max',
|
|
||||||
field: 'system.load.cores',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_usage_avg',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'system.memory.actual.used.pct',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memory_free_avg',
|
|
||||||
equation: 'A - B',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'max',
|
|
||||||
field: 'system.memory.total',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'B',
|
|
||||||
aggregation: 'avg',
|
|
||||||
field: 'system.memory.actual.used.bytes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'disk_usage_max',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'max',
|
|
||||||
field: 'system.filesystem.used.pct',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'rx_avg',
|
|
||||||
equation: 'A * 8',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'sum',
|
|
||||||
field: 'host.network.ingress.bytes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'tx_avg',
|
|
||||||
equation: 'A * 8',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'sum',
|
|
||||||
field: 'host.network.egress.bytes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,31 +8,16 @@
|
||||||
import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema';
|
import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema';
|
||||||
import { BUILT_IN_ID_PREFIX } from './constants';
|
import { BUILT_IN_ID_PREFIX } from './constants';
|
||||||
|
|
||||||
const serviceTransactionFilter = (additionalFilters: string[] = []) => {
|
|
||||||
const baseFilters = [
|
|
||||||
'processor.event: "metric"',
|
|
||||||
'metricset.name: "service_transaction"',
|
|
||||||
'metricset.interval: "1m"',
|
|
||||||
];
|
|
||||||
|
|
||||||
return [...baseFilters, ...additionalFilters].join(' AND ');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const builtInServicesFromEcsEntityDefinition: EntityDefinition =
|
export const builtInServicesFromEcsEntityDefinition: EntityDefinition =
|
||||||
entityDefinitionSchema.parse({
|
entityDefinitionSchema.parse({
|
||||||
version: '0.3.0',
|
version: '0.4.0',
|
||||||
id: `${BUILT_IN_ID_PREFIX}services_from_ecs_data`,
|
id: `${BUILT_IN_ID_PREFIX}services_from_ecs_data`,
|
||||||
name: 'Services from ECS data',
|
name: 'Services from ECS data',
|
||||||
description:
|
description:
|
||||||
'This definition extracts service entities from common data streams by looking for the ECS field service.name',
|
'This definition extracts service entities from common data streams by looking for the ECS field service.name',
|
||||||
type: 'service',
|
type: 'service',
|
||||||
managed: true,
|
managed: true,
|
||||||
indexPatterns: [
|
indexPatterns: ['logs-*', 'filebeat*', 'traces-apm*'],
|
||||||
'logs-*',
|
|
||||||
'filebeat*',
|
|
||||||
'metrics-apm.service_transaction.1m*',
|
|
||||||
'metrics-apm.service_summary.1m*',
|
|
||||||
],
|
|
||||||
history: {
|
history: {
|
||||||
timestampField: '@timestamp',
|
timestampField: '@timestamp',
|
||||||
interval: '1m',
|
interval: '1m',
|
||||||
|
@ -65,72 +50,9 @@ export const builtInServicesFromEcsEntityDefinition: EntityDefinition =
|
||||||
'cloud.provider',
|
'cloud.provider',
|
||||||
'cloud.availability_zone',
|
'cloud.availability_zone',
|
||||||
'cloud.machine.type',
|
'cloud.machine.type',
|
||||||
],
|
'kubernetes.namespace',
|
||||||
metrics: [
|
'orchestrator.cluster.name',
|
||||||
{
|
'k8s.namespace.name',
|
||||||
name: 'latency',
|
'k8s.cluster.name',
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'avg',
|
|
||||||
filter: serviceTransactionFilter(),
|
|
||||||
field: 'transaction.duration.histogram',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'throughput',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'value_count',
|
|
||||||
filter: serviceTransactionFilter(),
|
|
||||||
field: 'transaction.duration.summary',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'failedTransactionRate',
|
|
||||||
equation: '1 - (A / B)',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'sum',
|
|
||||||
filter: serviceTransactionFilter(),
|
|
||||||
field: 'event.success_count',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'B',
|
|
||||||
aggregation: 'value_count',
|
|
||||||
filter: serviceTransactionFilter(),
|
|
||||||
field: 'event.success_count',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'logErrorRate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter:
|
|
||||||
'log.level: "error" OR log.level: "ERROR" OR error.log.level: "error" OR error.log.level: "ERROR"',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'logRate',
|
|
||||||
equation: 'A',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
name: 'A',
|
|
||||||
aggregation: 'doc_count',
|
|
||||||
filter: 'data_stream.type: logs',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,16 +10,3 @@ export enum EntityDataStreamType {
|
||||||
TRACES = 'traces',
|
TRACES = 'traces',
|
||||||
LOGS = 'logs',
|
LOGS = 'logs',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TraceMetrics {
|
|
||||||
latency?: number | null;
|
|
||||||
throughput?: number | null;
|
|
||||||
failedTransactionRate?: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LogsMetrics {
|
|
||||||
logRate?: number | null;
|
|
||||||
logErrorRate?: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EntityMetrics = TraceMetrics & LogsMetrics;
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {
|
||||||
import type { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
import type { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
||||||
import { getEntityLatestServices } from './get_entity_latest_services';
|
import { getEntityLatestServices } from './get_entity_latest_services';
|
||||||
import type { EntityLatestServiceRaw } from './types';
|
import type { EntityLatestServiceRaw } from './types';
|
||||||
import { getEntityHistoryServicesMetrics } from './get_entity_history_services_metrics';
|
|
||||||
|
|
||||||
export function entitiesRangeQuery(start?: number, end?: number): QueryDslQueryContainer[] {
|
export function entitiesRangeQuery(start?: number, end?: number): QueryDslQueryContainer[] {
|
||||||
if (!start || !end) {
|
if (!start || !end) {
|
||||||
|
@ -64,30 +63,5 @@ export async function getEntities({
|
||||||
serviceName,
|
serviceName,
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceEntitiesHistoryMetricsMap = entityLatestServices.length
|
return entityLatestServices;
|
||||||
? await getEntityHistoryServicesMetrics({
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
entitiesESClient,
|
|
||||||
entityIds: entityLatestServices.map((latestEntity) => latestEntity.entity.id),
|
|
||||||
size,
|
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return entityLatestServices.map((latestEntity) => {
|
|
||||||
const historyEntityMetrics = serviceEntitiesHistoryMetricsMap?.[latestEntity.entity.id];
|
|
||||||
return {
|
|
||||||
...latestEntity,
|
|
||||||
entity: {
|
|
||||||
...latestEntity.entity,
|
|
||||||
metrics: historyEntityMetrics || {
|
|
||||||
latency: undefined,
|
|
||||||
logErrorRate: undefined,
|
|
||||||
failedTransactionRate: undefined,
|
|
||||||
logRate: undefined,
|
|
||||||
throughput: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,84 +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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { rangeQuery, termsQuery } from '@kbn/observability-plugin/server';
|
|
||||||
import {
|
|
||||||
ENTITY_ID,
|
|
||||||
ENTITY_LAST_SEEN,
|
|
||||||
} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
|
|
||||||
import { EntityMetrics } from '../../../common/entities/types';
|
|
||||||
import {
|
|
||||||
ENTITY_METRICS_FAILED_TRANSACTION_RATE,
|
|
||||||
ENTITY_METRICS_LATENCY,
|
|
||||||
ENTITY_METRICS_LOG_ERROR_RATE,
|
|
||||||
ENTITY_METRICS_LOG_RATE,
|
|
||||||
ENTITY_METRICS_THROUGHPUT,
|
|
||||||
} from '../../../common/es_fields/entities';
|
|
||||||
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
|
||||||
|
|
||||||
interface Params {
|
|
||||||
entitiesESClient: EntitiesESClient;
|
|
||||||
start: number;
|
|
||||||
end: number;
|
|
||||||
entityIds: string[];
|
|
||||||
size: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getEntityHistoryServicesMetrics({
|
|
||||||
end,
|
|
||||||
entityIds,
|
|
||||||
start,
|
|
||||||
entitiesESClient,
|
|
||||||
size,
|
|
||||||
}: Params) {
|
|
||||||
const response = await entitiesESClient.searchHistory('get_entities_history', {
|
|
||||||
body: {
|
|
||||||
size: 0,
|
|
||||||
track_total_hits: false,
|
|
||||||
query: {
|
|
||||||
bool: {
|
|
||||||
filter: [
|
|
||||||
...rangeQuery(start, end, ENTITY_LAST_SEEN),
|
|
||||||
...termsQuery(ENTITY_ID, ...entityIds),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
entityIds: {
|
|
||||||
terms: { field: ENTITY_ID, size },
|
|
||||||
aggs: {
|
|
||||||
latency: { avg: { field: ENTITY_METRICS_LATENCY } },
|
|
||||||
logErrorRate: { avg: { field: ENTITY_METRICS_LOG_ERROR_RATE } },
|
|
||||||
logRate: { avg: { field: ENTITY_METRICS_LOG_RATE } },
|
|
||||||
throughput: { avg: { field: ENTITY_METRICS_THROUGHPUT } },
|
|
||||||
failedTransactionRate: { avg: { field: ENTITY_METRICS_FAILED_TRANSACTION_RATE } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.aggregations) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.aggregations.entityIds.buckets.reduce<Record<string, EntityMetrics>>(
|
|
||||||
(acc, currBucket) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[currBucket.key]: {
|
|
||||||
latency: currBucket.latency.value,
|
|
||||||
logErrorRate: currBucket.logErrorRate.value,
|
|
||||||
logRate: currBucket.logRate.value,
|
|
||||||
throughput: currBucket.throughput.value,
|
|
||||||
failedTransactionRate: currBucket.failedTransactionRate.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,116 +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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getBucketSize } from '@kbn/apm-data-access-plugin/common';
|
|
||||||
import { rangeQuery, termsQuery } from '@kbn/observability-plugin/server';
|
|
||||||
import { ENTITY_LAST_SEEN } from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
|
|
||||||
import { keyBy } from 'lodash';
|
|
||||||
import { SERVICE_NAME } from '../../../common/es_fields/apm';
|
|
||||||
import {
|
|
||||||
ENTITY_METRICS_FAILED_TRANSACTION_RATE,
|
|
||||||
ENTITY_METRICS_LATENCY,
|
|
||||||
ENTITY_METRICS_LOG_ERROR_RATE,
|
|
||||||
ENTITY_METRICS_LOG_RATE,
|
|
||||||
ENTITY_METRICS_THROUGHPUT,
|
|
||||||
} from '../../../common/es_fields/entities';
|
|
||||||
import { environmentQuery } from '../../../common/utils/environment_query';
|
|
||||||
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
|
||||||
|
|
||||||
interface Params {
|
|
||||||
entitiesESClient: EntitiesESClient;
|
|
||||||
start: number;
|
|
||||||
end: number;
|
|
||||||
serviceNames: string[];
|
|
||||||
environment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getEntityHistoryServicesTimeseries({
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
serviceNames,
|
|
||||||
entitiesESClient,
|
|
||||||
environment,
|
|
||||||
}: Params) {
|
|
||||||
const { intervalString } = getBucketSize({
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
minBucketSize: 60,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await entitiesESClient.searchHistory('get_entities_history_timeseries', {
|
|
||||||
body: {
|
|
||||||
size: 0,
|
|
||||||
track_total_hits: false,
|
|
||||||
query: {
|
|
||||||
bool: {
|
|
||||||
filter: [
|
|
||||||
...rangeQuery(start, end, ENTITY_LAST_SEEN),
|
|
||||||
...termsQuery(SERVICE_NAME, ...serviceNames),
|
|
||||||
...environmentQuery(environment),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
serviceNames: {
|
|
||||||
terms: { field: SERVICE_NAME, size: serviceNames.length },
|
|
||||||
aggs: {
|
|
||||||
timeseries: {
|
|
||||||
date_histogram: {
|
|
||||||
field: '@timestamp',
|
|
||||||
fixed_interval: intervalString,
|
|
||||||
min_doc_count: 0,
|
|
||||||
extended_bounds: { min: start, max: end },
|
|
||||||
},
|
|
||||||
aggs: {
|
|
||||||
latency: { avg: { field: ENTITY_METRICS_LATENCY } },
|
|
||||||
logErrorRate: { avg: { field: ENTITY_METRICS_LOG_ERROR_RATE } },
|
|
||||||
logRate: { avg: { field: ENTITY_METRICS_LOG_RATE } },
|
|
||||||
throughput: { avg: { field: ENTITY_METRICS_THROUGHPUT } },
|
|
||||||
failedTransactionRate: { avg: { field: ENTITY_METRICS_FAILED_TRANSACTION_RATE } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.aggregations) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyBy(
|
|
||||||
response.aggregations.serviceNames.buckets.map((serviceBucket) => {
|
|
||||||
const serviceName = serviceBucket.key as string;
|
|
||||||
|
|
||||||
return {
|
|
||||||
serviceName,
|
|
||||||
latency: serviceBucket.timeseries.buckets.map((bucket) => ({
|
|
||||||
x: bucket.key,
|
|
||||||
y: bucket.latency.value ?? null,
|
|
||||||
})),
|
|
||||||
logErrorRate: serviceBucket.timeseries.buckets.map((bucket) => ({
|
|
||||||
x: bucket.key,
|
|
||||||
y: bucket.logErrorRate.value ?? null,
|
|
||||||
})),
|
|
||||||
logRate: serviceBucket.timeseries.buckets.map((bucket) => ({
|
|
||||||
x: bucket.key,
|
|
||||||
y: bucket.logRate.value ?? null,
|
|
||||||
})),
|
|
||||||
throughput: serviceBucket.timeseries.buckets.map((bucket) => ({
|
|
||||||
x: bucket.key,
|
|
||||||
y: bucket.throughput.value ?? null,
|
|
||||||
})),
|
|
||||||
failedTransactionRate: serviceBucket.timeseries.buckets.map((bucket) => ({
|
|
||||||
x: bucket.key,
|
|
||||||
y: bucket.failedTransactionRate.value ?? null,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
'serviceName'
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -10,7 +10,6 @@ import { WrappedElasticsearchClientError } from '@kbn/observability-plugin/serve
|
||||||
import { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
import { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
||||||
import { withApmSpan } from '../../../utils/with_apm_span';
|
import { withApmSpan } from '../../../utils/with_apm_span';
|
||||||
import { getEntities } from '../get_entities';
|
import { getEntities } from '../get_entities';
|
||||||
import { calculateAvgMetrics } from '../utils/calculate_avg_metrics';
|
|
||||||
import { mergeEntities } from '../utils/merge_entities';
|
import { mergeEntities } from '../utils/merge_entities';
|
||||||
|
|
||||||
export const MAX_NUMBER_OF_SERVICES = 1_000;
|
export const MAX_NUMBER_OF_SERVICES = 1_000;
|
||||||
|
@ -41,7 +40,7 @@ export async function getServiceEntities({
|
||||||
size: MAX_NUMBER_OF_SERVICES,
|
size: MAX_NUMBER_OF_SERVICES,
|
||||||
});
|
});
|
||||||
|
|
||||||
return calculateAvgMetrics(mergeEntities({ entities }));
|
return mergeEntities({ entities });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If the index does not exist, handle it gracefully
|
// If the index does not exist, handle it gracefully
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import type { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
import type { EntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
||||||
import { withApmSpan } from '../../../utils/with_apm_span';
|
import { withApmSpan } from '../../../utils/with_apm_span';
|
||||||
import { getEntityLatestServices } from '../get_entity_latest_services';
|
import { getEntityLatestServices } from '../get_entity_latest_services';
|
||||||
import { calculateAvgMetrics } from '../utils/calculate_avg_metrics';
|
|
||||||
import { mergeEntities } from '../utils/merge_entities';
|
import { mergeEntities } from '../utils/merge_entities';
|
||||||
import { MAX_NUMBER_OF_SERVICES } from './get_service_entities';
|
import { MAX_NUMBER_OF_SERVICES } from './get_service_entities';
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ export function getServiceEntitySummary({ entitiesESClient, environment, service
|
||||||
serviceName,
|
serviceName,
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceEntity = calculateAvgMetrics(mergeEntities({ entities: entityLatestServices }));
|
const serviceEntity = mergeEntities({ entities: entityLatestServices });
|
||||||
return serviceEntity[0];
|
return serviceEntity[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
import Boom from '@hapi/boom';
|
|
||||||
import { jsonRt } from '@kbn/io-ts-utils';
|
|
||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
import { environmentQuery } from '../../../../common/utils/environment_query';
|
import { environmentQuery } from '../../../../common/utils/environment_query';
|
||||||
import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
|
||||||
|
@ -13,7 +11,6 @@ import { createApmServerRoute } from '../../apm_routes/create_apm_server_route';
|
||||||
import { environmentRt, kueryRt, rangeRt } from '../../default_api_types';
|
import { environmentRt, kueryRt, rangeRt } from '../../default_api_types';
|
||||||
import { getServiceEntities } from './get_service_entities';
|
import { getServiceEntities } from './get_service_entities';
|
||||||
import { getServiceEntitySummary } from './get_service_entity_summary';
|
import { getServiceEntitySummary } from './get_service_entity_summary';
|
||||||
import { getEntityHistoryServicesTimeseries } from '../get_entity_history_services_timeseries';
|
|
||||||
|
|
||||||
const serviceEntitiesSummaryRoute = createApmServerRoute({
|
const serviceEntitiesSummaryRoute = createApmServerRoute({
|
||||||
endpoint: 'GET /internal/apm/entities/services/{serviceName}/summary',
|
endpoint: 'GET /internal/apm/entities/services/{serviceName}/summary',
|
||||||
|
@ -72,46 +69,6 @@ const servicesEntitiesRoute = createApmServerRoute({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const servicesEntitiesDetailedStatisticsRoute = createApmServerRoute({
|
|
||||||
endpoint: 'POST /internal/apm/entities/services/detailed_statistics',
|
|
||||||
params: t.type({
|
|
||||||
query: t.intersection([environmentRt, kueryRt, rangeRt]),
|
|
||||||
body: t.type({ serviceNames: jsonRt.pipe(t.array(t.string)) }),
|
|
||||||
}),
|
|
||||||
options: { tags: ['access:apm'] },
|
|
||||||
handler: async (resources) => {
|
|
||||||
const { context, params, request } = resources;
|
|
||||||
const coreContext = await context.core;
|
|
||||||
|
|
||||||
const entitiesESClient = await createEntitiesESClient({
|
|
||||||
request,
|
|
||||||
esClient: coreContext.elasticsearch.client.asCurrentUser,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { environment, start, end } = params.query;
|
|
||||||
|
|
||||||
const { serviceNames } = params.body;
|
|
||||||
|
|
||||||
if (!serviceNames.length) {
|
|
||||||
throw Boom.badRequest(`serviceNames cannot be empty`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceEntitiesTimeseries = await getEntityHistoryServicesTimeseries({
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
serviceNames,
|
|
||||||
environment,
|
|
||||||
entitiesESClient,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentPeriod: {
|
|
||||||
...serviceEntitiesTimeseries,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const serviceLogRateTimeseriesRoute = createApmServerRoute({
|
const serviceLogRateTimeseriesRoute = createApmServerRoute({
|
||||||
endpoint: 'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries',
|
endpoint: 'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries',
|
||||||
params: t.type({
|
params: t.type({
|
||||||
|
@ -183,6 +140,5 @@ export const servicesEntitiesRoutesRepository = {
|
||||||
...servicesEntitiesRoute,
|
...servicesEntitiesRoute,
|
||||||
...serviceLogRateTimeseriesRoute,
|
...serviceLogRateTimeseriesRoute,
|
||||||
...serviceLogErrorRateTimeseriesRoute,
|
...serviceLogErrorRateTimeseriesRoute,
|
||||||
...servicesEntitiesDetailedStatisticsRoute,
|
|
||||||
...serviceEntitiesSummaryRoute,
|
...serviceEntitiesSummaryRoute,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
import { AgentName } from '../../../typings/es_schemas/ui/fields/agent';
|
import { AgentName } from '../../../typings/es_schemas/ui/fields/agent';
|
||||||
import { EntityMetrics } from '../../../common/entities/types';
|
|
||||||
|
|
||||||
export enum EntityType {
|
export enum EntityType {
|
||||||
SERVICE = 'service',
|
SERVICE = 'service',
|
||||||
|
@ -30,5 +29,4 @@ interface Entity {
|
||||||
lastSeenTimestamp: string;
|
lastSeenTimestamp: string;
|
||||||
firstSeenTimestamp: string;
|
firstSeenTimestamp: string;
|
||||||
identityFields: string[];
|
identityFields: string[];
|
||||||
metrics: EntityMetrics;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,236 +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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { EntityMetrics, EntityDataStreamType } from '../../../../common/entities/types';
|
|
||||||
import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
|
|
||||||
import { calculateAvgMetrics, mergeMetrics } from './calculate_avg_metrics';
|
|
||||||
|
|
||||||
describe('calculateAverageMetrics', () => {
|
|
||||||
it('calculates average metrics', () => {
|
|
||||||
const entities = [
|
|
||||||
{
|
|
||||||
agentName: 'nodejs' as AgentName,
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS, EntityDataStreamType.LOGS],
|
|
||||||
environments: [],
|
|
||||||
latestTimestamp: '2024-03-05T10:34:40.810Z',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
agentName: 'java' as AgentName,
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS],
|
|
||||||
environments: [],
|
|
||||||
latestTimestamp: '2024-06-05T10:34:40.810Z',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 15,
|
|
||||||
latency: 15,
|
|
||||||
logErrorRate: 15,
|
|
||||||
logRate: 15,
|
|
||||||
throughput: 15,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-2',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = calculateAvgMetrics(entities);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
agentName: 'nodejs',
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS, EntityDataStreamType.LOGS],
|
|
||||||
environments: [],
|
|
||||||
latestTimestamp: '2024-03-05T10:34:40.810Z',
|
|
||||||
metrics: {
|
|
||||||
failedTransactionRate: 7.5,
|
|
||||||
latency: 7.5,
|
|
||||||
logErrorRate: 7.5,
|
|
||||||
logRate: 7.5,
|
|
||||||
throughput: 7.5,
|
|
||||||
},
|
|
||||||
serviceName: 'service-1',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
agentName: 'java' as AgentName,
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS],
|
|
||||||
environments: [],
|
|
||||||
latestTimestamp: '2024-06-05T10:34:40.810Z',
|
|
||||||
metrics: {
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
serviceName: 'service-2',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it('calculates average metrics with null', () => {
|
|
||||||
const entities = [
|
|
||||||
{
|
|
||||||
agentName: 'nodejs' as AgentName,
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS],
|
|
||||||
environments: ['env-service-1', 'env-service-2'],
|
|
||||||
latestTimestamp: '2024-03-05T10:34:40.810Z',
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: null,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: null,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = calculateAvgMetrics(entities);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
agentName: 'nodejs',
|
|
||||||
dataStreamTypes: [EntityDataStreamType.METRICS],
|
|
||||||
environments: ['env-service-1', 'env-service-2'],
|
|
||||||
latestTimestamp: '2024-03-05T10:34:40.810Z',
|
|
||||||
metrics: {
|
|
||||||
failedTransactionRate: 7.5,
|
|
||||||
logErrorRate: 7.5,
|
|
||||||
logRate: 7.5,
|
|
||||||
throughput: 7.5,
|
|
||||||
},
|
|
||||||
serviceName: 'service-1',
|
|
||||||
hasLogMetrics: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mergeMetrics', () => {
|
|
||||||
it('merges metrics correctly', () => {
|
|
||||||
const metrics = [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = mergeMetrics(metrics);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
failedTransactionRate: [5, 10],
|
|
||||||
latency: [5, 10],
|
|
||||||
logErrorRate: [5, 10],
|
|
||||||
logRate: [5, 10],
|
|
||||||
throughput: [5, 10],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles empty metrics array', () => {
|
|
||||||
const metrics: EntityMetrics[] = [];
|
|
||||||
|
|
||||||
const result = mergeMetrics(metrics);
|
|
||||||
|
|
||||||
expect(result).toEqual({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns metrics with zero value', () => {
|
|
||||||
const metrics = [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0,
|
|
||||||
latency: 4,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = mergeMetrics(metrics);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
failedTransactionRate: [0],
|
|
||||||
latency: [4],
|
|
||||||
logErrorRate: [5],
|
|
||||||
logRate: [5],
|
|
||||||
throughput: [5],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not return metrics with null', () => {
|
|
||||||
const metrics = [
|
|
||||||
{
|
|
||||||
failedTransactionRate: null,
|
|
||||||
latency: null,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: null,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = mergeMetrics(metrics);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
failedTransactionRate: [5],
|
|
||||||
logErrorRate: [5, 5],
|
|
||||||
logRate: [5, 5],
|
|
||||||
throughput: [5, 5],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,45 +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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { mapValues, isNumber } from 'lodash';
|
|
||||||
import { EntityMetrics } from '../../../../common/entities/types';
|
|
||||||
import type { MergedServiceEntity } from './merge_entities';
|
|
||||||
|
|
||||||
export function calculateAvgMetrics(entities: MergedServiceEntity[]) {
|
|
||||||
return entities.map((entity) => {
|
|
||||||
const transformedMetrics = mergeMetrics(entity.metrics);
|
|
||||||
const averages = mapValues(transformedMetrics, (values: number[]) => {
|
|
||||||
const sum = values.reduce((acc: number, val: number) => acc + (val !== null ? val : 0), 0);
|
|
||||||
return sum / values.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...entity,
|
|
||||||
metrics: averages,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
type MetricsKey = keyof EntityMetrics;
|
|
||||||
|
|
||||||
export function mergeMetrics(metrics: EntityMetrics[]) {
|
|
||||||
return metrics.reduce((acc, metric) => {
|
|
||||||
for (const key in metric) {
|
|
||||||
if (Object.hasOwn(metric, key)) {
|
|
||||||
const metricsKey = key as MetricsKey;
|
|
||||||
|
|
||||||
const value = metric[metricsKey];
|
|
||||||
if (isNumber(value)) {
|
|
||||||
if (!acc[metricsKey]) {
|
|
||||||
acc[metricsKey] = [];
|
|
||||||
}
|
|
||||||
acc[metricsKey].push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {} as { [key in MetricsKey]: number[] });
|
|
||||||
}
|
|
|
@ -22,13 +22,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -41,16 +34,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: ['metrics', 'logs'],
|
dataStreamTypes: ['metrics', 'logs'],
|
||||||
environments: ['test'],
|
environments: ['test'],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -68,13 +51,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:env-service-1',
|
id: 'service-1:env-service-1',
|
||||||
},
|
},
|
||||||
|
@ -89,13 +65,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'apm-only-1:synthtrace-env-2',
|
id: 'apm-only-1:synthtrace-env-2',
|
||||||
},
|
},
|
||||||
|
@ -110,13 +79,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 15,
|
|
||||||
logErrorRate: 15,
|
|
||||||
throughput: 15,
|
|
||||||
failedTransactionRate: 15,
|
|
||||||
latency: 15,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-2:env-service-3',
|
id: 'service-2:env-service-3',
|
||||||
},
|
},
|
||||||
|
@ -131,13 +93,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-2:env-service-3',
|
id: 'service-2:env-service-3',
|
||||||
},
|
},
|
||||||
|
@ -151,23 +106,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: ['foo', 'bar'],
|
dataStreamTypes: ['foo', 'bar'],
|
||||||
environments: ['env-service-1', 'env-service-2'],
|
environments: ['env-service-1', 'env-service-2'],
|
||||||
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 10,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -175,23 +113,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: ['baz'],
|
dataStreamTypes: ['baz'],
|
||||||
environments: ['env-service-3', 'env-service-4'],
|
environments: ['env-service-3', 'env-service-4'],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 15,
|
|
||||||
latency: 15,
|
|
||||||
logErrorRate: 15,
|
|
||||||
logRate: 15,
|
|
||||||
throughput: 15,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-2',
|
serviceName: 'service-2',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -208,13 +129,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -229,13 +143,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -250,13 +157,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 0.333,
|
|
||||||
logErrorRate: 0.333,
|
|
||||||
throughput: 0.333,
|
|
||||||
failedTransactionRate: 0.333,
|
|
||||||
latency: 0.333,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:prod',
|
id: 'service-1:prod',
|
||||||
},
|
},
|
||||||
|
@ -269,30 +169,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: ['metrics', 'logs', 'foo'],
|
dataStreamTypes: ['metrics', 'logs', 'foo'],
|
||||||
environments: ['test', 'prod'],
|
environments: ['test', 'prod'],
|
||||||
lastSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-23-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 5,
|
|
||||||
latency: 5,
|
|
||||||
logErrorRate: 5,
|
|
||||||
logRate: 5,
|
|
||||||
throughput: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: 10,
|
|
||||||
logRate: 10,
|
|
||||||
throughput: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.333,
|
|
||||||
latency: 0.333,
|
|
||||||
logErrorRate: 0.333,
|
|
||||||
logRate: 0.333,
|
|
||||||
throughput: 0.333,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -309,13 +185,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -328,16 +197,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: [],
|
dataStreamTypes: [],
|
||||||
environments: [],
|
environments: [],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -352,13 +211,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -372,13 +224,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -391,23 +236,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: [],
|
dataStreamTypes: [],
|
||||||
environments: [],
|
environments: [],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -424,13 +252,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -443,16 +264,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: [],
|
dataStreamTypes: [],
|
||||||
environments: [],
|
environments: [],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -467,13 +278,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -487,13 +291,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name'],
|
identityFields: ['service.name'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -506,23 +303,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: [],
|
dataStreamTypes: [],
|
||||||
environments: [],
|
environments: [],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: true,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
logErrorRate: null,
|
|
||||||
logRate: 1,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
logRate: 1,
|
|
||||||
logErrorRate: null,
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -540,11 +320,6 @@ describe('mergeEntities', () => {
|
||||||
entity: {
|
entity: {
|
||||||
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
metrics: {
|
|
||||||
throughput: 0,
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
},
|
|
||||||
identityFields: ['service.name', 'service.environment'],
|
identityFields: ['service.name', 'service.environment'],
|
||||||
id: 'service-1:test',
|
id: 'service-1:test',
|
||||||
},
|
},
|
||||||
|
@ -557,14 +332,6 @@ describe('mergeEntities', () => {
|
||||||
dataStreamTypes: ['metrics'],
|
dataStreamTypes: ['metrics'],
|
||||||
environments: ['test'],
|
environments: ['test'],
|
||||||
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
|
||||||
hasLogMetrics: false,
|
|
||||||
metrics: [
|
|
||||||
{
|
|
||||||
failedTransactionRate: 0.3333333333333333,
|
|
||||||
latency: 10,
|
|
||||||
throughput: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
serviceName: 'service-1',
|
serviceName: 'service-1',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -7,17 +7,14 @@
|
||||||
|
|
||||||
import { compact, uniq } from 'lodash';
|
import { compact, uniq } from 'lodash';
|
||||||
import type { EntityLatestServiceRaw } from '../types';
|
import type { EntityLatestServiceRaw } from '../types';
|
||||||
import { isFiniteNumber } from '../../../../common/utils/is_finite_number';
|
|
||||||
import type { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
|
import type { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
|
||||||
import type { EntityDataStreamType, EntityMetrics } from '../../../../common/entities/types';
|
import type { EntityDataStreamType } from '../../../../common/entities/types';
|
||||||
|
|
||||||
export interface MergedServiceEntity {
|
export interface MergedServiceEntity {
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
agentName: AgentName;
|
agentName: AgentName;
|
||||||
dataStreamTypes: EntityDataStreamType[];
|
dataStreamTypes: EntityDataStreamType[];
|
||||||
environments: string[];
|
environments: string[];
|
||||||
metrics: EntityMetrics[];
|
|
||||||
hasLogMetrics: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeEntities({
|
export function mergeEntities({
|
||||||
|
@ -40,10 +37,6 @@ export function mergeEntities({
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServiceEntity) {
|
function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServiceEntity) {
|
||||||
const hasLogMetrics = isFiniteNumber(entity.entity.metrics.logRate)
|
|
||||||
? entity.entity.metrics.logRate > 0
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const commonEntityFields = {
|
const commonEntityFields = {
|
||||||
serviceName: entity.service.name,
|
serviceName: entity.service.name,
|
||||||
agentName: entity.agent.name[0],
|
agentName: entity.agent.name[0],
|
||||||
|
@ -55,8 +48,6 @@ function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServic
|
||||||
...commonEntityFields,
|
...commonEntityFields,
|
||||||
dataStreamTypes: entity.source_data_stream.type,
|
dataStreamTypes: entity.source_data_stream.type,
|
||||||
environments: compact([entity?.service.environment]),
|
environments: compact([entity?.service.environment]),
|
||||||
metrics: [entity.entity.metrics],
|
|
||||||
hasLogMetrics,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -65,7 +56,5 @@ function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServic
|
||||||
compact([...(existingEntity?.dataStreamTypes ?? []), ...entity.source_data_stream.type])
|
compact([...(existingEntity?.dataStreamTypes ?? []), ...entity.source_data_stream.type])
|
||||||
),
|
),
|
||||||
environments: uniq(compact([...existingEntity?.environments, entity?.service.environment])),
|
environments: uniq(compact([...existingEntity?.environments, entity?.service.environment])),
|
||||||
metrics: [...existingEntity?.metrics, entity.entity.metrics],
|
|
||||||
hasLogMetrics: hasLogMetrics || existingEntity.hasLogMetrics,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
import expect from '@kbn/expect';
|
|
||||||
import { APIClientRequestParamsOf } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
|
||||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
|
||||||
|
|
||||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
|
||||||
const registry = getService('registry');
|
|
||||||
|
|
||||||
const apmApiClient = getService('apmApiClient');
|
|
||||||
|
|
||||||
const start = '2024-01-01T00:00:00.000Z';
|
|
||||||
const end = '2024-01-01T00:59:59.999Z';
|
|
||||||
|
|
||||||
const serviceNames = ['my-service', 'synth-go'];
|
|
||||||
|
|
||||||
async function getServiceEntitiesDetailedStats(
|
|
||||||
overrides?: Partial<
|
|
||||||
APIClientRequestParamsOf<'POST /internal/apm/entities/services/detailed_statistics'>['params']['query']
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
const response = await apmApiClient.readUser({
|
|
||||||
endpoint: `POST /internal/apm/entities/services/detailed_statistics`,
|
|
||||||
params: {
|
|
||||||
query: {
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
environment: 'ENVIRONMENT_ALL',
|
|
||||||
kuery: '',
|
|
||||||
...overrides,
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
serviceNames: JSON.stringify(serviceNames),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.when(
|
|
||||||
'Services entities detailed statistics when no data is generated',
|
|
||||||
{ config: 'basic', archives: [] },
|
|
||||||
() => {
|
|
||||||
describe('Service entities detailed', () => {
|
|
||||||
it('handles the empty state', async () => {
|
|
||||||
const response = await getServiceEntitiesDetailedStats();
|
|
||||||
expect(response.status).to.be(200);
|
|
||||||
expect(response.body.currentPeriod).to.empty();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue