From 93f66ecee47aeb88dba5f0ebb16419559ecf380e Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Tue, 9 Dec 2025 16:36:52 -0500 Subject: [PATCH 01/11] :sparkles: Add in new trace warning when trace is older than 30 days --- .../newTraceDetails/traceHeader/title.tsx | 2 +- .../partialTraceDataWarning.tsx | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx diff --git a/static/app/views/performance/newTraceDetails/traceHeader/title.tsx b/static/app/views/performance/newTraceDetails/traceHeader/title.tsx index 90375f515401a4..521c807224f92f 100644 --- a/static/app/views/performance/newTraceDetails/traceHeader/title.tsx +++ b/static/app/views/performance/newTraceDetails/traceHeader/title.tsx @@ -28,7 +28,7 @@ interface TitleProps { rootEventResults: TraceRootEventQueryResults; } -function getTitle(representativeEvent: RepresentativeTraceEvent): { +export function getTitle(representativeEvent: RepresentativeTraceEvent): { subtitle: string | undefined; title: string; } | null { diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx new file mode 100644 index 00000000000000..e7d0a8579a93c2 --- /dev/null +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx @@ -0,0 +1,67 @@ +import {useMemo} from 'react'; +import moment from 'moment-timezone'; + +import {Alert} from '@sentry/scraps/alert'; +import {Link} from '@sentry/scraps/link'; +import {Text} from '@sentry/scraps/text'; + +import {t, tct} from 'sentry/locale'; +import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; +import type {OurLogsResponseItem} from 'sentry/views/explore/logs/types'; +import {getRepresentativeTraceEvent} from 'sentry/views/performance/newTraceDetails/traceApi/utils'; +import {getTitle} from 'sentry/views/performance/newTraceDetails/traceHeader/title'; +import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; + +interface PartialTraceDataWarningProps { + logs: OurLogsResponseItem[] | undefined; + timestamp: number | undefined; + tree: TraceTree; +} + +export function PartialTraceDataWarning({ + logs, + timestamp, + tree, +}: PartialTraceDataWarningProps) { + const rep = getRepresentativeTraceEvent(tree, logs); + const traceTitle = getTitle(rep); + + const queryString = useMemo(() => { + const search = new MutableSearch(''); + search.addFilterValue('is_transaction', 'true'); + search.addFilterValue('span.op', traceTitle?.title ?? ''); + return search.formatString(); + }, [traceTitle?.title]); + + if (!timestamp) { + return null; + } + + const now = moment(); + const isTraceTooYoung = moment(timestamp * 1000).isAfter(now.subtract(30, 'days')); + + if (isTraceTooYoung) { + return null; + } + + const exploreUrl = `/explore/traces/?mode=${Mode.SAMPLES}&table=trace&statsPeriod=24h&query=${queryString}`; + + return ( + {t('Search similar traces in the past 24 hours')} + } + > + + {tct( + '[dataCategory] Trace may be missing spans since the age of the trace is older than 30 days', + { + dataCategory: {t('Partial Trace Data:')}, + } + )} + + + ); +} From b517435198e03233a727af012d790d4cd1b97152 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Tue, 9 Dec 2025 16:37:12 -0500 Subject: [PATCH 02/11] :white_check_mark: Add in tests --- .../partialTraceDataWarning.spec.tsx | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx new file mode 100644 index 00000000000000..5433cba5137c4d --- /dev/null +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx @@ -0,0 +1,110 @@ +import {OrganizationFixture} from 'sentry-fixture/organization'; + +import {render, screen} from 'sentry-test/reactTestingLibrary'; + +import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import { + makeEAPSpan, + makeEAPTrace, +} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; + +import {PartialTraceDataWarning} from './partialTraceDataWarning'; + +describe('PartialTraceDataWarning', () => { + describe('when the trace is older than 30 days', () => { + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date(2025, 0, 31)); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('should render warning', () => { + const organization = OrganizationFixture(); + const start = new Date('2024-01-01T00:00:00Z').getTime() / 1e3; + + const eapTrace = makeEAPTrace([ + makeEAPSpan({ + op: 'http.server', + start_timestamp: start, + end_timestamp: start + 2, + children: [makeEAPSpan({start_timestamp: start + 1, end_timestamp: start + 4})], + }), + ]); + + render( + , + {organization} + ); + + expect(screen.getByText('Partial Trace Data:')).toBeInTheDocument(); + + expect( + screen.getByText( + 'Trace may be missing spans since the age of the trace is older than 30 days' + ) + ).toBeInTheDocument(); + + expect( + screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) + ).toBeInTheDocument(); + + expect( + screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) + ).toHaveAttribute( + 'href', + '/explore/traces/?mode=samples&table=trace&statsPeriod=24h&query=is_transaction:true span.op:http.server' + ); + }); + }); + + describe('when the trace is younger than 30 days', () => { + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date(2025, 0, 1)); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('should not render the warning', () => { + const organization = OrganizationFixture(); + const start = new Date('2025-01-01T00:00:00Z').getTime() / 1e3; + + const eapTrace = makeEAPTrace([ + makeEAPSpan({ + op: 'http.server', + start_timestamp: start, + end_timestamp: start + 2, + children: [makeEAPSpan({start_timestamp: start + 1, end_timestamp: start + 4})], + }), + ]); + + render( + , + {organization} + ); + + expect(screen.queryByText('Partial Trace Data:')).not.toBeInTheDocument(); + + expect( + screen.queryByText( + 'Trace may be missing spans since the age of the trace is older than 30 days' + ) + ).not.toBeInTheDocument(); + + expect( + screen.queryByRole('link', {name: 'Search similar traces in the past 24 hours'}) + ).not.toBeInTheDocument(); + }); + }); +}); From 5d6f6e7c41369071cc32eeccb014a95f3d029744 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Tue, 9 Dec 2025 16:38:37 -0500 Subject: [PATCH 03/11] :necktie: Add in new alert to the trace details page --- static/app/views/performance/newTraceDetails/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/static/app/views/performance/newTraceDetails/index.tsx b/static/app/views/performance/newTraceDetails/index.tsx index 4bf58637ebd592..5e6307c1444465 100644 --- a/static/app/views/performance/newTraceDetails/index.tsx +++ b/static/app/views/performance/newTraceDetails/index.tsx @@ -22,6 +22,7 @@ import { } from 'sentry/views/performance/newTraceDetails/traceOurlogs'; import {TraceSummarySection} from 'sentry/views/performance/newTraceDetails/traceSummary'; import {TraceTabsAndVitals} from 'sentry/views/performance/newTraceDetails/traceTabsAndVitals'; +import {PartialTraceDataWarning} from 'sentry/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning'; import {TraceWaterfall} from 'sentry/views/performance/newTraceDetails/traceWaterfall'; import { TraceLayoutTabKeys, @@ -165,6 +166,11 @@ function TraceViewImpl({traceSlug}: {traceSlug: string}) { traceSlug={traceSlug} organization={organization} /> + Date: Wed, 10 Dec 2025 08:36:11 -0500 Subject: [PATCH 04/11] :sparkles: Add table arg to getExploreUrl --- static/app/views/explore/utils.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/static/app/views/explore/utils.tsx b/static/app/views/explore/utils.tsx index 1f97455b80f293..2289ab4fee6314 100644 --- a/static/app/views/explore/utils.tsx +++ b/static/app/views/explore/utils.tsx @@ -79,6 +79,7 @@ export function getExploreUrl({ referrer?: string; selection?: PageFilters; sort?: string; + table?: string; title?: string; visualize?: BaseVisualize[]; }) { From a40efbfc0c73a51b11833883b626a2158219b615 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 08:36:55 -0500 Subject: [PATCH 05/11] :recycle: Refactor to use getExploreUrl helper --- .../partialTraceDataWarning.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx index e7d0a8579a93c2..832afb829033b5 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx @@ -7,8 +7,11 @@ import {Text} from '@sentry/scraps/text'; import {t, tct} from 'sentry/locale'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import useOrganization from 'sentry/utils/useOrganization'; +import usePageFilters from 'sentry/utils/usePageFilters'; import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; import type {OurLogsResponseItem} from 'sentry/views/explore/logs/types'; +import {getExploreUrl} from 'sentry/views/explore/utils'; import {getRepresentativeTraceEvent} from 'sentry/views/performance/newTraceDetails/traceApi/utils'; import {getTitle} from 'sentry/views/performance/newTraceDetails/traceHeader/title'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; @@ -24,13 +27,18 @@ export function PartialTraceDataWarning({ timestamp, tree, }: PartialTraceDataWarningProps) { + const organization = useOrganization(); + const {selection} = usePageFilters(); + const rep = getRepresentativeTraceEvent(tree, logs); const traceTitle = getTitle(rep); const queryString = useMemo(() => { const search = new MutableSearch(''); search.addFilterValue('is_transaction', 'true'); - search.addFilterValue('span.op', traceTitle?.title ?? ''); + if (traceTitle?.title) { + search.addFilterValue('span.op', traceTitle.title); + } return search.formatString(); }, [traceTitle?.title]); @@ -45,7 +53,19 @@ export function PartialTraceDataWarning({ return null; } - const exploreUrl = `/explore/traces/?mode=${Mode.SAMPLES}&table=trace&statsPeriod=24h&query=${queryString}`; + const exploreUrl = getExploreUrl({ + organization, + mode: Mode.SAMPLES, + query: queryString, + table: 'trace', + selection: { + ...selection, + datetime: { + ...selection.datetime, + period: '24h', + }, + }, + }); return ( Date: Wed, 10 Dec 2025 08:41:10 -0500 Subject: [PATCH 06/11] :white_check_mark: Update tests --- .../traceTypeWarnings/partialTraceDataWarning.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx index 5433cba5137c4d..b92fd16f15ab91 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx @@ -58,7 +58,7 @@ describe('PartialTraceDataWarning', () => { screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) ).toHaveAttribute( 'href', - '/explore/traces/?mode=samples&table=trace&statsPeriod=24h&query=is_transaction:true span.op:http.server' + `/organizations/${organization.slug}/explore/traces/?mode=samples&query=${encodeURIComponent('is_transaction:true span.op:http.server')}&statsPeriod=24h` ); }); }); From be4e7b31c71918fa649068129a709732647f4f41 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 09:12:27 -0500 Subject: [PATCH 07/11] :necktie: Pass table to query params --- static/app/views/explore/utils.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/app/views/explore/utils.tsx b/static/app/views/explore/utils.tsx index 2289ab4fee6314..1e74bb8a849fa7 100644 --- a/static/app/views/explore/utils.tsx +++ b/static/app/views/explore/utils.tsx @@ -65,6 +65,7 @@ export function getExploreUrl({ sort, field, id, + table, title, referrer, }: { @@ -101,6 +102,7 @@ export function getExploreUrl({ field, utc, id, + table, title, referrer, }; From 1c57a63094eb6cc97ba19ebdf93ec5411d78c415 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 09:12:51 -0500 Subject: [PATCH 08/11] :necktie: Rework how we determine the title, and add project to query --- .../partialTraceDataWarning.tsx | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx index 832afb829033b5..16de01913f3959 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx @@ -13,7 +13,6 @@ import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode'; import type {OurLogsResponseItem} from 'sentry/views/explore/logs/types'; import {getExploreUrl} from 'sentry/views/explore/utils'; import {getRepresentativeTraceEvent} from 'sentry/views/performance/newTraceDetails/traceApi/utils'; -import {getTitle} from 'sentry/views/performance/newTraceDetails/traceHeader/title'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; interface PartialTraceDataWarningProps { @@ -31,16 +30,32 @@ export function PartialTraceDataWarning({ const {selection} = usePageFilters(); const rep = getRepresentativeTraceEvent(tree, logs); - const traceTitle = getTitle(rep); + + let op = ''; + if (rep?.event) { + op = + 'transaction.op' in rep.event + ? `${rep.event?.['transaction.op']}` + : 'op' in rep.event + ? `${rep.event.op}` + : ''; + } const queryString = useMemo(() => { const search = new MutableSearch(''); search.addFilterValue('is_transaction', 'true'); - if (traceTitle?.title) { - search.addFilterValue('span.op', traceTitle.title); + + const projectId = rep.event?.project_id; + if (projectId) { + search.addFilterValue('project.id', `${projectId}`); } + + if (op) { + search.addFilterValue('span.op', op); + } + return search.formatString(); - }, [traceTitle?.title]); + }, [op, rep.event?.project_id]); if (!timestamp) { return null; @@ -61,7 +76,9 @@ export function PartialTraceDataWarning({ selection: { ...selection, datetime: { - ...selection.datetime, + start: null, + end: null, + utc: null, period: '24h', }, }, From 1f94d1c6b785d7ca533613995dbfc76c11f1df1f Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 09:15:17 -0500 Subject: [PATCH 09/11] :white_check_mark: Update tests --- .../traceTypeWarnings/partialTraceDataWarning.spec.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx index b92fd16f15ab91..42df17b7f698bf 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx @@ -54,11 +54,14 @@ describe('PartialTraceDataWarning', () => { screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) ).toBeInTheDocument(); + const queryString = encodeURIComponent( + 'is_transaction:true project.id:1 span.op:http.server' + ); expect( screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) ).toHaveAttribute( 'href', - `/organizations/${organization.slug}/explore/traces/?mode=samples&query=${encodeURIComponent('is_transaction:true span.op:http.server')}&statsPeriod=24h` + `/organizations/${organization.slug}/explore/traces/?mode=samples&query=${queryString}&statsPeriod=24h&table=trace` ); }); }); From 694701879160772ef1f0d2102d85f3553c056d98 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 09:18:05 -0500 Subject: [PATCH 10/11] :rotating_light: Make knip happy --- .../app/views/performance/newTraceDetails/traceHeader/title.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/app/views/performance/newTraceDetails/traceHeader/title.tsx b/static/app/views/performance/newTraceDetails/traceHeader/title.tsx index 521c807224f92f..90375f515401a4 100644 --- a/static/app/views/performance/newTraceDetails/traceHeader/title.tsx +++ b/static/app/views/performance/newTraceDetails/traceHeader/title.tsx @@ -28,7 +28,7 @@ interface TitleProps { rootEventResults: TraceRootEventQueryResults; } -export function getTitle(representativeEvent: RepresentativeTraceEvent): { +function getTitle(representativeEvent: RepresentativeTraceEvent): { subtitle: string | undefined; title: string; } | null { From c96970fed37620213389c59adfcaa8cd2bbdde31 Mon Sep 17 00:00:00 2001 From: nsdeschenes Date: Wed, 10 Dec 2025 09:37:32 -0500 Subject: [PATCH 11/11] :rewind: Revert back to using project page filter selection --- .../partialTraceDataWarning.spec.tsx | 6 ++---- .../traceTypeWarnings/partialTraceDataWarning.tsx | 11 +++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx index 42df17b7f698bf..f162a15c7626f1 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.spec.tsx @@ -54,14 +54,12 @@ describe('PartialTraceDataWarning', () => { screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) ).toBeInTheDocument(); - const queryString = encodeURIComponent( - 'is_transaction:true project.id:1 span.op:http.server' - ); + const queryString = encodeURIComponent('is_transaction:true span.op:http.server'); expect( screen.getByRole('link', {name: 'Search similar traces in the past 24 hours'}) ).toHaveAttribute( 'href', - `/organizations/${organization.slug}/explore/traces/?mode=samples&query=${queryString}&statsPeriod=24h&table=trace` + `/organizations/${organization.slug}/explore/traces/?mode=samples&project=1&query=${queryString}&statsPeriod=24h&table=trace` ); }); }); diff --git a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx index 16de01913f3959..72fdf03e1a86f3 100644 --- a/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx +++ b/static/app/views/performance/newTraceDetails/traceTypeWarnings/partialTraceDataWarning.tsx @@ -45,17 +45,12 @@ export function PartialTraceDataWarning({ const search = new MutableSearch(''); search.addFilterValue('is_transaction', 'true'); - const projectId = rep.event?.project_id; - if (projectId) { - search.addFilterValue('project.id', `${projectId}`); - } - if (op) { search.addFilterValue('span.op', op); } return search.formatString(); - }, [op, rep.event?.project_id]); + }, [op]); if (!timestamp) { return null; @@ -68,6 +63,9 @@ export function PartialTraceDataWarning({ return null; } + const projects = + typeof rep.event?.project_id === 'number' ? [rep.event?.project_id] : []; + const exploreUrl = getExploreUrl({ organization, mode: Mode.SAMPLES, @@ -75,6 +73,7 @@ export function PartialTraceDataWarning({ table: 'trace', selection: { ...selection, + projects, datetime: { start: null, end: null,