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 (doesn't actually fetch this data yet)
  • Loading branch information
smith committed Feb 26, 2020
1 parent b56eead commit f40f13f
Show file tree
Hide file tree
Showing 14 changed files with 534 additions and 41 deletions.
@@ -0,0 +1,123 @@
/*
* 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 { EuiCard, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import cytoscape from 'cytoscape';
import React from 'react';
import { iconForNode } from './icons';

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 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 @@ -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 @@ -110,19 +116,22 @@ export function ServiceMetricList({
<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>
)}

{frameworkName ||
(numInstances && numInstances > 1 && (
<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,24 @@ const style: cytoscape.Stylesheet[] = [
'background-image': (el: cytoscape.NodeSingular) =>
iconForNode(el) ?? defaultIcon,
'background-height': (el: cytoscape.NodeSingular) =>
isService(el) ? '80%' : '40%',
isService(el) ? '60%' : '40%',
'background-width': (el: cytoscape.NodeSingular) =>
isService(el) ? '80%' : '40%',
isService(el) ? '60%' : '40%',
'border-color': (el: cytoscape.NodeSingular) =>
el.hasClass('primary')
? theme.euiColorSecondary
: theme.euiColorMediumShade,
'border-width': 2,
'border-style': 'double',
'border-width': 3,
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': -2,
'ghost-offset-y': 1,
'ghost-opacity': 0.15,
height: nodeHeight,
label: 'data(label)',
'min-zoomed-font-size': theme.euiSizeL,
Expand Down
46 changes: 21 additions & 25 deletions x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts
Expand Up @@ -4,26 +4,21 @@
* you may not use this file except in compliance with the Elastic License.
*/

import theme from '@elastic/eui/dist/eui_theme_light.json';
import cytoscape from 'cytoscape';
import databaseIcon from './icons/database.svg';
import documentsIcon from './icons/documents.svg';
import dotNetIcon from './icons/dot-net.svg';
import globeIcon from './icons/globe.svg';
import goIcon from './icons/go.svg';
import javaIcon from './icons/java.svg';
import nodeJsIcon from './icons/nodejs.svg';
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';

function getAvatarIcon(
text = '',
backgroundColor = 'transparent',
foregroundColor = 'white'
) {
return (
'data:image/svg+xml;utf8,' +
encodeURIComponent(`<svg width="80" height="80" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<circle cx="40" cy="40" fill="${backgroundColor}" r="40" stroke-width="0" />
<text fill="${foregroundColor}" font-family="'Inter UI', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica', 'Arial', sans-serif" font-size="36" text-anchor="middle" x="40" xml:space="preserve" y="52">${text}</text>
</svg>
`)
);
}
export const defaultIcon = defaultIconImport;

// The colors here are taken from the logos of the corresponding technologies
const icons: { [key: string]: string } = {
Expand All @@ -35,17 +30,16 @@ const icons: { [key: string]: string } = {
};

const serviceIcons: { [key: string]: string } = {
dotnet: getAvatarIcon('.N', '#8562AD'),
go: getAvatarIcon('Go', '#00A9D6'),
java: getAvatarIcon('Jv', '#41717E'),
'js-base': getAvatarIcon('JS', '#F0DB4E', theme.euiTextColor),
nodejs: getAvatarIcon('No', '#689E62'),
python: getAvatarIcon('Py', '#376994'),
ruby: getAvatarIcon('Rb', '#CC362E')
dotnet: dotNetIcon,
go: goIcon,
java: javaIcon,
'js-base': rumJsIcon,
nodejs: nodeJsIcon,
php: phpIcon,
python: pythonIcon,
ruby: rubyIcon
};

export const defaultIcon = getAvatarIcon();

// IE 11 does not properly load some SVGs, which causes a runtime error and the
// map to not work at all. We would prefer to do some kind of feature detection
// rather than browser detection, but IE 11 does support SVG, just not well
Expand All @@ -63,7 +57,9 @@ export function iconForNode(node: cytoscape.NodeSingular) {
return serviceIcons[node.data('agentName') as string];
} else if (isIE11) {
return defaultIcon;
} else {
} else if (icons[type]) {
return icons[type];
} else {
return defaultIcon;
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f40f13f

Please sign in to comment.