Added OTel information to service metadata icons (#154458)

## Summary

Added OpenTelemetry information to metadata icons on OTel services:

<img width="709" alt="image"
src="https://user-images.githubusercontent.com/866830/230113359-307c25dd-9846-4079-bb26-7d145472181a.png">
This commit is contained in:
Alexander Wert 2023-04-24 10:59:11 +02:00 committed by GitHub
parent 03464e79c2
commit 5f24c14d58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 156 additions and 24 deletions

View file

@ -5,7 +5,10 @@
* 2.0.
*/
import { AgentName } from '../typings/es_schemas/ui/fields/agent';
import {
AgentName,
OpenTelemetryAgentName,
} from '../typings/es_schemas/ui/fields/agent';
import { ServerlessType } from './serverless';
/*
@ -47,8 +50,11 @@ export const AGENT_NAMES: AgentName[] = [
...OPEN_TELEMETRY_AGENT_NAMES,
];
export const isOpenTelemetryAgentName = (agentName: AgentName) =>
OPEN_TELEMETRY_AGENT_NAMES.includes(agentName);
export function isOpenTelemetryAgentName(
agentName: string
): agentName is OpenTelemetryAgentName {
return OPEN_TELEMETRY_AGENT_NAMES.includes(agentName as AgentName);
}
export const JAVA_AGENT_NAMES: AgentName[] = ['java', 'opentelemetry/java'];

View file

@ -25,7 +25,7 @@ import darkIosIcon from './icons/ios_dark.svg';
import javaIcon from './icons/java.svg';
import nodeJsIcon from './icons/nodejs.svg';
import ocamlIcon from './icons/ocaml.svg';
import openTelemetryIcon from './icons/opentelemetry.svg';
import openTelemetryIcon from './icons/otel_default.svg';
import phpIcon from './icons/php.svg';
import pythonIcon from './icons/python.svg';
import rubyIcon from './icons/ruby.svg';

View file

@ -0,0 +1,12 @@
<svg width="128" height="128" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="4 4 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.75 6.165a1.5 1.5 0 00-1.5 0l-7.392 4.268a1.5 1.5 0 00-.75 1.3v8.535a1.5 1.5 0 00.75 1.299l7.392 4.268a1.5 1.5 0 001.5 0l7.392-4.268a1.5 1.5 0 00.75-1.299v-8.536a1.5 1.5 0 00-.75-1.299L16.75 6.165zm.75-1.299l7.392 4.268a3 3 0 011.5 2.598v8.536a3 3 0 01-1.5 2.598L17.5 27.134a3 3 0 01-3 0l-7.392-4.268a3 3 0 01-1.5-2.598v-8.536a3 3 0 011.5-2.598L14.5 4.866a3 3 0 013 0z" fill="#98A2B3"/>
</svg>
<svg x="32" y="32" width="64" height="64" xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 1024.40 1024.40">
<style>svg {enable-background:new 0 0 1000 1000}</style>
<path fill="#f5a800" d="M528.7 545.9c-42 42-42 110.1 0 152.1s110.1 42 152.1 0 42-110.1 0-152.1-110.1-42-152.1 0zm113.7 113.8c-20.8 20.8-54.5 20.8-75.3 0-20.8-20.8-20.8-54.5 0-75.3 20.8-20.8 54.5-20.8 75.3 0 20.8 20.7 20.8 54.5 0 75.3zm36.6-643l-65.9 65.9c-12.9 12.9-12.9 34.1 0 47l257.3 257.3c12.9 12.9 34.1 12.9 47 0l65.9-65.9c12.9-12.9 12.9-34.1 0-47L725.9 16.7c-12.9-12.9-34-12.9-46.9 0zM217.3 858.8c11.7-11.7 11.7-30.8 0-42.5l-33.5-33.5c-11.7-11.7-30.8-11.7-42.5 0L72.1 852l-.1.1-19-19c-10.5-10.5-27.6-10.5-38 0-10.5 10.5-10.5 27.6 0 38l114 114c10.5 10.5 27.6 10.5 38 0s10.5-27.6 0-38l-19-19 .1-.1 69.2-69.2z"/>
<path fill="#425cc7" d="M565.9 205.9L419.5 352.3c-13 13-13 34.4 0 47.4l90.4 90.4c63.9-46 153.5-40.3 211 17.2l73.2-73.2c13-13 13-34.4 0-47.4L613.3 205.9c-13-13.1-34.4-13.1-47.4 0zm-94 322.3l-53.4-53.4c-12.5-12.5-33-12.5-45.5 0L184.7 663.2c-12.5 12.5-12.5 33 0 45.5l106.7 106.7c12.5 12.5 33 12.5 45.5 0L458 694.1c-25.6-52.9-21-116.8 13.9-165.9z"/>
</svg>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -16,9 +16,12 @@ import { getServerlessIcon } from '../agent_icon/get_serverless_icon';
import { CloudDetails } from './cloud_details';
import { ServerlessDetails } from './serverless_details';
import { ContainerDetails } from './container_details';
import { OTelDetails } from './otel_details';
import { IconPopover } from './icon_popover';
import { ServiceDetails } from './service_details';
import { ServerlessType } from '../../../../common/serverless';
import { isOpenTelemetryAgentName } from '../../../../common/agent_name';
import openTelemetryIcon from '../agent_icon/icons/opentelemetry.svg';
interface Props {
serviceName: string;
@ -70,7 +73,13 @@ export function getContainerIcon(container?: ContainerType) {
}
}
type Icons = 'service' | 'container' | 'serverless' | 'cloud' | 'alerts';
type Icons =
| 'service'
| 'opentelemetry'
| 'container'
| 'serverless'
| 'cloud'
| 'alerts';
export interface PopoverItem {
key: Icons;
@ -142,6 +151,23 @@ export function ServiceIcons({ start, end, serviceName }: Props) {
}),
component: <ServiceDetails service={details?.service} />,
},
{
key: 'opentelemetry',
icon: {
type: openTelemetryIcon,
},
isVisible:
!!icons?.agentName && isOpenTelemetryAgentName(icons.agentName),
title: i18n.translate('xpack.apm.serviceIcons.opentelemetry', {
defaultMessage: 'OpenTelemetry',
}),
component: (
<OTelDetails
opentelemetry={details?.opentelemetry}
agentName={icons?.agentName}
/>
),
},
{
key: 'container',
icon: {

View file

@ -0,0 +1,64 @@
/*
* 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 { EuiDescriptionList, EuiDescriptionListProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { APIReturnType } from '../../../services/rest/create_call_apm_api';
type ServiceDetailsReturnType =
APIReturnType<'GET /internal/apm/services/{serviceName}/metadata/details'>;
interface Props {
opentelemetry: ServiceDetailsReturnType['opentelemetry'];
agentName?: string;
}
export function OTelDetails({ opentelemetry }: Props) {
if (!opentelemetry) {
return null;
}
const listItems: EuiDescriptionListProps['listItems'] = [];
listItems.push({
title: i18n.translate(
'xpack.apm.serviceIcons.otelDetails.opentelemetry.language',
{
defaultMessage: 'Language',
}
),
description: (
<>{!!opentelemetry.language ? opentelemetry.language : 'unknown'}</>
),
});
if (!!opentelemetry.sdkVersion) {
listItems.push({
title: i18n.translate(
'xpack.apm.serviceIcons.otelDetails.opentelemetry.sdkVersion',
{
defaultMessage: 'OTel SDK version',
}
),
description: <>{opentelemetry.sdkVersion}</>,
});
}
if (!!opentelemetry.autoVersion) {
listItems.push({
title: i18n.translate(
'xpack.apm.serviceIcons.otelDetails.opentelemetry.autoVersion',
{
defaultMessage: 'Auto instrumentation agent version',
}
),
description: <>{opentelemetry.autoVersion}</>,
});
}
return <EuiDescriptionList textStyle="reverse" listItems={listItems} />;
}

View file

@ -24,17 +24,18 @@ import {
SERVICE_VERSION,
FAAS_ID,
FAAS_TRIGGER_TYPE,
LABEL_TELEMETRY_AUTO_VERSION,
} from '../../../common/es_fields/apm';
import { ContainerType } from '../../../common/service_metadata';
import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw';
import { getProcessorEventForTransactions } from '../../lib/helpers/transactions';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
import { should } from './get_service_metadata_icons';
import { isOpenTelemetryAgentName } from '../../../common/agent_name';
type ServiceMetadataDetailsRaw = Pick<
TransactionRaw,
'service' | 'agent' | 'host' | 'container' | 'kubernetes' | 'cloud'
'service' | 'agent' | 'host' | 'container' | 'kubernetes' | 'cloud' | 'labels'
>;
export interface ServiceMetadataDetails {
@ -50,6 +51,11 @@ export interface ServiceMetadataDetails {
version: string;
};
};
opentelemetry?: {
language?: string;
sdkVersion?: string;
autoVersion?: string;
};
container?: {
ids?: string[];
image?: string;
@ -81,13 +87,11 @@ export interface ServiceMetadataDetails {
export async function getServiceMetadataDetails({
serviceName,
apmEventClient,
searchAggregatedTransactions,
start,
end,
}: {
serviceName: string;
apmEventClient: APMEventClient;
searchAggregatedTransactions: boolean;
start: number;
end: number;
}): Promise<ServiceMetadataDetails> {
@ -99,16 +103,27 @@ export async function getServiceMetadataDetails({
const params = {
apm: {
events: [
getProcessorEventForTransactions(searchAggregatedTransactions),
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
sort: [{ '@timestamp': { order: 'desc' as const } }],
sort: [
{ _score: { order: 'desc' as const } },
{ '@timestamp': { order: 'desc' as const } },
],
body: {
track_total_hits: 1,
size: 1,
_source: [SERVICE, AGENT, HOST, CONTAINER, KUBERNETES, CLOUD],
_source: [
SERVICE,
AGENT,
HOST,
CONTAINER,
KUBERNETES,
CLOUD,
LABEL_TELEMETRY_AUTO_VERSION,
],
query: { bool: { filter, should } },
aggs: {
serviceVersions: {
@ -178,8 +193,8 @@ export async function getServiceMetadataDetails({
};
}
const { service, agent, host, kubernetes, container, cloud } = response.hits
.hits[0]._source as ServiceMetadataDetailsRaw;
const { service, agent, host, kubernetes, container, cloud, labels } =
response.hits.hits[0]._source as ServiceMetadataDetailsRaw;
const serviceMetadataDetails = {
versions: response.aggregations?.serviceVersions.buckets.map(
@ -190,6 +205,17 @@ export async function getServiceMetadataDetails({
agent,
};
const otelDetails =
!!agent?.name && isOpenTelemetryAgentName(agent.name)
? {
language: agent.name.startsWith('opentelemetry')
? agent.name.replace(/^opentelemetry\//, '')
: undefined,
sdkVersion: agent?.version,
autoVersion: labels?.telemetry_auto_version as string,
}
: undefined;
const totalNumberInstances =
response.aggregations?.totalNumberInstances.value;
@ -238,6 +264,7 @@ export async function getServiceMetadataDetails({
return {
service: serviceMetadataDetails,
opentelemetry: otelDetails,
container: containerDetails,
serverless: serverlessDetails,
cloud: cloudDetails,

View file

@ -16,6 +16,9 @@ import {
SERVICE_NAME,
KUBERNETES_POD_NAME,
HOST_OS_PLATFORM,
LABEL_TELEMETRY_AUTO_VERSION,
AGENT_VERSION,
SERVICE_FRAMEWORK_NAME,
} from '../../../common/es_fields/apm';
import { ContainerType } from '../../../common/service_metadata';
import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw';
@ -44,6 +47,9 @@ export const should = [
{ exists: { field: CLOUD_PROVIDER } },
{ exists: { field: HOST_OS_PLATFORM } },
{ exists: { field: AGENT_NAME } },
{ exists: { field: AGENT_VERSION } },
{ exists: { field: SERVICE_FRAMEWORK_NAME } },
{ exists: { field: LABEL_TELEMETRY_AUTO_VERSION } },
];
export async function getServiceMetadataIcons({

View file

@ -240,22 +240,13 @@ const serviceMetadataDetailsRoute = createApmServerRoute({
handler: async (resources): Promise<ServiceMetadataDetails> => {
const apmEventClient = await getApmEventClient(resources);
const infraMetricsClient = createInfraMetricsClient(resources);
const { params, config } = resources;
const { params } = resources;
const { serviceName } = params.path;
const { start, end } = params.query;
const searchAggregatedTransactions = await getSearchTransactionsEvents({
apmEventClient,
config,
start,
end,
kuery: '',
});
const serviceMetadataDetails = await getServiceMetadataDetails({
serviceName,
apmEventClient,
searchAggregatedTransactions,
start,
end,
});