Skip to content

Commit fdd3f3c

Browse files
committed
[Discover][Traces] Adding error marks
1 parent dfa755e commit fdd3f3c

File tree

17 files changed

+256
-342
lines changed

17 files changed

+256
-342
lines changed

x-pack/platform/packages/shared/kbn-apm-types/src/errors.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ export interface ErrorData {
1919
}
2020

2121
export interface Error {
22+
id: string;
23+
parent?: { id?: string };
24+
trace?: { id?: string };
25+
span?: { id?: string };
26+
transaction?: { id?: string };
27+
service: { name: string };
2228
eventName?: string;
2329
error: ErrorData;
24-
timestamp?: TimestampUs | undefined;
30+
timestamp: TimestampUs;
2531
}
2632

2733
export interface ErrorsByTraceId {

x-pack/solutions/observability/plugins/apm/common/waterfall/typings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ export interface WaterfallSpan {
7171
child?: { id: string[] };
7272
}
7373

74+
/**
75+
* @deprecated Use Error instead
76+
*/
7477
export interface WaterfallError {
7578
id: string;
7679
timestamp: TimestampUs;

x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/marks/get_error_marks.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66
*/
77

88
import { isEmpty } from 'lodash';
9+
import type { Error } from '@kbn/apm-types';
910
import type { IWaterfallError } from '../waterfall/waterfall_helpers/waterfall_helpers';
1011
import type { Mark } from '.';
11-
import type { WaterfallError } from '../../../../../../../common/waterfall/typings';
1212

1313
export interface ErrorMark extends Mark {
1414
type: 'errorMark';
15-
error: WaterfallError;
15+
error: Error;
1616
serviceColor: string;
17+
withLink?: boolean;
18+
onClick?: () => void;
1719
}
1820

21+
/**
22+
* @deprecated Remove it once we remove the apm waterfall
23+
*/
1924
export const getErrorMarks = (errorItems: IWaterfallError[]): ErrorMark[] => {
2025
if (isEmpty(errorItems)) {
2126
return [];
@@ -25,8 +30,9 @@ export const getErrorMarks = (errorItems: IWaterfallError[]): ErrorMark[] => {
2530
type: 'errorMark',
2631
offset: Math.max(error.offset + error.skew, 0),
2732
verticalLine: false,
28-
id: error.doc.error.id,
33+
id: error.id,
2934
error: error.doc,
3035
serviceColor: error.color,
36+
withLink: true,
3137
}));
3238
};

x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import { euiPaletteColorBlind } from '@elastic/eui';
99
import { ProcessorEvent } from '@kbn/observability-plugin/common';
1010
import type { Dictionary } from 'lodash';
1111
import { first, flatten, groupBy, isEmpty, sortBy, uniq } from 'lodash';
12+
import type { Error } from '@kbn/apm-types';
1213
import type { IWaterfallLegend } from '../../../../../../../../common/waterfall/legend';
1314
import { WaterfallLegendType } from '../../../../../../../../common/waterfall/legend';
1415
import { isOpenTelemetryAgentName } from '../../../../../../../../common/agent_name';
1516
import type { CriticalPathSegment } from '../../../../../../../../common/critical_path/types';
1617
import type {
17-
WaterfallError,
1818
WaterfallSpan,
1919
WaterfallTransaction,
2020
} from '../../../../../../../../common/waterfall/typings';
@@ -78,7 +78,7 @@ interface IWaterfallItemBase<TDocument, TDoctype> {
7878
}
7979

8080
export type IWaterfallError = Omit<
81-
IWaterfallItemBase<WaterfallError, 'error'>,
81+
IWaterfallItemBase<Error, 'error'>,
8282
'duration' | 'legendValues' | 'spanLinksCount'
8383
>;
8484

@@ -158,7 +158,7 @@ export function getSpanItem(span: WaterfallSpan, linkedChildrenCount: number = 0
158158
}
159159

160160
function getErrorItem(
161-
error: WaterfallError,
161+
error: Error,
162162
items: IWaterfallItem[],
163163
entryWaterfallTransaction?: IWaterfallTransaction
164164
): IWaterfallError {
@@ -170,7 +170,7 @@ function getErrorItem(
170170
const errorItem: IWaterfallError = {
171171
docType: 'error',
172172
doc: error,
173-
id: error.error.id,
173+
id: error.error.id ?? error.id,
174174
parent,
175175
parentId: parent?.id,
176176
offset: error.timestamp.us - entryTimestamp,

x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/error_marker.tsx

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
* 2.0.
66
*/
77

8-
import { EuiPopover, EuiText, useEuiTheme } from '@elastic/eui';
9-
import React, { useState } from 'react';
8+
import { EuiButtonEmpty, EuiPopover, EuiText, useEuiTheme } from '@elastic/eui';
109
import styled from '@emotion/styled';
11-
import { useAnyOfApmParams } from '../../../../../hooks/use_apm_params';
12-
import { TRACE_ID, TRANSACTION_ID } from '../../../../../../common/es_fields/apm';
10+
import type { TypeOf } from '@kbn/typed-react-router-config';
11+
import React, { useState } from 'react';
1312
import { asDuration } from '../../../../../../common/utils/formatters';
1413
import type { ErrorMark } from '../../../../app/transaction_details/waterfall_with_summary/waterfall_container/marks/get_error_marks';
14+
import type { ApmRoutes } from '../../../../routing/apm_route_config';
1515
import { ErrorDetailLink } from '../../../links/apm/error_detail_link';
1616
import { Legend, Shape } from '../legend';
1717

1818
interface Props {
1919
mark: ErrorMark;
20+
query?: TypeOf<ApmRoutes, '/services/{serviceName}/errors/{groupId}'>['query'];
2021
}
2122

2223
const Popover = styled.div`
@@ -49,19 +50,9 @@ function truncateMessage(errorMessage?: string) {
4950
}
5051
}
5152

52-
export function ErrorMarker({ mark }: Props) {
53+
export function ErrorMarker({ mark, query }: Props) {
5354
const { euiTheme } = useEuiTheme();
5455
const [isPopoverOpen, showPopover] = useState(false);
55-
const { query } = useAnyOfApmParams(
56-
'/services/{serviceName}/overview',
57-
'/services/{serviceName}/errors',
58-
'/services/{serviceName}/transactions/view',
59-
'/mobile-services/{serviceName}/overview',
60-
'/mobile-services/{serviceName}/transactions/view',
61-
'/mobile-services/{serviceName}/errors-and-crashes',
62-
'/traces/explorer/waterfall',
63-
'/dependencies/operation'
64-
);
6556

6657
const togglePopover = () => showPopover(!isPopoverOpen);
6758

@@ -76,18 +67,8 @@ export function ErrorMarker({ mark }: Props) {
7667
);
7768

7869
const { error } = mark;
79-
const serviceGroup = 'serviceGroup' in query ? query.serviceGroup : '';
80-
81-
const queryParam = {
82-
...query,
83-
serviceGroup,
84-
kuery: [
85-
...(error.trace?.id ? [`${TRACE_ID} : "${error.trace?.id}"`] : []),
86-
...(error.transaction?.id ? [`${TRANSACTION_ID} : "${error.transaction?.id}"`] : []),
87-
].join(' and '),
88-
};
8970

90-
const errorMessage = error.error.log?.message || error.error.exception?.[0]?.message;
71+
const errorMessage = error.error.log?.message || error.error.exception?.message;
9172
const truncatedErrorMessage = truncateMessage(errorMessage);
9273

9374
return (
@@ -110,15 +91,29 @@ export function ErrorMarker({ mark }: Props) {
11091
indicator={<span />}
11192
/>
11293
<EuiText size="s">
113-
<ErrorLink
114-
data-test-subj="errorLink"
115-
serviceName={error.service.name}
116-
errorGroupId={error.error.grouping_key}
117-
query={queryParam}
118-
title={errorMessage}
119-
>
120-
{truncatedErrorMessage}
121-
</ErrorLink>
94+
{mark.onClick === undefined && error.error.grouping_key && query ? (
95+
<ErrorLink
96+
data-test-subj="errorLink"
97+
serviceName={error.service.name}
98+
errorGroupId={error.error.grouping_key}
99+
query={query}
100+
title={errorMessage}
101+
>
102+
{truncatedErrorMessage}
103+
</ErrorLink>
104+
) : mark.onClick ? (
105+
<EuiButtonEmpty
106+
data-test-subj="apmErrorMarkerButton"
107+
onClick={() => {
108+
togglePopover();
109+
mark.onClick?.();
110+
}}
111+
>
112+
{truncatedErrorMessage}
113+
</EuiButtonEmpty>
114+
) : (
115+
truncatedErrorMessage
116+
)}
122117
</EuiText>
123118
</Popover>
124119
</EuiPopover>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { ErrorMarker } from './error_marker';
10+
import { TRACE_ID, TRANSACTION_ID } from '../../../../../../common/es_fields/apm';
11+
import { useAnyOfApmParams } from '../../../../../hooks/use_apm_params';
12+
import type { ErrorMark } from '../../../../app/transaction_details/waterfall_with_summary/waterfall_container/marks/get_error_marks';
13+
14+
export function ErrorMarkerWithLink({ mark }: { mark: ErrorMark }) {
15+
const { query } = useAnyOfApmParams(
16+
'/services/{serviceName}/overview',
17+
'/services/{serviceName}/errors',
18+
'/services/{serviceName}/transactions/view',
19+
'/mobile-services/{serviceName}/overview',
20+
'/mobile-services/{serviceName}/transactions/view',
21+
'/mobile-services/{serviceName}/errors-and-crashes',
22+
'/traces/explorer/waterfall',
23+
'/dependencies/operation'
24+
);
25+
26+
const serviceGroup = 'serviceGroup' in query ? query.serviceGroup : '';
27+
28+
const queryParam = {
29+
...query,
30+
serviceGroup,
31+
kuery: [
32+
...(mark.error.trace?.id ? [`${TRACE_ID} : "${mark.error.trace?.id}"`] : []),
33+
...(mark.error.transaction?.id
34+
? [`${TRANSACTION_ID} : "${mark.error.transaction?.id}"`]
35+
: []),
36+
].join(' and '),
37+
};
38+
39+
return <ErrorMarker mark={mark} query={queryParam} />;
40+
}

x-pack/solutions/observability/plugins/apm/public/components/shared/charts/timeline/marker/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { AgentMark } from '../../../../app/transaction_details/waterfall_wi
1111
import type { ErrorMark } from '../../../../app/transaction_details/waterfall_with_summary/waterfall_container/marks/get_error_marks';
1212
import { AgentMarker } from './agent_marker';
1313
import { ErrorMarker } from './error_marker';
14+
import { ErrorMarkerWithLink } from './error_marker_with_link';
1415

1516
interface Props {
1617
mark: ErrorMark | AgentMark;
@@ -26,7 +27,15 @@ export function Marker({ mark, x }: Props) {
2627
const legendWidth = 11;
2728
return (
2829
<MarkerContainer style={{ left: x - legendWidth / 2 }}>
29-
{mark.type === 'errorMark' ? <ErrorMarker mark={mark} /> : <AgentMarker mark={mark} />}
30+
{mark.type === 'errorMark' ? (
31+
mark.withLink ? (
32+
<ErrorMarkerWithLink mark={mark} />
33+
) : (
34+
<ErrorMarker mark={mark} />
35+
)
36+
) : (
37+
<AgentMarker mark={mark} />
38+
)}
3039
</MarkerContainer>
3140
);
3241
}

x-pack/solutions/observability/plugins/apm/public/components/shared/trace_waterfall/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { AutoSizer, WindowScroller } from 'react-virtualized';
1212
import type { ListChildComponentProps } from 'react-window';
1313
import { VariableSizeList as List, areEqual } from 'react-window';
1414
import { APP_MAIN_SCROLL_CONTAINER_ID } from '@kbn/core-chrome-layout-constants';
15+
import type { Error } from '@kbn/apm-types';
1516
import type { IWaterfallGetRelatedErrorsHref } from '../../../../common/waterfall/typings';
1617
import type { TraceItem } from '../../../../common/waterfall/unified_trace_item';
1718
import { TimelineAxisContainer, VerticalLinesContainer } from '../charts/timeline';
@@ -24,6 +25,7 @@ import { WaterfallLegends } from './waterfall_legends';
2425

2526
export interface Props {
2627
traceItems: TraceItem[];
28+
errors?: Error[];
2729
showAccordion?: boolean;
2830
highlightedTraceId?: string;
2931
onClick?: OnNodeClick;
@@ -38,6 +40,7 @@ export interface Props {
3840

3941
export function TraceWaterfall({
4042
traceItems,
43+
errors,
4144
showAccordion = true,
4245
highlightedTraceId,
4346
onClick,
@@ -62,6 +65,7 @@ export function TraceWaterfall({
6265
showLegend={showLegend}
6366
serviceName={serviceName}
6467
isFiltered={isFiltered}
68+
errors={errors}
6569
>
6670
<TraceWarning>
6771
<TraceWaterfallComponent />
@@ -80,6 +84,7 @@ function TraceWaterfallComponent() {
8084
colorBy,
8185
showLegend,
8286
serviceName,
87+
errorMarks,
8388
} = useTraceWaterfallContext();
8489

8590
return (
@@ -110,6 +115,7 @@ function TraceWaterfallComponent() {
110115
bottom: 0,
111116
}}
112117
numberOfTicks={3}
118+
marks={errorMarks}
113119
/>
114120
</div>
115121
<VerticalLinesContainer
@@ -120,6 +126,7 @@ function TraceWaterfallComponent() {
120126
right,
121127
bottom: 0,
122128
}}
129+
marks={errorMarks}
123130
/>
124131
<div
125132
css={css`

0 commit comments

Comments
 (0)