[APM] Fix overlapping transaction names (#76083)

...in the table and the header. Did this by adding `word-break: break-all` to them.

Also:

* Rename List to TransactionList
* Add stories for TransactionList and ApmHeader
* Add missing type information to transactions based on sample transaction

Fixes #73960.
This commit is contained in:
Nathan L Smith 2020-09-04 15:10:52 -05:00 committed by GitHub
parent 6d4e772cec
commit 4d4eae4413
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 206 additions and 21 deletions

View file

@ -0,0 +1,119 @@
/*
* 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 { storiesOf } from '@storybook/react';
import React from 'react';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { TransactionGroup } from '../../../../../server/lib/transaction_groups/fetcher';
import { TransactionList } from './';
storiesOf('app/TransactionOverview/TransactionList', module).add(
'Single Row',
() => {
const items: TransactionGroup[] = [
{
name:
'GET /api/v1/regions/azure-eastus2/clusters/elasticsearch/xc18de071deb4262be54baebf5f6a1ce/proxy/_snapshot/found-snapshots/_all',
sample: {
container: {
id:
'xa802046074071c9c828e8db3b7ef92ea0484d9fe783b9c518f65a7b45dfdd2c',
},
agent: {
name: 'java',
ephemeral_id: 'x787d6b7-3241-4b55-ba49-0c96bc9857d1',
version: '1.17.0',
},
process: {
pid: 28,
title: '/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java',
},
processor: {
name: 'transaction',
event: 'transaction',
},
labels: {
path:
'/api/v1/regions/azure-eastus2/clusters/elasticsearch/xc18de071deb4262be54baebf5f6a1ce/proxy/_snapshot/found-snapshots/_all',
status_code: '200',
request_method: 'GET',
request_id: 'x273dc2477e021979125e0ec67e8d778',
},
observer: {
hostname: 'x840922c967b',
name: 'instance-000000000x',
id: 'xb384baf-c16a-415a-928a-a10635a04b81',
ephemeral_id: 'x9227f0e-848d-423e-a65a-5fdee321f4a9',
type: 'apm-server',
version: '7.8.1',
version_major: 7,
},
trace: {
id: 'x998d7e5db84aa8341b358a264a78984',
},
'@timestamp': '2020-08-26T14:40:31.472Z',
ecs: {
version: '1.5.0',
},
service: {
node: {
name:
'xa802046074071c9c828e8db3b7ef92ea0484d9fe783b9c518f65a7b45dfdd2c',
},
environment: 'qa',
framework: {
name: 'API',
},
name: 'adminconsole',
runtime: {
name: 'Java',
version: '1.8.0_265',
},
language: {
name: 'Java',
version: '1.8.0_265',
},
version: 'ms-44.1-BC_1',
},
host: {
hostname: 'xa8020460740',
os: {
platform: 'Linux',
},
ip: '3.83.239.24',
name: 'xa8020460740',
architecture: 'amd64',
},
transaction: {
duration: {
us: 8260617,
},
result: 'HTTP 2xx',
name:
'GET /api/v1/regions/azure-eastus2/clusters/elasticsearch/xc18de071deb4262be54baebf5f6a1ce/proxy/_snapshot/found-snapshots/_all',
span_count: {
dropped: 0,
started: 8,
},
id: 'xaa3cae6fd4f7023',
type: 'request',
sampled: true,
},
timestamp: {
us: 1598452831472001,
},
},
p95: 11974156,
averageResponseTime: 8087434.558974359,
transactionsPerMinute: 0.40625,
impact: 100,
impactRelative: 100,
},
];
return <TransactionList isLoading={false} items={items} />;
}
);

View file

@ -19,9 +19,16 @@ import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
import { EmptyMessage } from '../../../shared/EmptyMessage';
import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink';
// Truncate both the link and the child span (the tooltip anchor.) The link so
// it doesn't overflow, and the anchor so we get the ellipsis.
const TransactionNameLink = styled(TransactionDetailLink)`
${truncate('100%')};
font-family: ${fontFamilyCode};
white-space: nowrap;
${truncate('100%')};
> span {
${truncate('100%')};
}
`;
interface Props {
@ -41,20 +48,20 @@ export function TransactionList({ items, isLoading }: Props) {
sortable: true,
render: (_, { sample }: TransactionGroup) => {
return (
<EuiToolTip
id="transaction-name-link-tooltip"
content={sample.transaction.name || NOT_AVAILABLE_LABEL}
<TransactionNameLink
serviceName={sample.service.name}
transactionId={sample.transaction.id}
traceId={sample.trace.id}
transactionName={sample.transaction.name}
transactionType={sample.transaction.type}
>
<TransactionNameLink
serviceName={sample.service.name}
transactionId={sample.transaction.id}
traceId={sample.trace.id}
transactionName={sample.transaction.name}
transactionType={sample.transaction.type}
<EuiToolTip
id="transaction-name-link-tooltip"
content={sample.transaction.name || NOT_AVAILABLE_LABEL}
>
{sample.transaction.name || NOT_AVAILABLE_LABEL}
</TransactionNameLink>
</EuiToolTip>
<>{sample.transaction.name || NOT_AVAILABLE_LABEL}</>
</EuiToolTip>
</TransactionNameLink>
);
},
},

View file

@ -33,7 +33,7 @@ import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter';
import { TransactionList } from './List';
import { TransactionList } from './TransactionList';
import { useRedirect } from './useRedirect';
function getRedirectLocation({
@ -62,6 +62,7 @@ function getRedirectLocation({
export function TransactionOverview() {
const location = useLocation();
const { urlParams } = useUrlParams();
const { serviceName, transactionType } = urlParams;
// TODO: fetching of transaction types should perhaps be lifted since it is needed in several places. Context?

View file

@ -0,0 +1,30 @@
/*
* 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 { EuiTitle } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import React from 'react';
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
import { ApmHeader } from './';
storiesOf('shared/ApmHeader', module)
.addDecorator((storyFn) => {
return (
<MockApmPluginContextWrapper>{storyFn()}</MockApmPluginContextWrapper>
);
})
.add('Example', () => {
return (
<ApmHeader>
<EuiTitle size="l">
<h1>
GET
/api/v1/regions/azure-eastus2/clusters/elasticsearch/xc18de071deb4262be54baebf5f6a1ce/proxy/_snapshot/found-snapshots/_all
</h1>
</EuiTitle>
</ApmHeader>
);
});

View file

@ -6,15 +6,25 @@
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import React, { ReactNode } from 'react';
import { KueryBar } from '../KueryBar';
import styled from 'styled-components';
import { DatePicker } from '../DatePicker';
import { EnvironmentFilter } from '../EnvironmentFilter';
import { KueryBar } from '../KueryBar';
// Header titles with long, unbroken words, like you would see for a long URL in
// a transaction name, with the default `work-break`, don't break, and that ends
// up pushing the date picker off of the screen. Setting `break-all` here lets
// it wrap even if it has a long, unbroken work. The wrapped result is not great
// looking, since it wraps, but it doesn't push any controls off of the screen.
const ChildrenContainerFlexItem = styled(EuiFlexItem)`
word-break: break-all;
`;
export function ApmHeader({ children }: { children: ReactNode }) {
return (
<>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem>{children}</EuiFlexItem>
<ChildrenContainerFlexItem>{children}</ChildrenContainerFlexItem>
<EuiFlexItem grow={false}>
<DatePicker />
</EuiFlexItem>

View file

@ -185,10 +185,12 @@ export async function transactionGroupsFetcher(
}
export interface TransactionGroup {
key: Record<string, any> | string;
name?: string;
key?: Record<string, any> | string;
averageResponseTime: number | null | undefined;
transactionsPerMinute: number;
p95: number | null | undefined;
impact: number;
impactRelative?: number;
sample: Transaction;
}

View file

@ -5,5 +5,11 @@
*/
export interface Host {
architecture?: string;
hostname?: string;
name?: string;
ip?: string;
os?: {
platform?: string;
};
}

View file

@ -5,7 +5,11 @@
*/
export interface Observer {
ephemeral_id?: string;
hostname?: string;
id?: string;
name?: string;
type?: string;
version: string;
version_major: number;
}

View file

@ -5,8 +5,8 @@
*/
export interface Process {
args: string[];
args?: string[];
pid: number;
ppid: number;
title: string;
ppid?: number;
title?: string;
}

View file

@ -9,7 +9,10 @@ export interface Service {
environment?: string;
framework?: {
name: string;
version: string;
version?: string;
};
node?: {
name?: string;
};
runtime?: {
name: string;
@ -19,4 +22,5 @@ export interface Service {
name: string;
version?: string;
};
version?: string;
}

View file

@ -54,6 +54,7 @@ export interface TransactionRaw extends APMBaseDoc {
// Shared by errors and transactions
container?: Container;
ecs?: { version?: string };
host?: Host;
http?: Http;
kubernetes?: Kubernetes;

View file

@ -19,6 +19,7 @@ export type AgentName =
| 'ruby';
export interface Agent {
ephemeral_id?: string;
name: AgentName;
version: string;
}