Skip to content

Commit

Permalink
Service map language icons
Browse files Browse the repository at this point in the history
Add icons as described in #56235.

Also:

* Add double-border and ghost "shadow" on nodes
* Add framework name capability to popover metrics
  • Loading branch information
smith committed Feb 28, 2020
1 parent 967bef7 commit 294f83f
Show file tree
Hide file tree
Showing 22 changed files with 599 additions and 177 deletions.
Expand Up @@ -4,51 +4,52 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import cytoscape from 'cytoscape';
import React from 'react';
import { Cytoscape } from './Cytoscape';

const elements: cytoscape.ElementDefinition[] = [
{
data: {
id: 'opbeans-python',
label: 'opbeans-python',
agentName: 'python',
type: 'service'
}
},
{
data: {
id: 'opbeans-node',
label: 'opbeans-node',
agentName: 'nodejs',
type: 'service'
}
},
{
data: {
id: 'opbeans-ruby',
label: 'opbeans-ruby',
agentName: 'ruby',
type: 'service'
}
},
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
{
data: {
bidirectional: true,
source: 'opbeans-python',
target: 'opbeans-ruby'
}
}
];
const height = 300;
const serviceName = 'opbeans-python';
import { iconForNode } from './icons';

storiesOf('app/ServiceMap/Cytoscape', module).add(
'example',
() => {
const elements: cytoscape.ElementDefinition[] = [
{
data: {
id: 'opbeans-python',
label: 'opbeans-python',
agentName: 'python',
type: 'service'
}
},
{
data: {
id: 'opbeans-node',
label: 'opbeans-node',
agentName: 'nodejs',
type: 'service'
}
},
{
data: {
id: 'opbeans-ruby',
label: 'opbeans-ruby',
agentName: 'ruby',
type: 'service'
}
},
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
{
data: {
bidirectional: true,
source: 'opbeans-python',
target: 'opbeans-ruby'
}
}
];
const height = 300;
const serviceName = 'opbeans-python';
return (
<Cytoscape
elements={elements}
Expand All @@ -59,6 +60,119 @@ storiesOf('app/ServiceMap/Cytoscape', module).add(
},
{
info: {
propTables: false,
source: false
}
}
);

storiesOf('app/ServiceMap/Cytoscape', module).add(
'node icons',
() => {
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: 'dotnet',
label: 'dotnet service',
type: 'service',
agentName: 'dotnet'
}
},
{
data: {
id: 'go',
label: 'go service',
type: 'service',
agentName: 'go'
}
},
{
data: {
id: 'java',
label: 'java service',
type: 'service',
agentName: 'java'
}
},
{
data: {
id: 'js-base',
label: 'js-base service',
type: 'service',
agentName: 'js-base'
}
},
{
data: {
id: 'nodejs',
label: 'nodejs service',
type: 'service',
agentName: 'nodejs'
}
},
{
data: {
id: 'php',
label: 'php service',
type: 'service',
agentName: 'php'
}
},
{
data: {
id: 'python',
label: 'python service',
type: 'service',
agentName: 'python'
}
},
{
data: {
id: 'ruby',
label: 'ruby service',
type: 'service',
agentName: 'ruby'
}
}
];
cy.add(elements);

return (
<EuiFlexGroup gutterSize="l" wrap={true}>
{cy.nodes().map(node => (
<EuiFlexItem key={node.data('id')}>
<EuiCard
description={
<pre>
agentName: {node.data('agentName') || 'undefined'}, type:{' '}
{node.data('type') || 'undefined'}
</pre>
}
icon={
<img
alt={node.data('label')}
src={iconForNode(node)}
height={80}
width={80}
/>
}
title={node.data('label')}
/>
</EuiFlexItem>
))}
</EuiFlexGroup>
);
},
{
info: {
propTables: false,
source: false
}
}
Expand Down
Expand Up @@ -35,6 +35,7 @@ export function Contents({
onFocusClick,
selectedNodeServiceName
}: ContentsProps) {
const frameworkName = selectedNodeData.frameworkName;
return (
<EuiFlexGroup
direction="column"
Expand All @@ -49,7 +50,10 @@ export function Contents({
</EuiFlexItem>
<EuiFlexItem>
{isService ? (
<ServiceMetricFetcher serviceName={selectedNodeServiceName} />
<ServiceMetricFetcher
frameworkName={frameworkName}
serviceName={selectedNodeServiceName}
/>
) : (
<Info {...selectedNodeData} />
)}
Expand Down
Expand Up @@ -16,6 +16,7 @@ storiesOf('app/ServiceMap/Popover/ServiceMetricList', module)
avgRequestsPerMinute={164.47222031860858}
avgCpuUsage={0.32809666568309237}
avgMemoryUsage={0.5504868173242986}
frameworkName="Spring"
numInstances={2}
isLoading={false}
/>
Expand Down
Expand Up @@ -11,10 +11,12 @@ import { useUrlParams } from '../../../../hooks/useUrlParams';
import { ServiceMetricList } from './ServiceMetricList';

interface ServiceMetricFetcherProps {
frameworkName?: string;
serviceName: string;
}

export function ServiceMetricFetcher({
frameworkName,
serviceName
}: ServiceMetricFetcherProps) {
const {
Expand All @@ -37,5 +39,11 @@ export function ServiceMetricFetcher({
);
const isLoading = status === 'loading';

return <ServiceMetricList {...data} isLoading={isLoading} />;
return (
<ServiceMetricList
{...data}
frameworkName={frameworkName}
isLoading={isLoading}
/>
);
}
Expand Up @@ -30,6 +30,10 @@ function LoadingSpinner() {
);
}

const BadgeRow = styled(EuiFlexItem)`
padding-bottom: ${lightTheme.gutterTypes.gutterSmall};
`;

const ItemRow = styled('tr')`
line-height: 2;
`;
Expand All @@ -44,6 +48,7 @@ const ItemDescription = styled('td')`
`;

interface ServiceMetricListProps extends ServiceNodeMetrics {
frameworkName?: string;
isLoading: boolean;
}

Expand All @@ -53,6 +58,7 @@ export function ServiceMetricList({
avgErrorsPerMinute,
avgCpuUsage,
avgMemoryUsage,
frameworkName,
numInstances,
isLoading
}: ServiceMetricListProps) {
Expand Down Expand Up @@ -106,23 +112,27 @@ export function ServiceMetricList({
: null
}
];
const showBadgeRow = frameworkName || (numInstances && numInstances > 1);

return isLoading ? (
<LoadingSpinner />
) : (
<>
{numInstances && numInstances > 1 && (
<EuiFlexItem>
<div>
<EuiBadge iconType="apps" color="hollow">
{i18n.translate('xpack.apm.serviceMap.numInstancesMetric', {
values: { numInstances },
defaultMessage: '{numInstances} instances'
})}
</EuiBadge>
</div>
</EuiFlexItem>
{showBadgeRow && (
<BadgeRow>
<EuiFlexGroup gutterSize="none">
{frameworkName && <EuiBadge>{frameworkName}</EuiBadge>}
{numInstances && numInstances > 1 && (
<EuiBadge iconType="apps" color="hollow">
{i18n.translate('xpack.apm.serviceMap.numInstancesMetric', {
values: { numInstances },
defaultMessage: '{numInstances} instances'
})}
</EuiBadge>
)}
</EuiFlexGroup>
</BadgeRow>
)}

<table>
<tbody>
{listItems.map(
Expand Down
Expand Up @@ -42,19 +42,23 @@ const style: cytoscape.Stylesheet[] = [
'background-image': (el: cytoscape.NodeSingular) =>
iconForNode(el) ?? defaultIcon,
'background-height': (el: cytoscape.NodeSingular) =>
isService(el) ? '85%' : '40%',
isService(el) ? '60%' : '40%',
'background-width': (el: cytoscape.NodeSingular) =>
isService(el) ? '85%' : '40%',
isService(el) ? '60%' : '40%',
'border-color': (el: cytoscape.NodeSingular) =>
el.hasClass('primary') || el.selected()
? theme.euiColorPrimary
: theme.euiColorMediumShade,
'border-width': 1,
'border-width': 2,
color: theme.textColors.default,
// theme.euiFontFamily doesn't work here for some reason, so we're just
// specifying a subset of the fonts for the label text.
'font-family': 'Inter UI, Segoe UI, Helvetica, Arial, sans-serif',
'font-size': theme.euiFontSizeXS,
ghost: 'yes',
'ghost-offset-x': 0,
'ghost-offset-y': 2,
'ghost-opacity': 0.15,
height: nodeHeight,
label: 'data(label)',
'min-zoomed-font-size': theme.euiSizeL,
Expand Down
Expand Up @@ -105,7 +105,8 @@ export function getCytoscapeElements(
`/services/${node['service.name']}/service-map`,
search
),
agentName: node['agent.name'] || node['agent.name'],
agentName: node['agent.name'],
frameworkName: node['service.framework.name'],
type: 'service'
};
}
Expand Down

0 comments on commit 294f83f

Please sign in to comment.