mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-23 02:24:25 +08:00
fix: eslint and tsc fixes for logs (#1527)
* fix: eslint and tsc fixes for logs * chore: remove package-lock file
This commit is contained in:
parent
32fba00aa8
commit
f48a884f90
@ -126,6 +126,8 @@
|
|||||||
"@types/copy-webpack-plugin": "^8.0.1",
|
"@types/copy-webpack-plugin": "^8.0.1",
|
||||||
"@types/d3": "^6.2.0",
|
"@types/d3": "^6.2.0",
|
||||||
"@types/d3-tip": "^3.5.5",
|
"@types/d3-tip": "^3.5.5",
|
||||||
|
"@types/event-source-polyfill": "^1.0.0",
|
||||||
|
"@types/flat": "^5.0.2",
|
||||||
"@types/jest": "^27.5.1",
|
"@types/jest": "^27.5.1",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/mini-css-extract-plugin": "^2.5.1",
|
"@types/mini-css-extract-plugin": "^2.5.1",
|
||||||
|
@ -4,7 +4,7 @@ import { ENVIRONMENT } from 'constants/env';
|
|||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import { EventSourcePolyfill } from 'event-source-polyfill';
|
import { EventSourcePolyfill } from 'event-source-polyfill';
|
||||||
|
|
||||||
export const LiveTail = (queryParams) => {
|
export const LiveTail = (queryParams: string): EventSourcePolyfill => {
|
||||||
const dict = {
|
const dict = {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${getLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN)}`,
|
Authorization: `Bearer ${getLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN)}`,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Popover, Tag, Tooltip } from 'antd';
|
import { Button, Popover } from 'antd';
|
||||||
import getStep from 'lib/getStep';
|
import getStep from 'lib/getStep';
|
||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import React, { memo, useCallback, useMemo } from 'react';
|
import React, { memo, useCallback, useMemo } from 'react';
|
||||||
@ -11,15 +11,24 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
|
interface AddToQueryHOCProps {
|
||||||
|
fieldKey: string;
|
||||||
|
fieldValue: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||||
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => ReturnType<typeof getLogsAggregate>;
|
||||||
|
}
|
||||||
function AddToQueryHOC({
|
function AddToQueryHOC({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
fieldValue,
|
fieldValue,
|
||||||
children,
|
children,
|
||||||
getLogs,
|
getLogs,
|
||||||
getLogsAggregate,
|
getLogsAggregate,
|
||||||
}) {
|
}: AddToQueryHOCProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
logLinesPerPage,
|
logLinesPerPage,
|
||||||
@ -72,9 +81,7 @@ function AddToQueryHOC({
|
|||||||
...(idStart ? { idGt: idStart } : {}),
|
...(idStart ? { idGt: idStart } : {}),
|
||||||
...(idEnd ? { idLt: idEnd } : {}),
|
...(idEnd ? { idLt: idEnd } : {}),
|
||||||
});
|
});
|
||||||
}
|
} else if (liveTail === 'PLAYING') {
|
||||||
|
|
||||||
else if (liveTail === 'PLAYING') {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOGGLE_LIVE_TAIL,
|
type: TOGGLE_LIVE_TAIL,
|
||||||
payload: 'PAUSED',
|
payload: 'PAUSED',
|
||||||
@ -88,6 +95,7 @@ function AddToQueryHOC({
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
dispatch,
|
dispatch,
|
||||||
generatedQuery,
|
generatedQuery,
|
||||||
@ -121,8 +129,12 @@ function AddToQueryHOC({
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogs: (
|
||||||
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
|
props: Parameters<typeof getLogs>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
import { Button, Popover, Tooltip } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
|
||||||
function CopyClipboardHOC({ textToCopy, children }) {
|
interface CopyClipboardHOCProps {
|
||||||
const [_state, setCopy] = useCopyToClipboard();
|
textToCopy: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
function CopyClipboardHOC({
|
||||||
|
textToCopy,
|
||||||
|
children,
|
||||||
|
}: CopyClipboardHOCProps): JSX.Element {
|
||||||
|
const [, setCopy] = useCopyToClipboard();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
cursor: 'pointer'
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
onClick={() => setCopy(textToCopy)}
|
onClick={(): void => setCopy(textToCopy)}
|
||||||
|
onKeyDown={(): void => setCopy(textToCopy)}
|
||||||
role="button"
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
placement="top"
|
placement="top"
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import { blue, grey, orange } from '@ant-design/colors';
|
import { blue, grey, orange } from '@ant-design/colors';
|
||||||
import { CopyFilled, CopyrightCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
|
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Divider, Row, Typography } from 'antd';
|
import { Button, Divider, Row, Typography } from 'antd';
|
||||||
import { map } from 'd3';
|
import { map } from 'd3';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import { flatMap, flatMapDeep } from 'lodash-es';
|
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import AddToQueryHOC from '../AddToQueryHOC';
|
import AddToQueryHOC from '../AddToQueryHOC';
|
||||||
import CopyClipboardHOC from '../CopyClipboardHOC';
|
import CopyClipboardHOC from '../CopyClipboardHOC';
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
function LogGeneralField({ fieldKey, fieldValue }) {
|
interface LogFieldProps {
|
||||||
|
fieldKey: string;
|
||||||
|
fieldValue: string;
|
||||||
|
}
|
||||||
|
function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -35,8 +39,10 @@ function LogGeneralField({ fieldKey, fieldValue }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
function LogSelectedField({
|
||||||
function LogSelectedField({ fieldKey = '', fieldValue = '' }) {
|
fieldKey = '',
|
||||||
|
fieldValue = '',
|
||||||
|
}: LogFieldProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -66,13 +72,16 @@ function LogSelectedField({ fieldKey = '', fieldValue = '' }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogItem({ logData }) {
|
interface LogItemProps {
|
||||||
|
logData: ILog;
|
||||||
|
}
|
||||||
|
function LogItem({ logData }: LogItemProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
fields: { selected },
|
fields: { selected },
|
||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
|
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
|
||||||
const [_state, setCopy] = useCopyToClipboard();
|
const [, setCopy] = useCopyToClipboard();
|
||||||
|
|
||||||
const handleDetailedView = useCallback(() => {
|
const handleDetailedView = useCallback(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -81,22 +90,28 @@ function LogItem({ logData }) {
|
|||||||
});
|
});
|
||||||
}, [dispatch, logData]);
|
}, [dispatch, logData]);
|
||||||
|
|
||||||
const handleCopyJSON = () => {
|
const handleCopyJSON = (): void => {
|
||||||
setCopy(JSON.stringify(logData, null, 2))
|
setCopy(JSON.stringify(logData, null, 2));
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<div style={{ maxWidth: '100%' }}>
|
<div style={{ maxWidth: '100%' }}>
|
||||||
<div>
|
<div>
|
||||||
{'{'}
|
{'{'}
|
||||||
<div style={{ marginLeft: '0.5rem' }}>
|
<div style={{ marginLeft: '0.5rem' }}>
|
||||||
<LogGeneralField fieldKey="log" fieldValue={flattenLogData.body} />
|
<LogGeneralField
|
||||||
|
fieldKey="log"
|
||||||
|
fieldValue={flattenLogData.body as never}
|
||||||
|
/>
|
||||||
{flattenLogData.stream && (
|
{flattenLogData.stream && (
|
||||||
<LogGeneralField fieldKey="stream" fieldValue={flattenLogData.stream} />
|
<LogGeneralField
|
||||||
|
fieldKey="stream"
|
||||||
|
fieldValue={flattenLogData.stream as never}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<LogGeneralField
|
<LogGeneralField
|
||||||
fieldKey="timestamp"
|
fieldKey="timestamp"
|
||||||
fieldValue={dayjs(flattenLogData.timestamp / 1e6).format()}
|
fieldValue={dayjs((flattenLogData.timestamp as never) / 1e6).format()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{'}'}
|
{'}'}
|
||||||
@ -107,7 +122,7 @@ function LogItem({ logData }) {
|
|||||||
<LogSelectedField
|
<LogSelectedField
|
||||||
key={field.name}
|
key={field.name}
|
||||||
fieldKey={field.name}
|
fieldKey={field.name}
|
||||||
fieldValue={flattenLogData[field.name]}
|
fieldValue={flattenLogData[field.name] as never}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
})}
|
})}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable sonarjs/no-duplicate-string */
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -87,7 +87,7 @@ function Retention({
|
|||||||
<Col span={12} style={{ display: 'flex' }}>
|
<Col span={12} style={{ display: 'flex' }}>
|
||||||
<RetentionFieldLabel>{text}</RetentionFieldLabel>
|
<RetentionFieldLabel>{text}</RetentionFieldLabel>
|
||||||
</Col>
|
</Col>
|
||||||
<Row span={12} justify="end">
|
<Row justify="end">
|
||||||
<RetentionFieldInputContainer>
|
<RetentionFieldInputContainer>
|
||||||
<Input
|
<Input
|
||||||
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
|
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
ArrowLeftOutlined,
|
|
||||||
FastBackwardOutlined,
|
FastBackwardOutlined,
|
||||||
LeftOutlined,
|
LeftOutlined,
|
||||||
RightOutlined,
|
RightOutlined,
|
||||||
@ -19,7 +18,7 @@ import {
|
|||||||
SET_LOG_LINES_PER_PAGE,
|
SET_LOG_LINES_PER_PAGE,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
@ -27,7 +26,10 @@ const { Option } = Select;
|
|||||||
|
|
||||||
const ITEMS_PER_PAGE_OPTIONS = [25, 50, 100, 200];
|
const ITEMS_PER_PAGE_OPTIONS = [25, 50, 100, 200];
|
||||||
|
|
||||||
function LogControls({ getLogs }) {
|
interface LogControlsProps {
|
||||||
|
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||||
|
}
|
||||||
|
function LogControls({ getLogs }: LogControlsProps): JSX.Element | null {
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
@ -40,14 +42,14 @@ function LogControls({ getLogs }) {
|
|||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleLogLinesPerPageChange = (e: number) => {
|
const handleLogLinesPerPageChange = (e: number): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_LOG_LINES_PER_PAGE,
|
type: SET_LOG_LINES_PER_PAGE,
|
||||||
payload: e,
|
payload: e,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGoToLatest = () => {
|
const handleGoToLatest = (): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RESET_ID_START_AND_END,
|
type: RESET_ID_START_AND_END,
|
||||||
});
|
});
|
||||||
@ -65,12 +67,12 @@ function LogControls({ getLogs }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNavigatePrevious = () => {
|
const handleNavigatePrevious = (): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: GET_PREVIOUS_LOG_LINES,
|
type: GET_PREVIOUS_LOG_LINES,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleNavigateNext = () => {
|
const handleNavigateNext = (): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: GET_NEXT_LOG_LINES,
|
type: GET_NEXT_LOG_LINES,
|
||||||
});
|
});
|
||||||
@ -105,7 +107,9 @@ function LogControls({ getLogs }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogs: (
|
||||||
|
props: Parameters<typeof getLogs>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -2,7 +2,7 @@ import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
|
|||||||
import { Button, Col, Popover } from 'antd';
|
import { Button, Col, Popover } from 'antd';
|
||||||
import getStep from 'lib/getStep';
|
import getStep from 'lib/getStep';
|
||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import React, { Dispatch, memo, useCallback, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
@ -12,9 +12,9 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
const removeJSONStringifyQuotes = (s: string) => {
|
const removeJSONStringifyQuotes = (s: string): string => {
|
||||||
if (!s || !s.length) {
|
if (!s || !s.length) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -24,7 +24,21 @@ const removeJSONStringifyQuotes = (s: string) => {
|
|||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|
||||||
|
interface ActionItemProps {
|
||||||
|
fieldKey: string;
|
||||||
|
fieldValue: string;
|
||||||
|
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||||
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => ReturnType<typeof getLogsAggregate>;
|
||||||
|
}
|
||||||
|
function ActionItem({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
getLogs,
|
||||||
|
getLogsAggregate,
|
||||||
|
}: ActionItemProps): JSX.Element | unknown {
|
||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
logLinesPerPage,
|
logLinesPerPage,
|
||||||
@ -38,7 +52,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleQueryAdd = (newQueryString) => {
|
const handleQueryAdd = (newQueryString: string): void => {
|
||||||
let updatedQueryString = queryString || '';
|
let updatedQueryString = queryString || '';
|
||||||
|
|
||||||
if (updatedQueryString.length === 0) {
|
if (updatedQueryString.length === 0) {
|
||||||
@ -94,7 +108,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() =>
|
onClick={(): void =>
|
||||||
handleQueryAdd(
|
handleQueryAdd(
|
||||||
generateFilterQuery({
|
generateFilterQuery({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
@ -110,7 +124,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() =>
|
onClick={(): void =>
|
||||||
handleQueryAdd(
|
handleQueryAdd(
|
||||||
generateFilterQuery({
|
generateFilterQuery({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
@ -124,7 +138,8 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
),
|
),
|
||||||
[],
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[fieldKey, validatedFieldValue],
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Popover placement="bottomLeft" content={PopOverMenuContent} trigger="click">
|
<Popover placement="bottomLeft" content={PopOverMenuContent} trigger="click">
|
||||||
@ -134,10 +149,11 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
|
|||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogs: (props: Parameters<typeof getLogs>[0]) => (dispatch: never) => void;
|
||||||
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => (dispatch: never) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
@ -147,4 +163,5 @@ const mapDispatchToProps = (
|
|||||||
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(memo(ActionItem));
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export default connect(null, mapDispatchToProps)(memo(ActionItem as any));
|
||||||
|
@ -4,31 +4,41 @@ import { Button, Row } from 'antd';
|
|||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
function JSONView({ logData }) {
|
interface JSONViewProps {
|
||||||
const [_state, copyToClipboard] = useCopyToClipboard();
|
logData: ILog;
|
||||||
const LogJsonData = useMemo(() => JSON.stringify(logData, null, 2), [logData]);
|
}
|
||||||
return (
|
function JSONView({ logData }: JSONViewProps): JSX.Element {
|
||||||
<div>
|
const [, copyToClipboard] = useCopyToClipboard();
|
||||||
<Row
|
const LogJsonData = useMemo(() => JSON.stringify(logData, null, 2), [logData]);
|
||||||
style={{
|
return (
|
||||||
justifyContent: 'flex-end',
|
<div>
|
||||||
margin: '0.5rem 0',
|
<Row
|
||||||
}}
|
style={{
|
||||||
>
|
justifyContent: 'flex-end',
|
||||||
<Button
|
margin: '0.5rem 0',
|
||||||
size="small"
|
}}
|
||||||
type="text"
|
>
|
||||||
onClick={(): void => copyToClipboard(LogJsonData)}
|
<Button
|
||||||
>
|
size="small"
|
||||||
<CopyFilled /> <span style={{ color: blue[5] }}>Copy to Clipboard</span>
|
type="text"
|
||||||
</Button>
|
onClick={(): void => copyToClipboard(LogJsonData)}
|
||||||
</Row>
|
>
|
||||||
<div style={{ marginTop: '0.5rem' }}>
|
<CopyFilled /> <span style={{ color: blue[5] }}>Copy to Clipboard</span>
|
||||||
<Editor value={LogJsonData} language="json" height="70vh" readOnly />
|
</Button>
|
||||||
</div>
|
</Row>
|
||||||
</div>
|
<div style={{ marginTop: '0.5rem' }}>
|
||||||
);
|
<Editor
|
||||||
|
value={LogJsonData}
|
||||||
|
language="json"
|
||||||
|
height="70vh"
|
||||||
|
readOnly
|
||||||
|
onChange={(): void => {}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default JSONView;
|
export default JSONView;
|
||||||
|
@ -1,47 +1,52 @@
|
|||||||
import { blue, orange } from '@ant-design/colors';
|
import { blue, orange } from '@ant-design/colors';
|
||||||
import {
|
import { Input, Table } from 'antd';
|
||||||
MenuFoldOutlined,
|
|
||||||
MinusCircleOutlined,
|
|
||||||
PlusCircleFilled,
|
|
||||||
PlusCircleOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Button, Col, Input, Popover, Table, Typography } from 'antd';
|
|
||||||
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
|
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
|
||||||
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
||||||
import flatten from 'flat';
|
import flatten from 'flat';
|
||||||
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
|
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
import ActionItem from './ActionItem';
|
import ActionItem from './ActionItem';
|
||||||
|
|
||||||
// Fields which should be restricted from adding it to query
|
// Fields which should be restricted from adding it to query
|
||||||
const RESTRICTED_FIELDS = ['timestamp'];
|
const RESTRICTED_FIELDS = ['timestamp'];
|
||||||
|
|
||||||
function TableView({ logData }) {
|
interface TableViewProps {
|
||||||
|
logData: ILog;
|
||||||
|
}
|
||||||
|
function TableView({ logData }: TableViewProps): JSX.Element | null {
|
||||||
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
||||||
|
|
||||||
const flattenLogData = useMemo(() => (logData ? flatten(logData) : null), [
|
const flattenLogData: Record<string, never> | null = useMemo(
|
||||||
logData,
|
() => (logData ? flatten(logData) : null),
|
||||||
]);
|
[logData],
|
||||||
|
);
|
||||||
if (logData === null) {
|
if (logData === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataSource = Object.keys(flattenLogData)
|
const dataSource =
|
||||||
.filter((field) => fieldSearchFilter(field, fieldSearchInput))
|
flattenLogData !== null &&
|
||||||
.map((key) => {
|
Object.keys(flattenLogData)
|
||||||
return {
|
.filter((field) => fieldSearchFilter(field, fieldSearchInput))
|
||||||
key,
|
.map((key) => {
|
||||||
field: key,
|
return {
|
||||||
value: JSON.stringify(flattenLogData[key]),
|
key,
|
||||||
};
|
field: key,
|
||||||
});
|
value: JSON.stringify(flattenLogData[key]),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dataSource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: 'Action',
|
title: 'Action',
|
||||||
width: 75,
|
width: 75,
|
||||||
render: (fieldData) => {
|
render: (fieldData: Record<string, string>): JSX.Element | null => {
|
||||||
const fieldKey = fieldData.field.split('.').slice(-1);
|
const fieldKey = fieldData.field.split('.').slice(-1);
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||||
return <ActionItem fieldKey={fieldKey} fieldValue={fieldData.value} />;
|
return <ActionItem fieldKey={fieldKey} fieldValue={fieldData.value} />;
|
||||||
@ -54,13 +59,13 @@ function TableView({ logData }) {
|
|||||||
dataIndex: 'field',
|
dataIndex: 'field',
|
||||||
key: 'field',
|
key: 'field',
|
||||||
width: '35%',
|
width: '35%',
|
||||||
render: (field: string) => {
|
render: (field: string): JSX.Element => {
|
||||||
const fieldKey = field.split('.').slice(-1);
|
const fieldKey = field.split('.').slice(-1);
|
||||||
const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
|
const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
|
||||||
|
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||||
return (
|
return (
|
||||||
<AddToQueryHOC fieldKey={fieldKey} fieldValue={flattenLogData[field]}>
|
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
||||||
{' '}
|
{' '}
|
||||||
{renderedField}
|
{renderedField}
|
||||||
</AddToQueryHOC>
|
</AddToQueryHOC>
|
||||||
@ -74,7 +79,7 @@ function TableView({ logData }) {
|
|||||||
dataIndex: 'value',
|
dataIndex: 'value',
|
||||||
key: 'value',
|
key: 'value',
|
||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
render: (field) => (
|
render: (field: never): JSX.Element => (
|
||||||
<CopyClipboardHOC textToCopy={field}>
|
<CopyClipboardHOC textToCopy={field}>
|
||||||
<span style={{ color: orange[6] }}>{field}</span>
|
<span style={{ color: orange[6] }}>{field}</span>
|
||||||
</CopyClipboardHOC>
|
</CopyClipboardHOC>
|
||||||
@ -88,13 +93,13 @@ function TableView({ logData }) {
|
|||||||
placeholder="Search field names"
|
placeholder="Search field names"
|
||||||
size="large"
|
size="large"
|
||||||
value={fieldSearchInput}
|
value={fieldSearchInput}
|
||||||
onChange={(e) => setFieldSearchInput(e.target.value)}
|
onChange={(e): void => setFieldSearchInput(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
// scroll={{ x: true }}
|
// scroll={{ x: true }}
|
||||||
tableLayout="fixed"
|
tableLayout="fixed"
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
columns={columns}
|
columns={columns as never}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,49 +3,51 @@ import React from 'react';
|
|||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import JSONView from './JsonView';
|
import JSONView from './JsonView';
|
||||||
import TableView from './TableView';
|
import TableView from './TableView';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
function LogDetailedView() {
|
function LogDetailedView(): JSX.Element {
|
||||||
const { detailedLog } = useSelector<AppState, ILogsReducer>(
|
const { detailedLog } = useSelector<AppState, ILogsReducer>(
|
||||||
(state) => state.logs,
|
(state) => state.logs,
|
||||||
);
|
);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const onDrawerClose = () => {
|
const onDrawerClose = (): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_DETAILED_LOG_DATA,
|
type: SET_DETAILED_LOG_DATA,
|
||||||
payload: null,
|
payload: null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{}}>
|
<div style={{}}>
|
||||||
<Drawer
|
<Drawer
|
||||||
width="60%"
|
width="60%"
|
||||||
title="Log Details"
|
title="Log Details"
|
||||||
placement="right"
|
placement="right"
|
||||||
closable
|
closable
|
||||||
mask={false}
|
mask={false}
|
||||||
onClose={onDrawerClose}
|
onClose={onDrawerClose}
|
||||||
visible={detailedLog !== null}
|
visible={detailedLog !== null}
|
||||||
getContainer={false}
|
getContainer={false}
|
||||||
style={{ overscrollBehavior: 'contain' }}
|
style={{ overscrollBehavior: 'contain' }}
|
||||||
>
|
>
|
||||||
<Tabs defaultActiveKey="1">
|
{detailedLog && (
|
||||||
<TabPane tab="Table" key="1">
|
<Tabs defaultActiveKey="1">
|
||||||
<TableView logData={detailedLog} />
|
<TabPane tab="Table" key="1">
|
||||||
</TabPane>
|
<TableView logData={detailedLog} />
|
||||||
<TabPane tab="JSON" key="2">
|
</TabPane>
|
||||||
<JSONView logData={detailedLog} />
|
<TabPane tab="JSON" key="2">
|
||||||
</TabPane>
|
<JSONView logData={detailedLog} />
|
||||||
</Tabs>
|
</TabPane>
|
||||||
</Drawer>
|
</Tabs>
|
||||||
</div>
|
)}
|
||||||
);
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogDetailedView;
|
export default LogDetailedView;
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function OptionIcon({ isDarkMode }) {
|
interface OptionIconProps {
|
||||||
return (
|
isDarkMode: boolean;
|
||||||
<svg
|
}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
function OptionIcon({ isDarkMode }: OptionIconProps): JSX.Element {
|
||||||
x="0px"
|
return (
|
||||||
y="0px"
|
<svg
|
||||||
width="1rem"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
height="1rem"
|
x="0px"
|
||||||
viewBox="0 0 52 52"
|
y="0px"
|
||||||
enableBackground="new 0 0 52 52"
|
width="1rem"
|
||||||
fill={isDarkMode ? "#eee" : '#222'}
|
height="1rem"
|
||||||
>
|
viewBox="0 0 52 52"
|
||||||
<path
|
enableBackground="new 0 0 52 52"
|
||||||
d="M20,44c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6S20,47.3,20,44z M20,26c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6
|
fill={isDarkMode ? '#eee' : '#222'}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20,44c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6S20,47.3,20,44z M20,26c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6
|
||||||
S20,29.3,20,26z M20,8c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6S20,11.3,20,8z"
|
S20,29.3,20,26z M20,8c0-3.3,2.7-6,6-6s6,2.7,6,6s-2.7,6-6,6S20,11.3,20,8z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OptionIcon;
|
export default OptionIcon;
|
||||||
|
@ -1,27 +1,11 @@
|
|||||||
import { green, red } from '@ant-design/colors';
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import { green } from '@ant-design/colors';
|
||||||
ArrowRightOutlined,
|
import { PauseOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
||||||
IeSquareFilled,
|
import { Button, Popover, Row, Select } from 'antd';
|
||||||
PauseOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
PlaySquareFilled,
|
|
||||||
ReloadOutlined,
|
|
||||||
StopFilled,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
||||||
import { Button, Card, Popover, Row, Select, Typography } from 'antd';
|
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
|
||||||
import { LiveTail } from 'api/logs/livetail';
|
import { LiveTail } from 'api/logs/livetail';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { debounce, throttle } from 'lodash-es';
|
import { throttle } from 'lodash-es';
|
||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import {
|
import {
|
||||||
@ -32,7 +16,7 @@ import {
|
|||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
import { TLogsLiveTailState } from 'types/api/logs/liveTail';
|
import { TLogsLiveTailState } from 'types/api/logs/liveTail';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import OptionIcon from './OptionIcon';
|
import OptionIcon from './OptionIcon';
|
||||||
import { TimePickerCard, TimePickerSelect } from './styles';
|
import { TimePickerCard, TimePickerSelect } from './styles';
|
||||||
@ -66,7 +50,7 @@ const TIME_PICKER_OPTIONS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function LogLiveTail() {
|
function LogLiveTail(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
liveTail,
|
liveTail,
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
@ -75,15 +59,16 @@ function LogLiveTail() {
|
|||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const handleLiveTail = (toggleState: TLogsLiveTailState) => {
|
const handleLiveTail = (toggleState: TLogsLiveTailState): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOGGLE_LIVE_TAIL,
|
type: TOGGLE_LIVE_TAIL,
|
||||||
payload: toggleState,
|
payload: toggleState,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const batchedEventsRef = useRef([]);
|
const batchedEventsRef = useRef<Record<string, unknown>[]>([]);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
const pushLiveLog = useCallback(
|
const pushLiveLog = useCallback(
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -96,46 +81,42 @@ function LogLiveTail() {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const batchLiveLog = (e) => {
|
const batchLiveLog = (e: { data: string }): void => {
|
||||||
// console.log('EVENT BATCHED');
|
// console.log('EVENT BATCHED');
|
||||||
batchedEventsRef.current.push(JSON.parse(e.data));
|
batchedEventsRef.current.push(JSON.parse(e.data as string) as never);
|
||||||
pushLiveLog();
|
pushLiveLog();
|
||||||
};
|
};
|
||||||
|
|
||||||
// This ref depicts thats whether the live tail is played from paused state or not.
|
// This ref depicts thats whether the live tail is played from paused state or not.
|
||||||
const liveTailSourceRef = useRef(null);
|
const liveTailSourceRef = useRef<EventSource | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (liveTail === 'PLAYING') {
|
if (liveTail === 'PLAYING') {
|
||||||
// console.log('Starting Live Tail', logs.length);
|
// console.log('Starting Live Tail', logs.length);
|
||||||
const timeStamp = dayjs().subtract(liveTailStartRange, 'minute').valueOf();
|
const timeStamp = dayjs().subtract(liveTailStartRange, 'minute').valueOf();
|
||||||
const queryParams = new URLSearchParams({
|
const queryParams = new URLSearchParams({
|
||||||
...(queryString ? { q: queryString } : {}),
|
...(queryString ? { q: queryString } : {}),
|
||||||
timestampStart: timeStamp * 1e6,
|
timestampStart: (timeStamp * 1e6) as never,
|
||||||
...(liveTailSourceRef.current && logs.length > 0
|
...(liveTailSourceRef.current && logs.length > 0
|
||||||
? {
|
? {
|
||||||
idGt: logs[0].id,
|
idGt: logs[0].id,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
});
|
});
|
||||||
const source = LiveTail(queryParams.toString());
|
const source = LiveTail(queryParams.toString());
|
||||||
liveTailSourceRef.current = source;
|
liveTailSourceRef.current = source;
|
||||||
source.onmessage = function (e) {
|
source.onmessage = function connectionMessage(e): void {
|
||||||
// pushLiveLog(e)
|
|
||||||
batchLiveLog(e);
|
batchLiveLog(e);
|
||||||
};
|
};
|
||||||
source.onopen = function (event) {
|
// source.onopen = function connectionOpen(): void { };
|
||||||
// console.log('open event');
|
source.onerror = function connectionError(event: unknown): void {
|
||||||
// console.log(event);
|
console.error(event);
|
||||||
};
|
|
||||||
source.onerror = function (event) {
|
|
||||||
// console.log(event);
|
|
||||||
source.close();
|
source.close();
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOGGLE_LIVE_TAIL,
|
type: TOGGLE_LIVE_TAIL,
|
||||||
payload: false,
|
payload: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
} else if (liveTailSourceRef.current) {
|
} else if (liveTailSourceRef.current && liveTailSourceRef.current.close) {
|
||||||
liveTailSourceRef.current?.close();
|
liveTailSourceRef.current?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +125,7 @@ function LogLiveTail() {
|
|||||||
}
|
}
|
||||||
}, [liveTail]);
|
}, [liveTail]);
|
||||||
|
|
||||||
const handleLiveTailStart = () => {
|
const handleLiveTailStart = (): void => {
|
||||||
handleLiveTail('PLAYING');
|
handleLiveTail('PLAYING');
|
||||||
if (!liveTailSourceRef.current) {
|
if (!liveTailSourceRef.current) {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -158,7 +139,7 @@ function LogLiveTail() {
|
|||||||
<TimePickerSelect
|
<TimePickerSelect
|
||||||
disabled={liveTail === 'PLAYING'}
|
disabled={liveTail === 'PLAYING'}
|
||||||
value={liveTailStartRange}
|
value={liveTailStartRange}
|
||||||
onChange={(value) => {
|
onChange={(value): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_LIVE_TAIL_START_TIME,
|
type: SET_LIVE_TAIL_START_TIME,
|
||||||
payload: value,
|
payload: value,
|
||||||
@ -183,7 +164,7 @@ function LogLiveTail() {
|
|||||||
{liveTail === 'PLAYING' ? (
|
{liveTail === 'PLAYING' ? (
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => handleLiveTail('PAUSED')}
|
onClick={(): void => handleLiveTail('PAUSED')}
|
||||||
title="Pause live tail"
|
title="Pause live tail"
|
||||||
style={{ background: green[6] }}
|
style={{ background: green[6] }}
|
||||||
>
|
>
|
||||||
@ -201,7 +182,7 @@ function LogLiveTail() {
|
|||||||
{liveTail !== 'STOPPED' && (
|
{liveTail !== 'STOPPED' && (
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
onClick={() => handleLiveTail('STOPPED')}
|
onClick={(): void => handleLiveTail('STOPPED')}
|
||||||
title="Exit live tail"
|
title="Exit live tail"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -15,7 +15,10 @@ import { GetLogsFields } from 'store/actions/logs/getFields';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
||||||
|
|
||||||
function Logs({ getLogsFields }) {
|
interface LogsProps {
|
||||||
|
getLogsFields: VoidFunction;
|
||||||
|
}
|
||||||
|
function Logs({ getLogsFields }: LogsProps): JSX.Element {
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
|
||||||
const urlQuery = useMemo(() => {
|
const urlQuery = useMemo(() => {
|
||||||
@ -29,7 +32,7 @@ function Logs({ getLogsFields }) {
|
|||||||
type: SET_SEARCH_QUERY_STRING,
|
type: SET_SEARCH_QUERY_STRING,
|
||||||
payload: urlQuery.get('q'),
|
payload: urlQuery.get('q'),
|
||||||
});
|
});
|
||||||
}, [dispatch]);
|
}, [dispatch, urlQuery]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getLogsFields();
|
getLogsFields();
|
||||||
@ -39,16 +42,16 @@ function Logs({ getLogsFields }) {
|
|||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<Row style={{ justifyContent: 'center', alignItems: 'center' }}>
|
<Row style={{ justifyContent: 'center', alignItems: 'center' }}>
|
||||||
<SearchFilter />
|
<SearchFilter />
|
||||||
<Divider type='vertical' style={{ height: '2rem' }} />
|
<Divider type="vertical" style={{ height: '2rem' }} />
|
||||||
<LogLiveTail />
|
<LogLiveTail />
|
||||||
</Row>
|
</Row>
|
||||||
<LogsAggregate />
|
<LogsAggregate />
|
||||||
<LogControls />
|
<LogControls />
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: 0 }} />
|
||||||
<Row gutter={20} style={{ flexWrap: 'nowrap' }}>
|
<Row gutter={20} style={{ flexWrap: 'nowrap' }}>
|
||||||
<LogsFilters flex="450px" />
|
<LogsFilters />
|
||||||
<Divider type="vertical" style={{ height: '100%', margin: 0 }} />
|
<Divider type="vertical" style={{ height: '100%', margin: 0 }} />
|
||||||
<LogsTable flex="auto" />
|
<LogsTable />
|
||||||
</Row>
|
</Row>
|
||||||
<LogDetailedView />
|
<LogDetailedView />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,42 +1,41 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { blue } from '@ant-design/colors';
|
import { blue } from '@ant-design/colors';
|
||||||
import Graph from 'components/Graph';
|
import Graph from 'components/Graph';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import getStep from 'lib/getStep';
|
import getStep from 'lib/getStep';
|
||||||
import React, { memo, useEffect, useRef } from 'react';
|
import React, { memo, useEffect, useRef } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
function LogsAggregate({ getLogsAggregate }) {
|
interface LogsAggregateProps {
|
||||||
|
getLogsAggregate: (arg0: Parameters<typeof getLogsAggregate>[0]) => void;
|
||||||
|
}
|
||||||
|
function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
logs,
|
|
||||||
logLinesPerPage,
|
|
||||||
idEnd,
|
idEnd,
|
||||||
idStart,
|
idStart,
|
||||||
isLoading,
|
|
||||||
isLoadingAggregate,
|
isLoadingAggregate,
|
||||||
logsAggregate,
|
logsAggregate,
|
||||||
liveTail,
|
liveTail,
|
||||||
liveTailStartRange,
|
liveTailStartRange,
|
||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const reFetchIntervalRef = useRef(null);
|
const reFetchIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log('LIVE TAIL LOG AGG', liveTail)
|
|
||||||
switch (liveTail) {
|
switch (liveTail) {
|
||||||
case 'STOPPED': {
|
case 'STOPPED': {
|
||||||
if (reFetchIntervalRef.current) {
|
if (reFetchIntervalRef.current) {
|
||||||
@ -59,7 +58,7 @@ function LogsAggregate({ getLogsAggregate }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'PLAYING': {
|
case 'PLAYING': {
|
||||||
const aggregateCall = () => {
|
const aggregateCall = (): void => {
|
||||||
const startTime =
|
const startTime =
|
||||||
dayjs().subtract(liveTailStartRange, 'minute').valueOf() * 1e6;
|
dayjs().subtract(liveTailStartRange, 'minute').valueOf() * 1e6;
|
||||||
const endTime = dayjs().valueOf() * 1e6;
|
const endTime = dayjs().valueOf() * 1e6;
|
||||||
@ -72,17 +71,15 @@ function LogsAggregate({ getLogsAggregate }) {
|
|||||||
inputFormat: 'ns',
|
inputFormat: 'ns',
|
||||||
}),
|
}),
|
||||||
q: queryString,
|
q: queryString,
|
||||||
...(idStart ? {idGt: idStart } : {}),
|
...(idStart ? { idGt: idStart } : {}),
|
||||||
...(idEnd ? { idLt: idEnd } : {}),
|
...(idEnd ? { idLt: idEnd } : {}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
aggregateCall();
|
aggregateCall();
|
||||||
reFetchIntervalRef.current = setInterval(aggregateCall, 60000);
|
reFetchIntervalRef.current = setInterval(aggregateCall, 60000);
|
||||||
// console.log('LA Play', reFetchIntervalRef.current);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'PAUSED': {
|
case 'PAUSED': {
|
||||||
// console.log('LA Pause', reFetchIntervalRef.current);
|
|
||||||
if (reFetchIntervalRef.current) {
|
if (reFetchIntervalRef.current) {
|
||||||
clearInterval(reFetchIntervalRef.current);
|
clearInterval(reFetchIntervalRef.current);
|
||||||
}
|
}
|
||||||
@ -98,7 +95,6 @@ function LogsAggregate({ getLogsAggregate }) {
|
|||||||
labels: logsAggregate.map((s) => new Date(s.timestamp / 1000000)),
|
labels: logsAggregate.map((s) => new Date(s.timestamp / 1000000)),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
// label: 'Span Count',
|
|
||||||
data: logsAggregate.map((s) => s.value),
|
data: logsAggregate.map((s) => s.value),
|
||||||
backgroundColor: blue[4],
|
backgroundColor: blue[4],
|
||||||
},
|
},
|
||||||
@ -123,7 +119,9 @@ function LogsAggregate({ getLogsAggregate }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import {
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
CloseCircleFilled,
|
|
||||||
CloseOutlined,
|
|
||||||
LoadingOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Button, Popover, Spin } from 'antd';
|
import { Button, Popover, Spin } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import React, { useState } from 'react';
|
||||||
import React, { useRef, useState } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useHover, useHoverDirty } from 'react-use';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { Field } from './styles';
|
import { Field } from './styles';
|
||||||
|
|
||||||
|
interface FieldItemProps {
|
||||||
|
name: string;
|
||||||
|
buttonIcon: React.ReactNode;
|
||||||
|
buttonOnClick: (arg0: Record<string, unknown>) => void;
|
||||||
|
fieldData: Record<string, never>;
|
||||||
|
fieldIndex: number;
|
||||||
|
isLoading: boolean;
|
||||||
|
iconHoverText: string;
|
||||||
|
}
|
||||||
export function FieldItem({
|
export function FieldItem({
|
||||||
name,
|
name,
|
||||||
buttonIcon,
|
buttonIcon,
|
||||||
@ -20,16 +23,16 @@ export function FieldItem({
|
|||||||
fieldData,
|
fieldData,
|
||||||
fieldIndex,
|
fieldIndex,
|
||||||
isLoading,
|
isLoading,
|
||||||
iconHoverText
|
iconHoverText,
|
||||||
}) {
|
}: FieldItemProps): JSX.Element {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
onMouseEnter={() => {
|
onMouseEnter={(): void => {
|
||||||
setIsHovered(true);
|
setIsHovered(true);
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={(): void => setIsHovered(false)}
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
>
|
>
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
@ -43,7 +46,7 @@ export function FieldItem({
|
|||||||
type="text"
|
type="text"
|
||||||
size="small"
|
size="small"
|
||||||
icon={buttonIcon}
|
icon={buttonIcon}
|
||||||
onClick={() => buttonOnClick({ fieldData, fieldIndex })}
|
onClick={(): void => buttonOnClick({ fieldData, fieldIndex })}
|
||||||
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
|
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
|
/* eslint-disable react/no-array-index-key */
|
||||||
import { red } from '@ant-design/colors';
|
import { red } from '@ant-design/colors';
|
||||||
import {
|
import { CloseOutlined, PlusCircleFilled } from '@ant-design/icons';
|
||||||
CloseCircleFilled,
|
|
||||||
CloseOutlined,
|
|
||||||
PlusCircleFilled,
|
|
||||||
PlusCircleOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
||||||
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
||||||
@ -17,34 +13,40 @@ import { ThunkDispatch } from 'redux-thunk';
|
|||||||
import { GetLogsFields } from 'store/actions/logs/getFields';
|
import { GetLogsFields } from 'store/actions/logs/getFields';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { IInterestingFields, ISelectedFields } from 'types/api/logs/fields';
|
||||||
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { FieldItem } from './FieldItem';
|
import { FieldItem } from './FieldItem';
|
||||||
import {
|
import { CategoryContainer, Container, FieldContainer } from './styles';
|
||||||
CategoryContainer,
|
|
||||||
Container,
|
|
||||||
ExtractField,
|
|
||||||
Field,
|
|
||||||
FieldContainer,
|
|
||||||
} from './styles';
|
|
||||||
|
|
||||||
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
|
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
|
||||||
|
|
||||||
function LogsFilters({ getLogsFields }) {
|
interface LogsFiltersProps {
|
||||||
|
getLogsFields: () => void;
|
||||||
|
}
|
||||||
|
function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
fields: { interesting, selected },
|
fields: { interesting, selected },
|
||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
const [selectedFieldLoading, setSelectedFieldLoading] = useState([]);
|
const [selectedFieldLoading, setSelectedFieldLoading] = useState<number[]>([]);
|
||||||
const [interestingFieldLoading, setInterestingFieldLoading] = useState([]);
|
const [interestingFieldLoading, setInterestingFieldLoading] = useState<
|
||||||
|
number[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const [filterValuesInput, setFilterValuesInput] = useState('');
|
const [filterValuesInput, setFilterValuesInput] = useState('');
|
||||||
const handleSearch = (e) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
setFilterValuesInput(e.target.value);
|
setFilterValuesInput((e.target as HTMLInputElement).value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddInterestingToSelected = async ({ fieldData, fieldIndex }) => {
|
const handleAddInterestingToSelected = async ({
|
||||||
setInterestingFieldLoading((prevState) => {
|
fieldData,
|
||||||
|
fieldIndex,
|
||||||
|
}: {
|
||||||
|
fieldData: IInterestingFields;
|
||||||
|
fieldIndex: number;
|
||||||
|
}): Promise<void> => {
|
||||||
|
setInterestingFieldLoading((prevState: number[]) => {
|
||||||
prevState.push(fieldIndex);
|
prevState.push(fieldIndex);
|
||||||
return [...prevState];
|
return [...prevState];
|
||||||
});
|
});
|
||||||
@ -59,7 +61,13 @@ function LogsFilters({ getLogsFields }) {
|
|||||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const handleRemoveSelectedField = async ({ fieldData, fieldIndex }) => {
|
const handleRemoveSelectedField = async ({
|
||||||
|
fieldData,
|
||||||
|
fieldIndex,
|
||||||
|
}: {
|
||||||
|
fieldData: ISelectedFields;
|
||||||
|
fieldIndex: number;
|
||||||
|
}): Promise<void> => {
|
||||||
setSelectedFieldLoading((prevState) => {
|
setSelectedFieldLoading((prevState) => {
|
||||||
prevState.push(fieldIndex);
|
prevState.push(fieldIndex);
|
||||||
return [...prevState];
|
return [...prevState];
|
||||||
@ -77,7 +85,7 @@ function LogsFilters({ getLogsFields }) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container flex="450px">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Values"
|
placeholder="Filter Values"
|
||||||
onInput={handleSearch}
|
onInput={handleSearch}
|
||||||
@ -93,14 +101,14 @@ function LogsFilters({ getLogsFields }) {
|
|||||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||||
.map((field, idx) => (
|
.map((field, idx) => (
|
||||||
<FieldItem
|
<FieldItem
|
||||||
key={field + idx}
|
key={`${JSON.stringify(field)}-${idx}`}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
fieldData={field}
|
fieldData={field as never}
|
||||||
fieldIndex={idx}
|
fieldIndex={idx}
|
||||||
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
|
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
|
||||||
buttonOnClick={
|
buttonOnClick={
|
||||||
!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
|
(!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
|
||||||
handleRemoveSelectedField
|
handleRemoveSelectedField) as never
|
||||||
}
|
}
|
||||||
isLoading={selectedFieldLoading.includes(idx)}
|
isLoading={selectedFieldLoading.includes(idx)}
|
||||||
iconHoverText="Remove from Selected Fields"
|
iconHoverText="Remove from Selected Fields"
|
||||||
@ -115,12 +123,12 @@ function LogsFilters({ getLogsFields }) {
|
|||||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||||
.map((field, idx) => (
|
.map((field, idx) => (
|
||||||
<FieldItem
|
<FieldItem
|
||||||
key={field + idx}
|
key={`${JSON.stringify(field)}-${idx}`}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
fieldData={field}
|
fieldData={field as never}
|
||||||
fieldIndex={idx}
|
fieldIndex={idx}
|
||||||
buttonIcon={<PlusCircleFilled />}
|
buttonIcon={<PlusCircleFilled />}
|
||||||
buttonOnClick={handleAddInterestingToSelected}
|
buttonOnClick={handleAddInterestingToSelected as never}
|
||||||
isLoading={interestingFieldLoading.includes(idx)}
|
isLoading={interestingFieldLoading.includes(idx)}
|
||||||
iconHoverText="Add to Selected Fields"
|
iconHoverText="Add to Selected Fields"
|
||||||
/>
|
/>
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function FieldKey({ name, type }) {
|
interface FieldKeyProps {
|
||||||
return (
|
name: string;
|
||||||
<span style={{ margin: '0.25rem 0', display: 'flex', gap: '0.5rem' }}>
|
type: string;
|
||||||
<Typography.Text>{name}</Typography.Text>
|
}
|
||||||
<Typography.Text type="secondary" italic>
|
|
||||||
{type}
|
function FieldKey({ name, type }: FieldKeyProps): JSX.Element {
|
||||||
</Typography.Text>
|
return (
|
||||||
</span>
|
<span style={{ margin: '0.25rem 0', display: 'flex', gap: '0.5rem' }}>
|
||||||
);
|
<Typography.Text>{name}</Typography.Text>
|
||||||
|
<Typography.Text type="secondary" italic>
|
||||||
|
{type}
|
||||||
|
</Typography.Text>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FieldKey;
|
export default FieldKey;
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
/* eslint-disable sonarjs/no-identical-functions */
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
/* eslint-disable react/no-array-index-key */
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Button, Input, Select, Typography } from 'antd';
|
import { Button, Input, Select } from 'antd';
|
||||||
import CategoryHeading from 'components/Logs/CategoryHeading';
|
import CategoryHeading from 'components/Logs/CategoryHeading';
|
||||||
import {
|
import {
|
||||||
ConditionalOperators,
|
ConditionalOperators,
|
||||||
QueryOperatorsMultiVal,
|
QueryOperatorsMultiVal,
|
||||||
QueryOperatorsSingleVal,
|
QueryOperatorsSingleVal,
|
||||||
QueryTypes,
|
|
||||||
} from 'lib/logql/tokens';
|
} from 'lib/logql/tokens';
|
||||||
import { chunk, cloneDeep, debounce, flatten } from 'lodash-es';
|
import { flatten } from 'lodash-es';
|
||||||
import React, {
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useHoverDirty, useLocation } from 'react-use';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import FieldKey from '../FieldKey';
|
import FieldKey from '../FieldKey';
|
||||||
@ -26,12 +24,24 @@ import { QueryConditionContainer, QueryFieldContainer } from '../styles';
|
|||||||
import { createParsedQueryStructure } from '../utils';
|
import { createParsedQueryStructure } from '../utils';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
function QueryField({ query, queryIndex, onUpdate, onDelete }) {
|
interface QueryFieldProps {
|
||||||
|
query: { value: string | string[]; type: string }[];
|
||||||
|
queryIndex: number;
|
||||||
|
onUpdate: (query: unknown, queryIndex: number) => void;
|
||||||
|
onDelete: (queryIndex: number) => void;
|
||||||
|
}
|
||||||
|
function QueryField({
|
||||||
|
query,
|
||||||
|
queryIndex,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
}: QueryFieldProps): JSX.Element | null {
|
||||||
const {
|
const {
|
||||||
fields: { selected },
|
fields: { selected },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
|
|
||||||
const getFieldType = (inputKey) => {
|
const getFieldType = (inputKey: string): string => {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
for (const selectedField of selected) {
|
for (const selectedField of selected) {
|
||||||
if (inputKey === selectedField.name) {
|
if (inputKey === selectedField.name) {
|
||||||
return selectedField.type;
|
return selectedField.type;
|
||||||
@ -39,11 +49,10 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
const fieldType = useMemo(() => getFieldType(query[0].value), [
|
const fieldType = useMemo(() => getFieldType(query[0].value as string), [
|
||||||
query,
|
query,
|
||||||
selected,
|
|
||||||
]);
|
]);
|
||||||
const handleChange = (qIdx, value) => {
|
const handleChange = (qIdx: number, value: string): void => {
|
||||||
query[qIdx].value = value || '';
|
query[qIdx].value = value || '';
|
||||||
|
|
||||||
if (qIdx === 1) {
|
if (qIdx === 1) {
|
||||||
@ -51,16 +60,17 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
|
|||||||
if (!Array.isArray(query[2].value)) {
|
if (!Array.isArray(query[2].value)) {
|
||||||
query[2].value = [];
|
query[2].value = [];
|
||||||
}
|
}
|
||||||
} else if (Object.values(QueryOperatorsSingleVal).includes(value)) {
|
} else if (
|
||||||
if (Array.isArray(query[2].value)) {
|
Object.values(QueryOperatorsSingleVal).includes(value) &&
|
||||||
query[2].value = '';
|
Array.isArray(query[2].value)
|
||||||
}
|
) {
|
||||||
|
query[2].value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onUpdate(query, queryIndex);
|
onUpdate(query, queryIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = (): void => {
|
||||||
onDelete(queryIndex);
|
onDelete(queryIndex);
|
||||||
};
|
};
|
||||||
if (!Array.isArray(query)) {
|
if (!Array.isArray(query)) {
|
||||||
@ -72,39 +82,43 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
|
|||||||
style={{ ...(queryIndex === 0 && { gridColumnStart: 2 }) }}
|
style={{ ...(queryIndex === 0 && { gridColumnStart: 2 }) }}
|
||||||
>
|
>
|
||||||
<div style={{ flex: 1, minWidth: 100 }}>
|
<div style={{ flex: 1, minWidth: 100 }}>
|
||||||
<FieldKey name={query[0] && query[0].value} type={fieldType} />
|
<FieldKey name={(query[0] && query[0].value) as string} type={fieldType} />
|
||||||
</div>
|
</div>
|
||||||
<Select
|
<Select
|
||||||
defaultActiveFirstOption={false}
|
defaultActiveFirstOption={false}
|
||||||
placeholder="Select Operator"
|
placeholder="Select Operator"
|
||||||
defaultValue={
|
defaultValue={
|
||||||
query[1] && query[1].value ? query[1].value.toUpperCase() : null
|
query[1] && query[1].value
|
||||||
|
? (query[1].value as string).toUpperCase()
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
onChange={(e) => handleChange(1, e)}
|
onChange={(e): void => handleChange(1, e)}
|
||||||
style={{ minWidth: 150 }}
|
style={{ minWidth: 150 }}
|
||||||
>
|
>
|
||||||
{Object.values({
|
{Object.values({
|
||||||
...QueryOperatorsMultiVal,
|
...QueryOperatorsMultiVal,
|
||||||
...QueryOperatorsSingleVal,
|
...QueryOperatorsSingleVal,
|
||||||
}).map((cond) => (
|
}).map((cond) => (
|
||||||
<Option key={cond} value={cond} label={cond} />
|
<Option key={cond} value={cond} label={cond}>
|
||||||
|
{cond}
|
||||||
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
<div style={{ flex: 2 }}>
|
<div style={{ flex: 2 }}>
|
||||||
{Array.isArray(query[2].value) ||
|
{Array.isArray(query[2].value) ||
|
||||||
Object.values(QueryOperatorsMultiVal).some(
|
Object.values(QueryOperatorsMultiVal).some(
|
||||||
(op) => op.toUpperCase() === query[1].value?.toUpperCase(),
|
(op) => op.toUpperCase() === (query[1].value as string)?.toUpperCase(),
|
||||||
) ? (
|
) ? (
|
||||||
<Select
|
<Select
|
||||||
mode="tags"
|
mode="tags"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
onChange={(e) => handleChange(2, e)}
|
onChange={(e): void => handleChange(2, e as never)}
|
||||||
defaultValue={(query[2] && query[2].value) || []}
|
defaultValue={(query[2] && query[2].value) || []}
|
||||||
notFoundContent={null}
|
notFoundContent={null}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => handleChange(2, e.target.value)}
|
onChange={(e): void => handleChange(2, e.target.value)}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
defaultValue={query[2] && query[2].value}
|
defaultValue={query[2] && query[2].value}
|
||||||
/>
|
/>
|
||||||
@ -120,24 +134,39 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
|
|||||||
</QueryFieldContainer>
|
</QueryFieldContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function QueryConditionField({ query, queryIndex, onUpdate }) {
|
|
||||||
|
interface QueryConditionFieldProps {
|
||||||
|
query: { value: string | string[]; type: string }[];
|
||||||
|
queryIndex: number;
|
||||||
|
onUpdate: (arg0: unknown, arg1: number) => void;
|
||||||
|
}
|
||||||
|
function QueryConditionField({
|
||||||
|
query,
|
||||||
|
queryIndex,
|
||||||
|
onUpdate,
|
||||||
|
}: QueryConditionFieldProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<QueryConditionContainer>
|
<QueryConditionContainer>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={query.value.toUpperCase()}
|
defaultValue={
|
||||||
onChange={(e) => {
|
(query as any).value &&
|
||||||
|
(((query as any)?.value as any) as string).toUpperCase()
|
||||||
|
}
|
||||||
|
onChange={(e): void => {
|
||||||
onUpdate({ ...query, value: e }, queryIndex);
|
onUpdate({ ...query, value: e }, queryIndex);
|
||||||
}}
|
}}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
>
|
>
|
||||||
{Object.values(ConditionalOperators).map((cond) => (
|
{Object.values(ConditionalOperators).map((cond) => (
|
||||||
<Option key={cond} value={cond} label={cond} />
|
<Option key={cond} value={cond} label={cond}>
|
||||||
|
{cond}
|
||||||
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</QueryConditionContainer>
|
</QueryConditionContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const hashCode = (s) => {
|
const hashCode = (s: string): string => {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
@ -149,14 +178,20 @@ const hashCode = (s) => {
|
|||||||
)}`;
|
)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function QueryBuilder({ updateParsedQuery }) {
|
function QueryBuilder({
|
||||||
|
updateParsedQuery,
|
||||||
|
}: {
|
||||||
|
updateParsedQuery: (arg0: unknown) => void;
|
||||||
|
}): JSX.Element {
|
||||||
const {
|
const {
|
||||||
searchFilter: { parsedQuery },
|
searchFilter: { parsedQuery },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
|
|
||||||
const keyPrefixRef = useRef(hashCode(JSON.stringify(parsedQuery)));
|
const keyPrefixRef = useRef(hashCode(JSON.stringify(parsedQuery)));
|
||||||
const [keyPrefix, setKeyPrefix] = useState(keyPrefixRef.current);
|
const [keyPrefix, setKeyPrefix] = useState(keyPrefixRef.current);
|
||||||
const generatedQueryStructure = createParsedQueryStructure(parsedQuery);
|
const generatedQueryStructure = createParsedQueryStructure(
|
||||||
|
parsedQuery as never[],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const incomingHashCode = hashCode(JSON.stringify(parsedQuery));
|
const incomingHashCode = hashCode(JSON.stringify(parsedQuery));
|
||||||
@ -166,18 +201,19 @@ function QueryBuilder({ updateParsedQuery }) {
|
|||||||
}
|
}
|
||||||
}, [parsedQuery]);
|
}, [parsedQuery]);
|
||||||
|
|
||||||
|
const handleUpdate = (
|
||||||
|
query: { value: string | string[]; type: string }[],
|
||||||
const handleUpdate = (query, queryIndex): void => {
|
queryIndex: number,
|
||||||
|
): void => {
|
||||||
const updatedParsedQuery = generatedQueryStructure;
|
const updatedParsedQuery = generatedQueryStructure;
|
||||||
updatedParsedQuery[queryIndex] = query;
|
updatedParsedQuery[queryIndex] = query as never;
|
||||||
|
|
||||||
const flatParsedQuery = flatten(updatedParsedQuery).filter((q) => q.value);
|
const flatParsedQuery = flatten(updatedParsedQuery).filter((q) => q.value);
|
||||||
keyPrefixRef.current = hashCode(JSON.stringify(flatParsedQuery));
|
keyPrefixRef.current = hashCode(JSON.stringify(flatParsedQuery));
|
||||||
updateParsedQuery(flatParsedQuery);
|
updateParsedQuery(flatParsedQuery);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (queryIndex) => {
|
const handleDelete = (queryIndex: number): void => {
|
||||||
const updatedParsedQuery = generatedQueryStructure;
|
const updatedParsedQuery = generatedQueryStructure;
|
||||||
updatedParsedQuery.splice(queryIndex - 1, 2);
|
updatedParsedQuery.splice(queryIndex - 1, 2);
|
||||||
|
|
||||||
@ -186,15 +222,15 @@ function QueryBuilder({ updateParsedQuery }) {
|
|||||||
updateParsedQuery(flatParsedQuery);
|
updateParsedQuery(flatParsedQuery);
|
||||||
};
|
};
|
||||||
|
|
||||||
const QueryUI = () =>
|
const QueryUI = (): JSX.Element | JSX.Element[] =>
|
||||||
generatedQueryStructure.map((query, idx) => {
|
generatedQueryStructure.map((query, idx) => {
|
||||||
if (Array.isArray(query))
|
if (Array.isArray(query))
|
||||||
return (
|
return (
|
||||||
<QueryField
|
<QueryField
|
||||||
key={keyPrefix + idx}
|
key={keyPrefix + idx}
|
||||||
query={query}
|
query={query as never}
|
||||||
queryIndex={idx}
|
queryIndex={idx}
|
||||||
onUpdate={handleUpdate}
|
onUpdate={handleUpdate as never}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -204,7 +240,7 @@ function QueryBuilder({ updateParsedQuery }) {
|
|||||||
key={keyPrefix + idx}
|
key={keyPrefix + idx}
|
||||||
query={query}
|
query={query}
|
||||||
queryIndex={idx}
|
queryIndex={idx}
|
||||||
onUpdate={handleUpdate}
|
onUpdate={handleUpdate as never}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
import { Button, Typography } from 'antd';
|
import { Button } from 'antd';
|
||||||
import CategoryHeading from 'components/Logs/CategoryHeading';
|
import CategoryHeading from 'components/Logs/CategoryHeading';
|
||||||
import { map } from 'lodash-es';
|
import { map } from 'lodash-es';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { ADD_SEARCH_FIELD_QUERY_STRING } from 'types/actions/logs';
|
import { ADD_SEARCH_FIELD_QUERY_STRING } from 'types/actions/logs';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import FieldKey from './FieldKey';
|
import FieldKey from './FieldKey';
|
||||||
|
|
||||||
function SuggestedItem({ name, type }) {
|
interface SuggestedItemProps {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
function SuggestedItem({ name, type }: SuggestedItemProps): JSX.Element {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const addSuggestedField = () => {
|
const addSuggestedField = (): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ADD_SEARCH_FIELD_QUERY_STRING,
|
type: ADD_SEARCH_FIELD_QUERY_STRING,
|
||||||
payload: name,
|
payload: name,
|
||||||
@ -29,7 +33,7 @@ function SuggestedItem({ name, type }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Suggestions() {
|
function Suggestions(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
fields: { selected },
|
fields: { selected },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import QueryBuilder from './QueryBuilder/QueryBuilder';
|
import QueryBuilder from './QueryBuilder/QueryBuilder';
|
||||||
import Suggestions from './Suggestions';
|
import Suggestions from './Suggestions';
|
||||||
|
|
||||||
function SearchFields({updateParsedQuery}): JSX.Element {
|
interface SearchFieldsProps {
|
||||||
|
updateParsedQuery: () => void;
|
||||||
|
}
|
||||||
|
function SearchFields({ updateParsedQuery }: SearchFieldsProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<QueryBuilder updateParsedQuery={updateParsedQuery} />
|
<QueryBuilder updateParsedQuery={updateParsedQuery} />
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-ignore
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
import { QueryTypes } from 'lib/logql/tokens';
|
import { QueryTypes } from 'lib/logql/tokens';
|
||||||
|
|
||||||
export const queryKOVPair = () => [
|
export const queryKOVPair = () => [
|
||||||
@ -30,7 +34,7 @@ export const createParsedQueryStructure = (parsedQuery = []) => {
|
|||||||
cond = null;
|
cond = null;
|
||||||
qCtr = -1;
|
qCtr = -1;
|
||||||
}
|
}
|
||||||
let stagingArr = structuredArray[structuredArray.length - 1];
|
const stagingArr = structuredArray[structuredArray.length - 1];
|
||||||
const prevQuery =
|
const prevQuery =
|
||||||
Array.isArray(stagingArr) && qCtr >= 0 ? stagingArr[qCtr] : null;
|
Array.isArray(stagingArr) && qCtr >= 0 ? stagingArr[qCtr] : null;
|
||||||
|
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
import {
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
CloseCircleFilled,
|
import { CloseSquareOutlined } from '@ant-design/icons';
|
||||||
CloseCircleOutlined,
|
|
||||||
CloseSquareOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Button, Input } from 'antd';
|
import { Button, Input } from 'antd';
|
||||||
import useClickOutside from 'hooks/useClickOutside';
|
import useClickOutside from 'hooks/useClickOutside';
|
||||||
import getStep from 'lib/getStep';
|
import getStep from 'lib/getStep';
|
||||||
import { debounce, throttle } from 'lodash-es';
|
import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import React, {
|
|
||||||
memo,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useLayoutEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { useClickAway, useLocation } from 'react-use';
|
import { useLocation } from 'react-use';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { getLogs } from 'store/actions/logs/getLogs';
|
import { getLogs } from 'store/actions/logs/getLogs';
|
||||||
@ -26,7 +14,7 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
import { TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import SearchFields from './SearchFields';
|
import SearchFields from './SearchFields';
|
||||||
import { DropDownContainer } from './styles';
|
import { DropDownContainer } from './styles';
|
||||||
@ -34,7 +22,16 @@ import { useSearchParser } from './useSearchParser';
|
|||||||
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
|
||||||
function SearchFilter({ getLogs, getLogsAggregate }) {
|
interface SearchFilterProps {
|
||||||
|
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||||
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => ReturnType<typeof getLogsAggregate>;
|
||||||
|
}
|
||||||
|
function SearchFilter({
|
||||||
|
getLogs,
|
||||||
|
getLogsAggregate,
|
||||||
|
}: SearchFilterProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
queryString,
|
queryString,
|
||||||
updateParsedQuery,
|
updateParsedQuery,
|
||||||
@ -146,26 +143,29 @@ function SearchFilter({ getLogs, getLogsAggregate }) {
|
|||||||
<DropDownContainer>
|
<DropDownContainer>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
onClick={() => setShowDropDown(false)}
|
onClick={(): void => setShowDropDown(false)}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseSquareOutlined size="large" />
|
<CloseSquareOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<SearchFields updateParsedQuery={updateParsedQuery} />
|
<SearchFields updateParsedQuery={updateParsedQuery as never} />
|
||||||
</DropDownContainer>
|
</DropDownContainer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogs: (
|
||||||
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
|
props: Parameters<typeof getLogs>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
|
getLogsAggregate: (
|
||||||
|
props: Parameters<typeof getLogsAggregate>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { parseQuery, reverseParser } from 'lib/logql';
|
import { parseQuery, reverseParser } from 'lib/logql';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useCallback, useEffect, useReducer, useState } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import {
|
import {
|
||||||
SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
||||||
SET_SEARCH_QUERY_STRING,
|
SET_SEARCH_QUERY_STRING,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
export function useSearchParser() {
|
export function useSearchParser(): {
|
||||||
|
queryString: string;
|
||||||
|
parsedQuery: unknown;
|
||||||
|
updateParsedQuery: (arg0: unknown) => void;
|
||||||
|
updateQueryString: (arg0: unknown) => void;
|
||||||
|
} {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const {
|
const {
|
||||||
searchFilter: { parsedQuery, queryString },
|
searchFilter: { parsedQuery, queryString },
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
/* eslint-disable no-nested-ternary */
|
||||||
import { Card, Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { LiveTail } from 'api/logs/livetail';
|
|
||||||
import LogItem from 'components/Logs/LogItem';
|
import LogItem from 'components/Logs/LogItem';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { map } from 'lodash-es';
|
import { map } from 'lodash-es';
|
||||||
import React, { memo, useEffect, useRef } from 'react';
|
import React, { memo, useEffect } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { getLogs } from 'store/actions/logs/getLogs';
|
import { getLogs } from 'store/actions/logs/getLogs';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { PUSH_LIVE_TAIL_EVENT } from 'types/actions/logs';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { Container, Heading } from './styles';
|
import { Container, Heading } from './styles';
|
||||||
|
|
||||||
function LogsTable({ getLogs }) {
|
interface LogsTableProps {
|
||||||
|
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||||
|
}
|
||||||
|
function LogsTable({ getLogs }: LogsTableProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
logs,
|
logs,
|
||||||
@ -51,7 +52,7 @@ function LogsTable({ getLogs }) {
|
|||||||
return <Spinner height={20} tip="Getting Logs" />;
|
return <Spinner height={20} tip="Getting Logs" />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container flex="auto">
|
||||||
<Heading>
|
<Heading>
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
style={{
|
style={{
|
||||||
@ -74,7 +75,9 @@ function LogsTable({ getLogs }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
|
getLogs: (
|
||||||
|
props: Parameters<typeof getLogs>[0],
|
||||||
|
) => (dispatch: Dispatch<AppActions>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { grey } from '@ant-design/colors';
|
|
||||||
import { Card, Col } from 'antd';
|
import { Card, Col } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@ -9,7 +8,6 @@ export const Container = styled(Col)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const Heading = styled(Card)`
|
export const Heading = styled(Card)`
|
||||||
|
|
||||||
margin-bottom: 0.1rem;
|
margin-bottom: 0.1rem;
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
padding: 0.3rem 0.5rem;
|
padding: 0.3rem 0.5rem;
|
||||||
|
@ -49,7 +49,8 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
||||||
|
|
||||||
history.replace(
|
history.replace(
|
||||||
`${ROUTES.TRACE
|
`${
|
||||||
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -100,17 +101,12 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
||||||
|
|
||||||
history.replace(
|
history.replace(
|
||||||
`${ROUTES.TRACE
|
`${
|
||||||
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
console.log(getWidget([
|
|
||||||
{
|
|
||||||
query: `max(sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"${resourceAttributePromQLQuery}}[5m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."${resourceAttributePromQLQuery}}[5m]))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"${resourceAttributePromQLQuery}}[5m]))) < 1000 OR vector(0)`,
|
|
||||||
legend: 'Error Percentage',
|
|
||||||
},
|
|
||||||
]))
|
|
||||||
debugger;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
|
@ -18,7 +18,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.ERROR_DETAIL]: 'Errors',
|
[ROUTES.ERROR_DETAIL]: 'Errors',
|
||||||
[ROUTES.LIST_ALL_ALERT]: 'Alerts',
|
[ROUTES.LIST_ALL_ALERT]: 'Alerts',
|
||||||
[ROUTES.ALL_DASHBOARD]: 'Dashboard',
|
[ROUTES.ALL_DASHBOARD]: 'Dashboard',
|
||||||
[ROUTES.LOGS]: 'Logs'
|
[ROUTES.LOGS]: 'Logs',
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable sonarjs/no-duplicate-string */
|
||||||
/* eslint no-useless-escape: 0 */
|
/* eslint no-useless-escape: 0 */
|
||||||
|
|
||||||
const logqlQueries = [
|
const logqlQueries = [
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { logqlQueries } from 'lib/__fixtures__/logql';
|
import { logqlQueries } from 'lib/__fixtures__/logql';
|
||||||
import reverseParser from 'lib/logql/reverseParser';
|
import { reverseParser } from 'lib/logql/reverseParser';
|
||||||
|
|
||||||
describe('lib/logql/reverseParser', () => {
|
describe('lib/logql/reverseParser', () => {
|
||||||
test('reverse parse valid queries', () => {
|
test('reverse parse valid queries', () => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import splitter from 'lib/logql/splitter';
|
|
||||||
import { logqlQueries } from 'lib/__fixtures__/logql';
|
import { logqlQueries } from 'lib/__fixtures__/logql';
|
||||||
|
import { splitter } from 'lib/logql/splitter';
|
||||||
|
|
||||||
describe('lib/logql/splitter', () => {
|
describe('lib/logql/splitter', () => {
|
||||||
test('splitter valid quereies', () => {
|
test('splitter valid quereies', () => {
|
||||||
logqlQueries.forEach((queryObject) => {
|
logqlQueries.forEach((queryObject) => {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-ignore
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import {
|
||||||
|
ErrorConvertToFullText,
|
||||||
|
ErrorInvalidQueryPair,
|
||||||
|
} from 'lib/logql/errors';
|
||||||
import splitter from 'lib/logql/splitter';
|
import splitter from 'lib/logql/splitter';
|
||||||
import {
|
import {
|
||||||
ConditionalOperators,
|
ConditionalOperators,
|
||||||
@ -6,10 +13,6 @@ import {
|
|||||||
QueryOperatorsSingleVal,
|
QueryOperatorsSingleVal,
|
||||||
QueryTypes,
|
QueryTypes,
|
||||||
} from 'lib/logql/tokens';
|
} from 'lib/logql/tokens';
|
||||||
import {
|
|
||||||
ErrorConvertToFullText,
|
|
||||||
ErrorInvalidQueryPair,
|
|
||||||
} from 'lib/logql/errors';
|
|
||||||
|
|
||||||
const validateMultiValue = (queryToken: string): boolean => {
|
const validateMultiValue = (queryToken: string): boolean => {
|
||||||
const queryValues = [];
|
const queryValues = [];
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-ignore
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
export const reverseParser = (
|
export const reverseParser = (
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-ignore
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
export const splitter = (queryString) => {
|
export const splitter = (queryString: string): string[] => {
|
||||||
const splittedParts = [];
|
const splittedParts: string[] = [];
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let isBracketStart = false;
|
let isBracketStart = false;
|
||||||
let isQuoteStart = false;
|
let isQuoteStart = false;
|
||||||
@ -13,10 +15,6 @@ export const splitter = (queryString) => {
|
|||||||
for (let idx = 0; idx < queryString.length; idx += 1) {
|
for (let idx = 0; idx < queryString.length; idx += 1) {
|
||||||
const currentChar = queryString[idx];
|
const currentChar = queryString[idx];
|
||||||
|
|
||||||
// if (start === null) {
|
|
||||||
// start = idx;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (currentChar === ' ') {
|
if (currentChar === ' ') {
|
||||||
if (!isBracketStart && !isQuoteStart) {
|
if (!isBracketStart && !isQuoteStart) {
|
||||||
pushPart(idx);
|
pushPart(idx);
|
||||||
@ -48,7 +46,6 @@ export const splitter = (queryString) => {
|
|||||||
pushPart(queryString.length);
|
pushPart(queryString.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(splittedParts.filter(Boolean));
|
|
||||||
return splittedParts.map((s) => String.raw`${s}`).filter(Boolean);
|
return splittedParts.map((s) => String.raw`${s}`).filter(Boolean);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ export const ConditionalOperators = {
|
|||||||
OR: 'OR',
|
OR: 'OR',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const QueryTypes = {
|
export const QueryTypes = {
|
||||||
QUERY_KEY: 'QUERY_KEY',
|
QUERY_KEY: 'QUERY_KEY',
|
||||||
QUERY_OPERATOR: 'QUERY_OPERATOR',
|
QUERY_OPERATOR: 'QUERY_OPERATOR',
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
export const fieldSearchFilter = (searchSpace = '', currentValue = '') => {
|
export const fieldSearchFilter = (
|
||||||
|
searchSpace = '',
|
||||||
|
currentValue = '',
|
||||||
|
): boolean => {
|
||||||
if (!currentValue || !searchSpace) {
|
if (!currentValue || !searchSpace) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
export function FlatLogData(log: ILog): Record<string, never> {
|
export function FlatLogData(log: ILog): Record<string, unknown> {
|
||||||
let flattenLogObject: Record<string, never> = {};
|
const flattenLogObject: Record<string, unknown> = {};
|
||||||
|
|
||||||
Object.keys(log).forEach((key: string) => {
|
Object.keys(log).forEach((key: string): void => {
|
||||||
if (typeof log[key] !== 'object') {
|
if (typeof log[key as never] !== 'object') {
|
||||||
flattenLogObject[key] = log[key];
|
flattenLogObject[key] = log[key as never];
|
||||||
} else {
|
} else {
|
||||||
Object.keys(log[key]).forEach((childKey) => {
|
Object.keys(log[key as never]).forEach((childKey) => {
|
||||||
flattenLogObject[childKey] = log[key][childKey]
|
flattenLogObject[childKey] = log[key as never][childKey];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
export const generateFilterQuery = ({ fieldKey, fieldValue, type }) => {
|
import { QueryOperatorsMultiVal } from 'lib/logql/tokens';
|
||||||
|
|
||||||
|
type Keys = keyof typeof QueryOperatorsMultiVal;
|
||||||
|
type Values = typeof QueryOperatorsMultiVal[Keys];
|
||||||
|
|
||||||
|
interface GenerateFilterQueryParams {
|
||||||
|
fieldKey: string;
|
||||||
|
fieldValue: string;
|
||||||
|
type: Values;
|
||||||
|
}
|
||||||
|
export const generateFilterQuery = ({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
type,
|
||||||
|
}: GenerateFilterQueryParams): string => {
|
||||||
let generatedQueryString = `${fieldKey} ${type} `;
|
let generatedQueryString = `${fieldKey} ${type} `;
|
||||||
if (typeof fieldValue === 'number') {
|
if (typeof fieldValue === 'number') {
|
||||||
generatedQueryString += `(${fieldValue})`;
|
generatedQueryString += `(${fieldValue})`;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Logs from 'container/Logs';
|
import Logs from 'container/Logs';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function LogsHome() {
|
function LogsHome(): JSX.Element {
|
||||||
return <Logs />;
|
return <Logs />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import GetSearchFields from 'api/logs/GetSearchFields';
|
import GetSearchFields from 'api/logs/GetSearchFields';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GET_FIELDS, SET_FIELDS } from 'types/actions/logs';
|
import { SET_FIELDS } from 'types/actions/logs';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
|
||||||
|
|
||||||
export const AddToSelectedField = (): ((dispatch: Dispatch<AppActions>) => void) => {
|
export const AddToSelectedField = (): ((
|
||||||
return async (dispatch): void => {
|
dispatch: Dispatch<AppActions>,
|
||||||
|
) => void) => {
|
||||||
|
return async (dispatch): Promise<void> => {
|
||||||
const response = await GetSearchFields();
|
const response = await GetSearchFields();
|
||||||
|
if (response.payload)
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_FIELDS,
|
type: SET_FIELDS,
|
||||||
payload: response.payload,
|
payload: response.payload,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import GetSearchFields from 'api/logs/GetSearchFields';
|
import GetSearchFields from 'api/logs/GetSearchFields';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GET_FIELDS, SET_FIELDS } from 'types/actions/logs';
|
import { SET_FIELDS } from 'types/actions/logs';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
|
||||||
|
|
||||||
const IGNORED_SELECTED_FIELDS = ['timestamp'];
|
const IGNORED_SELECTED_FIELDS = ['timestamp'];
|
||||||
|
|
||||||
export const GetLogsFields = (): ((dispatch: Dispatch<AppActions>) => void) => {
|
export const GetLogsFields = (): ((dispatch: Dispatch<AppActions>) => void) => {
|
||||||
return async (dispatch): void => {
|
return async (dispatch): Promise<void> => {
|
||||||
const response = await GetSearchFields();
|
const response = await GetSearchFields();
|
||||||
if (response.payload) {
|
if (response.payload) {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -2,9 +2,12 @@ import GetLogs from 'api/logs/GetLogs';
|
|||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { SET_LOADING, SET_LOGS } from 'types/actions/logs';
|
import { SET_LOADING, SET_LOGS } from 'types/actions/logs';
|
||||||
|
import { Props } from 'types/api/logs/getLogs';
|
||||||
|
|
||||||
export const getLogs = (props): ((dispatch: Dispatch<AppActions>) => void) => {
|
export const getLogs = (
|
||||||
return async (dispatch): void => {
|
props: Props,
|
||||||
|
): ((dispatch: Dispatch<AppActions>) => void) => {
|
||||||
|
return async (dispatch): Promise<void> => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_LOADING,
|
type: SET_LOADING,
|
||||||
payload: true,
|
payload: true,
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import GetLogs from 'api/logs/GetLogs';
|
|
||||||
import GetLogsAggregate from 'api/logs/GetLogsAggregate';
|
import GetLogsAggregate from 'api/logs/GetLogsAggregate';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import {
|
import {
|
||||||
SET_LOADING,
|
|
||||||
SET_LOADING_AGGREGATE,
|
SET_LOADING_AGGREGATE,
|
||||||
SET_LOGS,
|
|
||||||
SET_LOGS_AGGREGATE_SERIES,
|
SET_LOGS_AGGREGATE_SERIES,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
|
import { Props } from 'types/api/logs/getLogsAggregate';
|
||||||
import { ILogsAggregate } from 'types/api/logs/logAggregate';
|
import { ILogsAggregate } from 'types/api/logs/logAggregate';
|
||||||
|
|
||||||
export const getLogsAggregate = (
|
export const getLogsAggregate = (
|
||||||
props,
|
props: Props,
|
||||||
): ((dispatch: Dispatch<AppActions>) => void) => {
|
): ((dispatch: Dispatch<AppActions>) => void) => {
|
||||||
return async (dispatch): void => {
|
return async (dispatch): Promise<void> => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_LOADING_AGGREGATE,
|
type: SET_LOADING_AGGREGATE,
|
||||||
payload: true,
|
payload: true,
|
||||||
|
@ -3,7 +3,7 @@ import { combineReducers } from 'redux';
|
|||||||
import appReducer from './app';
|
import appReducer from './app';
|
||||||
import dashboardReducer from './dashboard';
|
import dashboardReducer from './dashboard';
|
||||||
import globalTimeReducer from './global';
|
import globalTimeReducer from './global';
|
||||||
import LogsReducer from './logs';
|
import { LogsReducer } from './logs';
|
||||||
import metricsReducers from './metric';
|
import metricsReducers from './metric';
|
||||||
import { ServiceMapReducer } from './serviceMap';
|
import { ServiceMapReducer } from './serviceMap';
|
||||||
import traceReducer from './trace';
|
import traceReducer from './trace';
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
STOP_LIVE_TAIL,
|
STOP_LIVE_TAIL,
|
||||||
TOGGLE_LIVE_TAIL,
|
TOGGLE_LIVE_TAIL,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
import ILogsReducer from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
const initialState: ILogsReducer = {
|
const initialState: ILogsReducer = {
|
||||||
fields: {
|
fields: {
|
||||||
@ -39,33 +39,10 @@ const initialState: ILogsReducer = {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
isLoadingAggregate: false,
|
isLoadingAggregate: false,
|
||||||
logsAggregate: [],
|
logsAggregate: [],
|
||||||
detailedLog: null,
|
|
||||||
liveTail: 'STOPPED',
|
liveTail: 'STOPPED',
|
||||||
liveTailStartRange: 15,
|
liveTailStartRange: 15,
|
||||||
// detailedLog: {
|
selectedLogId: null,
|
||||||
// timestamp: 1659360016955270100,
|
detailedLog: null,
|
||||||
// id: '2CkBCauK8m3nkyKR19YhCw6WfvD',
|
|
||||||
// traceId: '',
|
|
||||||
// spanId: '',
|
|
||||||
// traceFlags: 0,
|
|
||||||
// severityText: '',
|
|
||||||
// severityNumber: 0,
|
|
||||||
// body:
|
|
||||||
// '49.207.215.17 - - [01/Aug/2022:13:20:16 +0000] "OPTIONS /api/v1/logs/fields HTTP/1.1" 200 23 "http://localhost:3301/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" "-"\n',
|
|
||||||
// resourcesString: {
|
|
||||||
// source: 'docker',
|
|
||||||
// },
|
|
||||||
// attributesString: {
|
|
||||||
// container_id:
|
|
||||||
// 'b3c6808322609f7671c18a09515d9c84909c873471b560da65c1afe0dfd933ea',
|
|
||||||
// log_file_path:
|
|
||||||
// '/var/lib/docker/containers/b3c6808322609f7671c18a09515d9c84909c873471b560da65c1afe0dfd933ea/b3c6808322609f7671c18a09515d9c84909c873471b560da65c1afe0dfd933ea-json.log',
|
|
||||||
// stream: 'stdout',
|
|
||||||
// time: '2022-08-01T13:20:16.955270245Z',
|
|
||||||
// },
|
|
||||||
// attributesInt: {},
|
|
||||||
// attributesFloat: {},
|
|
||||||
// },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LogsReducer = (
|
export const LogsReducer = (
|
||||||
@ -123,9 +100,12 @@ export const LogsReducer = (
|
|||||||
|
|
||||||
case ADD_SEARCH_FIELD_QUERY_STRING: {
|
case ADD_SEARCH_FIELD_QUERY_STRING: {
|
||||||
const updatedQueryString =
|
const updatedQueryString =
|
||||||
state.searchFilter.queryString +
|
state.searchFilter.queryString ||
|
||||||
(state.searchFilter.queryString.length > 0 ? ' and ' : '') +
|
`${
|
||||||
action.payload;
|
state.searchFilter.queryString && state.searchFilter.queryString.length > 0
|
||||||
|
? ' and '
|
||||||
|
: ''
|
||||||
|
}${action.payload}`;
|
||||||
|
|
||||||
const updatedParsedQuery = parseQuery(updatedQueryString);
|
const updatedParsedQuery = parseQuery(updatedQueryString);
|
||||||
return {
|
return {
|
||||||
|
@ -123,42 +123,6 @@ export interface SetLiveTailStartTime {
|
|||||||
type: typeof SET_LIVE_TAIL_START_TIME;
|
type: typeof SET_LIVE_TAIL_START_TIME;
|
||||||
payload: number;
|
payload: number;
|
||||||
}
|
}
|
||||||
// export interface GetServiceListLoading {
|
|
||||||
// type:
|
|
||||||
// | typeof GET_SERVICE_LIST_LOADING_START
|
|
||||||
// | typeof GET_INITIAL_APPLICATION_LOADING;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface GetServiceListError {
|
|
||||||
// type: typeof GET_SERVICE_LIST_ERROR | typeof GET_INITIAL_APPLICATION_ERROR;
|
|
||||||
// payload: {
|
|
||||||
// errorMessage: string;
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface GetInitialApplicationData {
|
|
||||||
// type: typeof GET_INTIAL_APPLICATION_DATA;
|
|
||||||
// payload: {
|
|
||||||
// topEndPoints: TopEndPoints[];
|
|
||||||
// // dbOverView: DBOverView[];
|
|
||||||
// // externalService: ExternalService[];
|
|
||||||
// // externalAverageDuration: ExternalAverageDuration[];
|
|
||||||
// // externalError: ExternalError[];
|
|
||||||
// serviceOverview: ServiceOverview[];
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface ResetInitialApplicationData {
|
|
||||||
// type: typeof RESET_INITIAL_APPLICATION_DATA;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export interface SetResourceAttributeQueries {
|
|
||||||
// type: typeof SET_RESOURCE_ATTRIBUTE_QUERIES;
|
|
||||||
// payload: {
|
|
||||||
// queries: IResourceAttributeQuery[];
|
|
||||||
// promQLQuery: string;
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
export type LogsActions =
|
export type LogsActions =
|
||||||
| GetFields
|
| GetFields
|
||||||
|
@ -6,4 +6,8 @@ export type Props = {
|
|||||||
limit: number;
|
limit: number;
|
||||||
orderBy: string;
|
orderBy: string;
|
||||||
order: string;
|
order: string;
|
||||||
|
idGt?: string;
|
||||||
|
idLt?: string;
|
||||||
|
timestampStart?: number;
|
||||||
|
timestampEnd?: number;
|
||||||
};
|
};
|
||||||
|
@ -9,4 +9,7 @@ export type Props = {
|
|||||||
timestampStart: number;
|
timestampStart: number;
|
||||||
timestampEnd: number;
|
timestampEnd: number;
|
||||||
step: number;
|
step: number;
|
||||||
|
q?: string;
|
||||||
|
idGt?: string;
|
||||||
|
idLt?: string;
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ export interface ILogsReducer {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isLoadingAggregate: boolean;
|
isLoadingAggregate: boolean;
|
||||||
logsAggregate: ILogsAggregate[];
|
logsAggregate: ILogsAggregate[];
|
||||||
selectedLogId: string;
|
selectedLogId: string | null;
|
||||||
detailedLog: null | ILog;
|
detailedLog: null | ILog;
|
||||||
liveTail: TLogsLiveTailState;
|
liveTail: TLogsLiveTailState;
|
||||||
liveTailStartRange: number; // time in minutes
|
liveTailStartRange: number; // time in minutes
|
||||||
|
3568
frontend/yarn.lock
3568
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user