Skip to content

Commit 43a07a7

Browse files
committed
feat(ourlogs): Better frozen handling, more tests
1 parent 4490518 commit 43a07a7

File tree

9 files changed

+224
-32
lines changed

9 files changed

+224
-32
lines changed

static/app/components/events/breadcrumbs/combinedBreadcrumbsAndLogsSection.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function CombinedBreadcrumbsAndLogsSection({
2525

2626
return (
2727
<LogsPageParamsProvider
28-
isOnEmbeddedView
28+
isTableFrozen
2929
limitToTraceId={event.contexts?.trace?.trace_id}
3030
analyticsPageSource={LogsAnalyticsPageSource.ISSUE_DETAILS}
3131
>
@@ -55,7 +55,6 @@ function CombinedBreadcrumbsAndLogsSectionContent({
5555
/>
5656
<LogsIssuesSection
5757
initialCollapse={shouldCollapseLogs}
58-
isOnEmbeddedView
5958
limitToTraceId={event.contexts?.trace?.trace_id}
6059
event={event}
6160
group={group}

static/app/views/explore/contexts/logs/logsPageParams.tsx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface LogsPageParams {
3333
readonly analyticsPageSource: LogsAnalyticsPageSource;
3434
readonly cursor: string;
3535
readonly fields: string[];
36-
readonly isTableEditingFrozen: boolean | undefined;
36+
readonly isTableFrozen: boolean | undefined;
3737
readonly search: MutableSearch;
3838
readonly sortBys: Sort[];
3939
/**
@@ -57,7 +57,7 @@ const [_LogsPageParamsProvider, _useLogsPageParams, LogsPageParamsContext] =
5757
export interface LogsPageParamsProviderProps {
5858
analyticsPageSource: LogsAnalyticsPageSource;
5959
children: React.ReactNode;
60-
isOnEmbeddedView?: boolean;
60+
isTableFrozen?: boolean;
6161
limitToProjectIds?: number[];
6262
limitToSpanId?: string;
6363
limitToTraceId?: string;
@@ -68,7 +68,7 @@ export function LogsPageParamsProvider({
6868
limitToTraceId,
6969
limitToSpanId,
7070
limitToProjectIds,
71-
isOnEmbeddedView,
71+
isTableFrozen,
7272
analyticsPageSource,
7373
}: LogsPageParamsProviderProps) {
7474
const location = useLocation();
@@ -83,15 +83,12 @@ export function LogsPageParamsProvider({
8383
baseSearch = baseSearch ?? new MutableSearch('');
8484
baseSearch.addFilterValue(OurLogKnownFieldKey.TRACE_ID, limitToTraceId);
8585
}
86-
const isTableEditingFrozen = isOnEmbeddedView;
87-
const fields = isTableEditingFrozen
88-
? defaultLogFields()
89-
: getLogFieldsFromLocation(location);
90-
const sortBys = isTableEditingFrozen
86+
const fields = isTableFrozen ? defaultLogFields() : getLogFieldsFromLocation(location);
87+
const sortBys = isTableFrozen
9188
? [logsTimestampDescendingSortBy]
9289
: getLogSortBysFromLocation(location, fields);
9390
const pageFilters = usePageFilters();
94-
const projectIds = isOnEmbeddedView
91+
const projectIds = isTableFrozen
9592
? (limitToProjectIds ?? [-1])
9693
: pageFilters.selection.projects;
9794
// TODO we should handle environments in a similar way to projects - otherwise page filters might break embedded views
@@ -105,7 +102,7 @@ export function LogsPageParamsProvider({
105102
search,
106103
sortBys,
107104
cursor,
108-
isTableEditingFrozen,
105+
isTableFrozen,
109106
baseSearch,
110107
projectIds,
111108
analyticsPageSource,
@@ -133,7 +130,7 @@ function setLogsPageParams(location: Location, pageParams: LogPageParamsUpdate)
133130
updateNullableLocation(target, LOGS_QUERY_KEY, pageParams.search?.formatString());
134131
updateNullableLocation(target, LOGS_CURSOR_KEY, pageParams.cursor);
135132
updateNullableLocation(target, LOGS_FIELDS_KEY, pageParams.fields);
136-
if (!pageParams.isTableEditingFrozen) {
133+
if (!pageParams.isTableFrozen) {
137134
updateLocationWithLogSortBys(target, pageParams.sortBys);
138135
}
139136
return target;
@@ -203,9 +200,9 @@ export function useSetLogsSearch() {
203200
);
204201
}
205202

206-
export function useLogsIsTableEditingFrozen() {
207-
const {isTableEditingFrozen} = useLogsPageParams();
208-
return isTableEditingFrozen;
203+
export function useLogsIsTableFrozen() {
204+
const {isTableFrozen} = useLogsPageParams();
205+
return isTableFrozen;
209206
}
210207

211208
export function usePersistedLogsPageParams() {

static/app/views/explore/logs/logsIssuesSection.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSectio
2929

3030
export function LogsIssuesSection({
3131
initialCollapse,
32-
isOnEmbeddedView,
3332
limitToTraceId,
3433
event,
3534
project,
@@ -53,7 +52,7 @@ export function LogsIssuesSection({
5352
() => (
5453
<LogsPageParamsProvider
5554
analyticsPageSource={LogsAnalyticsPageSource.ISSUE_DETAILS}
56-
isOnEmbeddedView
55+
isTableFrozen
5756
limitToTraceId={limitToTraceId}
5857
>
5958
<TraceItemAttributeProvider traceItemType={TraceItemDataset.LOGS} enabled>
@@ -89,7 +88,7 @@ export function LogsIssuesSection({
8988
>
9089
<LogsPageParamsProvider
9190
analyticsPageSource={LogsAnalyticsPageSource.ISSUE_DETAILS}
92-
isOnEmbeddedView={isOnEmbeddedView}
91+
isTableFrozen
9392
limitToTraceId={limitToTraceId}
9493
>
9594
<LogsSectionContent tableData={tableData} openDrawer={onOpenLogsDrawer} />
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import {LocationFixture} from 'sentry-fixture/locationFixture';
2+
3+
import {initializeOrg} from 'sentry-test/initializeOrg';
4+
import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary';
5+
6+
import ProjectsStore from 'sentry/stores/projectsStore';
7+
import {LogsAnalyticsPageSource} from 'sentry/utils/analytics/logsAnalyticsEvent';
8+
import {useLocation} from 'sentry/utils/useLocation';
9+
import {
10+
LOGS_FIELDS_KEY,
11+
LOGS_QUERY_KEY,
12+
LogsPageParamsProvider,
13+
} from 'sentry/views/explore/contexts/logs/logsPageParams';
14+
import {LOGS_SORT_BYS_KEY} from 'sentry/views/explore/contexts/logs/sortBys';
15+
import {LogsTable} from 'sentry/views/explore/logs/logsTable';
16+
import {OurLogKnownFieldKey} from 'sentry/views/explore/logs/types';
17+
import type {UseExploreLogsTableResult} from 'sentry/views/explore/logs/useLogsQuery';
18+
19+
jest.mock('sentry/utils/useLocation');
20+
const mockUseLocation = jest.mocked(useLocation);
21+
22+
jest.mock('sentry/utils/useRelease', () => ({
23+
useRelease: jest.fn().mockReturnValue({
24+
data: {
25+
id: 10,
26+
lastCommit: {
27+
id: '1e5a9462e6ac23908299b218e18377837297bda1',
28+
},
29+
},
30+
}),
31+
}));
32+
33+
jest.mock('sentry/components/events/interfaces/frame/useStacktraceLink', () => ({
34+
__esModule: true,
35+
default: jest.fn().mockReturnValue({
36+
data: {
37+
sourceUrl: 'https://some-stacktrace-link',
38+
integrations: [],
39+
},
40+
error: null,
41+
isPending: false,
42+
}),
43+
}));
44+
45+
describe('LogsTable', function () {
46+
const {organization, project} = initializeOrg({
47+
organization: {
48+
features: ['ourlogs'],
49+
},
50+
});
51+
ProjectsStore.loadInitialData([project]);
52+
53+
const tableData = {
54+
data: [
55+
{
56+
'sentry.item_id': '0196a1bc022d76d3bff2106ebbf65f49',
57+
'project.id': project.id,
58+
trace: '32986bcdac1f43ed87445a2021b0099c',
59+
severity_number: 9,
60+
severity: 'info',
61+
timestamp: '2025-05-05T18:36:15+00:00',
62+
message:
63+
'10.5.55.212 - - [05/May/2025:18:36:15 +0000] "POST /v1/automation/autofix/state HTTP/1.1" 200 293642 "-" "python-requests/2.32.3"',
64+
'sentry.release': '985bae16edc2f3f8132e346a4f6c5a559f7c968b',
65+
'code.file.path': '/usr/local/lib/python3.11/dist-packages/gunicorn/glogging.py',
66+
'tags[sentry.timestamp_precise,number]': 1.7464701752771756e18,
67+
},
68+
{
69+
'sentry.item_id': '0196a1bc00e3720f8d47c84c53131891',
70+
'project.id': project.id,
71+
trace: '6141dca24986471398232d340a4fd588',
72+
severity_number: 9,
73+
severity: 'info',
74+
timestamp: '2025-05-05T18:36:14+00:00',
75+
message:
76+
'10.5.58.189 - - [05/May/2025:18:36:14 +0000] "POST /v0/issues/similar-issues HTTP/1.1" 200 131 "-" "python-urllib3/2.2.2"',
77+
'sentry.release': '985bae16edc2f3f8132e346a4f6c5a559f7c968b',
78+
'code.file.path': '/usr/local/lib/python3.11/dist-packages/gunicorn/glogging.py',
79+
'tags[sentry.timestamp_precise,number]': 1.746470174947077e18,
80+
},
81+
{
82+
'sentry.item_id': '0196a1bc007c7dfbbe099b6328e41d12',
83+
'project.id': project.id,
84+
trace: '6141dca24986471398232d340a4fd588',
85+
severity_number: 9,
86+
severity: 'info',
87+
timestamp: '2025-05-05T18:36:14+00:00',
88+
message:
89+
'10.5.62.140 - - [05/May/2025:18:36:14 +0000] "POST /v0/issues/similar-issues HTTP/1.1" 200 586 "-" "python-urllib3/2.2.2"',
90+
'sentry.release': '985bae16edc2f3f8132e346a4f6c5a559f7c968b',
91+
'code.file.path': '/usr/local/lib/python3.11/dist-packages/gunicorn/glogging.py',
92+
'tags[sentry.timestamp_precise,number]': 1.7464701748443016e18,
93+
},
94+
],
95+
meta: {
96+
fields: {
97+
'sentry.item_id': 'string',
98+
'project.id': 'string',
99+
trace: 'string',
100+
severity_number: 'integer',
101+
severity: 'string',
102+
timestamp: 'string',
103+
message: 'string',
104+
'sentry.release': 'string',
105+
'code.file.path': 'string',
106+
'tags[sentry.timestamp_precise,number]': 'number',
107+
},
108+
},
109+
isLoading: false,
110+
isPending: false,
111+
isError: false,
112+
error: null,
113+
pageLinks: undefined,
114+
} as unknown as UseExploreLogsTableResult;
115+
116+
const visibleColumnFields = [
117+
'message',
118+
'trace',
119+
'severity_number',
120+
'severity',
121+
'timestamp',
122+
'sentry.release',
123+
'code.file.path',
124+
];
125+
const frozenColumnFields = [OurLogKnownFieldKey.TIMESTAMP, OurLogKnownFieldKey.MESSAGE];
126+
127+
beforeEach(function () {
128+
MockApiClient.clearMockResponses();
129+
mockUseLocation.mockReturnValue(
130+
LocationFixture({
131+
pathname: `/organizations/${organization.slug}/explore/logs/?end=2025-04-10T20%3A04%3A51&project=${project.id}&start=2025-04-10T14%3A37%3A55`,
132+
query: {
133+
[LOGS_FIELDS_KEY]: visibleColumnFields,
134+
[LOGS_SORT_BYS_KEY]: '-timestamp',
135+
[LOGS_QUERY_KEY]: 'severity:error',
136+
},
137+
})
138+
);
139+
140+
MockApiClient.addMockResponse({
141+
url: `/organizations/${organization.slug}/releases/stats/`,
142+
method: 'GET',
143+
body: {},
144+
});
145+
});
146+
147+
it('should be interactable', async () => {
148+
render(
149+
<LogsPageParamsProvider analyticsPageSource={LogsAnalyticsPageSource.EXPLORE_LOGS}>
150+
<LogsTable tableData={tableData} showHeader />
151+
</LogsPageParamsProvider>
152+
);
153+
154+
const allTreeRows = await screen.findAllByTestId('log-table-row');
155+
expect(allTreeRows).toHaveLength(3);
156+
for (const row of allTreeRows) {
157+
for (const field of visibleColumnFields) {
158+
const cell = await within(row).findByTestId(`log-table-cell-${field}`);
159+
await userEvent.hover(cell);
160+
const actionsButton = within(cell).queryByRole('button', {
161+
name: 'Actions',
162+
});
163+
if (field === 'timestamp') {
164+
expect(actionsButton).toBeNull();
165+
} else {
166+
expect(actionsButton).toBeInTheDocument();
167+
}
168+
}
169+
}
170+
});
171+
172+
it('should not be interactable on embedded views', async () => {
173+
render(
174+
<LogsPageParamsProvider
175+
analyticsPageSource={LogsAnalyticsPageSource.EXPLORE_LOGS}
176+
isTableFrozen
177+
>
178+
<LogsTable tableData={tableData} showHeader />
179+
</LogsPageParamsProvider>
180+
);
181+
182+
const allTreeRows = await screen.findAllByTestId('log-table-row');
183+
expect(allTreeRows).toHaveLength(3);
184+
for (const row of allTreeRows) {
185+
for (const field of frozenColumnFields) {
186+
const cell = await within(row).findByTestId(`log-table-cell-${field}`);
187+
await userEvent.hover(cell);
188+
const actionsButton = within(cell).queryByRole('button', {
189+
name: 'Actions',
190+
});
191+
expect(actionsButton).not.toBeInTheDocument();
192+
}
193+
}
194+
});
195+
});

static/app/views/explore/logs/logsTable.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
} from 'sentry/views/explore/components/table';
2121
import {
2222
useLogsFields,
23-
useLogsIsTableEditingFrozen,
23+
useLogsIsTableFrozen,
2424
useLogsSearch,
2525
useLogsSortBys,
2626
useSetLogsCursor,
@@ -58,8 +58,8 @@ export function LogsTable({
5858
const fields = useLogsFields();
5959
const search = useLogsSearch();
6060
const setCursor = useSetLogsCursor();
61-
const isTableEditingFrozen = useLogsIsTableEditingFrozen();
62-
const hideTableBorder = !!isTableEditingFrozen;
61+
const isTableFrozen = useLogsIsTableFrozen();
62+
const hideTableBorder = !!isTableFrozen;
6363

6464
const {data, isError, isPending, pageLinks, meta} = tableData;
6565

@@ -113,10 +113,8 @@ export function LogsTable({
113113
isFirst={index === 0}
114114
>
115115
<TableHeadCellContent
116-
onClick={
117-
isTableEditingFrozen ? undefined : () => setSortBys([{field}])
118-
}
119-
isFrozen={isTableEditingFrozen}
116+
onClick={isTableFrozen ? undefined : () => setSortBys([{field}])}
117+
isFrozen={isTableFrozen}
120118
>
121119
<Tooltip showOnlyOnOverflow title={headerLabel}>
122120
{headerLabel}

static/app/views/explore/logs/logsTableRow.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {AttributesTree} from 'sentry/views/explore/components/traceItemAttribute
2121
import {
2222
useLogsAnalyticsPageSource,
2323
useLogsFields,
24+
useLogsIsTableFrozen,
2425
useLogsSearch,
2526
useSetLogsSearch,
2627
} from 'sentry/views/explore/contexts/logs/logsPageParams';
@@ -92,6 +93,7 @@ export function LogRowContent({
9293
const fields = useLogsFields();
9394
const search = useLogsSearch();
9495
const setLogsSearch = useSetLogsSearch();
96+
const isTableFrozen = useLogsIsTableFrozen();
9597

9698
function toggleExpanded() {
9799
setExpanded(e => !e);
@@ -203,7 +205,7 @@ export function LogRowContent({
203205
};
204206

205207
return (
206-
<LogTableBodyCell key={field}>
208+
<LogTableBodyCell key={field} data-test-id={'log-table-cell-' + field}>
207209
<CellAction
208210
column={discoverColumn}
209211
dataRow={dataRow as unknown as TableDataRow}
@@ -227,7 +229,9 @@ export function LogRowContent({
227229
}
228230
}}
229231
allowActions={
230-
field === OurLogKnownFieldKey.TIMESTAMP ? [] : ALLOWED_CELL_ACTIONS
232+
field === OurLogKnownFieldKey.TIMESTAMP || isTableFrozen
233+
? []
234+
: ALLOWED_CELL_ACTIONS
231235
}
232236
>
233237
<LogFieldRenderer

0 commit comments

Comments
 (0)