diff --git a/static/app/views/performance/transactionSummary/transactionEvents/content.tsx b/static/app/views/performance/transactionSummary/transactionEvents/content.tsx index 2e163c909e344d..3ba03379c9bfbf 100644 --- a/static/app/views/performance/transactionSummary/transactionEvents/content.tsx +++ b/static/app/views/performance/transactionSummary/transactionEvents/content.tsx @@ -1,3 +1,4 @@ +import {useMemo} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import omit from 'lodash/omit'; @@ -72,57 +73,84 @@ function EventsContent(props: Props) { projects, } = props; const routes = useRoutes(); - const eventView = originalEventView.clone(); - const transactionsListTitles = TRANSACTIONS_LIST_TITLES.slice(); - const project = projects.find(p => p.id === projectId); - const fields = [...eventView.fields]; + const {eventView, titles} = useMemo(() => { + const eventViewClone = originalEventView.clone(); + const transactionsListTitles = TRANSACTIONS_LIST_TITLES.slice(); + const project = projects.find(p => p.id === projectId); - if (webVital) { - transactionsListTitles.splice(3, 0, webVital); - } + const fields = [...eventViewClone.fields]; - const spanOperationBreakdownConditions = filterToSearchConditions( - spanOperationBreakdownFilter, - location - ); + if (webVital) { + transactionsListTitles.splice(3, 0, webVital); + } - if (spanOperationBreakdownConditions) { - eventView.query = `${eventView.query} ${spanOperationBreakdownConditions}`.trim(); - transactionsListTitles.splice(2, 1, t('%s duration', spanOperationBreakdownFilter)); - } + const spanOperationBreakdownConditions = filterToSearchConditions( + spanOperationBreakdownFilter, + location + ); - const platform = platformToPerformanceType(projects, eventView.project); - if (platform === ProjectPerformanceType.BACKEND) { - const userIndex = transactionsListTitles.indexOf('user'); - if (userIndex > 0) { - transactionsListTitles.splice(userIndex + 1, 0, 'http.method'); - fields.splice(userIndex + 1, 0, {field: 'http.method'}); + if (spanOperationBreakdownConditions) { + eventViewClone.query = + `${eventViewClone.query} ${spanOperationBreakdownConditions}`.trim(); + transactionsListTitles.splice(2, 1, t('%s duration', spanOperationBreakdownFilter)); } - } - if ( - organization.features.includes('profiling') && - project && - // only show for projects that already sent a profile - // once we have a more compact design we will show this for - // projects that support profiling as well - project.hasProfiles - ) { - transactionsListTitles.push(t('profile')); - fields.push({field: 'profile.id'}); - } + const platform = platformToPerformanceType(projects, eventViewClone.project); + if (platform === ProjectPerformanceType.BACKEND) { + const userIndex = transactionsListTitles.indexOf('user'); + if (userIndex > 0) { + transactionsListTitles.splice(userIndex + 1, 0, 'http.method'); + fields.splice(userIndex + 1, 0, {field: 'http.method'}); + } + } - if ( - organization.features.includes('session-replay') && - project && - projectSupportsReplay(project) - ) { - transactionsListTitles.push(t('replay')); - fields.push({field: 'replayId'}); - } + if ( + // only show for projects that already sent a profile + // once we have a more compact design we will show this for + // projects that support profiling as well + project?.hasProfiles && + (organization.features.includes('profiling') || + organization.features.includes('continuous-profiling')) + ) { + transactionsListTitles.push(t('profile')); - eventView.fields = fields; + if (organization.features.includes('profiling')) { + fields.push({field: 'profile.id'}); + } + + if (organization.features.includes('continuous-profiling')) { + fields.push({field: 'profiler.id'}); + fields.push({field: 'thread.id'}); + fields.push({field: 'precise.start_ts'}); + fields.push({field: 'precise.finish_ts'}); + } + } + + if ( + organization.features.includes('session-replay') && + project && + projectSupportsReplay(project) + ) { + transactionsListTitles.push(t('replay')); + fields.push({field: 'replayId'}); + } + + eventViewClone.fields = fields; + + return { + eventView: eventViewClone, + titles: transactionsListTitles, + }; + }, [ + originalEventView, + location, + organization, + projects, + projectId, + spanOperationBreakdownFilter, + webVital, + ]); return ( @@ -133,7 +161,7 @@ function EventsContent(props: Props) { routes={routes} location={location} setError={setError} - columnTitles={transactionsListTitles} + columnTitles={titles} transactionName={transactionName} /> diff --git a/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx b/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx index 0f738ea100f6b3..f96b02f86f575e 100644 --- a/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx +++ b/static/app/views/performance/transactionSummary/transactionEvents/eventsTable.tsx @@ -5,6 +5,7 @@ import type {Location, LocationDescriptor, LocationDescriptorObject} from 'histo import groupBy from 'lodash/groupBy'; import {Client} from 'sentry/api'; +import {LinkButton} from 'sentry/components/button'; import type {GridColumn} from 'sentry/components/gridEditable'; import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable'; import SortLink from 'sentry/components/gridEditable/sortLink'; @@ -12,6 +13,7 @@ import Link from 'sentry/components/links/link'; import Pagination from 'sentry/components/pagination'; import QuestionTooltip from 'sentry/components/questionTooltip'; import {Tooltip} from 'sentry/components/tooltip'; +import {IconProfiling} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import type {IssueAttachment, Organization} from 'sentry/types'; import {trackAnalytics} from 'sentry/utils/analytics'; @@ -29,6 +31,7 @@ import { } from 'sentry/utils/discover/fields'; import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls'; import ViewReplayLink from 'sentry/utils/discover/viewReplayLink'; +import {isEmptyObject} from 'sentry/utils/object/isEmptyObject'; import parseLinkHeader from 'sentry/utils/parseLinkHeader'; import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry'; import CellAction, {Actions, updateQuery} from 'sentry/views/discover/table/cellAction'; @@ -47,6 +50,23 @@ import { import type {TitleProps} from './operationSort'; import OperationSort from './operationSort'; +function shouldRenderColumn(containsSpanOpsBreakdown: boolean, col: string): boolean { + if (containsSpanOpsBreakdown && isSpanOperationBreakdownField(col)) { + return false; + } + + if ( + col === 'profiler.id' || + col === 'thread.id' || + col === 'precise.start_ts' || + col === 'precise.finish_ts' + ) { + return false; + } + + return true; +} + function OperationTitle({onClick}: TitleProps) { return (
@@ -242,7 +262,15 @@ class EventsTable extends Component { handleCellAction={this.handleCellAction(column)} allowActions={allowActions} > - {target ? {rendered} : rendered} +
+ + + +
); @@ -379,7 +407,7 @@ class EventsTable extends Component { totalEventsView.fields = [{field: 'count()', width: -1}]; const {widths} = this.state; - const containsSpanOpsBreakdown = eventView + const containsSpanOpsBreakdown = !!eventView .getColumns() .find( (col: TableColumn) => @@ -388,9 +416,8 @@ class EventsTable extends Component { const columnOrder = eventView .getColumns() - .filter( - (col: TableColumn) => - !containsSpanOpsBreakdown || !isSpanOperationBreakdownField(col.name) + .filter((col: TableColumn) => + shouldRenderColumn(containsSpanOpsBreakdown, col.name) ) .map((col: TableColumn, i: number) => { if (typeof widths[i] === 'number') {