Просмотр исходного кода

feat(perf): Use Span ID as first column in HTTP samples list (#68789)

- changes "Transaction ID" column to "Span ID"
- updates chart sample tooltip to show Span ID
- links sample table to new trace view properly
George Gritsouk 11 месяцев назад
Родитель
Сommit
d2836f6c72

+ 49 - 10
static/app/views/performance/http/httpSamplesPanel.spec.tsx

@@ -62,6 +62,11 @@ describe('HTTPSamplesPanel', () => {
     eventsRequestMock = MockApiClient.addMockResponse({
       url: `/organizations/${organization.slug}/events/`,
       method: 'GET',
+      match: [
+        MockApiClient.matchQuery({
+          referrer: 'api.starfish.http-module-samples-panel-metrics-ribbon',
+        }),
+      ],
       body: {
         data: [
           {
@@ -93,8 +98,8 @@ describe('HTTPSamplesPanel', () => {
     jest.resetAllMocks();
   });
 
-  describe('status panel', () => {
-    let eventsStatsRequestMock;
+  describe('Status panel', () => {
+    let eventsStatsRequestMock, samplesRequestMock;
 
     beforeEach(() => {
       jest.mocked(useLocation).mockReturnValue({
@@ -137,6 +142,32 @@ describe('HTTPSamplesPanel', () => {
           },
         },
       });
+
+      samplesRequestMock = MockApiClient.addMockResponse({
+        url: `/organizations/${organization.slug}/events/`,
+        method: 'GET',
+        match: [
+          MockApiClient.matchQuery({
+            referrer: 'api.starfish.http-module-samples-panel-response-code-samples',
+          }),
+        ],
+        body: {
+          data: [
+            {
+              span_id: 'b1bf1acde131623a',
+              trace: '2b60b2eb415c4bfba3efeaf65c21c605',
+              'span.description':
+                'GET https://sentry.io/api/0/organizations/sentry/info/?projectId=1',
+              project: 'javascript',
+              timestamp: '2024-03-25T20:31:36+00:00',
+              'span.status_code': '200',
+              'transaction.id': '11c910c9c10b3ec4ecf8f209b8c6ce48',
+              'span.self_time': 320.300102,
+            },
+          ],
+          meta: {},
+        },
+      });
     });
 
     it('fetches panel data', async () => {
@@ -195,8 +226,8 @@ describe('HTTPSamplesPanel', () => {
         })
       );
 
-      expect(eventsRequestMock).toHaveBeenNthCalledWith(
-        2,
+      expect(samplesRequestMock).toHaveBeenNthCalledWith(
+        1,
         `/organizations/${organization.slug}/events/`,
         expect.objectContaining({
           method: 'GET',
@@ -207,10 +238,12 @@ describe('HTTPSamplesPanel', () => {
             project: [],
             field: [
               'project',
+              'trace',
               'transaction.id',
+              'span_id',
+              'timestamp',
               'span.description',
               'span.status_code',
-              'span_id',
             ],
             sort: '-span_id',
             referrer: 'api.starfish.http-module-samples-panel-response-code-samples',
@@ -287,6 +320,7 @@ describe('HTTPSamplesPanel', () => {
           data: [
             {
               span_id: 'b1bf1acde131623a',
+              trace: '2b60b2eb415c4bfba3efeaf65c21c605',
               'span.description':
                 'GET https://sentry.io/api/0/organizations/sentry/info/?projectId=1',
               project: 'javascript',
@@ -334,7 +368,12 @@ describe('HTTPSamplesPanel', () => {
             query:
               'span.module:http span.domain:"\\*.sentry.dev" transaction:/api/0/users',
             project: [],
-            additionalFields: ['transaction.id', 'span.description', 'span.status_code'],
+            additionalFields: [
+              'trace',
+              'transaction.id',
+              'span.description',
+              'span.status_code',
+            ],
             lowerBound: 0,
             firstBound: expect.closeTo(333.3333),
             secondBound: expect.closeTo(666.6666),
@@ -374,14 +413,14 @@ describe('HTTPSamplesPanel', () => {
       // Samples table
       expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
 
-      expect(screen.getByRole('columnheader', {name: 'Event ID'})).toBeInTheDocument();
+      expect(screen.getByRole('columnheader', {name: 'Span ID'})).toBeInTheDocument();
       expect(screen.getByRole('columnheader', {name: 'Status'})).toBeInTheDocument();
       expect(screen.getByRole('columnheader', {name: 'URL'})).toBeInTheDocument();
 
-      expect(screen.getByRole('cell', {name: '11c910c9'})).toBeInTheDocument();
-      expect(screen.getByRole('link', {name: '11c910c9'})).toHaveAttribute(
+      expect(screen.getByRole('cell', {name: 'b1bf1acde131623a'})).toBeInTheDocument();
+      expect(screen.getByRole('link', {name: 'b1bf1acde131623a'})).toHaveAttribute(
         'href',
-        '/organizations/org-slug/performance/javascript:11c910c9c10b3ec4ecf8f209b8c6ce48#span-b1bf1acde131623a'
+        '/organizations/org-slug/performance/javascript:11c910c9c10b3ec4ecf8f209b8c6ce48/?domain=%2A.sentry.dev&panel=duration&statsPeriod=10d&transactionMethod=GET'
       );
       expect(screen.getByRole('cell', {name: '200'})).toBeInTheDocument();
     });

+ 4 - 1
static/app/views/performance/http/httpSamplesPanel.tsx

@@ -183,6 +183,7 @@ export function HTTPSamplesPanel() {
   } = useSpanSamples({
     search,
     fields: [
+      SpanIndexedField.TRACE,
       SpanIndexedField.TRANSACTION_ID,
       SpanIndexedField.SPAN_DESCRIPTION,
       SpanIndexedField.RESPONSE_CODE,
@@ -202,10 +203,12 @@ export function HTTPSamplesPanel() {
     filters,
     fields: [
       SpanIndexedField.PROJECT,
+      SpanIndexedField.TRACE,
       SpanIndexedField.TRANSACTION_ID,
+      SpanIndexedField.ID,
+      SpanIndexedField.TIMESTAMP,
       SpanIndexedField.SPAN_DESCRIPTION,
       SpanIndexedField.RESPONSE_CODE,
-      SpanIndexedField.ID,
     ],
     sorts: [SPAN_SAMPLES_SORT],
     limit: SPAN_SAMPLE_LIMIT,

+ 2 - 0
static/app/views/performance/http/settings.ts

@@ -11,3 +11,5 @@ export const releaseLevelAsBadgeProps = {
   isBeta: (RELEASE_LEVEL as BadgeType) === 'beta',
   isNew: (RELEASE_LEVEL as BadgeType) === 'new',
 };
+
+export const SPAN_ID_DISPLAY_LENGTH = 16;

+ 18 - 10
static/app/views/performance/http/spanSamplesTable.tsx

@@ -13,25 +13,32 @@ import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
-import {TransactionIdCell} from 'sentry/views/starfish/components/tableCells/transactionIdCell';
+import {SpanIdCell} from 'sentry/views/starfish/components/tableCells/spanIdCell';
 import type {IndexedResponse} from 'sentry/views/starfish/types';
 import {SpanIndexedField} from 'sentry/views/starfish/types';
 
-type ColumnKeys =
+type DataRowKeys =
   | SpanIndexedField.PROJECT
   | SpanIndexedField.TRANSACTION_ID
+  | SpanIndexedField.TRACE
+  | SpanIndexedField.TIMESTAMP
+  | SpanIndexedField.ID
+  | SpanIndexedField.SPAN_DESCRIPTION
+  | SpanIndexedField.RESPONSE_CODE;
+
+type ColumnKeys =
   | SpanIndexedField.ID
   | SpanIndexedField.SPAN_DESCRIPTION
   | SpanIndexedField.RESPONSE_CODE;
 
-type Row = Pick<IndexedResponse, ColumnKeys>;
+type DataRow = Pick<IndexedResponse, DataRowKeys>;
 
 type Column = GridColumnHeader<ColumnKeys>;
 
 const COLUMN_ORDER: Column[] = [
   {
-    key: SpanIndexedField.TRANSACTION_ID,
-    name: t('Event ID'),
+    key: SpanIndexedField.ID,
+    name: t('Span ID'),
     width: COL_WIDTH_UNDEFINED,
   },
   {
@@ -47,7 +54,7 @@ const COLUMN_ORDER: Column[] = [
 ];
 
 interface Props {
-  data: Row[];
+  data: DataRow[];
   isLoading: boolean;
   error?: Error | null;
   highlightedSpanId?: string;
@@ -95,16 +102,17 @@ export function SpanSamplesTable({
 
 function renderBodyCell(
   column: Column,
-  row: Row,
+  row: DataRow,
   meta: EventsMetaType | undefined,
   location: Location,
   organization: Organization
 ) {
-  if (column.key === SpanIndexedField.TRANSACTION_ID) {
+  if (column.key === SpanIndexedField.ID) {
     return (
-      <TransactionIdCell
-        orgSlug={organization.slug}
+      <SpanIdCell
         projectSlug={row.project}
+        traceId={row.trace}
+        timestamp={row.timestamp}
         transactionId={row[SpanIndexedField.TRANSACTION_ID]}
         spanId={row[SpanIndexedField.ID]}
       />

+ 49 - 0
static/app/views/starfish/components/tableCells/spanIdCell.tsx

@@ -0,0 +1,49 @@
+import Link from 'sentry/components/links/link';
+import EventView from 'sentry/utils/discover/eventView';
+import {
+  generateEventSlug,
+  generateLinkToEventInTraceView,
+} from 'sentry/utils/discover/urls';
+import {useLocation} from 'sentry/utils/useLocation';
+import useOrganization from 'sentry/utils/useOrganization';
+import {normalizeUrl} from 'sentry/utils/withDomainRequired';
+import {SPAN_ID_DISPLAY_LENGTH} from 'sentry/views/performance/http/settings';
+
+interface Props {
+  projectSlug: string;
+  spanId: string;
+  timestamp: string;
+  traceId: string;
+  transactionId: string;
+}
+
+export function SpanIdCell({
+  projectSlug,
+  traceId,
+  transactionId,
+  spanId,
+  timestamp,
+}: Props) {
+  const organization = useOrganization();
+  const location = useLocation();
+
+  const url = normalizeUrl(
+    generateLinkToEventInTraceView({
+      eventSlug: generateEventSlug({
+        id: transactionId,
+        project: projectSlug,
+      }),
+      organization,
+      location,
+      eventView: EventView.fromLocation(location),
+      dataRow: {
+        id: transactionId,
+        trace: traceId,
+        timestamp,
+      },
+      spanId,
+    })
+  );
+
+  return <Link to={url}>{spanId.slice(0, SPAN_ID_DISPLAY_LENGTH)}</Link>;
+}

+ 2 - 1
static/app/views/starfish/views/spanSummaryPage/sampleList/durationChart/useSampleScatterPlotSeries.tsx

@@ -3,6 +3,7 @@ import {useTheme} from '@emotion/react';
 import {t} from 'sentry/locale';
 import type {Series} from 'sentry/types/echarts';
 import {defined} from 'sentry/utils';
+import {SPAN_ID_DISPLAY_LENGTH} from 'sentry/views/performance/http/settings';
 import {AVG_COLOR} from 'sentry/views/starfish/colours';
 import type {IndexedResponse} from 'sentry/views/starfish/types';
 import {getSampleChartSymbol} from 'sentry/views/starfish/views/spanSummaryPage/sampleList/durationChart/getSampleChartSymbol';
@@ -35,7 +36,7 @@ export function useSampleScatterPlotSeries(
       symbol,
       color,
       symbolSize: span?.span_id === highlightedSpanId ? 19 : 14,
-      seriesName: span?.['transaction.id']?.substring(0, 8) ?? t('Sample'),
+      seriesName: span?.span_id?.substring(0, SPAN_ID_DISPLAY_LENGTH) ?? t('Sample'),
     };
 
     return series;