diff --git a/frontend/src/components/QuickFilters/QuickFilters.styles.scss b/frontend/src/components/QuickFilters/QuickFilters.styles.scss index 01a17d83f1..5e64885c10 100644 --- a/frontend/src/components/QuickFilters/QuickFilters.styles.scss +++ b/frontend/src/components/QuickFilters/QuickFilters.styles.scss @@ -3,6 +3,7 @@ flex-direction: column; height: 100%; border-right: 1px solid var(--bg-slate-400); + color: var(--bg-vanilla-100); .header { display: flex; @@ -74,6 +75,7 @@ .quick-filters { background-color: var(--bg-vanilla-100); border-right: 1px solid var(--bg-vanilla-300); + color: var(--bg-ink-200); .header { border-bottom: 1px solid var(--bg-vanilla-300); diff --git a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx index 2d68f9e09b..5b4ad983d0 100644 --- a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx +++ b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx @@ -5,6 +5,7 @@ import RawLogView from 'components/Logs/RawLogView'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { LOCALSTORAGE } from 'constants/localStorage'; import { QueryParams } from 'constants/query'; +import ROUTES from 'constants/routes'; import ShowButton from 'container/LogsContextList/ShowButton'; import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils'; import { useOptionsMenu } from 'container/OptionsMenu'; @@ -14,7 +15,6 @@ import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/co import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import useUrlQuery from 'hooks/useUrlQuery'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useLocation } from 'react-router-dom'; import { Virtuoso } from 'react-virtuoso'; import { ILog } from 'types/api/logs/log'; import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData'; @@ -106,8 +106,6 @@ function ContextLogRenderer({ const urlQuery = useUrlQuery(); - const { pathname } = useLocation(); - const handleLogClick = useCallback( (logId: string): void => { urlQuery.set(QueryParams.activeLogId, `"${logId}"`); @@ -117,11 +115,10 @@ function ContextLogRenderer({ encodeURIComponent(JSON.stringify(query)), ); - const link = `${pathname}?${urlQuery.toString()}`; - + const link = `${ROUTES.LOGS_EXPLORER}?${urlQuery.toString()}`; window.open(link, '_blank', 'noopener,noreferrer'); }, - [pathname, query, urlQuery], + [query, urlQuery], ); const getItemContent = useCallback( @@ -143,7 +140,9 @@ function ContextLogRenderer({ linesPerRow={1} fontSize={options.fontSize} selectedFields={convertKeysToColumnFields( - options.selectColumns ?? defaultLogsSelectedColumns, + options.selectColumns?.length + ? options.selectColumns + : defaultLogsSelectedColumns, )} /> diff --git a/frontend/src/container/LogDetailedView/ContextView/__tests__/ContextLogRenderer.test.tsx b/frontend/src/container/LogDetailedView/ContextView/__tests__/ContextLogRenderer.test.tsx new file mode 100644 index 0000000000..32b2b6d718 --- /dev/null +++ b/frontend/src/container/LogDetailedView/ContextView/__tests__/ContextLogRenderer.test.tsx @@ -0,0 +1,145 @@ +import { + act, + render, + RenderResult, + screen, + waitFor, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { ENVIRONMENT } from 'constants/env'; +import { initialQueriesMap } from 'constants/queryBuilder'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { QueryBuilderContext } from 'providers/QueryBuilder'; +import MockQueryClientProvider from 'providers/test/MockQueryClientProvider'; +import TimezoneProvider from 'providers/Timezone'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import { VirtuosoMockContext } from 'react-virtuoso'; +import store from 'store'; + +import ContextLogRenderer from '../ContextLogRenderer'; +import { + mockLog, + mockQuery, + mockQueryRangeResponse, + mockTagFilter, +} from './mockData'; + +// Mock the useContextLogData hook +const mockHandleRunQuery = jest.fn(); + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + const uplotMock = jest.fn(() => ({ + paths, + })); + return { + paths, + default: uplotMock, + }; +}); + +jest.mock('container/OptionsMenu', () => ({ + useOptionsMenu: (): any => ({ + options: { + fontSize: 'medium', + selectColumns: [], + }, + }), +})); + +jest.mock('hooks/useSafeNavigate', () => ({ + useSafeNavigate: (): any => ({ + safeNavigate: jest.fn(), + }), +})); + +// Common wrapper component for tests +const renderContextLogRenderer = (): RenderResult => { + const defaultProps = { + isEdit: false, + query: mockQuery, + log: mockLog, + filters: mockTagFilter, + }; + + return render( + + + + + + + + + + + + + , + ); +}; + +describe('ContextLogRenderer', () => { + beforeEach(() => { + server.use( + rest.get(`${ENVIRONMENT.baseURL}/api/v1/logs`, (req, res, ctx) => + res(ctx.status(200), ctx.json({ logs: [mockLog] })), + ), + ); + server.use( + rest.post(`${ENVIRONMENT.baseURL}/api/v3/query_range`, (req, res, ctx) => + res(ctx.status(200), ctx.json(mockQueryRangeResponse)), + ), + ); + }); + + it('renders without crashing', async () => { + await act(async () => { + renderContextLogRenderer(); + }); + + await waitFor(() => { + expect(screen.getAllByText('Load more')).toHaveLength(2); + expect(screen.getByText(/Test log message/)).toBeInTheDocument(); + }); + }); + + it('loads new logs when clicking Load more button', async () => { + await act(async () => { + renderContextLogRenderer(); + }); + + await waitFor(() => { + expect(screen.getAllByText('Load more')).toHaveLength(2); + expect(screen.getByText(/Test log message/)).toBeInTheDocument(); + }); + + const loadMoreButtons = screen.getAllByText('Load more'); + await act(async () => { + await userEvent.click(loadMoreButtons[1]); + }); + + await waitFor(() => { + expect(screen.getAllByText(/Failed to authenticate/)).toHaveLength(3); + }); + }); +}); diff --git a/frontend/src/container/LogDetailedView/ContextView/__tests__/mockData.ts b/frontend/src/container/LogDetailedView/ContextView/__tests__/mockData.ts new file mode 100644 index 0000000000..9cc991090a --- /dev/null +++ b/frontend/src/container/LogDetailedView/ContextView/__tests__/mockData.ts @@ -0,0 +1,146 @@ +import { ILog } from 'types/api/logs/log'; +import { + BaseAutocompleteData, + DataTypes, +} from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; +import { DataSource } from 'types/common/queryBuilder'; + +export const mockLog: ILog = { + id: 'test-log-id', + date: '2024-03-20T10:00:00Z', + timestamp: '2024-03-20T10:00:00Z', + body: 'Test log message', + attributesString: {}, + attributesInt: {}, + attributesFloat: {}, + attributes_string: {}, + severityText: 'info', + severityNumber: 0, + traceId: '', + spanID: '', + traceFlags: 0, + resources_string: {}, + scope_string: {}, + severity_text: 'info', + severity_number: 0, +}; + +export const mockQuery: Query = { + queryType: EQueryType.QUERY_BUILDER, + builder: { + queryData: [ + { + aggregateOperator: 'count', + disabled: false, + queryName: 'A', + groupBy: [], + orderBy: [], + limit: 100, + dataSource: DataSource.LOGS, + aggregateAttribute: { + key: 'body', + type: 'string', + dataType: DataTypes.String, + isColumn: true, + }, + timeAggregation: 'sum', + functions: [], + having: [], + stepInterval: 60, + legend: '', + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + reduceTo: 'sum', + }, + ], + queryFormulas: [], + }, + clickhouse_sql: [], + id: 'test-query-id', + promql: [], +}; + +const mockBaseAutocompleteData: BaseAutocompleteData = { + key: 'service', + type: 'string', + dataType: DataTypes.String, + isColumn: true, +}; + +export const mockTagFilter: TagFilter = { + items: [ + { + id: 'test-filter-id', + key: mockBaseAutocompleteData, + op: '=', + value: 'test-service', + }, + ], + op: 'AND', +}; + +export const mockQueryRangeResponse = { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + list: [ + { + timestamp: '2025-04-29T09:55:22.462039242Z', + data: { + attributes_bool: {}, + attributes_number: {}, + attributes_string: { + 'log.file.path': + '/var/log/pods/generator_mongodb-0_755b8973-28c1-4698-a20f-22ee85c52c3f/mongodb/0.log', + 'log.iostream': 'stdout', + logtag: 'F', + }, + body: + '{"t":{"$date":"2025-04-29T09:55:22.461+00:00"},"s":"I", "c":"ACCESS", "id":5286307, "ctx":"conn231150","msg":"Failed to authenticate","attr":{"client":"10.32.2.33:58258","isSpeculative":false,"isClusterMember":false,"mechanism":"SCRAM-SHA-1","user":"$(MONGO_USER)","db":"admin","error":"UserNotFound: Could not find user \\"$(MONGO_USER)\\" for db \\"admin\\"","result":11,"metrics":{"conversation_duration":{"micros":473,"summary":{"0":{"step":1,"step_total":2,"duration_micros":446}}}},"extraInfo":{}}}', + id: '2wOlVEhbqYipTUgs3PRMFF1hqjJ', + resources_string: { + 'cloud.account.id': 'signoz-staging', + 'cloud.availability_zone': 'us-central1-c', + 'cloud.platform': 'gcp_kubernetes_engine', + 'cloud.provider': 'gcp', + 'container.image.name': 'docker.io/bitnami/mongodb', + 'container.image.tag': '7.0.14-debian-12-r0', + 'deployment.environment': 'sample-flask', + 'host.id': '6006012725680193244', + 'host.name': 'gke-mgmt-pl-generator-e2st4-sp-41c1bdc8-d54x', + 'k8s.cluster.name': 'mgmt', + 'k8s.container.name': 'mongodb', + 'k8s.container.restart_count': '0', + 'k8s.namespace.name': 'generator', + 'k8s.node.name': 'gke-mgmt-pl-generator-e2st4-sp-41c1bdc8-d54x', + 'k8s.node.uid': 'ef650183-226d-41c0-8295-aeec210b15dd', + 'k8s.pod.name': 'mongodb-0', + 'k8s.pod.start_time': '2025-04-26T04:47:44Z', + 'k8s.pod.uid': '755b8973-28c1-4698-a20f-22ee85c52c3f', + 'k8s.statefulset.name': 'mongodb', + 'os.type': 'linux', + 'service.name': 'mongodb', + }, + scope_name: '', + scope_string: {}, + scope_version: '', + severity_number: 0, + severity_text: '', + span_id: '', + trace_flags: 0, + trace_id: '', + }, + }, + ], + }, + ], + }, +}; diff --git a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts index 3d07ea0af9..427834cd64 100644 --- a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts +++ b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts @@ -134,6 +134,8 @@ export const useContextLogData = ({ enabled: !!requestData, onSuccess: handleSuccess, }, + undefined, // params + false, // isDependentOnQB ); const handleShowNextLines = useCallback(() => { diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss index 49f04baf0e..edee591592 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss @@ -285,6 +285,12 @@ .lightMode { .query-builder-search-v2 { + .ant-select-selector { + border-color: var(--bg-vanilla-300) !important; + background-color: var(--bg-vanilla-100) !important; + color: var(--bg-ink-200); + } + .content { .operator-for { .operator-for-text { diff --git a/frontend/src/pages/AllErrors/AllErrors.styles.scss b/frontend/src/pages/AllErrors/AllErrors.styles.scss index adc9220122..07e953a2a7 100644 --- a/frontend/src/pages/AllErrors/AllErrors.styles.scss +++ b/frontend/src/pages/AllErrors/AllErrors.styles.scss @@ -4,13 +4,12 @@ .all-errors-quick-filter-section { width: 0%; flex-shrink: 0; - color: var(--bg-vanilla-100); } .all-errors-right-section { padding: 0 10px; } - + .ant-tabs { margin: 0 8px; }