Skip to content

Commit 36311f5

Browse files
TackAdamAdam Tackett
and
Adam Tackett
authored
TraceView - Optimization of queries (opensearch-project#2349)
* optimize trace veiw removing queries Signed-off-by: Adam Tackett <[email protected]> * fix date format Signed-off-by: Adam Tackett <[email protected]> * update app cypress for rounding Signed-off-by: Adam Tackett <[email protected]> * address comments Signed-off-by: Adam Tackett <[email protected]> * fix jaeger tree span and overview Signed-off-by: Adam Tackett <[email protected]> * match old behavior for jaeger, if an error is present display it in overview Signed-off-by: Adam Tackett <[email protected]> * update test to reflect error in jaeger trace Signed-off-by: Adam Tackett <[email protected]> * address comments Signed-off-by: Adam Tackett <[email protected]> * add cypress test for jaeger tree view Signed-off-by: Adam Tackett <[email protected]> * add fallback value for latency Signed-off-by: Adam Tackett <[email protected]> * fix sorting on span table Signed-off-by: Adam Tackett <[email protected]> * add back total use effect Signed-off-by: Adam Tackett <[email protected]> * status code of 2 indicates error Signed-off-by: Adam Tackett <[email protected]> * address comments Signed-off-by: Adam Tackett <[email protected]> * add sorting support to jaeger span table Signed-off-by: Adam Tackett <[email protected]> * address comments, refactor sorting Signed-off-by: Adam Tackett <[email protected]> * fix flaky cypress test Signed-off-by: Adam Tackett <[email protected]> * combine adding sort, and sorting action Signed-off-by: Adam Tackett <[email protected]> * fix jaeger sorting for errors, fix gantt chart error override from text Signed-off-by: Adam Tackett <[email protected]> --------- Signed-off-by: Adam Tackett <[email protected]> Co-authored-by: Adam Tackett <[email protected]>
1 parent 5703582 commit 36311f5

20 files changed

+1552
-759
lines changed

.cypress/integration/app_analytics_test/app_analytics.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ describe('Viewing application', () => {
322322
cy.get('[title="03f9c770db5ee2f1caac0afc36db49ba"]').click();
323323
cy.get('[data-test-subj="traceDetailFlyoutTitle"]').should('be.visible');
324324
cy.get('[data-test-subj="traceDetailFlyout"]').within(($flyout) => {
325-
cy.get('[data-test-subj="LatencyDescriptionList"]').should('contain', '224.99');
325+
cy.get('[data-test-subj="LatencyDescriptionList"]').should('contain', '225.00');
326326
});
327327
cy.get('[data-test-subj="euiFlyoutCloseButton"]').click();
328328
cy.get('[data-test-subj="traceDetailFlyout"]').should('not.exist');

.cypress/integration/trace_analytics_test/trace_analytics_traces.spec.js

+17
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,21 @@ describe('Testing switch mode to jaeger', () => {
282282
cy.contains('Time spent by service').should('exist');
283283
cy.get("[data-test-subj='span-gantt-chart-panel']").should('exist');
284284
});
285+
286+
it('Checks tree view for specific traceId in Jaeger mode', () => {
287+
cy.contains('15b0b4004a651c4c').click();
288+
cy.get('[data-test-subj="globalLoadingIndicator"]').should('not.exist');
289+
290+
cy.get('.euiButtonGroup').contains('Tree view').click();
291+
cy.get("[data-test-subj='treeExpandAll']").should('exist');
292+
cy.get("[data-test-subj='treeCollapseAll']").should('exist');
293+
294+
// Waiting time for render to complete
295+
cy.get("[data-test-subj='treeExpandAll']").click();
296+
cy.get("[data-test-subj='treeCollapseAll']").click();
297+
298+
cy.get("[data-test-subj='treeViewExpandArrow']").should('have.length', 1);
299+
cy.get("[data-test-subj='treeExpandAll']").click();
300+
cy.get("[data-test-subj='treeViewExpandArrow']").should('have.length.greaterThan', 1);
301+
});
285302
});

public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap

+349-69
Large diffs are not rendered by default.

public/components/application_analytics/components/flyout_components/trace_detail_render.tsx

+27-7
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import { HttpStart } from '../../../../../../../src/core/public';
99
import { TraceAnalyticsMode } from '../../../../../common/types/trace_analytics';
1010
import { ServiceBreakdownPanel } from '../../../trace_analytics/components/traces/service_breakdown_panel';
1111
import { SpanDetailPanel } from '../../../trace_analytics/components/traces/span_detail_panel';
12-
import {
13-
handlePayloadRequest,
14-
handleServicesPieChartRequest,
15-
handleTraceViewRequest,
16-
} from '../../../trace_analytics/requests/traces_request_handler';
12+
import { handlePayloadRequest } from '../../../trace_analytics/requests/traces_request_handler';
1713
import { getListItem } from '../../helpers/utils';
14+
import {
15+
getOverviewFields,
16+
getServiceBreakdownData,
17+
} from '../../../trace_analytics/components/traces/trace_view_helpers';
1818

1919
interface TraceDetailRenderProps {
2020
traceId: string;
@@ -78,6 +78,7 @@ export const TraceDetailRender = ({
7878
mode={mode}
7979
dataSourceMDSId={dataSourceMDSId}
8080
isApplicationFlyout={true}
81+
payloadData={payloadData}
8182
/>
8283
<EuiSpacer size="xs" />
8384
<EuiHorizontalRule margin="s" />
@@ -95,10 +96,29 @@ export const TraceDetailRender = ({
9596
}, [traceId, fields, serviceBreakdownData, colorMap, payloadData]);
9697

9798
useEffect(() => {
98-
handleTraceViewRequest(traceId, http, fields, setFields, mode);
99-
handleServicesPieChartRequest(traceId, http, setServiceBreakdownData, setColorMap, mode);
10099
handlePayloadRequest(traceId, http, payloadData, setPayloadData, mode);
101100
}, [traceId]);
102101

102+
useEffect(() => {
103+
if (!payloadData) return;
104+
105+
try {
106+
const parsedPayload = JSON.parse(payloadData);
107+
const overview = getOverviewFields(parsedPayload, mode);
108+
if (overview) {
109+
setFields(overview);
110+
}
111+
112+
const {
113+
serviceBreakdownData: queryServiceBreakdownData,
114+
colorMap: queryColorMap,
115+
} = getServiceBreakdownData(parsedPayload, mode);
116+
setServiceBreakdownData(queryServiceBreakdownData);
117+
setColorMap(queryColorMap);
118+
} catch (error) {
119+
console.error('Error processing payloadData:', error);
120+
}
121+
}, [payloadData, mode]);
122+
103123
return renderContent;
104124
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
// Conversion factor for nanoseconds to milliseconds
7+
export const NANOS_TO_MS = 1e6;
8+
9+
export const MILI_TO_SEC = 1000;
10+
11+
export const pieChartColors = [
12+
'#7492e7',
13+
'#c33d69',
14+
'#2ea597',
15+
'#8456ce',
16+
'#e07941',
17+
'#3759ce',
18+
'#ce567c',
19+
'#9469d6',
20+
'#4066df',
21+
'#da7596',
22+
];
23+
24+
export interface Span {
25+
traceId: string;
26+
spanId: string;
27+
traceState: string;
28+
parentSpanId: string;
29+
name: string;
30+
kind: string;
31+
startTime: string;
32+
endTime: string;
33+
durationInNanos: number;
34+
serviceName: string;
35+
events: any[];
36+
links: any[];
37+
droppedAttributesCount: number;
38+
droppedEventsCount: number;
39+
droppedLinksCount: number;
40+
traceGroup: string;
41+
traceGroupFields: {
42+
endTime: string;
43+
durationInNanos: number;
44+
statusCode: number;
45+
};
46+
status: {
47+
code: number;
48+
};
49+
instrumentationLibrary: {
50+
name: string;
51+
version: string;
52+
};
53+
}
54+
55+
export interface ParsedHit {
56+
_index: string;
57+
_id: string;
58+
_score: number;
59+
_source: Span;
60+
sort?: any[];
61+
}

public/components/trace_analytics/components/common/helper_functions.tsx

+37
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { FieldCapResponse } from '../../../common/types';
2828
import { serviceMapColorPalette } from './color_palette';
2929
import { FilterType } from './filters/filters';
3030
import { ServiceObject } from './plots/service_map';
31+
import { NANOS_TO_MS, ParsedHit } from './constants';
3132

3233
const missingJaegerTracesConfigurationMessage = `The indices required for trace analytics (${JAEGER_INDEX_NAME} and ${JAEGER_SERVICE_INDEX_NAME}) do not exist or you do not have permission to access them.`;
3334

@@ -615,3 +616,39 @@ export const generateServiceUrl = (
615616

616617
return url;
617618
};
619+
620+
/*
621+
* Parse an ISO timestamp with up to nanosecond precision.
622+
* For example, "2025-01-28T03:12:37.293990144Z" will be converted
623+
* to a number representing the total nanoseconds since the Unix epoch.
624+
*/
625+
export function parseIsoToNano(iso: string): number {
626+
const match = iso.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d+))?Z$/);
627+
if (!match) {
628+
throw new Error(`Invalid ISO timestamp: ${iso}`);
629+
}
630+
// Parse the base part using Date.parse (which gives ms)
631+
const baseMs = new Date(match[1] + 'Z').getTime();
632+
// Get the fractional part (if any), pad to 9 digits for nanosecond precision
633+
let fraction = match[2] || '0';
634+
fraction = fraction.padEnd(9, '0'); // ensure it has 9 digits
635+
return baseMs * NANOS_TO_MS + Number(fraction);
636+
}
637+
638+
export const parseHits = (payloadData: string): ParsedHit[] => {
639+
try {
640+
const parsed = JSON.parse(payloadData);
641+
let hits: ParsedHit[] = [];
642+
643+
if (parsed.hits && Array.isArray(parsed.hits.hits)) {
644+
hits = parsed.hits.hits;
645+
} else if (Array.isArray(parsed)) {
646+
hits = parsed;
647+
}
648+
649+
return hits;
650+
} catch (error) {
651+
console.error('Error processing payloadData:', error);
652+
return [];
653+
}
654+
};

public/components/trace_analytics/components/traces/__tests__/__snapshots__/service_breakdown_panel.test.tsx.snap

+2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ exports[`Service breakdown panel component renders service breakdown panel 1`] =
135135
data={
136136
Array [
137137
Object {
138+
"hoverinfo": "label+percent",
138139
"hovertemplate": "%{label}<br>%{value:.2f}%<extra></extra>",
139140
"labels": Array [
140141
"inventory",
@@ -177,6 +178,7 @@ exports[`Service breakdown panel component renders service breakdown panel 1`] =
177178
data={
178179
Array [
179180
Object {
181+
"hoverinfo": "label+percent",
180182
"hovertemplate": "%{label}<br>%{value:.2f}%<extra></extra>",
181183
"labels": Array [
182184
"inventory",

public/components/trace_analytics/components/traces/__tests__/__snapshots__/span_detail_panel.test.tsx.snap

+9-54
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
1616
<EuiFlexItem>
1717
<PanelTitle
1818
title="Spans"
19-
totalItems={0.5}
19+
totalItems={0}
2020
/>
2121
</EuiFlexItem>
2222
<EuiFlexItem
@@ -42,7 +42,6 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
4242
>
4343
<EuiButtonGroup
4444
idSelected="timeline"
45-
isDisabled={false}
4645
legend="Select view of spans"
4746
onChange={[Function]}
4847
options={
@@ -74,26 +73,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
7473
grow={false}
7574
>
7675
<Plt
77-
data={
78-
Array [
79-
Object {
80-
"hoverinfo": "none",
81-
"marker": Object {
82-
"color": "#fff",
83-
},
84-
"orientation": "h",
85-
"showlegend": false,
86-
"type": "bar",
87-
"width": 0.4,
88-
"x": Array [
89-
10,
90-
],
91-
"y": Array [
92-
"service1",
93-
],
94-
},
95-
]
96-
}
76+
data={Array []}
9777
layout={
9878
Object {
9979
"dragmode": "select",
@@ -116,7 +96,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
11696
},
11797
"type": "rect",
11898
"x0": 0,
119-
"x1": 22,
99+
"x1": 0,
120100
"xref": "x",
121101
"y0": 0,
122102
"y1": 1,
@@ -128,7 +108,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
128108
"color": "#91989c",
129109
"range": Array [
130110
0,
131-
22,
111+
0,
132112
],
133113
"showline": true,
134114
"side": "top",
@@ -153,31 +133,10 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
153133
}
154134
>
155135
<Plt
156-
data={
157-
Array [
158-
Object {
159-
"hoverinfo": "none",
160-
"marker": Object {
161-
"color": "#fff",
162-
},
163-
"orientation": "h",
164-
"showlegend": false,
165-
"text": "10.00 ms",
166-
"textposition": "outside",
167-
"type": "bar",
168-
"width": 0.4,
169-
"x": Array [
170-
10,
171-
],
172-
"y": Array [
173-
"service1",
174-
],
175-
},
176-
]
177-
}
136+
data={Array []}
178137
layout={
179138
Object {
180-
"height": 85,
139+
"height": 60,
181140
"margin": Object {
182141
"b": 30,
183142
"l": 150,
@@ -191,7 +150,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
191150
"color": "#91989c",
192151
"range": Array [
193152
0,
194-
22,
153+
0,
195154
],
196155
"showline": true,
197156
"side": "top",
@@ -200,12 +159,8 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = `
200159
"yaxis": Object {
201160
"fixedrange": true,
202161
"showgrid": false,
203-
"ticktext": Array [
204-
"",
205-
],
206-
"tickvals": Array [
207-
"service1",
208-
],
162+
"ticktext": Array [],
163+
"tickvals": Array [],
209164
},
210165
}
211166
}

0 commit comments

Comments
 (0)