feat: trace to logs and logs to trace is added (#2699)

* feat: trace to logs and logs to trace is added

* chore: icons and spanId is updated

* chore: feedback changes are updated

---------

Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
Palash Gupta 2023-05-18 12:36:02 +05:30 committed by GitHub
parent 5637188e72
commit a0c320e47e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 27 deletions

View File

@ -1,13 +1,14 @@
import { Button, Popover } from 'antd';
import { Popover } from 'antd';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import React, { memo, useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
import { ILogsReducer } from 'types/reducer/logs';
import { ButtonContainer } from './styles';
function AddToQueryHOC({
fieldKey,
fieldValue,
@ -16,7 +17,6 @@ function AddToQueryHOC({
const {
searchFilter: { queryString },
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const dispatch = useDispatch<Dispatch<AppActions>>();
const generatedQuery = useMemo(
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
@ -31,24 +31,20 @@ function AddToQueryHOC({
} else {
updatedQueryString += ` AND ${generatedQuery}`;
}
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: {
searchQueryString: updatedQueryString,
},
});
}, [dispatch, generatedQuery, queryString]);
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
}, [generatedQuery, queryString]);
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
fieldKey,
]);
return (
<Button size="small" type="text" onClick={handleQueryAdd}>
<ButtonContainer size="small" type="text" onClick={handleQueryAdd}>
<Popover placement="top" content={popOverContent}>
{children}
</Popover>
</Button>
</ButtonContainer>
);
}

View File

@ -0,0 +1,8 @@
import { Button } from 'antd';
import styled from 'styled-components';
export const ButtonContainer = styled(Button)`
&&& {
padding-left: 0;
}
`;

View File

@ -1,14 +1,22 @@
import { blue, orange } from '@ant-design/colors';
import { Input } from 'antd';
import { LinkOutlined } from '@ant-design/icons';
import { Input, Space, Tooltip } from 'antd';
import { ColumnsType } from 'antd/es/table';
import Editor from 'components/Editor';
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { ResizeTable } from 'components/ResizeTable';
import ROUTES from 'constants/routes';
import flatten from 'flat';
import history from 'lib/history';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { isEmpty } from 'lodash-es';
import React, { useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { generatePath } from 'react-router-dom';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
import { ILog } from 'types/api/logs/log';
import ActionItem from './ActionItem';
@ -23,6 +31,8 @@ interface TableViewProps {
function TableView({ logData }: TableViewProps): JSX.Element | null {
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
const dispatch = useDispatch<Dispatch<AppActions>>();
const flattenLogData: Record<string, never> | null = useMemo(
() => (logData ? flatten(logData) : null),
[logData],
@ -41,6 +51,29 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
value: JSON.stringify(flattenLogData[key]),
}));
const onTraceHandler = (record: DataType) => (): void => {
if (flattenLogData === null) return;
const traceId = flattenLogData[record.field];
const spanId = flattenLogData?.span_id;
if (traceId) {
dispatch({
type: SET_DETAILED_LOG_DATA,
payload: null,
});
const basePath = generatePath(ROUTES.TRACE_DETAIL, {
id: traceId,
});
const route = spanId ? `${basePath}?spanId=${spanId}` : basePath;
history.push(route);
}
};
if (!dataSource) {
return null;
}
@ -62,11 +95,38 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
dataIndex: 'field',
key: 'field',
width: 30,
align: 'left',
ellipsis: true,
render: (field: string): JSX.Element => {
render: (field: string, record): JSX.Element => {
const fieldKey = field.split('.').slice(-1);
const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
if (record.field === 'trace_id') {
const traceId = flattenLogData[record.field];
return (
<Space size="middle">
{renderedField}
{traceId && (
<Tooltip title="Inspect in Trace">
<div
style={{ cursor: 'pointer' }}
role="presentation"
onClick={onTraceHandler(record)}
>
<LinkOutlined
style={{
width: '15px',
}}
/>
</div>
</Tooltip>
)}
</Space>
);
}
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
return (
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>

View File

@ -3,7 +3,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';
import { parseQuery } from 'lib/logql';
import isEqual from 'lodash-es/isEqual';
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
@ -28,7 +28,7 @@ export function useSearchParser(): {
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const urlQuery = useUrlQuery();
const parsedFilters = useMemo(() => urlQuery.get('q'), [urlQuery]);
const parsedFilters = urlQuery.get('q');
const { minTime, maxTime, selectedTime } = useSelector<
AppState,
@ -62,16 +62,12 @@ export function useSearchParser(): {
},
// need to hide this warning as we don't want to update the query string on every change
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch, parsedQuery, selectedTime],
[dispatch, parsedQuery, selectedTime, queryString],
);
useEffect(() => {
if (!queryString && parsedFilters) {
updateQueryString(parsedFilters);
} else if (queryString) {
updateQueryString(queryString);
}
}, [queryString, updateQueryString, parsedFilters]);
updateQueryString(parsedFilters || '');
}, [parsedFilters, updateQueryString]);
return {
queryString,

View File

@ -1,8 +1,11 @@
import { Modal, Tabs, Tooltip, Typography } from 'antd';
import { Button, Modal, Tabs, Tooltip, Typography } from 'antd';
import Editor from 'components/Editor';
import { StyledSpace } from 'components/Styled';
import ROUTES from 'constants/routes';
import { useIsDarkMode } from 'hooks/useDarkMode';
import history from 'lib/history';
import React, { useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { ITraceTree } from 'types/api/trace/getTraceItem';
import Events from './Events';
@ -18,6 +21,8 @@ import Tags from './Tags';
function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
const { tree, firstSpanStartTime } = props;
const { id: traceId } = useParams<Params>();
const isDarkMode = useIsDarkMode();
const OverLayComponentName = useMemo(() => tree?.name, [tree?.name]);
@ -69,6 +74,12 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
},
];
const onLogsHandler = (): void => {
const query = encodeURIComponent(`trace_id IN ('${traceId}')`);
history.push(`${ROUTES.LOGS}?q=${query}`);
};
return (
<CardContainer>
<StyledSpace
@ -78,6 +89,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
<Typography.Text strong> Details for selected Span </Typography.Text>
<CustomTitle>Service</CustomTitle>
<Tooltip overlay={OverLayComponentServiceName}>
<CustomText ellipsis>{tree.serviceName}</CustomText>
</Tooltip>
@ -86,6 +98,8 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
<Tooltip overlay={OverLayComponentName}>
<CustomText ellipsis>{tree.name}</CustomText>
</Tooltip>
<Button onClick={onLogsHandler}>Go to Related logs</Button>
</StyledSpace>
<Modal
@ -125,4 +139,8 @@ export interface ModalText {
subText: string;
}
interface Params {
id: string;
}
export default SelectedSpanDetails;