From b5f460fb6e7fc551aceabded65f4a2f148e324a0 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Fri, 20 Mar 2020 13:14:39 -0500 Subject: [PATCH] Remove all client-side map munging (#60701) Remove the getCytoscapeElements function. On the server: * Replace `source` with `sourceData`, `destination` with `targetData`, `source.id` with `source`, and `destination.id` with `target`. * Return a single array as an `elements` property instead of `nodes` and `connections` * Map all of the items data to be inside of a `data` object * Replace SERVICE_AGENT_NAME with AGENT_NAME * Add some missing constants On the client: * Remove getCytoscapeElements * Move all presentation-specific data transformation to use the original attributes in the place where they're needed * Remove `href` since it wasn't being used * Move BetaBadge to its own file * Move cytoscapeDivStyle to cytoscapeOptions * Fix storybook to work with new data formats --- .../components/app/ServiceMap/BetaBadge.tsx | 37 + .../app/ServiceMap/Cytoscape.stories.tsx | 80 +- .../components/app/ServiceMap/Cytoscape.tsx | 19 +- .../app/ServiceMap/Popover/Contents.tsx | 3 +- .../app/ServiceMap/Popover/Info.tsx | 13 +- .../app/ServiceMap/Popover/index.tsx | 3 +- .../cytoscape-layout-test-response.json | 860 +++++++++++++++++- .../app/ServiceMap/cytoscapeOptions.ts | 38 +- .../app/ServiceMap/get_cytoscape_elements.ts | 77 -- .../public/components/app/ServiceMap/icons.ts | 15 +- .../components/app/ServiceMap/index.tsx | 61 +- .../elasticsearch_fieldnames.test.ts.snap | 12 +- .../apm/common/elasticsearch_fieldnames.ts | 2 +- x-pack/plugins/apm/common/service_map.ts | 23 +- .../java/gc/fetch_and_transform_gc_metrics.ts | 4 +- .../by_agent/java/heap_memory/index.ts | 4 +- .../by_agent/java/non_heap_memory/index.ts | 4 +- .../by_agent/java/thread_count/index.ts | 4 +- .../lib/service_map/dedupe_connections.ts | 51 +- .../server/lib/service_map/get_service_map.ts | 27 +- .../get_service_map_from_trace_ids.ts | 23 +- .../lib/services/get_service_agent_name.ts | 4 +- .../get_services/get_services_items.ts | 4 +- .../get_agent_name_by_service.ts | 4 +- .../lib/ui_filters/local_ui_filters/config.ts | 4 +- 25 files changed, 1110 insertions(+), 266 deletions(-) create mode 100644 x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx delete mode 100644 x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx new file mode 100644 index 00000000000000..b40efce020a638 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiBetaBadge } from '@elastic/eui'; +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import styled from 'styled-components'; + +const BetaBadgeContainer = styled.div` + right: ${theme.gutterTypes.gutterMedium}; + position: absolute; + top: ${theme.gutterTypes.gutterSmall}; + z-index: 1; /* The element containing the cytoscape canvas has z-index = 0. */ +`; + +export function BetaBadge() { + return ( + + + + ); +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx index b18f462b541710..155695f7596dd8 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx @@ -9,11 +9,10 @@ import { storiesOf } from '@storybook/react'; import cytoscape from 'cytoscape'; import React from 'react'; import { Cytoscape } from './Cytoscape'; -import { getCytoscapeElements } from './get_cytoscape_elements'; import serviceMapResponse from './cytoscape-layout-test-response.json'; import { iconForNode } from './icons'; -const elementsFromResponses = getCytoscapeElements(serviceMapResponse, ''); +const elementsFromResponses = serviceMapResponse.elements; storiesOf('app/ServiceMap/Cytoscape', module).add( 'example', @@ -22,25 +21,22 @@ storiesOf('app/ServiceMap/Cytoscape', module).add( { data: { id: 'opbeans-python', - label: 'opbeans-python', - agentName: 'python', - type: 'service' + 'service.name': 'opbeans-python', + 'agent.name': 'python' } }, { data: { id: 'opbeans-node', - label: 'opbeans-node', - agentName: 'nodejs', - type: 'service' + 'service.name': 'opbeans-node', + 'agent.name': 'nodejs' } }, { data: { id: 'opbeans-ruby', - label: 'opbeans-ruby', - agentName: 'ruby', - type: 'service' + 'service.name': 'opbeans-ruby', + 'agent.name': 'ruby' } }, { data: { source: 'opbeans-python', target: 'opbeans-node' } }, @@ -78,74 +74,74 @@ storiesOf('app/ServiceMap/Cytoscape', module) () => { const cy = cytoscape(); const elements = [ - { data: { id: 'default', label: 'default', type: undefined } }, - { data: { id: 'cache', label: 'cache', type: 'cache' } }, - { data: { id: 'database', label: 'database', type: 'database' } }, - { data: { id: 'external', label: 'external', type: 'external' } }, - { data: { id: 'messaging', label: 'messaging', type: 'messaging' } }, + { data: { id: 'default' } }, + { data: { id: 'cache', label: 'cache', 'span.type': 'cache' } }, + { data: { id: 'database', label: 'database', 'span.type': 'db' } }, + { + data: { id: 'external', label: 'external', 'span.type': 'external' } + }, + { + data: { + id: 'messaging', + label: 'messaging', + 'span.type': 'messaging' + } + }, { data: { id: 'dotnet', - label: 'dotnet service', - type: 'service', - agentName: 'dotnet' + 'service.name': 'dotnet service', + 'agent.name': 'dotnet' } }, { data: { id: 'go', - label: 'go service', - type: 'service', - agentName: 'go' + 'service.name': 'go service', + 'agent.name': 'go' } }, { data: { id: 'java', - label: 'java service', - type: 'service', - agentName: 'java' + 'service.name': 'java service', + 'agent.name': 'java' } }, { data: { id: 'js-base', - label: 'js-base service', - type: 'service', - agentName: 'js-base' + 'service.name': 'js-base service', + 'agent.name': 'js-base' } }, { data: { id: 'nodejs', - label: 'nodejs service', - type: 'service', - agentName: 'nodejs' + 'service.name': 'nodejs service', + 'agent.name': 'nodejs' } }, { data: { id: 'php', - label: 'php service', - type: 'service', - agentName: 'php' + 'service.name': 'php service', + 'agent.name': 'php' } }, { data: { id: 'python', - label: 'python service', - type: 'service', - agentName: 'python' + 'service.name': 'python service', + 'agent.name': 'python' } }, { data: { id: 'ruby', - label: 'ruby service', - type: 'service', - agentName: 'ruby' + 'service.name': 'ruby service', + 'agent.name': 'ruby' } } ]; @@ -158,8 +154,8 @@ storiesOf('app/ServiceMap/Cytoscape', module) - agentName: {node.data('agentName') || 'undefined'}, type:{' '} - {node.data('type') || 'undefined'} + agent.name: {node.data('agent.name') || 'undefined'}, + span.type: {node.data('span.type') || 'undefined'} } icon={ diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index ae6b06b10fd1d0..3197c269fd90c8 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +import cytoscape from 'cytoscape'; import React, { + createContext, CSSProperties, - useState, - useRef, - useEffect, ReactNode, - createContext, - useCallback + useCallback, + useEffect, + useRef, + useState } from 'react'; -import cytoscape from 'cytoscape'; import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; +import { AGENT_NAME } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import { + animationOptions, cytoscapeOptions, - nodeHeight, - animationOptions + nodeHeight } from './cytoscapeOptions'; export const CytoscapeContext = createContext( @@ -81,7 +82,7 @@ function getLayoutOptions( function selectRoots(cy: cytoscape.Core): string[] { const nodes = cy.nodes(); const roots = nodes.roots(); - const rumNodes = nodes.filter(node => isRumAgentName(node.data('agentName'))); + const rumNodes = nodes.filter(node => isRumAgentName(node.data(AGENT_NAME))); return rumNodes.union(roots).map(node => node.id()); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index 7db064632a7f11..52263878ca9153 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -12,6 +12,7 @@ import { } from '@elastic/eui'; import cytoscape from 'cytoscape'; import React from 'react'; +import { SERVICE_FRAMEWORK_NAME } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import { Buttons } from './Buttons'; import { Info } from './Info'; import { ServiceMetricFetcher } from './ServiceMetricFetcher'; @@ -33,7 +34,7 @@ export function Contents({ onFocusClick, selectedNodeServiceName }: ContentsProps) { - const frameworkName = selectedNodeData.frameworkName; + const frameworkName = selectedNodeData[SERVICE_FRAMEWORK_NAME]; return ( 172.17.0.1", + "id": "apm-server~>172.17.0.1", + "sourceData": { + "service.environment": null, + "service.name": "apm-server", + "agent.name": "go", + "id": "apm-server" + }, + "targetData": { + "destination.address": "172.17.0.1", + "span.subtype": "http", + "span.type": "external", + "id": ">172.17.0.1" + } + } + }, + { + "data": { + "source": "client", + "target": ">opbeans-node", + "id": "client~>opbeans-node", + "sourceData": { + "service.environment": null, + "service.name": "client", + "agent.name": "js-base", + "id": "client" + }, + "targetData": { + "destination.address": "opbeans-node", + "span.subtype": null, + "span.type": "resource", + "id": ">opbeans-node" + } + } + }, + { + "data": { + "source": "client", + "target": "opbeans-node", + "id": "client~opbeans-node", + "sourceData": { + "service.environment": null, + "service.name": "client", + "agent.name": "js-base", + "id": "client" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + } + } + }, + { + "data": { + "source": "opbeans-dotnet", + "target": "opbeans-go", + "id": "opbeans-dotnet~opbeans-go", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-dotnet", + "target": "opbeans-java", + "id": "opbeans-dotnet~opbeans-java", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-dotnet", + "target": "opbeans-node", + "id": "opbeans-dotnet~opbeans-node", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-dotnet", + "target": "opbeans-python", + "id": "opbeans-dotnet~opbeans-python", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-dotnet", + "target": "opbeans-ruby", + "id": "opbeans-dotnet~opbeans-ruby", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-go", + "target": ">postgres", + "id": "opbeans-go~>postgres", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "destination.address": "postgres", + "span.subtype": "postgresql", + "span.type": "db", + "id": ">postgres" + } + } + }, + { + "data": { + "source": "opbeans-go", + "target": "opbeans-dotnet", + "id": "opbeans-go~opbeans-dotnet", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-go", + "target": "opbeans-java", + "id": "opbeans-go~opbeans-java", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-go", + "target": "opbeans-node", + "id": "opbeans-go~opbeans-node", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-go", + "target": "opbeans-python", + "id": "opbeans-go~opbeans-python", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-go", + "target": "opbeans-ruby", + "id": "opbeans-go~opbeans-ruby", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-java", + "target": ">postgres", + "id": "opbeans-java~>postgres", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "destination.address": "postgres", + "span.subtype": "postgresql", + "span.type": "db", + "id": ">postgres" + } + } + }, + { + "data": { + "source": "opbeans-java", + "target": "opbeans-dotnet", + "id": "opbeans-java~opbeans-dotnet", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-java", + "target": "opbeans-go", + "id": "opbeans-java~opbeans-go", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-java", + "target": "opbeans-node", + "id": "opbeans-java~opbeans-node", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-java", + "target": "opbeans-python", + "id": "opbeans-java~opbeans-python", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-java", + "target": "opbeans-ruby", + "id": "opbeans-java~opbeans-ruby", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-node", + "target": "opbeans-dotnet", + "id": "opbeans-node~opbeans-dotnet", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-node", + "target": "opbeans-go", + "id": "opbeans-node~opbeans-go", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-node", + "target": "opbeans-java", + "id": "opbeans-node~opbeans-java", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-node", + "target": "opbeans-python", + "id": "opbeans-node~opbeans-python", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-node", + "target": "opbeans-ruby", + "id": "opbeans-node~opbeans-ruby", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-python", + "target": "opbeans-dotnet", + "id": "opbeans-python~opbeans-dotnet", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-python", + "target": "opbeans-go", + "id": "opbeans-python~opbeans-go", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-python", + "target": "opbeans-java", + "id": "opbeans-python~opbeans-java", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-python", + "target": "opbeans-node", + "id": "opbeans-python~opbeans-node", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-python", + "target": "opbeans-ruby", + "id": "opbeans-python~opbeans-ruby", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "bidirectional": true + } + }, + { + "data": { + "source": "opbeans-ruby", + "target": "opbeans-dotnet", + "id": "opbeans-ruby~opbeans-dotnet", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-ruby", + "target": "opbeans-go", + "id": "opbeans-ruby~opbeans-go", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-ruby", + "target": "opbeans-java", + "id": "opbeans-ruby~opbeans-java", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-ruby", + "target": "opbeans-node", + "id": "opbeans-ruby~opbeans-node", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + }, + "isInverseEdge": true + } + }, + { + "data": { + "source": "opbeans-ruby", + "target": "opbeans-python", + "id": "opbeans-ruby~opbeans-python", + "sourceData": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + }, + "targetData": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + }, + "isInverseEdge": true + } + }, + { + "data": { + "service.environment": null, + "service.name": "client", + "agent.name": "js-base", + "id": "client" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-node", + "agent.name": "nodejs", + "id": "opbeans-node" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-go", + "agent.name": "go", + "id": "opbeans-go" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-java", + "agent.name": "java", + "id": "opbeans-java" + } + }, + { + "data": { + "destination.address": "postgres", + "span.subtype": "postgresql", + "span.type": "db", + "id": ">postgres" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "id": "opbeans-ruby" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "id": "opbeans-dotnet" + } + }, + { + "data": { + "service.environment": "production", + "service.name": "opbeans-python", + "agent.name": "python", + "id": "opbeans-python" + } + }, + { + "data": { + "destination.address": "opbeans-node", + "span.subtype": null, + "span.type": "resource", + "id": ">opbeans-node" + } + }, + { + "data": { + "service.environment": null, + "service.name": "apm-server", + "agent.name": "go", + "id": "apm-server" + } + }, + { + "data": { + "destination.address": "172.17.0.1", + "span.subtype": "http", + "span.type": "external", + "id": ">172.17.0.1" + } + }, + { + "data": { + "service.name": "apm-server", + "agent.name": "go", + "service.environment": null, + "service.framework.name": null, + "id": "apm-server" + } + }, + { + "data": { + "service.name": "opbeans-python", + "agent.name": "python", + "service.environment": null, + "service.framework.name": "django", + "id": "opbeans-python" + } + }, + { + "data": { + "service.name": "opbeans-ruby", + "agent.name": "ruby", + "service.environment": null, + "service.framework.name": "Ruby on Rails", + "id": "opbeans-ruby" + } + }, + { + "data": { + "service.name": "opbeans-node", + "agent.name": "nodejs", + "service.environment": null, + "service.framework.name": "express", + "id": "opbeans-node" + } + }, + { + "data": { + "service.name": "opbeans-go", + "agent.name": "go", + "service.environment": null, + "service.framework.name": "gin", + "id": "opbeans-go" + } + }, + { + "data": { + "service.name": "opbeans-java", + "agent.name": "java", + "service.environment": null, + "service.framework.name": null, + "id": "opbeans-java" + } + }, + { + "data": { + "service.name": "opbeans-dotnet", + "agent.name": "dotnet", + "service.environment": null, + "service.framework.name": "ASP.NET Core", + "id": "opbeans-dotnet" + } + }, + { + "data": { + "service.name": "client", + "agent.name": "js-base", + "service.environment": null, + "service.framework.name": null, + "id": "client" + } + } + ] +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 87008d87907884..30b36b58cb0011 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -5,6 +5,11 @@ */ import theme from '@elastic/eui/dist/eui_theme_light.json'; import cytoscape from 'cytoscape'; +import { CSSProperties } from 'react'; +import { + DESTINATION_ADDRESS, + SERVICE_NAME +} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import { defaultIcon, iconForNode } from './icons'; export const animationOptions: cytoscape.AnimationOptions = { @@ -20,7 +25,7 @@ const zIndexEdgeHover = 120; export const nodeHeight = parseInt(theme.avatarSizing.l.size, 10); function isService(el: cytoscape.NodeSingular) { - return el.data('type') === 'service'; + return el.data(SERVICE_NAME) !== undefined; } const style: cytoscape.Stylesheet[] = [ @@ -53,7 +58,8 @@ const style: cytoscape.Stylesheet[] = [ 'ghost-offset-y': 2, 'ghost-opacity': 0.15, height: nodeHeight, - label: 'data(label)', + label: (el: cytoscape.NodeSingular) => + isService(el) ? el.data(SERVICE_NAME) : el.data(DESTINATION_ADDRESS), 'min-zoomed-font-size': theme.euiSizeL, 'overlay-opacity': 0, shape: (el: cytoscape.NodeSingular) => @@ -101,6 +107,12 @@ const style: cytoscape.Stylesheet[] = [ 'target-distance-from-node': theme.paddingSizes.xs } }, + // @ts-ignore DefinitelyTyped says visibility is "none" but it's + // actually "hidden" + { + selector: 'edge[isInverseEdge]', + style: { visibility: 'hidden' } + }, // @ts-ignore { selector: '.invisible', @@ -133,6 +145,28 @@ const style: cytoscape.Stylesheet[] = [ } ]; +// The CSS styles for the div containing the cytoscape element. Makes a +// background grid of dots. +export const cytoscapeDivStyle: CSSProperties = { + background: `linear-gradient( + 90deg, + ${theme.euiPageBackgroundColor} + calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)), + transparent 1% +) +center, +linear-gradient( + ${theme.euiPageBackgroundColor} + calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)), + transparent 1% +) +center, +${theme.euiColorLightShade}`, + backgroundSize: `${theme.euiSizeL} ${theme.euiSizeL}`, + margin: `-${theme.gutterTypes.gutterLarge}`, + marginTop: 0 +}; + export const cytoscapeOptions: cytoscape.CytoscapeOptions = { autoungrabify: true, boxSelectionEnabled: false, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts deleted file mode 100644 index 4017aa2e3cdd90..00000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts +++ /dev/null @@ -1,77 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { ValuesType } from 'utility-types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ServiceMapAPIResponse } from '../../../../../../../plugins/apm/server/lib/service_map/get_service_map'; -import { getAPMHref } from '../../shared/Links/apm/APMLink'; - -export function getCytoscapeElements( - response: ServiceMapAPIResponse, - search: string -) { - const { nodes, connections } = response; - - const nodesById = nodes.reduce((nodeMap, node) => { - return { - ...nodeMap, - [node.id]: node - }; - }, {} as Record>); - - const cyNodes = (Object.values(nodesById) as Array< - ValuesType - >).map(node => { - let data = {}; - - if ('service.name' in node) { - data = { - href: getAPMHref( - `/services/${node['service.name']}/service-map`, - search - ), - agentName: node['agent.name'], - frameworkName: node['service.framework.name'], - type: 'service' - }; - } - - if ('span.type' in node) { - data = { - // For nodes with span.type "db", convert it to "database". Otherwise leave it as-is. - type: node['span.type'] === 'db' ? 'database' : node['span.type'], - // Externals should not have a subtype so make it undefined if the type is external. - subtype: node['span.type'] !== 'external' && node['span.subtype'] - }; - } - - return { - group: 'nodes' as const, - data: { - id: node.id, - label: - 'service.name' in node - ? node['service.name'] - : node['destination.address'], - ...data - } - }; - }); - - const cyEdges = connections.map(connection => { - return { - group: 'edges' as const, - classes: connection.isInverseEdge ? 'invisible' : undefined, - data: { - id: connection.id, - source: connection.source.id, - target: connection.destination.id, - bidirectional: connection.bidirectional ? true : undefined - } - }; - }, []); - - return [...cyNodes, ...cyEdges]; -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts index 1b57cd52082d80..5102dfc02f7570 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts @@ -5,7 +5,13 @@ */ import cytoscape from 'cytoscape'; +import { + AGENT_NAME, + SERVICE_NAME, + SPAN_TYPE +} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import databaseIcon from './icons/database.svg'; +import defaultIconImport from './icons/default.svg'; import documentsIcon from './icons/documents.svg'; import dotNetIcon from './icons/dot-net.svg'; import globeIcon from './icons/globe.svg'; @@ -16,14 +22,13 @@ import phpIcon from './icons/php.svg'; import pythonIcon from './icons/python.svg'; import rubyIcon from './icons/ruby.svg'; import rumJsIcon from './icons/rumjs.svg'; -import defaultIconImport from './icons/default.svg'; export const defaultIcon = defaultIconImport; // The colors here are taken from the logos of the corresponding technologies const icons: { [key: string]: string } = { cache: databaseIcon, - database: databaseIcon, + db: databaseIcon, external: globeIcon, messaging: documentsIcon, resource: globeIcon @@ -52,10 +57,10 @@ const serviceIcons: { [key: string]: string } = { const isIE11 = !!window.MSInputMethodContext && !!document.documentMode; export function iconForNode(node: cytoscape.NodeSingular) { - const type = node.data('type'); + const type = node.data(SPAN_TYPE); - if (type === 'service') { - return serviceIcons[node.data('agentName') as string]; + if (node.data(SERVICE_NAME)) { + return serviceIcons[node.data(AGENT_NAME) as string]; } else if (isIE11) { return defaultIcon; } else if (icons[type]) { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 6222a00a9e8885..7040c27765a8db 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -4,22 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBetaBadge } from '@elastic/eui'; import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { i18n } from '@kbn/i18n'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; +import React from 'react'; import { isValidPlatinumLicense } from '../../../../../../../plugins/apm/common/service_map'; import { useDeepObjectIdentity } from '../../../hooks/useDeepObjectIdentity'; import { useFetcher } from '../../../hooks/useFetcher'; import { useLicense } from '../../../hooks/useLicense'; -import { useLocation } from '../../../hooks/useLocation'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { callApmApi } from '../../../services/rest/createCallApmApi'; +import { BetaBadge } from './BetaBadge'; import { Controls } from './Controls'; import { Cytoscape } from './Cytoscape'; +import { cytoscapeDivStyle } from './cytoscapeOptions'; import { EmptyBanner } from './EmptyBanner'; -import { getCytoscapeElements } from './get_cytoscape_elements'; import { PlatinumLicensePrompt } from './PlatinumLicensePrompt'; import { Popover } from './Popover'; import { useRefDimensions } from './useRefDimensions'; @@ -28,35 +25,8 @@ interface ServiceMapProps { serviceName?: string; } -const cytoscapeDivStyle = { - background: `linear-gradient( - 90deg, - ${theme.euiPageBackgroundColor} - calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)), - transparent 1% -) -center, -linear-gradient( - ${theme.euiPageBackgroundColor} - calc(${theme.euiSizeL} - calc(${theme.euiSizeXS} / 2)), - transparent 1% -) -center, -${theme.euiColorLightShade}`, - backgroundSize: `${theme.euiSizeL} ${theme.euiSizeL}`, - margin: `-${theme.gutterTypes.gutterLarge}`, - marginTop: 0 -}; -const BetaBadgeContainer = styled.div` - right: ${theme.gutterTypes.gutterMedium}; - position: absolute; - top: ${theme.gutterTypes.gutterSmall}; - z-index: 1; /* The element containing the cytoscape canvas has z-index = 0. */ -`; - export function ServiceMap({ serviceName }: ServiceMapProps) { const license = useLicense(); - const { search } = useLocation(); const { urlParams, uiFilters } = useUrlParams(); const params = useDeepObjectIdentity({ start: urlParams.start, @@ -86,10 +56,6 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { } }, [params]); - const elements = useMemo(() => { - return data ? getCytoscapeElements(data as any, search) : []; - }, [data, search]); - const { ref, height, width } = useRefDimensions(); if (!license) { @@ -102,29 +68,16 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { ref={ref} > + {serviceName && } - - - ) : ( diff --git a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index b4b4e7866e9b7d..9a557532aae937 100644 --- a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Error AGENT_NAME 1`] = `"java"`; + exports[`Error CLIENT_GEO_COUNTRY_ISO_CODE 1`] = `undefined`; exports[`Error CONTAINER_ID 1`] = `undefined`; @@ -62,8 +64,6 @@ exports[`Error POD_NAME 1`] = `undefined`; exports[`Error PROCESSOR_EVENT 1`] = `"error"`; -exports[`Error SERVICE_AGENT_NAME 1`] = `"java"`; - exports[`Error SERVICE_ENVIRONMENT 1`] = `undefined`; exports[`Error SERVICE_FRAMEWORK_NAME 1`] = `undefined`; @@ -112,6 +112,8 @@ exports[`Error USER_AGENT_NAME 1`] = `undefined`; exports[`Error USER_ID 1`] = `undefined`; +exports[`Span AGENT_NAME 1`] = `"java"`; + exports[`Span CLIENT_GEO_COUNTRY_ISO_CODE 1`] = `undefined`; exports[`Span CONTAINER_ID 1`] = `undefined`; @@ -174,8 +176,6 @@ exports[`Span POD_NAME 1`] = `undefined`; exports[`Span PROCESSOR_EVENT 1`] = `"span"`; -exports[`Span SERVICE_AGENT_NAME 1`] = `"java"`; - exports[`Span SERVICE_ENVIRONMENT 1`] = `undefined`; exports[`Span SERVICE_FRAMEWORK_NAME 1`] = `undefined`; @@ -224,6 +224,8 @@ exports[`Span USER_AGENT_NAME 1`] = `undefined`; exports[`Span USER_ID 1`] = `undefined`; +exports[`Transaction AGENT_NAME 1`] = `"java"`; + exports[`Transaction CLIENT_GEO_COUNTRY_ISO_CODE 1`] = `undefined`; exports[`Transaction CONTAINER_ID 1`] = `"container1234567890abcdef"`; @@ -286,8 +288,6 @@ exports[`Transaction POD_NAME 1`] = `undefined`; exports[`Transaction PROCESSOR_EVENT 1`] = `"transaction"`; -exports[`Transaction SERVICE_AGENT_NAME 1`] = `"java"`; - exports[`Transaction SERVICE_ENVIRONMENT 1`] = `undefined`; exports[`Transaction SERVICE_FRAMEWORK_NAME 1`] = `undefined`; diff --git a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts index 14233aad0f53c2..8f1b306a34eb06 100644 --- a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +export const AGENT_NAME = 'agent.name'; export const SERVICE_NAME = 'service.name'; export const SERVICE_ENVIRONMENT = 'service.environment'; -export const SERVICE_AGENT_NAME = 'agent.name'; export const SERVICE_FRAMEWORK_NAME = 'service.framework.name'; export const SERVICE_NODE_NAME = 'service.node.name'; export const SERVICE_VERSION = 'service.version'; diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts index f4354baa97655d..8c749cd00bd328 100644 --- a/x-pack/plugins/apm/common/service_map.ts +++ b/x-pack/plugins/apm/common/service_map.ts @@ -6,17 +6,26 @@ import { i18n } from '@kbn/i18n'; import { ILicense } from '../../licensing/public'; +import { + AGENT_NAME, + DESTINATION_ADDRESS, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_NAME, + SPAN_SUBTYPE, + SPAN_TYPE +} from './elasticsearch_fieldnames'; export interface ServiceConnectionNode { - 'service.name': string; - 'service.environment': string | null; - 'service.framework.name': string | null; - 'agent.name': string; + [SERVICE_NAME]: string; + [SERVICE_ENVIRONMENT]: string | null; + [SERVICE_FRAMEWORK_NAME]: string | null; + [AGENT_NAME]: string; } export interface ExternalConnectionNode { - 'destination.address': string; - 'span.type': string; - 'span.subtype': string; + [DESTINATION_ADDRESS]: string; + [SPAN_TYPE]: string; + [SPAN_SUBTYPE]: string; } export type ConnectionNode = ServiceConnectionNode | ExternalConnectionNode; diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index 8ffc115a193483..881993610feee4 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -21,7 +21,7 @@ import { ChartBase } from '../../../types'; import { getMetricsProjection } from '../../../../../../common/projections/metrics'; import { mergeProjection } from '../../../../../../common/projections/util/merge_projection'; import { - SERVICE_AGENT_NAME, + AGENT_NAME, LABEL_NAME, METRIC_JAVA_GC_COUNT, METRIC_JAVA_GC_TIME @@ -64,7 +64,7 @@ export async function fetchAndTransformGcMetrics({ filter: [ ...projection.body.query.bool.filter, { exists: { field: fieldName } }, - { term: { [SERVICE_AGENT_NAME]: 'java' } } + { term: { [AGENT_NAME]: 'java' } } ] } }, diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts index 901812815b3f3b..c910a2371f4b12 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts @@ -10,7 +10,7 @@ import { METRIC_JAVA_HEAP_MEMORY_MAX, METRIC_JAVA_HEAP_MEMORY_COMMITTED, METRIC_JAVA_HEAP_MEMORY_USED, - SERVICE_AGENT_NAME + AGENT_NAME } from '../../../../../../common/elasticsearch_fieldnames'; import { Setup, @@ -71,6 +71,6 @@ export async function getHeapMemoryChart( }, heapMemoryUsed: { avg: { field: METRIC_JAVA_HEAP_MEMORY_USED } } }, - additionalFilters: [{ term: { [SERVICE_AGENT_NAME]: 'java' } }] + additionalFilters: [{ term: { [AGENT_NAME]: 'java' } }] }); } diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts index 7ff4e073e919b5..d997e5b97d5161 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts @@ -10,7 +10,7 @@ import { METRIC_JAVA_NON_HEAP_MEMORY_MAX, METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED, METRIC_JAVA_NON_HEAP_MEMORY_USED, - SERVICE_AGENT_NAME + AGENT_NAME } from '../../../../../../common/elasticsearch_fieldnames'; import { Setup, @@ -70,6 +70,6 @@ export async function getNonHeapMemoryChart( avg: { field: METRIC_JAVA_NON_HEAP_MEMORY_USED } } }, - additionalFilters: [{ term: { [SERVICE_AGENT_NAME]: 'java' } }] + additionalFilters: [{ term: { [AGENT_NAME]: 'java' } }] }); } diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts index cf8e120b00e0d3..0f54b3afdff77f 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts @@ -8,7 +8,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { METRIC_JAVA_THREAD_COUNT, - SERVICE_AGENT_NAME + AGENT_NAME } from '../../../../../../common/elasticsearch_fieldnames'; import { Setup, @@ -57,6 +57,6 @@ export async function getThreadCountChart( threadCount: { avg: { field: METRIC_JAVA_THREAD_COUNT } }, threadCountMax: { max: { field: METRIC_JAVA_THREAD_COUNT } } }, - additionalFilters: [{ term: { [SERVICE_AGENT_NAME]: 'java' } }] + additionalFilters: [{ term: { [AGENT_NAME]: 'java' } }] }); } diff --git a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections.ts b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections.ts index 21f48bd5899993..485958cc17afd4 100644 --- a/x-pack/plugins/apm/server/lib/service_map/dedupe_connections.ts +++ b/x-pack/plugins/apm/server/lib/service_map/dedupe_connections.ts @@ -5,15 +5,24 @@ */ import { isEqual, sortBy } from 'lodash'; import { ValuesType } from 'utility-types'; -import { ConnectionNode, Connection } from '../../../common/service_map'; +import { + DESTINATION_ADDRESS, + SERVICE_NAME +} from '../../../common/elasticsearch_fieldnames'; +import { + Connection, + ConnectionNode, + ExternalConnectionNode, + ServiceConnectionNode +} from '../../../common/service_map'; import { ConnectionsResponse, ServicesResponse } from './get_service_map'; function getConnectionNodeId(node: ConnectionNode): string { - if ('destination.address' in node) { + if (DESTINATION_ADDRESS in node) { // use a prefix to distinguish exernal destination ids from services - return `>${node['destination.address']}`; + return `>${(node as ExternalConnectionNode)[DESTINATION_ADDRESS]}`; } - return node['service.name']; + return (node as ServiceConnectionNode)[SERVICE_NAME]; } function getConnectionId(connection: Connection) { @@ -29,14 +38,14 @@ export function dedupeConnections(response: ServiceMapResponse) { const serviceNodes = services.map(service => ({ ...service, - id: service['service.name'] + id: service[SERVICE_NAME] })); // maps destination.address to service.name if possible function getConnectionNode(node: ConnectionNode) { let mappedNode: ConnectionNode | undefined; - if ('destination.address' in node) { + if (DESTINATION_ADDRESS in node) { mappedNode = discoveredServices.find(map => isEqual(map.from, node))?.to; } @@ -53,19 +62,21 @@ export function dedupeConnections(response: ServiceMapResponse) { // build connections with mapped nodes const mappedConnections = connections .map(connection => { - const source = getConnectionNode(connection.source); - const destination = getConnectionNode(connection.destination); + const sourceData = getConnectionNode(connection.source); + const targetData = getConnectionNode(connection.destination); return { - source, - destination, - id: getConnectionId({ source, destination }) + source: sourceData.id, + target: targetData.id, + id: getConnectionId({ source: sourceData, destination: targetData }), + sourceData, + targetData }; }) - .filter(connection => connection.source.id !== connection.destination.id); + .filter(connection => connection.source !== connection.target); const nodes = mappedConnections - .flatMap(connection => [connection.source, connection.destination]) + .flatMap(connection => [connection.sourceData, connection.targetData]) .concat(serviceNodes); const dedupedNodes: typeof nodes = []; @@ -100,9 +111,7 @@ export function dedupeConnections(response: ServiceMapResponse) { > >((prev, connection) => { const reversedConnection = prev.find( - c => - c.destination.id === connection.source.id && - c.source.id === connection.destination.id + c => c.target === connection.source && c.source === connection.target ); if (reversedConnection) { @@ -116,8 +125,10 @@ export function dedupeConnections(response: ServiceMapResponse) { return prev.concat(connection); }, []); - return { - nodes: dedupedNodes, - connections: dedupedConnections - }; + // Put everything together in elements, with everything in the "data" property + const elements = [...dedupedConnections, ...dedupedNodes].map(element => ({ + data: element + })); + + return { elements }; } diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 96acfb7986c68e..1414f743e8a031 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -4,22 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ import { chunk } from 'lodash'; +import { + AGENT_NAME, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_NAME +} from '../../../common/elasticsearch_fieldnames'; +import { getServicesProjection } from '../../../common/projections/services'; +import { mergeProjection } from '../../../common/projections/util/merge_projection'; import { PromiseReturnType } from '../../../typings/common'; import { Setup, SetupTimeRange, SetupUIFilters } from '../helpers/setup_request'; +import { dedupeConnections } from './dedupe_connections'; import { getServiceMapFromTraceIds } from './get_service_map_from_trace_ids'; import { getTraceSampleIds } from './get_trace_sample_ids'; -import { getServicesProjection } from '../../../common/projections/services'; -import { mergeProjection } from '../../../common/projections/util/merge_projection'; -import { - SERVICE_AGENT_NAME, - SERVICE_NAME, - SERVICE_FRAMEWORK_NAME -} from '../../../common/elasticsearch_fieldnames'; -import { dedupeConnections } from './dedupe_connections'; export interface IEnvOptions { setup: Setup & SetupTimeRange & SetupUIFilters; @@ -104,7 +105,7 @@ async function getServicesData(options: IEnvOptions) { aggs: { agent_name: { terms: { - field: SERVICE_AGENT_NAME + field: AGENT_NAME } }, service_framework_name: { @@ -125,11 +126,11 @@ async function getServicesData(options: IEnvOptions) { return ( response.aggregations?.services.buckets.map(bucket => { return { - 'service.name': bucket.key as string, - 'agent.name': + [SERVICE_NAME]: bucket.key as string, + [AGENT_NAME]: (bucket.agent_name.buckets[0]?.key as string | undefined) || '', - 'service.environment': options.environment || null, - 'service.framework.name': + [SERVICE_ENVIRONMENT]: options.environment || null, + [SERVICE_FRAMEWORK_NAME]: (bucket.service_framework_name.buckets[0]?.key as | string | undefined) || null diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts index d3711e9582d158..d6d9e9b875408f 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts @@ -3,18 +3,20 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { uniq, find } from 'lodash'; -import { Setup } from '../helpers/setup_request'; +import { find, uniq } from 'lodash'; import { - TRACE_ID, - PROCESSOR_EVENT + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRACE_ID } from '../../../common/elasticsearch_fieldnames'; import { Connection, - ServiceConnectionNode, ConnectionNode, - ExternalConnectionNode + ExternalConnectionNode, + ServiceConnectionNode } from '../../../common/service_map'; +import { Setup } from '../helpers/setup_request'; export async function getServiceMapFromTraceIds({ setup, @@ -242,14 +244,15 @@ export async function getServiceMapFromTraceIds({ if (serviceName) { matches = matches && - 'service.name' in node && - node['service.name'] === serviceName; + SERVICE_NAME in node && + (node as ServiceConnectionNode)[SERVICE_NAME] === serviceName; } if (environment) { matches = matches && - 'service.environment' in node && - node['service.environment'] === environment; + SERVICE_ENVIRONMENT in node && + (node as ServiceConnectionNode)[SERVICE_ENVIRONMENT] === + environment; } return matches; }); diff --git a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts index a1a2c1a38b3d41..a8ed296ea4145b 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts @@ -5,7 +5,7 @@ */ import { PROCESSOR_EVENT, - SERVICE_AGENT_NAME, + AGENT_NAME, SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../helpers/range_filter'; @@ -39,7 +39,7 @@ export async function getServiceAgentName( }, aggs: { agents: { - terms: { field: SERVICE_AGENT_NAME, size: 1 } + terms: { field: AGENT_NAME, size: 1 } } } } diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index 2f44b9231eae29..81fd39a8a82555 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -7,7 +7,7 @@ import { mergeProjection } from '../../../../common/projections/util/merge_projection'; import { PROCESSOR_EVENT, - SERVICE_AGENT_NAME, + AGENT_NAME, SERVICE_ENVIRONMENT, TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames'; @@ -41,7 +41,7 @@ export async function getServicesItems( avg: { field: TRANSACTION_DURATION } }, agents: { - terms: { field: SERVICE_AGENT_NAME, size: 1 } + terms: { field: AGENT_NAME, size: 1 } }, events: { terms: { field: PROCESSOR_EVENT } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts index a9af1f6174fd51..1601aa09df2306 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts @@ -9,7 +9,7 @@ import { PROCESSOR_EVENT, SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames'; -import { SERVICE_AGENT_NAME } from '../../../../common/elasticsearch_fieldnames'; +import { AGENT_NAME } from '../../../../common/elasticsearch_fieldnames'; export async function getAgentNameByService({ serviceName, @@ -41,7 +41,7 @@ export async function getAgentNameByService({ }, aggs: { agent_names: { - terms: { field: SERVICE_AGENT_NAME, size: 1 } + terms: { field: AGENT_NAME, size: 1 } } } } diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts index 06e701e9928f65..86ba5e654d0ee3 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts +++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CONTAINER_ID, POD_NAME, - SERVICE_AGENT_NAME, + AGENT_NAME, HOST_NAME, TRANSACTION_RESULT, SERVICE_VERSION @@ -24,7 +24,7 @@ const filtersByName = { title: i18n.translate('xpack.apm.localFilters.titles.agentName', { defaultMessage: 'Agent name' }), - fieldName: SERVICE_AGENT_NAME + fieldName: AGENT_NAME }, containerId: { title: i18n.translate('xpack.apm.localFilters.titles.containerId', {