Skip to content

Commit 0cb26ae

Browse files
committed
Service map language icons (#58633)
Add icons as described in #56235. Also: * Add double-border and ghost "shadow" on nodes * Add framework name capability to popover metrics
1 parent a309865 commit 0cb26ae

22 files changed

Lines changed: 599 additions & 177 deletions

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx

Lines changed: 151 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,52 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7+
import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
78
import { storiesOf } from '@storybook/react';
89
import cytoscape from 'cytoscape';
910
import React from 'react';
1011
import { Cytoscape } from './Cytoscape';
11-
12-
const elements: cytoscape.ElementDefinition[] = [
13-
{
14-
data: {
15-
id: 'opbeans-python',
16-
label: 'opbeans-python',
17-
agentName: 'python',
18-
type: 'service'
19-
}
20-
},
21-
{
22-
data: {
23-
id: 'opbeans-node',
24-
label: 'opbeans-node',
25-
agentName: 'nodejs',
26-
type: 'service'
27-
}
28-
},
29-
{
30-
data: {
31-
id: 'opbeans-ruby',
32-
label: 'opbeans-ruby',
33-
agentName: 'ruby',
34-
type: 'service'
35-
}
36-
},
37-
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
38-
{
39-
data: {
40-
bidirectional: true,
41-
source: 'opbeans-python',
42-
target: 'opbeans-ruby'
43-
}
44-
}
45-
];
46-
const height = 300;
47-
const serviceName = 'opbeans-python';
12+
import { iconForNode } from './icons';
4813

4914
storiesOf('app/ServiceMap/Cytoscape', module).add(
5015
'example',
5116
() => {
17+
const elements: cytoscape.ElementDefinition[] = [
18+
{
19+
data: {
20+
id: 'opbeans-python',
21+
label: 'opbeans-python',
22+
agentName: 'python',
23+
type: 'service'
24+
}
25+
},
26+
{
27+
data: {
28+
id: 'opbeans-node',
29+
label: 'opbeans-node',
30+
agentName: 'nodejs',
31+
type: 'service'
32+
}
33+
},
34+
{
35+
data: {
36+
id: 'opbeans-ruby',
37+
label: 'opbeans-ruby',
38+
agentName: 'ruby',
39+
type: 'service'
40+
}
41+
},
42+
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
43+
{
44+
data: {
45+
bidirectional: true,
46+
source: 'opbeans-python',
47+
target: 'opbeans-ruby'
48+
}
49+
}
50+
];
51+
const height = 300;
52+
const serviceName = 'opbeans-python';
5253
return (
5354
<Cytoscape
5455
elements={elements}
@@ -59,6 +60,119 @@ storiesOf('app/ServiceMap/Cytoscape', module).add(
5960
},
6061
{
6162
info: {
63+
propTables: false,
64+
source: false
65+
}
66+
}
67+
);
68+
69+
storiesOf('app/ServiceMap/Cytoscape', module).add(
70+
'node icons',
71+
() => {
72+
const cy = cytoscape();
73+
const elements = [
74+
{ data: { id: 'default', label: 'default', type: undefined } },
75+
{ data: { id: 'cache', label: 'cache', type: 'cache' } },
76+
{ data: { id: 'database', label: 'database', type: 'database' } },
77+
{ data: { id: 'external', label: 'external', type: 'external' } },
78+
{ data: { id: 'messaging', label: 'messaging', type: 'messaging' } },
79+
80+
{
81+
data: {
82+
id: 'dotnet',
83+
label: 'dotnet service',
84+
type: 'service',
85+
agentName: 'dotnet'
86+
}
87+
},
88+
{
89+
data: {
90+
id: 'go',
91+
label: 'go service',
92+
type: 'service',
93+
agentName: 'go'
94+
}
95+
},
96+
{
97+
data: {
98+
id: 'java',
99+
label: 'java service',
100+
type: 'service',
101+
agentName: 'java'
102+
}
103+
},
104+
{
105+
data: {
106+
id: 'js-base',
107+
label: 'js-base service',
108+
type: 'service',
109+
agentName: 'js-base'
110+
}
111+
},
112+
{
113+
data: {
114+
id: 'nodejs',
115+
label: 'nodejs service',
116+
type: 'service',
117+
agentName: 'nodejs'
118+
}
119+
},
120+
{
121+
data: {
122+
id: 'php',
123+
label: 'php service',
124+
type: 'service',
125+
agentName: 'php'
126+
}
127+
},
128+
{
129+
data: {
130+
id: 'python',
131+
label: 'python service',
132+
type: 'service',
133+
agentName: 'python'
134+
}
135+
},
136+
{
137+
data: {
138+
id: 'ruby',
139+
label: 'ruby service',
140+
type: 'service',
141+
agentName: 'ruby'
142+
}
143+
}
144+
];
145+
cy.add(elements);
146+
147+
return (
148+
<EuiFlexGroup gutterSize="l" wrap={true}>
149+
{cy.nodes().map(node => (
150+
<EuiFlexItem key={node.data('id')}>
151+
<EuiCard
152+
description={
153+
<pre>
154+
agentName: {node.data('agentName') || 'undefined'}, type:{' '}
155+
{node.data('type') || 'undefined'}
156+
</pre>
157+
}
158+
icon={
159+
<img
160+
alt={node.data('label')}
161+
src={iconForNode(node)}
162+
height={80}
163+
width={80}
164+
/>
165+
}
166+
title={node.data('label')}
167+
/>
168+
</EuiFlexItem>
169+
))}
170+
</EuiFlexGroup>
171+
);
172+
},
173+
{
174+
info: {
175+
propTables: false,
62176
source: false
63177
}
64178
}

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function Contents({
3535
onFocusClick,
3636
selectedNodeServiceName
3737
}: ContentsProps) {
38+
const frameworkName = selectedNodeData.frameworkName;
3839
return (
3940
<EuiFlexGroup
4041
direction="column"
@@ -49,7 +50,10 @@ export function Contents({
4950
</EuiFlexItem>
5051
<EuiFlexItem>
5152
{isService ? (
52-
<ServiceMetricFetcher serviceName={selectedNodeServiceName} />
53+
<ServiceMetricFetcher
54+
frameworkName={frameworkName}
55+
serviceName={selectedNodeServiceName}
56+
/>
5357
) : (
5458
<Info {...selectedNodeData} />
5559
)}

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ storiesOf('app/ServiceMap/Popover/ServiceMetricList', module)
1616
avgRequestsPerMinute={164.47222031860858}
1717
avgCpuUsage={0.32809666568309237}
1818
avgMemoryUsage={0.5504868173242986}
19+
frameworkName="Spring"
1920
numInstances={2}
2021
isLoading={false}
2122
/>

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import { useUrlParams } from '../../../../hooks/useUrlParams';
1111
import { ServiceMetricList } from './ServiceMetricList';
1212

1313
interface ServiceMetricFetcherProps {
14+
frameworkName?: string;
1415
serviceName: string;
1516
}
1617

1718
export function ServiceMetricFetcher({
19+
frameworkName,
1820
serviceName
1921
}: ServiceMetricFetcherProps) {
2022
const {
@@ -37,5 +39,11 @@ export function ServiceMetricFetcher({
3739
);
3840
const isLoading = status === 'loading';
3941

40-
return <ServiceMetricList {...data} isLoading={isLoading} />;
42+
return (
43+
<ServiceMetricList
44+
{...data}
45+
frameworkName={frameworkName}
46+
isLoading={isLoading}
47+
/>
48+
);
4149
}

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ function LoadingSpinner() {
3030
);
3131
}
3232

33+
const BadgeRow = styled(EuiFlexItem)`
34+
padding-bottom: ${lightTheme.gutterTypes.gutterSmall};
35+
`;
36+
3337
const ItemRow = styled('tr')`
3438
line-height: 2;
3539
`;
@@ -44,6 +48,7 @@ const ItemDescription = styled('td')`
4448
`;
4549

4650
interface ServiceMetricListProps extends ServiceNodeMetrics {
51+
frameworkName?: string;
4752
isLoading: boolean;
4853
}
4954

@@ -53,6 +58,7 @@ export function ServiceMetricList({
5358
avgErrorsPerMinute,
5459
avgCpuUsage,
5560
avgMemoryUsage,
61+
frameworkName,
5662
numInstances,
5763
isLoading
5864
}: ServiceMetricListProps) {
@@ -106,23 +112,27 @@ export function ServiceMetricList({
106112
: null
107113
}
108114
];
115+
const showBadgeRow = frameworkName || numInstances > 1;
116+
109117
return isLoading ? (
110118
<LoadingSpinner />
111119
) : (
112120
<>
113-
{numInstances && numInstances > 1 && (
114-
<EuiFlexItem>
115-
<div>
116-
<EuiBadge iconType="apps" color="hollow">
117-
{i18n.translate('xpack.apm.serviceMap.numInstancesMetric', {
118-
values: { numInstances },
119-
defaultMessage: '{numInstances} instances'
120-
})}
121-
</EuiBadge>
122-
</div>
123-
</EuiFlexItem>
121+
{showBadgeRow && (
122+
<BadgeRow>
123+
<EuiFlexGroup gutterSize="none">
124+
{frameworkName && <EuiBadge>{frameworkName}</EuiBadge>}
125+
{numInstances > 1 && (
126+
<EuiBadge iconType="apps" color="hollow">
127+
{i18n.translate('xpack.apm.serviceMap.numInstancesMetric', {
128+
values: { numInstances },
129+
defaultMessage: '{numInstances} instances'
130+
})}
131+
</EuiBadge>
132+
)}
133+
</EuiFlexGroup>
134+
</BadgeRow>
124135
)}
125-
126136
<table>
127137
<tbody>
128138
{listItems.map(

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,23 @@ const style: cytoscape.Stylesheet[] = [
4242
'background-image': (el: cytoscape.NodeSingular) =>
4343
iconForNode(el) ?? defaultIcon,
4444
'background-height': (el: cytoscape.NodeSingular) =>
45-
isService(el) ? '85%' : '40%',
45+
isService(el) ? '60%' : '40%',
4646
'background-width': (el: cytoscape.NodeSingular) =>
47-
isService(el) ? '85%' : '40%',
47+
isService(el) ? '60%' : '40%',
4848
'border-color': (el: cytoscape.NodeSingular) =>
4949
el.hasClass('primary') || el.selected()
5050
? theme.euiColorPrimary
5151
: theme.euiColorMediumShade,
52-
'border-width': 1,
52+
'border-width': 2,
5353
color: theme.textColors.default,
5454
// theme.euiFontFamily doesn't work here for some reason, so we're just
5555
// specifying a subset of the fonts for the label text.
5656
'font-family': 'Inter UI, Segoe UI, Helvetica, Arial, sans-serif',
5757
'font-size': theme.euiFontSizeXS,
58+
ghost: 'yes',
59+
'ghost-offset-x': 0,
60+
'ghost-offset-y': 2,
61+
'ghost-opacity': 0.15,
5862
height: nodeHeight,
5963
label: 'data(label)',
6064
'min-zoomed-font-size': theme.euiSizeL,

x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ export function getCytoscapeElements(
105105
`/services/${node['service.name']}/service-map`,
106106
search
107107
),
108-
agentName: node['agent.name'] || node['agent.name'],
108+
agentName: node['agent.name'],
109+
frameworkName: node['service.framework.name'],
109110
type: 'service'
110111
};
111112
}

0 commit comments

Comments
 (0)