mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 05:15:56 +08:00
feat: update where clause based on log field (#3103)
* feat: update where clause based on log field * chore: small changes are updated --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
e746bae8db
commit
c656803162
@ -1,3 +1,7 @@
|
|||||||
|
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
export type LogDetailProps = { log: ILog | null; onClose: () => void };
|
export type LogDetailProps = {
|
||||||
|
log: ILog | null;
|
||||||
|
onClose: () => void;
|
||||||
|
} & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||||
|
@ -4,7 +4,11 @@ import TableView from 'container/LogDetailedView/TableView';
|
|||||||
|
|
||||||
import { LogDetailProps } from './LogDetail.interfaces';
|
import { LogDetailProps } from './LogDetail.interfaces';
|
||||||
|
|
||||||
function LogDetail({ log, onClose }: LogDetailProps): JSX.Element {
|
function LogDetail({
|
||||||
|
log,
|
||||||
|
onClose,
|
||||||
|
onAddToQuery,
|
||||||
|
}: LogDetailProps): JSX.Element {
|
||||||
const onDrawerClose = (): void => {
|
const onDrawerClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
@ -13,7 +17,7 @@ function LogDetail({ log, onClose }: LogDetailProps): JSX.Element {
|
|||||||
{
|
{
|
||||||
label: 'Table',
|
label: 'Table',
|
||||||
key: '1',
|
key: '1',
|
||||||
children: log && <TableView logData={log} />,
|
children: log && <TableView logData={log} onAddToQuery={onAddToQuery} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'JSON',
|
label: 'JSON',
|
||||||
|
@ -1,39 +1,17 @@
|
|||||||
import { Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import history from 'lib/history';
|
|
||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
|
||||||
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
|
||||||
|
|
||||||
import { ButtonContainer } from './styles';
|
import { ButtonContainer } from './styles';
|
||||||
|
|
||||||
function AddToQueryHOC({
|
function AddToQueryHOC({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
fieldValue,
|
fieldValue,
|
||||||
|
onAddToQuery,
|
||||||
children,
|
children,
|
||||||
}: AddToQueryHOCProps): JSX.Element {
|
}: AddToQueryHOCProps): JSX.Element {
|
||||||
const {
|
|
||||||
searchFilter: { queryString },
|
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
|
||||||
|
|
||||||
const generatedQuery = useMemo(
|
|
||||||
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
|
||||||
[fieldKey, fieldValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleQueryAdd = useCallback(() => {
|
const handleQueryAdd = useCallback(() => {
|
||||||
let updatedQueryString = queryString || '';
|
onAddToQuery(fieldKey, fieldValue);
|
||||||
|
}, [fieldKey, fieldValue, onAddToQuery]);
|
||||||
if (updatedQueryString.length === 0) {
|
|
||||||
updatedQueryString += `${generatedQuery}`;
|
|
||||||
} else {
|
|
||||||
updatedQueryString += ` AND ${generatedQuery}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
|
||||||
}, [generatedQuery, queryString]);
|
|
||||||
|
|
||||||
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
||||||
fieldKey,
|
fieldKey,
|
||||||
@ -48,9 +26,10 @@ function AddToQueryHOC({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddToQueryHOCProps {
|
export interface AddToQueryHOCProps {
|
||||||
fieldKey: string;
|
fieldKey: string;
|
||||||
fieldValue: string;
|
fieldValue: string;
|
||||||
|
onAddToQuery: (fieldKey: string, fieldValue: string) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,22 @@ import { blue, grey, orange } from '@ant-design/colors';
|
|||||||
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||||
import Convert from 'ansi-to-html';
|
import Convert from 'ansi-to-html';
|
||||||
import { Button, Divider, Row, Typography } from 'antd';
|
import { Button, Divider, Row, Typography } from 'antd';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import dompurify from 'dompurify';
|
import dompurify from 'dompurify';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
// utils
|
// utils
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
// interfaces
|
// interfaces
|
||||||
import { IField } from 'types/api/logs/fields';
|
import { IField } from 'types/api/logs/fields';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import AddToQueryHOC from '../AddToQueryHOC';
|
import AddToQueryHOC from '../AddToQueryHOC';
|
||||||
@ -57,9 +63,37 @@ function LogSelectedField({
|
|||||||
fieldKey = '',
|
fieldKey = '',
|
||||||
fieldValue = '',
|
fieldValue = '',
|
||||||
}: LogFieldProps): JSX.Element {
|
}: LogFieldProps): JSX.Element {
|
||||||
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
searchFilter: { queryString },
|
||||||
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
|
const handleQueryAdd = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string) => {
|
||||||
|
const generatedQuery = generateFilterQuery({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
type: 'IN',
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedQueryString = queryString || '';
|
||||||
|
if (updatedQueryString.length === 0) {
|
||||||
|
updatedQueryString += `${generatedQuery}`;
|
||||||
|
} else {
|
||||||
|
updatedQueryString += ` AND ${generatedQuery}`;
|
||||||
|
}
|
||||||
|
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||||
|
},
|
||||||
|
[history, queryString],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectedLog>
|
<SelectedLog>
|
||||||
<AddToQueryHOC fieldKey={fieldKey} fieldValue={fieldValue}>
|
<AddToQueryHOC
|
||||||
|
fieldKey={fieldKey}
|
||||||
|
fieldValue={fieldValue}
|
||||||
|
onAddToQuery={handleQueryAdd}
|
||||||
|
>
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
<span style={{ color: blue[4] }}>{fieldKey}</span>
|
<span style={{ color: blue[4] }}>{fieldKey}</span>
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
@ -66,7 +66,6 @@ export const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
|
|||||||
export enum QueryBuilderKeys {
|
export enum QueryBuilderKeys {
|
||||||
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
||||||
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
||||||
GET_ATTRIBUTE_KEY = 'GET_ATTRIBUTE_KEY',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapOfOperators = {
|
export const mapOfOperators = {
|
||||||
|
@ -3,7 +3,9 @@ import { LinkOutlined } from '@ant-design/icons';
|
|||||||
import { Input, Space, Tooltip } from 'antd';
|
import { Input, Space, Tooltip } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
|
import AddToQueryHOC, {
|
||||||
|
AddToQueryHOCProps,
|
||||||
|
} from 'components/Logs/AddToQueryHOC';
|
||||||
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -27,7 +29,10 @@ const RESTRICTED_FIELDS = ['timestamp'];
|
|||||||
interface TableViewProps {
|
interface TableViewProps {
|
||||||
logData: ILog;
|
logData: ILog;
|
||||||
}
|
}
|
||||||
function TableView({ logData }: TableViewProps): JSX.Element | null {
|
|
||||||
|
type Props = TableViewProps & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||||
|
|
||||||
|
function TableView({ logData, onAddToQuery }: Props): JSX.Element | null {
|
||||||
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
@ -128,7 +133,11 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
|
|||||||
|
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||||
return (
|
return (
|
||||||
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
<AddToQueryHOC
|
||||||
|
fieldKey={fieldKey[0]}
|
||||||
|
fieldValue={flattenLogData[field]}
|
||||||
|
onAddToQuery={onAddToQuery}
|
||||||
|
>
|
||||||
{renderedField}
|
{renderedField}
|
||||||
</AddToQueryHOC>
|
</AddToQueryHOC>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import LogDetail from 'components/LogDetail';
|
import LogDetail from 'components/LogDetail';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
@ -7,9 +11,11 @@ import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
|||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
function LogDetailedView(): JSX.Element {
|
function LogDetailedView(): JSX.Element {
|
||||||
const { detailedLog } = useSelector<AppState, ILogsReducer>(
|
const history = useHistory();
|
||||||
(state) => state.logs,
|
const {
|
||||||
);
|
detailedLog,
|
||||||
|
searchFilter: { queryString },
|
||||||
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
@ -20,7 +26,32 @@ function LogDetailedView(): JSX.Element {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return <LogDetail log={detailedLog} onClose={onDrawerClose} />;
|
const handleQueryAdd = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string) => {
|
||||||
|
const generatedQuery = generateFilterQuery({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
type: 'IN',
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedQueryString = queryString || '';
|
||||||
|
if (updatedQueryString.length === 0) {
|
||||||
|
updatedQueryString += `${generatedQuery}`;
|
||||||
|
} else {
|
||||||
|
updatedQueryString += ` AND ${generatedQuery}`;
|
||||||
|
}
|
||||||
|
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||||
|
},
|
||||||
|
[history, queryString],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LogDetail
|
||||||
|
log={detailedLog}
|
||||||
|
onClose={onDrawerClose}
|
||||||
|
onAddToQuery={handleQueryAdd}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogDetailedView;
|
export default LogDetailedView;
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
import LogDetail from 'components/LogDetail';
|
import LogDetail from 'components/LogDetail';
|
||||||
|
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useQueryClient } from 'react-query';
|
||||||
|
import { SuccessResponse } from 'types/api';
|
||||||
|
import {
|
||||||
|
BaseAutocompleteData,
|
||||||
|
IQueryAutocompleteResponse,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import { LogExplorerDetailedViewProps } from './LogExplorerDetailedView.interfaces';
|
import { LogExplorerDetailedViewProps } from './LogExplorerDetailedView.interfaces';
|
||||||
|
|
||||||
@ -6,11 +18,52 @@ function LogExplorerDetailedView({
|
|||||||
log,
|
log,
|
||||||
onClose,
|
onClose,
|
||||||
}: LogExplorerDetailedViewProps): JSX.Element {
|
}: LogExplorerDetailedViewProps): JSX.Element {
|
||||||
const onDrawerClose = (): void => {
|
const queryClient = useQueryClient();
|
||||||
onClose();
|
const { redirectWithQueryBuilderData, currentQuery } = useQueryBuilder();
|
||||||
|
|
||||||
|
const handleAddQuery = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string): void => {
|
||||||
|
const keysAutocomplete: BaseAutocompleteData[] =
|
||||||
|
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
||||||
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS],
|
||||||
|
{ exact: false },
|
||||||
|
)?.payload.attributeKeys || [];
|
||||||
|
|
||||||
|
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
||||||
|
keysAutocomplete,
|
||||||
|
[fieldKey],
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const nextQuery: Query = {
|
||||||
|
...currentQuery,
|
||||||
|
builder: {
|
||||||
|
...currentQuery.builder,
|
||||||
|
queryData: currentQuery.builder.queryData.map((item) => ({
|
||||||
|
...item,
|
||||||
|
filters: {
|
||||||
|
...item.filters,
|
||||||
|
items: [
|
||||||
|
...item.filters.items.filter(
|
||||||
|
(item) => item.key?.id !== existAutocompleteKey.id,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
id: uuid(),
|
||||||
|
key: existAutocompleteKey,
|
||||||
|
op: '=',
|
||||||
|
value: fieldValue,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <LogDetail log={log} onClose={onDrawerClose} />;
|
redirectWithQueryBuilderData(nextQuery);
|
||||||
|
},
|
||||||
|
[currentQuery, queryClient, redirectWithQueryBuilderData],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <LogDetail log={log} onClose={onClose} onAddToQuery={handleAddQuery} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogExplorerDetailedView;
|
export default LogExplorerDetailedView;
|
||||||
|
@ -6,8 +6,8 @@ import {
|
|||||||
getTagToken,
|
getTagToken,
|
||||||
isInNInOperator,
|
isInNInOperator,
|
||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
|
import useDebounceValue from 'hooks/useDebounce';
|
||||||
import { isEqual, uniqWith } from 'lodash-es';
|
import { isEqual, uniqWith } from 'lodash-es';
|
||||||
import debounce from 'lodash-es/debounce';
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useDebounce } from 'react-use';
|
import { useDebounce } from 'react-use';
|
||||||
@ -39,25 +39,23 @@ export const useFetchKeysAndValues = (
|
|||||||
const [sourceKeys, setSourceKeys] = useState<BaseAutocompleteData[]>([]);
|
const [sourceKeys, setSourceKeys] = useState<BaseAutocompleteData[]>([]);
|
||||||
const [results, setResults] = useState<string[]>([]);
|
const [results, setResults] = useState<string[]>([]);
|
||||||
|
|
||||||
const searchParams = useMemo(
|
const memoizedSearchParams = useMemo(
|
||||||
() =>
|
|
||||||
debounce(
|
|
||||||
() => [
|
() => [
|
||||||
searchKey,
|
searchKey,
|
||||||
query.dataSource,
|
query.dataSource,
|
||||||
query.aggregateOperator,
|
query.aggregateOperator,
|
||||||
query.aggregateAttribute.key,
|
query.aggregateAttribute.key,
|
||||||
],
|
],
|
||||||
300,
|
|
||||||
),
|
|
||||||
[
|
[
|
||||||
query.aggregateAttribute.key,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.dataSource,
|
|
||||||
searchKey,
|
searchKey,
|
||||||
|
query.dataSource,
|
||||||
|
query.aggregateOperator,
|
||||||
|
query.aggregateAttribute.key,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const searchParams = useDebounceValue(memoizedSearchParams, 300);
|
||||||
|
|
||||||
const isQueryEnabled = useMemo(
|
const isQueryEnabled = useMemo(
|
||||||
() =>
|
() =>
|
||||||
query.dataSource === DataSource.METRICS
|
query.dataSource === DataSource.METRICS
|
||||||
@ -73,7 +71,7 @@ export const useFetchKeysAndValues = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { data, isFetching, status } = useQuery(
|
const { data, isFetching, status } = useQuery(
|
||||||
[QueryBuilderKeys.GET_ATTRIBUTE_KEY, searchParams()],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchParams],
|
||||||
async () =>
|
async () =>
|
||||||
getAggregateKeys({
|
getAggregateKeys({
|
||||||
searchText: searchKey,
|
searchText: searchKey,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user