Skip to content

Commit 26e7630

Browse files
nsdeschenesclaude
andcommitted
feat(traces): Disable trace links older than 30 days on profiling and event surfaces
Add 30-day age checks to trace links in profiling headers, flamegraph, profile events table, span evidence key-value list, profile event evidence, and statistical detector event display. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c566ae8 commit 26e7630

File tree

8 files changed

+140
-38
lines changed

8 files changed

+140
-38
lines changed

static/app/components/events/eventStatisticalDetector/eventComparison/eventDisplay.tsx

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {DiscoverDatasets} from 'sentry/utils/discover/types';
3434
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
3535
import {getShortEventId} from 'sentry/utils/events';
3636
import {useApiQuery} from 'sentry/utils/queryClient';
37+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
3738
import {useLocation} from 'sentry/utils/useLocation';
3839
import {useOrganization} from 'sentry/utils/useOrganization';
3940

@@ -208,6 +209,7 @@ function EventDisplay({
208209

209210
const waterfallModel = new WaterfallModel(eventData);
210211
const traceSlug = eventData.contexts?.trace?.trace_id ?? '';
212+
const isOld = isPartialSpanOrTraceData(eventData.endTimestamp);
211213
const fullEventTarget = generateLinkToEventInTraceView({
212214
eventId: eventData.id,
213215
traceSlug,
@@ -245,13 +247,19 @@ function EventDisplay({
245247
</OverlayTrigger.Button>
246248
)}
247249
/>
248-
<LinkButton
249-
tooltipProps={{title: t('Full Event Details')}}
250-
size={BUTTON_SIZE}
251-
to={fullEventTarget}
252-
aria-label={t('Full Event Details')}
253-
icon={<IconOpen />}
254-
/>
250+
<Tooltip
251+
title={t('Trace data is only available for the last 30 days')}
252+
disabled={!isOld}
253+
>
254+
<LinkButton
255+
tooltipProps={{title: isOld ? undefined : t('Full Event Details')}}
256+
size={BUTTON_SIZE}
257+
to={fullEventTarget}
258+
disabled={isOld}
259+
aria-label={t('Full Event Details')}
260+
icon={<IconOpen />}
261+
/>
262+
</Tooltip>
255263
</StyledEventControls>
256264
<div>
257265
<NavButtons>
@@ -279,7 +287,7 @@ function EventDisplay({
279287
</div>
280288
</Flex>
281289
<ComparisonContentWrapper>
282-
<Link to={fullEventTarget}>
290+
{isOld ? (
283291
<MinimapContainer>
284292
<MinimapPositioningContainer>
285293
<ActualMinimap
@@ -297,7 +305,27 @@ function EventDisplay({
297305
/>
298306
</MinimapPositioningContainer>
299307
</MinimapContainer>
300-
</Link>
308+
) : (
309+
<Link to={fullEventTarget}>
310+
<MinimapContainer>
311+
<MinimapPositioningContainer>
312+
<ActualMinimap
313+
theme={theme}
314+
spans={waterfallModel.getWaterfall({
315+
viewStart: 0,
316+
viewEnd: 1,
317+
})}
318+
generateBounds={waterfallModel.generateBounds({
319+
viewStart: 0,
320+
viewEnd: 1,
321+
})}
322+
dividerPosition={0}
323+
rootSpan={waterfallModel.rootSpan.span}
324+
/>
325+
</MinimapPositioningContainer>
326+
</MinimapContainer>
327+
</Link>
328+
)}
301329

302330
<OpsBreakdown event={eventData} operationNameFilters={noFilter} hideHeader />
303331
</ComparisonContentWrapper>

static/app/components/events/interfaces/performance/spanEvidenceKeyValueList.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
getSpanSubTimings,
2525
SpanSubTimingName,
2626
} from 'sentry/components/events/interfaces/spans/utils';
27+
import {getEventTimestampInSeconds} from 'sentry/components/events/interfaces/utils';
2728
import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
2829
import {t} from 'sentry/locale';
2930
import type {Entry, EntryRequest, Event, EventTransaction} from 'sentry/types/event';
@@ -40,6 +41,7 @@ import {formatBytesBase2} from 'sentry/utils/bytes/formatBytesBase2';
4041
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
4142
import {toRoundedPercent} from 'sentry/utils/number/toRoundedPercent';
4243
import {SQLishFormatter} from 'sentry/utils/sqlish/SQLishFormatter';
44+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
4345
import {safeURL} from 'sentry/utils/url/safeURL';
4446
import {useLocation} from 'sentry/utils/useLocation';
4547
import {useOrganization} from 'sentry/utils/useOrganization';
@@ -374,10 +376,17 @@ function AIDetectedSpanEvidence({
374376
organization,
375377
});
376378

379+
const eventTimestamp = getEventTimestampInSeconds(event);
380+
const isOld = eventTimestamp ? isPartialSpanOrTraceData(eventTimestamp) : false;
377381
const actionButton = projectSlug ? (
378-
<LinkButton size="xs" to={eventDetailsLocation}>
379-
{t('View Full Trace')}
380-
</LinkButton>
382+
<Tooltip
383+
title={t('Trace data is only available for the last 30 days')}
384+
disabled={!isOld}
385+
>
386+
<LinkButton size="xs" to={eventDetailsLocation} disabled={isOld}>
387+
{t('View Full Trace')}
388+
</LinkButton>
389+
</Tooltip>
381390
) : undefined;
382391

383392
const transactionRow = makeRow(
@@ -616,10 +625,17 @@ const makeTransactionNameRow = (
616625
organization,
617626
});
618627

628+
const eventTimestamp = getEventTimestampInSeconds(event);
629+
const isOld = eventTimestamp ? isPartialSpanOrTraceData(eventTimestamp) : false;
619630
const actionButton = projectSlug ? (
620-
<LinkButton size="xs" to={eventDetailsLocation}>
621-
{t('View Full Trace')}
622-
</LinkButton>
631+
<Tooltip
632+
title={t('Trace data is only available for the last 30 days')}
633+
disabled={!isOld}
634+
>
635+
<LinkButton size="xs" to={eventDetailsLocation} disabled={isOld}>
636+
{t('View Full Trace')}
637+
</LinkButton>
638+
</Tooltip>
623639
) : undefined;
624640

625641
return makeRow(

static/app/components/events/profileEventEvidence.tsx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {LinkButton} from '@sentry/scraps/button';
2+
import {Tooltip} from '@sentry/scraps/tooltip';
23

34
import {KeyValueList} from 'sentry/components/events/interfaces/keyValueList';
45
import {IconProfiling} from 'sentry/icons';
56
import {t} from 'sentry/locale';
67
import type {Event} from 'sentry/types/event';
78
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
89
import {generateProfileFlamechartRouteWithHighlightFrame} from 'sentry/utils/profiling/routes';
10+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
911
import {useLocation} from 'sentry/utils/useLocation';
1012
import {useOrganization} from 'sentry/utils/useOrganization';
1113
import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
@@ -19,6 +21,7 @@ export function ProfileEventEvidence({event, projectSlug}: ProfileEvidenceProps)
1921
const evidenceData = event.occurrence?.evidenceData ?? {};
2022
const evidenceDisplay = event.occurrence?.evidenceDisplay ?? [];
2123
const traceSlug = event.contexts?.trace?.trace_id ?? '';
24+
const isOld = isPartialSpanOrTraceData(evidenceData.timestamp);
2225

2326
const keyValueListData = [
2427
...(evidenceData.transactionId && evidenceData.transactionName
@@ -28,18 +31,27 @@ export function ProfileEventEvidence({event, projectSlug}: ProfileEvidenceProps)
2831
key: 'Transaction Name',
2932
value: evidenceData.transactionName,
3033
actionButton: traceSlug ? (
31-
<LinkButton
32-
size="xs"
33-
to={generateLinkToEventInTraceView({
34-
traceSlug,
35-
timestamp: evidenceData.timestamp,
36-
eventId: evidenceData.transactionId,
37-
location: {...location, query: {...location.query, referrer: 'issue'}},
38-
organization,
39-
})}
34+
<Tooltip
35+
title={t('Trace data is only available for the last 30 days')}
36+
disabled={!isOld}
4037
>
41-
{t('View Transaction')}
42-
</LinkButton>
38+
<LinkButton
39+
size="xs"
40+
disabled={isOld}
41+
to={generateLinkToEventInTraceView({
42+
traceSlug,
43+
timestamp: evidenceData.timestamp,
44+
eventId: evidenceData.transactionId,
45+
location: {
46+
...location,
47+
query: {...location.query, referrer: 'issue'},
48+
},
49+
organization,
50+
})}
51+
>
52+
{t('View Transaction')}
53+
</LinkButton>
54+
</Tooltip>
4355
) : null,
4456
},
4557
]

static/app/components/profiling/continuousProfileHeader.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {useCallback, useMemo} from 'react';
22
import styled from '@emotion/styled';
33

44
import {LinkButton} from '@sentry/scraps/button';
5+
import {Tooltip} from '@sentry/scraps/tooltip';
56

67
import {FeedbackButton} from 'sentry/components/feedbackButton/feedbackButton';
78
import * as Layout from 'sentry/components/layouts/thirds';
@@ -11,6 +12,7 @@ import {t} from 'sentry/locale';
1112
import type {Event} from 'sentry/types/event';
1213
import {trackAnalytics} from 'sentry/utils/analytics';
1314
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
15+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
1416
import {useLocation} from 'sentry/utils/useLocation';
1517
import {useOrganization} from 'sentry/utils/useOrganization';
1618

@@ -27,6 +29,8 @@ export function ContinuousProfileHeader({transaction}: ContinuousProfileHeader)
2729
return [{type: 'landing', payload: {query: {}}}];
2830
}, []);
2931

32+
const isOld = isPartialSpanOrTraceData(transaction?.endTimestamp);
33+
3034
const transactionTarget = transaction?.id
3135
? generateLinkToEventInTraceView({
3236
timestamp: transaction.endTimestamp ?? '',
@@ -53,9 +57,19 @@ export function ContinuousProfileHeader({transaction}: ContinuousProfileHeader)
5357
<StyledHeaderActions>
5458
<FeedbackButton />
5559
{transactionTarget && (
56-
<LinkButton size="sm" onClick={handleGoToTransaction} to={transactionTarget}>
57-
{t('Go to Trace')}
58-
</LinkButton>
60+
<Tooltip
61+
title={t('Trace data is only available for the last 30 days')}
62+
disabled={!isOld}
63+
>
64+
<LinkButton
65+
size="sm"
66+
onClick={handleGoToTransaction}
67+
to={transactionTarget}
68+
disabled={isOld}
69+
>
70+
{t('Go to Trace')}
71+
</LinkButton>
72+
</Tooltip>
5973
)}
6074
</StyledHeaderActions>
6175
</SmallerLayoutHeader>

static/app/components/profiling/flamegraph/flamegraphDrawer/profileDetails.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {FlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/flam
1919
import {useFlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/hooks/useFlamegraphPreferences';
2020
import type {ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
2121
import {makeFormatter} from 'sentry/utils/profiling/units/units';
22+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
2223
import {useLocation} from 'sentry/utils/useLocation';
2324
import {useOrganization} from 'sentry/utils/useOrganization';
2425
import {useProjects} from 'sentry/utils/useProjects';
@@ -283,11 +284,12 @@ function TransactionEventDetails({
283284
{
284285
key: 'transaction',
285286
label: t('Transaction'),
286-
value: transactionTarget ? (
287-
<Link to={transactionTarget}>{transaction.title}</Link>
288-
) : (
289-
transaction.title
290-
),
287+
value:
288+
transactionTarget && !isPartialSpanOrTraceData(transaction.endTimestamp) ? (
289+
<Link to={transactionTarget}>{transaction.title}</Link>
290+
) : (
291+
transaction.title
292+
),
291293
},
292294
{
293295
key: 'timestamp',
@@ -410,7 +412,7 @@ function ProfileEventDetails({
410412
organization,
411413
})
412414
: null;
413-
if (transactionTarget) {
415+
if (transactionTarget && !isPartialSpanOrTraceData(transaction?.endTimestamp)) {
414416
return (
415417
<DetailsRow key={key}>
416418
<strong>{label}:</strong>

static/app/components/profiling/flamegraph/flamegraphSpans.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {SpanChartRenderer2D} from 'sentry/utils/profiling/renderers/spansRendere
2626
import {SpansTextRenderer} from 'sentry/utils/profiling/renderers/spansTextRenderer';
2727
import type {SpanChart, SpanChartNode} from 'sentry/utils/profiling/spanChart';
2828
import {Rect} from 'sentry/utils/profiling/speedscope';
29+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
2930
import {useLocation} from 'sentry/utils/useLocation';
3031
import {useOrganization} from 'sentry/utils/useOrganization';
3132
import {TraceViewSources} from 'sentry/views/performance/newTraceDetails/traceHeader/breadcrumbs';
@@ -451,6 +452,11 @@ export function FlamegraphSpans({
451452
return;
452453
}
453454

455+
if (isPartialSpanOrTraceData(node.span.timestamp)) {
456+
addErrorMessage(t('Trace data is only available for the last 30 days'));
457+
return;
458+
}
459+
454460
const nodePath: Record<string, string> = {};
455461

456462
if (node.span.op === 'transaction' && node.span.event_id) {

static/app/components/profiling/profileEventsTable.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {getShortEventId} from 'sentry/utils/events';
2828
import type {EventsResults} from 'sentry/utils/profiling/hooks/types';
2929
import {generateProfileFlamechartRoute} from 'sentry/utils/profiling/routes';
3030
import {renderTableHead} from 'sentry/utils/profiling/tableRenderer';
31+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
3132
import {useLocation} from 'sentry/utils/useLocation';
3233
import {useOrganization} from 'sentry/utils/useOrganization';
3334
import {useProjects} from 'sentry/utils/useProjects';
@@ -175,6 +176,10 @@ function ProfileEventsCell<F extends FieldType>(props: ProfileEventsCellProps<F>
175176
props.baggage.location
176177
).normalizeDateSelection(props.baggage.location);
177178

179+
if (isPartialSpanOrTraceData(timestamp)) {
180+
return <Container>{getShortEventId(traceId)}</Container>;
181+
}
182+
178183
return (
179184
<Container>
180185
<Link
@@ -200,6 +205,11 @@ function ProfileEventsCell<F extends FieldType>(props: ProfileEventsCellProps<F>
200205
return <Container>{transactionId}</Container>;
201206
}
202207

208+
const txTimestamp = getTimeStampFromTableDateField(props.dataRow.timestamp);
209+
if (isPartialSpanOrTraceData(txTimestamp)) {
210+
return <Container>{transactionId}</Container>;
211+
}
212+
203213
return (
204214
<Container>
205215
<Link

static/app/components/profiling/profileHeader.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {useCallback, useMemo} from 'react';
22
import styled from '@emotion/styled';
33

44
import {LinkButton} from '@sentry/scraps/button';
5+
import {Tooltip} from '@sentry/scraps/tooltip';
56

67
import {FeedbackButton} from 'sentry/components/feedbackButton/feedbackButton';
78
import * as Layout from 'sentry/components/layouts/thirds';
@@ -11,6 +12,7 @@ import type {Event} from 'sentry/types/event';
1112
import {trackAnalytics} from 'sentry/utils/analytics';
1213
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
1314
import {isSchema, isSentrySampledProfile} from 'sentry/utils/profiling/guards/profile';
15+
import {isPartialSpanOrTraceData} from 'sentry/utils/trace/isOlderThan30Days';
1416
import {useLocation} from 'sentry/utils/useLocation';
1517
import {useOrganization} from 'sentry/utils/useOrganization';
1618
import {useProfiles} from 'sentry/views/profiling/profilesProvider';
@@ -42,6 +44,8 @@ function ProfileHeader({transaction, projectId, eventId}: ProfileHeaderProps) {
4244
const profileId = eventId ?? '';
4345
const projectSlug = projectId ?? '';
4446

47+
const isOld = isPartialSpanOrTraceData(transaction?.endTimestamp);
48+
4549
const transactionTarget = transaction?.id
4650
? generateLinkToEventInTraceView({
4751
timestamp: transaction.endTimestamp ?? '',
@@ -91,9 +95,19 @@ function ProfileHeader({transaction, projectId, eventId}: ProfileHeaderProps) {
9195
<StyledHeaderActions>
9296
<FeedbackButton />
9397
{transactionTarget && (
94-
<LinkButton size="sm" onClick={handleGoToTransaction} to={transactionTarget}>
95-
{t('Go to Trace')}
96-
</LinkButton>
98+
<Tooltip
99+
title={t('Trace data is only available for the last 30 days')}
100+
disabled={!isOld}
101+
>
102+
<LinkButton
103+
size="sm"
104+
onClick={handleGoToTransaction}
105+
to={transactionTarget}
106+
disabled={isOld}
107+
>
108+
{t('Go to Trace')}
109+
</LinkButton>
110+
</Tooltip>
97111
)}
98112
</StyledHeaderActions>
99113
</SmallerLayoutHeader>

0 commit comments

Comments
 (0)