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:
Pranshu Chittora 2022-08-19 17:16:04 +05:30 committed by GitHub
parent 32fba00aa8
commit f48a884f90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2373 additions and 2264 deletions

View File

@ -126,6 +126,8 @@
"@types/copy-webpack-plugin": "^8.0.1",
"@types/d3": "^6.2.0",
"@types/d3-tip": "^3.5.5",
"@types/event-source-polyfill": "^1.0.0",
"@types/flat": "^5.0.2",
"@types/jest": "^27.5.1",
"@types/lodash-es": "^4.17.4",
"@types/mini-css-extract-plugin": "^2.5.1",

View File

@ -4,7 +4,7 @@ import { ENVIRONMENT } from 'constants/env';
import { LOCALSTORAGE } from 'constants/localStorage';
import { EventSourcePolyfill } from 'event-source-polyfill';
export const LiveTail = (queryParams) => {
export const LiveTail = (queryParams: string): EventSourcePolyfill => {
const dict = {
headers: {
Authorization: `Bearer ${getLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN)}`,

View File

@ -1,4 +1,4 @@
import { Button, Popover, Tag, Tooltip } from 'antd';
import { Button, Popover } from 'antd';
import getStep from 'lib/getStep';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import React, { memo, useCallback, useMemo } from 'react';
@ -11,15 +11,24 @@ import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
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({
fieldKey,
fieldValue,
children,
getLogs,
getLogsAggregate,
}) {
}: AddToQueryHOCProps): JSX.Element {
const {
searchFilter: { queryString },
logLinesPerPage,
@ -72,9 +81,7 @@ function AddToQueryHOC({
...(idStart ? { idGt: idStart } : {}),
...(idEnd ? { idLt: idEnd } : {}),
});
}
else if (liveTail === 'PLAYING') {
} else if (liveTail === 'PLAYING') {
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: 'PAUSED',
@ -88,6 +95,7 @@ function AddToQueryHOC({
0,
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
dispatch,
generatedQuery,
@ -121,8 +129,12 @@ function AddToQueryHOC({
}
interface DispatchProps {
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
getLogs: (
props: Parameters<typeof getLogs>[0],
) => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (

View File

@ -1,19 +1,28 @@
import { Button, Popover, Tooltip } from 'antd';
import { Popover } from 'antd';
import React from 'react';
import { useCopyToClipboard } from 'react-use';
function CopyClipboardHOC({ textToCopy, children }) {
const [_state, setCopy] = useCopyToClipboard();
interface CopyClipboardHOCProps {
textToCopy: string;
children: React.ReactNode;
}
function CopyClipboardHOC({
textToCopy,
children,
}: CopyClipboardHOCProps): JSX.Element {
const [, setCopy] = useCopyToClipboard();
return (
<span
style={{
margin: 0,
padding: 0,
cursor: 'pointer'
cursor: 'pointer',
}}
onClick={() => setCopy(textToCopy)}
onClick={(): void => setCopy(textToCopy)}
onKeyDown={(): void => setCopy(textToCopy)}
role="button"
tabIndex={0}
>
<Popover
placement="top"

View File

@ -1,22 +1,26 @@
import { blue, grey, orange } from '@ant-design/colors';
import { CopyFilled, CopyrightCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
import { Button, Card, Divider, Row, Typography } from 'antd';
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
import { Button, Divider, Row, Typography } from 'antd';
import { map } from 'd3';
import dayjs from 'dayjs';
import { FlatLogData } from 'lib/logs/flatLogData';
import { flatMap, flatMapDeep } from 'lodash-es';
import React, { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useCopyToClipboard } from 'react-use';
import { AppState } from 'store/reducers';
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 CopyClipboardHOC from '../CopyClipboardHOC';
import { Container } from './styles';
function LogGeneralField({ fieldKey, fieldValue }) {
interface LogFieldProps {
fieldKey: string;
fieldValue: string;
}
function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element {
return (
<div
style={{
@ -35,8 +39,10 @@ function LogGeneralField({ fieldKey, fieldValue }) {
</div>
);
}
function LogSelectedField({ fieldKey = '', fieldValue = '' }) {
function LogSelectedField({
fieldKey = '',
fieldValue = '',
}: LogFieldProps): JSX.Element {
return (
<div
style={{
@ -66,13 +72,16 @@ function LogSelectedField({ fieldKey = '', fieldValue = '' }) {
);
}
function LogItem({ logData }) {
interface LogItemProps {
logData: ILog;
}
function LogItem({ logData }: LogItemProps): JSX.Element {
const {
fields: { selected },
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const dispatch = useDispatch();
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
const [_state, setCopy] = useCopyToClipboard();
const [, setCopy] = useCopyToClipboard();
const handleDetailedView = useCallback(() => {
dispatch({
@ -81,22 +90,28 @@ function LogItem({ logData }) {
});
}, [dispatch, logData]);
const handleCopyJSON = () => {
setCopy(JSON.stringify(logData, null, 2))
}
const handleCopyJSON = (): void => {
setCopy(JSON.stringify(logData, null, 2));
};
return (
<Container>
<div style={{ maxWidth: '100%' }}>
<div>
{'{'}
<div style={{ marginLeft: '0.5rem' }}>
<LogGeneralField fieldKey="log" fieldValue={flattenLogData.body} />
<LogGeneralField
fieldKey="log"
fieldValue={flattenLogData.body as never}
/>
{flattenLogData.stream && (
<LogGeneralField fieldKey="stream" fieldValue={flattenLogData.stream} />
<LogGeneralField
fieldKey="stream"
fieldValue={flattenLogData.stream as never}
/>
)}
<LogGeneralField
fieldKey="timestamp"
fieldValue={dayjs(flattenLogData.timestamp / 1e6).format()}
fieldValue={dayjs((flattenLogData.timestamp as never) / 1e6).format()}
/>
</div>
{'}'}
@ -107,7 +122,7 @@ function LogItem({ logData }) {
<LogSelectedField
key={field.name}
fieldKey={field.name}
fieldValue={flattenLogData[field.name]}
fieldValue={flattenLogData[field.name] as never}
/>
) : null;
})}

View File

@ -1,3 +1,4 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { LoadingOutlined } from '@ant-design/icons';
import {
Button,

View File

@ -87,7 +87,7 @@ function Retention({
<Col span={12} style={{ display: 'flex' }}>
<RetentionFieldLabel>{text}</RetentionFieldLabel>
</Col>
<Row span={12} justify="end">
<Row justify="end">
<RetentionFieldInputContainer>
<Input
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}

View File

@ -1,5 +1,4 @@
import {
ArrowLeftOutlined,
FastBackwardOutlined,
LeftOutlined,
RightOutlined,
@ -19,7 +18,7 @@ import {
SET_LOG_LINES_PER_PAGE,
} from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
import { Container } from './styles';
@ -27,7 +26,10 @@ const { Option } = Select;
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>(
(state) => state.globalTime,
);
@ -40,14 +42,14 @@ function LogControls({ getLogs }) {
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const dispatch = useDispatch();
const handleLogLinesPerPageChange = (e: number) => {
const handleLogLinesPerPageChange = (e: number): void => {
dispatch({
type: SET_LOG_LINES_PER_PAGE,
payload: e,
});
};
const handleGoToLatest = () => {
const handleGoToLatest = (): void => {
dispatch({
type: RESET_ID_START_AND_END,
});
@ -65,12 +67,12 @@ function LogControls({ getLogs }) {
});
};
const handleNavigatePrevious = () => {
const handleNavigatePrevious = (): void => {
dispatch({
type: GET_PREVIOUS_LOG_LINES,
});
};
const handleNavigateNext = () => {
const handleNavigateNext = (): void => {
dispatch({
type: GET_NEXT_LOG_LINES,
});
@ -105,7 +107,9 @@ function LogControls({ getLogs }) {
}
interface DispatchProps {
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
getLogs: (
props: Parameters<typeof getLogs>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (

View File

@ -2,7 +2,7 @@ import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Popover } from 'antd';
import getStep from 'lib/getStep';
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 { bindActionCreators } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
@ -12,9 +12,9 @@ import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
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) {
return s;
}
@ -24,7 +24,21 @@ const removeJSONStringifyQuotes = (s: string) => {
}
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 {
searchFilter: { queryString },
logLinesPerPage,
@ -38,7 +52,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
(state) => state.globalTime,
);
const handleQueryAdd = (newQueryString) => {
const handleQueryAdd = (newQueryString: string): void => {
let updatedQueryString = queryString || '';
if (updatedQueryString.length === 0) {
@ -94,7 +108,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
<Button
type="text"
size="small"
onClick={() =>
onClick={(): void =>
handleQueryAdd(
generateFilterQuery({
fieldKey,
@ -110,7 +124,7 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
<Button
type="text"
size="small"
onClick={() =>
onClick={(): void =>
handleQueryAdd(
generateFilterQuery({
fieldKey,
@ -124,7 +138,8 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
</Button>
</Col>
),
[],
// eslint-disable-next-line react-hooks/exhaustive-deps
[fieldKey, validatedFieldValue],
);
return (
<Popover placement="bottomLeft" content={PopOverMenuContent} trigger="click">
@ -134,10 +149,11 @@ function ActionItem({ fieldKey, fieldValue, getLogs, getLogsAggregate }) {
</Popover>
);
}
interface DispatchProps {
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
getLogs: (props: Parameters<typeof getLogs>[0]) => (dispatch: never) => void;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => (dispatch: never) => void;
}
const mapDispatchToProps = (
@ -147,4 +163,5 @@ const mapDispatchToProps = (
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));

View File

@ -4,9 +4,13 @@ import { Button, Row } from 'antd';
import Editor from 'components/Editor';
import React, { useMemo } from 'react';
import { useCopyToClipboard } from 'react-use';
import { ILog } from 'types/api/logs/log';
function JSONView({ logData }) {
const [_state, copyToClipboard] = useCopyToClipboard();
interface JSONViewProps {
logData: ILog;
}
function JSONView({ logData }: JSONViewProps): JSX.Element {
const [, copyToClipboard] = useCopyToClipboard();
const LogJsonData = useMemo(() => JSON.stringify(logData, null, 2), [logData]);
return (
<div>
@ -25,7 +29,13 @@ function JSONView({ logData }) {
</Button>
</Row>
<div style={{ marginTop: '0.5rem' }}>
<Editor value={LogJsonData} language="json" height="70vh" readOnly />
<Editor
value={LogJsonData}
language="json"
height="70vh"
readOnly
onChange={(): void => {}}
/>
</div>
</div>
);

View File

@ -1,33 +1,34 @@
import { blue, orange } from '@ant-design/colors';
import {
MenuFoldOutlined,
MinusCircleOutlined,
PlusCircleFilled,
PlusCircleOutlined,
} from '@ant-design/icons';
import { Button, Col, Input, Popover, Table, Typography } from 'antd';
import { Input, Table } from 'antd';
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import flatten from 'flat';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import React, { useMemo, useState } from 'react';
import { ILog } from 'types/api/logs/log';
import ActionItem from './ActionItem';
// Fields which should be restricted from adding it to query
const RESTRICTED_FIELDS = ['timestamp'];
function TableView({ logData }) {
interface TableViewProps {
logData: ILog;
}
function TableView({ logData }: TableViewProps): JSX.Element | null {
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
const flattenLogData = useMemo(() => (logData ? flatten(logData) : null), [
logData,
]);
const flattenLogData: Record<string, never> | null = useMemo(
() => (logData ? flatten(logData) : null),
[logData],
);
if (logData === null) {
return null;
}
const dataSource = Object.keys(flattenLogData)
const dataSource =
flattenLogData !== null &&
Object.keys(flattenLogData)
.filter((field) => fieldSearchFilter(field, fieldSearchInput))
.map((key) => {
return {
@ -37,11 +38,15 @@ function TableView({ logData }) {
};
});
if (!dataSource) {
return null;
}
const columns = [
{
title: 'Action',
width: 75,
render: (fieldData) => {
render: (fieldData: Record<string, string>): JSX.Element | null => {
const fieldKey = fieldData.field.split('.').slice(-1);
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
return <ActionItem fieldKey={fieldKey} fieldValue={fieldData.value} />;
@ -54,13 +59,13 @@ function TableView({ logData }) {
dataIndex: 'field',
key: 'field',
width: '35%',
render: (field: string) => {
render: (field: string): JSX.Element => {
const fieldKey = field.split('.').slice(-1);
const renderedField = <span style={{ color: blue[4] }}>{field}</span>;
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
return (
<AddToQueryHOC fieldKey={fieldKey} fieldValue={flattenLogData[field]}>
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
{' '}
{renderedField}
</AddToQueryHOC>
@ -74,7 +79,7 @@ function TableView({ logData }) {
dataIndex: 'value',
key: 'value',
ellipsis: false,
render: (field) => (
render: (field: never): JSX.Element => (
<CopyClipboardHOC textToCopy={field}>
<span style={{ color: orange[6] }}>{field}</span>
</CopyClipboardHOC>
@ -88,13 +93,13 @@ function TableView({ logData }) {
placeholder="Search field names"
size="large"
value={fieldSearchInput}
onChange={(e) => setFieldSearchInput(e.target.value)}
onChange={(e): void => setFieldSearchInput(e.target.value)}
/>
<Table
// scroll={{ x: true }}
tableLayout="fixed"
dataSource={dataSource}
columns={columns}
columns={columns as never}
pagination={false}
/>
</div>

View File

@ -3,19 +3,19 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
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 TableView from './TableView';
const { TabPane } = Tabs;
function LogDetailedView() {
function LogDetailedView(): JSX.Element {
const { detailedLog } = useSelector<AppState, ILogsReducer>(
(state) => state.logs,
);
const dispatch = useDispatch();
const onDrawerClose = () => {
const onDrawerClose = (): void => {
dispatch({
type: SET_DETAILED_LOG_DATA,
payload: null,
@ -35,6 +35,7 @@ function LogDetailedView() {
getContainer={false}
style={{ overscrollBehavior: 'contain' }}
>
{detailedLog && (
<Tabs defaultActiveKey="1">
<TabPane tab="Table" key="1">
<TableView logData={detailedLog} />
@ -43,6 +44,7 @@ function LogDetailedView() {
<JSONView logData={detailedLog} />
</TabPane>
</Tabs>
)}
</Drawer>
</div>
);

View File

@ -1,6 +1,9 @@
import React from 'react';
function OptionIcon({ isDarkMode }) {
interface OptionIconProps {
isDarkMode: boolean;
}
function OptionIcon({ isDarkMode }: OptionIconProps): JSX.Element {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
@ -10,7 +13,7 @@ function OptionIcon({ isDarkMode }) {
height="1rem"
viewBox="0 0 52 52"
enableBackground="new 0 0 52 52"
fill={isDarkMode ? "#eee" : '#222'}
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

View File

@ -1,27 +1,11 @@
import { green, red } from '@ant-design/colors';
import {
ArrowRightOutlined,
IeSquareFilled,
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';
/* eslint-disable react-hooks/exhaustive-deps */
import { green } from '@ant-design/colors';
import { PauseOutlined, PlayCircleOutlined } from '@ant-design/icons';
import { Button, Popover, Row, Select } from 'antd';
import { LiveTail } from 'api/logs/livetail';
import { LOCALSTORAGE } from 'constants/localStorage';
import dayjs from 'dayjs';
import { debounce, throttle } from 'lodash-es';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { throttle } from 'lodash-es';
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import {
@ -32,7 +16,7 @@ import {
} from 'types/actions/logs';
import { TLogsLiveTailState } from 'types/api/logs/liveTail';
import AppReducer from 'types/reducer/app';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
import OptionIcon from './OptionIcon';
import { TimePickerCard, TimePickerSelect } from './styles';
@ -66,7 +50,7 @@ const TIME_PICKER_OPTIONS = [
},
];
function LogLiveTail() {
function LogLiveTail(): JSX.Element {
const {
liveTail,
searchFilter: { queryString },
@ -75,15 +59,16 @@ function LogLiveTail() {
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const dispatch = useDispatch();
const handleLiveTail = (toggleState: TLogsLiveTailState) => {
const handleLiveTail = (toggleState: TLogsLiveTailState): void => {
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: toggleState,
});
};
const batchedEventsRef = useRef([]);
const batchedEventsRef = useRef<Record<string, unknown>[]>([]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const pushLiveLog = useCallback(
throttle(() => {
dispatch({
@ -96,21 +81,21 @@ function LogLiveTail() {
[],
);
const batchLiveLog = (e) => {
const batchLiveLog = (e: { data: string }): void => {
// console.log('EVENT BATCHED');
batchedEventsRef.current.push(JSON.parse(e.data));
batchedEventsRef.current.push(JSON.parse(e.data as string) as never);
pushLiveLog();
};
// 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(() => {
if (liveTail === 'PLAYING') {
// console.log('Starting Live Tail', logs.length);
const timeStamp = dayjs().subtract(liveTailStartRange, 'minute').valueOf();
const queryParams = new URLSearchParams({
...(queryString ? { q: queryString } : {}),
timestampStart: timeStamp * 1e6,
timestampStart: (timeStamp * 1e6) as never,
...(liveTailSourceRef.current && logs.length > 0
? {
idGt: logs[0].id,
@ -119,23 +104,19 @@ function LogLiveTail() {
});
const source = LiveTail(queryParams.toString());
liveTailSourceRef.current = source;
source.onmessage = function (e) {
// pushLiveLog(e)
source.onmessage = function connectionMessage(e): void {
batchLiveLog(e);
};
source.onopen = function (event) {
// console.log('open event');
// console.log(event);
};
source.onerror = function (event) {
// console.log(event);
// source.onopen = function connectionOpen(): void { };
source.onerror = function connectionError(event: unknown): void {
console.error(event);
source.close();
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: false,
});
};
} else if (liveTailSourceRef.current) {
} else if (liveTailSourceRef.current && liveTailSourceRef.current.close) {
liveTailSourceRef.current?.close();
}
@ -144,7 +125,7 @@ function LogLiveTail() {
}
}, [liveTail]);
const handleLiveTailStart = () => {
const handleLiveTailStart = (): void => {
handleLiveTail('PLAYING');
if (!liveTailSourceRef.current) {
dispatch({
@ -158,7 +139,7 @@ function LogLiveTail() {
<TimePickerSelect
disabled={liveTail === 'PLAYING'}
value={liveTailStartRange}
onChange={(value) => {
onChange={(value): void => {
dispatch({
type: SET_LIVE_TAIL_START_TIME,
payload: value,
@ -183,7 +164,7 @@ function LogLiveTail() {
{liveTail === 'PLAYING' ? (
<Button
type="primary"
onClick={() => handleLiveTail('PAUSED')}
onClick={(): void => handleLiveTail('PAUSED')}
title="Pause live tail"
style={{ background: green[6] }}
>
@ -201,7 +182,7 @@ function LogLiveTail() {
{liveTail !== 'STOPPED' && (
<Button
type="dashed"
onClick={() => handleLiveTail('STOPPED')}
onClick={(): void => handleLiveTail('STOPPED')}
title="Exit live tail"
>
<div

View File

@ -15,7 +15,10 @@ import { GetLogsFields } from 'store/actions/logs/getFields';
import AppActions from 'types/actions';
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 urlQuery = useMemo(() => {
@ -29,7 +32,7 @@ function Logs({ getLogsFields }) {
type: SET_SEARCH_QUERY_STRING,
payload: urlQuery.get('q'),
});
}, [dispatch]);
}, [dispatch, urlQuery]);
useEffect(() => {
getLogsFields();
@ -39,16 +42,16 @@ function Logs({ getLogsFields }) {
<div style={{ position: 'relative' }}>
<Row style={{ justifyContent: 'center', alignItems: 'center' }}>
<SearchFilter />
<Divider type='vertical' style={{ height: '2rem' }} />
<Divider type="vertical" style={{ height: '2rem' }} />
<LogLiveTail />
</Row>
<LogsAggregate />
<LogControls />
<Divider style={{ margin: 0 }} />
<Row gutter={20} style={{ flexWrap: 'nowrap' }}>
<LogsFilters flex="450px" />
<LogsFilters />
<Divider type="vertical" style={{ height: '100%', margin: 0 }} />
<LogsTable flex="auto" />
<LogsTable />
</Row>
<LogDetailedView />
</div>

View File

@ -1,42 +1,41 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { blue } from '@ant-design/colors';
import Graph from 'components/Graph';
import Spinner from 'components/Spinner';
import dayjs from 'dayjs';
import getStep from 'lib/getStep';
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 { ThunkDispatch } from 'redux-thunk';
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { GlobalReducer } from 'types/reducer/globalTime';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
import { Container } from './styles';
function LogsAggregate({ getLogsAggregate }) {
interface LogsAggregateProps {
getLogsAggregate: (arg0: Parameters<typeof getLogsAggregate>[0]) => void;
}
function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element {
const {
searchFilter: { queryString },
logs,
logLinesPerPage,
idEnd,
idStart,
isLoading,
isLoadingAggregate,
logsAggregate,
liveTail,
liveTailStartRange,
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const dispatch = useDispatch();
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const reFetchIntervalRef = useRef(null);
const reFetchIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => {
// console.log('LIVE TAIL LOG AGG', liveTail)
switch (liveTail) {
case 'STOPPED': {
if (reFetchIntervalRef.current) {
@ -59,7 +58,7 @@ function LogsAggregate({ getLogsAggregate }) {
}
case 'PLAYING': {
const aggregateCall = () => {
const aggregateCall = (): void => {
const startTime =
dayjs().subtract(liveTailStartRange, 'minute').valueOf() * 1e6;
const endTime = dayjs().valueOf() * 1e6;
@ -78,11 +77,9 @@ function LogsAggregate({ getLogsAggregate }) {
};
aggregateCall();
reFetchIntervalRef.current = setInterval(aggregateCall, 60000);
// console.log('LA Play', reFetchIntervalRef.current);
break;
}
case 'PAUSED': {
// console.log('LA Pause', reFetchIntervalRef.current);
if (reFetchIntervalRef.current) {
clearInterval(reFetchIntervalRef.current);
}
@ -98,7 +95,6 @@ function LogsAggregate({ getLogsAggregate }) {
labels: logsAggregate.map((s) => new Date(s.timestamp / 1000000)),
datasets: [
{
// label: 'Span Count',
data: logsAggregate.map((s) => s.value),
backgroundColor: blue[4],
},
@ -123,7 +119,9 @@ function LogsAggregate({ getLogsAggregate }) {
}
interface DispatchProps {
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (

View File

@ -1,18 +1,21 @@
import {
CloseCircleFilled,
CloseOutlined,
LoadingOutlined,
} from '@ant-design/icons';
import { LoadingOutlined } from '@ant-design/icons';
import { Button, Popover, Spin } from 'antd';
import Spinner from 'components/Spinner';
import React, { useRef, useState } from 'react';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { useHover, useHoverDirty } from 'react-use';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
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({
name,
buttonIcon,
@ -20,16 +23,16 @@ export function FieldItem({
fieldData,
fieldIndex,
isLoading,
iconHoverText
}) {
iconHoverText,
}: FieldItemProps): JSX.Element {
const [isHovered, setIsHovered] = useState(false);
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
return (
<Field
onMouseEnter={() => {
onMouseEnter={(): void => {
setIsHovered(true);
}}
onMouseLeave={() => setIsHovered(false)}
onMouseLeave={(): void => setIsHovered(false)}
isDarkMode={isDarkMode}
>
<span>{name}</span>
@ -43,7 +46,7 @@ export function FieldItem({
type="text"
size="small"
icon={buttonIcon}
onClick={() => buttonOnClick({ fieldData, fieldIndex })}
onClick={(): void => buttonOnClick({ fieldData, fieldIndex })}
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
/>
</Popover>

View File

@ -1,10 +1,6 @@
/* eslint-disable react/no-array-index-key */
import { red } from '@ant-design/colors';
import {
CloseCircleFilled,
CloseOutlined,
PlusCircleFilled,
PlusCircleOutlined,
} from '@ant-design/icons';
import { CloseOutlined, PlusCircleFilled } from '@ant-design/icons';
import { Input } from 'antd';
import AddToSelectedFields from 'api/logs/AddToSelectedField';
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
@ -17,34 +13,40 @@ import { ThunkDispatch } from 'redux-thunk';
import { GetLogsFields } from 'store/actions/logs/getFields';
import { AppState } from 'store/reducers';
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 {
CategoryContainer,
Container,
ExtractField,
Field,
FieldContainer,
} from './styles';
import { CategoryContainer, Container, FieldContainer } from './styles';
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
function LogsFilters({ getLogsFields }) {
interface LogsFiltersProps {
getLogsFields: () => void;
}
function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
const {
fields: { interesting, selected },
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const [selectedFieldLoading, setSelectedFieldLoading] = useState([]);
const [interestingFieldLoading, setInterestingFieldLoading] = useState([]);
const [selectedFieldLoading, setSelectedFieldLoading] = useState<number[]>([]);
const [interestingFieldLoading, setInterestingFieldLoading] = useState<
number[]
>([]);
const [filterValuesInput, setFilterValuesInput] = useState('');
const handleSearch = (e) => {
setFilterValuesInput(e.target.value);
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>): void => {
setFilterValuesInput((e.target as HTMLInputElement).value);
};
const handleAddInterestingToSelected = async ({ fieldData, fieldIndex }) => {
setInterestingFieldLoading((prevState) => {
const handleAddInterestingToSelected = async ({
fieldData,
fieldIndex,
}: {
fieldData: IInterestingFields;
fieldIndex: number;
}): Promise<void> => {
setInterestingFieldLoading((prevState: number[]) => {
prevState.push(fieldIndex);
return [...prevState];
});
@ -59,7 +61,13 @@ function LogsFilters({ getLogsFields }) {
interestingFieldLoading.filter((e) => e !== fieldIndex),
);
};
const handleRemoveSelectedField = async ({ fieldData, fieldIndex }) => {
const handleRemoveSelectedField = async ({
fieldData,
fieldIndex,
}: {
fieldData: ISelectedFields;
fieldIndex: number;
}): Promise<void> => {
setSelectedFieldLoading((prevState) => {
prevState.push(fieldIndex);
return [...prevState];
@ -77,7 +85,7 @@ function LogsFilters({ getLogsFields }) {
);
};
return (
<Container>
<Container flex="450px">
<Input
placeholder="Filter Values"
onInput={handleSearch}
@ -93,14 +101,14 @@ function LogsFilters({ getLogsFields }) {
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
.map((field, idx) => (
<FieldItem
key={field + idx}
key={`${JSON.stringify(field)}-${idx}`}
name={field.name}
fieldData={field}
fieldData={field as never}
fieldIndex={idx}
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
buttonOnClick={
!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
handleRemoveSelectedField
(!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
handleRemoveSelectedField) as never
}
isLoading={selectedFieldLoading.includes(idx)}
iconHoverText="Remove from Selected Fields"
@ -115,12 +123,12 @@ function LogsFilters({ getLogsFields }) {
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
.map((field, idx) => (
<FieldItem
key={field + idx}
key={`${JSON.stringify(field)}-${idx}`}
name={field.name}
fieldData={field}
fieldData={field as never}
fieldIndex={idx}
buttonIcon={<PlusCircleFilled />}
buttonOnClick={handleAddInterestingToSelected}
buttonOnClick={handleAddInterestingToSelected as never}
isLoading={interestingFieldLoading.includes(idx)}
iconHoverText="Add to Selected Fields"
/>

View File

@ -1,7 +1,12 @@
import { Typography } from 'antd';
import React from 'react';
function FieldKey({ name, type }) {
interface FieldKeyProps {
name: string;
type: string;
}
function FieldKey({ name, type }: FieldKeyProps): JSX.Element {
return (
<span style={{ margin: '0.25rem 0', display: 'flex', gap: '0.5rem' }}>
<Typography.Text>{name}</Typography.Text>

View File

@ -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 { Button, Input, Select, Typography } from 'antd';
import { Button, Input, Select } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading';
import {
ConditionalOperators,
QueryOperatorsMultiVal,
QueryOperatorsSingleVal,
QueryTypes,
} from 'lib/logql/tokens';
import { chunk, cloneDeep, debounce, flatten } from 'lodash-es';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { flatten } from 'lodash-es';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import { useHoverDirty, useLocation } from 'react-use';
import { AppState } from 'store/reducers';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
import { v4 } from 'uuid';
import FieldKey from '../FieldKey';
@ -26,12 +24,24 @@ import { QueryConditionContainer, QueryFieldContainer } from '../styles';
import { createParsedQueryStructure } from '../utils';
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 {
fields: { selected },
} = 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) {
if (inputKey === selectedField.name) {
return selectedField.type;
@ -39,11 +49,10 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
}
return '';
};
const fieldType = useMemo(() => getFieldType(query[0].value), [
const fieldType = useMemo(() => getFieldType(query[0].value as string), [
query,
selected,
]);
const handleChange = (qIdx, value) => {
const handleChange = (qIdx: number, value: string): void => {
query[qIdx].value = value || '';
if (qIdx === 1) {
@ -51,16 +60,17 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
if (!Array.isArray(query[2].value)) {
query[2].value = [];
}
} else if (Object.values(QueryOperatorsSingleVal).includes(value)) {
if (Array.isArray(query[2].value)) {
} else if (
Object.values(QueryOperatorsSingleVal).includes(value) &&
Array.isArray(query[2].value)
) {
query[2].value = '';
}
}
}
onUpdate(query, queryIndex);
};
const handleClear = () => {
const handleClear = (): void => {
onDelete(queryIndex);
};
if (!Array.isArray(query)) {
@ -72,39 +82,43 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
style={{ ...(queryIndex === 0 && { gridColumnStart: 2 }) }}
>
<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>
<Select
defaultActiveFirstOption={false}
placeholder="Select Operator"
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 }}
>
{Object.values({
...QueryOperatorsMultiVal,
...QueryOperatorsSingleVal,
}).map((cond) => (
<Option key={cond} value={cond} label={cond} />
<Option key={cond} value={cond} label={cond}>
{cond}
</Option>
))}
</Select>
<div style={{ flex: 2 }}>
{Array.isArray(query[2].value) ||
Object.values(QueryOperatorsMultiVal).some(
(op) => op.toUpperCase() === query[1].value?.toUpperCase(),
(op) => op.toUpperCase() === (query[1].value as string)?.toUpperCase(),
) ? (
<Select
mode="tags"
style={{ width: '100%' }}
onChange={(e) => handleChange(2, e)}
onChange={(e): void => handleChange(2, e as never)}
defaultValue={(query[2] && query[2].value) || []}
notFoundContent={null}
/>
) : (
<Input
onChange={(e) => handleChange(2, e.target.value)}
onChange={(e): void => handleChange(2, e.target.value)}
style={{ width: '100%' }}
defaultValue={query[2] && query[2].value}
/>
@ -120,24 +134,39 @@ function QueryField({ query, queryIndex, onUpdate, onDelete }) {
</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 (
<QueryConditionContainer>
<Select
defaultValue={query.value.toUpperCase()}
onChange={(e) => {
defaultValue={
(query as any).value &&
(((query as any)?.value as any) as string).toUpperCase()
}
onChange={(e): void => {
onUpdate({ ...query, value: e }, queryIndex);
}}
style={{ width: '100%' }}
>
{Object.values(ConditionalOperators).map((cond) => (
<Option key={cond} value={cond} label={cond} />
<Option key={cond} value={cond} label={cond}>
{cond}
</Option>
))}
</Select>
</QueryConditionContainer>
);
}
const hashCode = (s) => {
const hashCode = (s: string): string => {
if (!s) {
return '0';
}
@ -149,14 +178,20 @@ const hashCode = (s) => {
)}`;
};
function QueryBuilder({ updateParsedQuery }) {
function QueryBuilder({
updateParsedQuery,
}: {
updateParsedQuery: (arg0: unknown) => void;
}): JSX.Element {
const {
searchFilter: { parsedQuery },
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const keyPrefixRef = useRef(hashCode(JSON.stringify(parsedQuery)));
const [keyPrefix, setKeyPrefix] = useState(keyPrefixRef.current);
const generatedQueryStructure = createParsedQueryStructure(parsedQuery);
const generatedQueryStructure = createParsedQueryStructure(
parsedQuery as never[],
);
useEffect(() => {
const incomingHashCode = hashCode(JSON.stringify(parsedQuery));
@ -166,18 +201,19 @@ function QueryBuilder({ updateParsedQuery }) {
}
}, [parsedQuery]);
const handleUpdate = (query, queryIndex): void => {
const handleUpdate = (
query: { value: string | string[]; type: string }[],
queryIndex: number,
): void => {
const updatedParsedQuery = generatedQueryStructure;
updatedParsedQuery[queryIndex] = query;
updatedParsedQuery[queryIndex] = query as never;
const flatParsedQuery = flatten(updatedParsedQuery).filter((q) => q.value);
keyPrefixRef.current = hashCode(JSON.stringify(flatParsedQuery));
updateParsedQuery(flatParsedQuery);
};
const handleDelete = (queryIndex) => {
const handleDelete = (queryIndex: number): void => {
const updatedParsedQuery = generatedQueryStructure;
updatedParsedQuery.splice(queryIndex - 1, 2);
@ -186,15 +222,15 @@ function QueryBuilder({ updateParsedQuery }) {
updateParsedQuery(flatParsedQuery);
};
const QueryUI = () =>
const QueryUI = (): JSX.Element | JSX.Element[] =>
generatedQueryStructure.map((query, idx) => {
if (Array.isArray(query))
return (
<QueryField
key={keyPrefix + idx}
query={query}
query={query as never}
queryIndex={idx}
onUpdate={handleUpdate}
onUpdate={handleUpdate as never}
onDelete={handleDelete}
/>
);
@ -204,7 +240,7 @@ function QueryBuilder({ updateParsedQuery }) {
key={keyPrefix + idx}
query={query}
queryIndex={idx}
onUpdate={handleUpdate}
onUpdate={handleUpdate as never}
/>
);
});

View File

@ -1,18 +1,22 @@
import { Button, Typography } from 'antd';
import { Button } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading';
import { map } from 'lodash-es';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
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';
function SuggestedItem({ name, type }) {
interface SuggestedItemProps {
name: string;
type: string;
}
function SuggestedItem({ name, type }: SuggestedItemProps): JSX.Element {
const dispatch = useDispatch();
const addSuggestedField = () => {
const addSuggestedField = (): void => {
dispatch({
type: ADD_SEARCH_FIELD_QUERY_STRING,
payload: name,
@ -29,7 +33,7 @@ function SuggestedItem({ name, type }) {
);
}
function Suggestions() {
function Suggestions(): JSX.Element {
const {
fields: { selected },
} = useSelector<AppState, ILogsReducer>((store) => store.logs);

View File

@ -1,9 +1,12 @@
import React from 'react';
import QueryBuilder from './QueryBuilder/QueryBuilder';
import Suggestions from './Suggestions';
function SearchFields({updateParsedQuery}): JSX.Element {
interface SearchFieldsProps {
updateParsedQuery: () => void;
}
function SearchFields({ updateParsedQuery }: SearchFieldsProps): JSX.Element {
return (
<>
<QueryBuilder updateParsedQuery={updateParsedQuery} />

View File

@ -1,3 +1,7 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
import { QueryTypes } from 'lib/logql/tokens';
export const queryKOVPair = () => [
@ -30,7 +34,7 @@ export const createParsedQueryStructure = (parsedQuery = []) => {
cond = null;
qCtr = -1;
}
let stagingArr = structuredArray[structuredArray.length - 1];
const stagingArr = structuredArray[structuredArray.length - 1];
const prevQuery =
Array.isArray(stagingArr) && qCtr >= 0 ? stagingArr[qCtr] : null;

View File

@ -1,23 +1,11 @@
import {
CloseCircleFilled,
CloseCircleOutlined,
CloseSquareOutlined,
} from '@ant-design/icons';
/* eslint-disable react-hooks/exhaustive-deps */
import { CloseSquareOutlined } from '@ant-design/icons';
import { Button, Input } from 'antd';
import useClickOutside from 'hooks/useClickOutside';
import getStep from 'lib/getStep';
import { debounce, throttle } from 'lodash-es';
import React, {
memo,
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import { useClickAway, useLocation } from 'react-use';
import { useLocation } from 'react-use';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { getLogs } from 'store/actions/logs/getLogs';
@ -26,7 +14,7 @@ import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { TOGGLE_LIVE_TAIL } from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
import SearchFields from './SearchFields';
import { DropDownContainer } from './styles';
@ -34,7 +22,16 @@ import { useSearchParser } from './useSearchParser';
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 {
queryString,
updateParsedQuery,
@ -146,26 +143,29 @@ function SearchFilter({ getLogs, getLogsAggregate }) {
<DropDownContainer>
<Button
type="text"
onClick={() => setShowDropDown(false)}
onClick={(): void => setShowDropDown(false)}
style={{
position: 'absolute',
top: 0,
right: 0,
}}
>
<CloseSquareOutlined size="large" />
<CloseSquareOutlined />
</Button>
<SearchFields updateParsedQuery={updateParsedQuery} />
<SearchFields updateParsedQuery={updateParsedQuery as never} />
</DropDownContainer>
)}
</div>
</div>
);
}
interface DispatchProps {
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: () => (dispatch: Dispatch<AppActions>) => void;
getLogs: (
props: Parameters<typeof getLogs>[0],
) => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (

View File

@ -1,16 +1,21 @@
import history from 'lib/history';
import { parseQuery, reverseParser } from 'lib/logql';
import { isEqual } from 'lodash-es';
import { useCallback, useEffect, useReducer, useState } from 'react';
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import {
SET_SEARCH_QUERY_PARSED_PAYLOAD,
SET_SEARCH_QUERY_STRING,
} 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 {
searchFilter: { parsedQuery, queryString },

View File

@ -1,23 +1,24 @@
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { Card, Typography } from 'antd';
import { LiveTail } from 'api/logs/livetail';
/* eslint-disable no-nested-ternary */
import { Typography } from 'antd';
import LogItem from 'components/Logs/LogItem';
import Spinner from 'components/Spinner';
import { map } from 'lodash-es';
import React, { memo, useEffect, useRef } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import React, { memo, useEffect } from 'react';
import { connect, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { getLogs } from 'store/actions/logs/getLogs';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { PUSH_LIVE_TAIL_EVENT } from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
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 {
searchFilter: { queryString },
logs,
@ -51,7 +52,7 @@ function LogsTable({ getLogs }) {
return <Spinner height={20} tip="Getting Logs" />;
}
return (
<Container>
<Container flex="auto">
<Heading>
<Typography.Text
style={{
@ -74,7 +75,9 @@ function LogsTable({ getLogs }) {
}
interface DispatchProps {
getLogs: () => (dispatch: Dispatch<AppActions>) => void;
getLogs: (
props: Parameters<typeof getLogs>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (

View File

@ -1,4 +1,3 @@
import { grey } from '@ant-design/colors';
import { Card, Col } from 'antd';
import styled from 'styled-components';
@ -9,7 +8,6 @@ export const Container = styled(Col)`
`;
export const Heading = styled(Card)`
margin-bottom: 0.1rem;
.ant-card-body {
padding: 0.3rem 0.5rem;

View File

@ -49,7 +49,8 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
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`,
);
};
@ -100,17 +101,12 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
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`,
);
};
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 (
<>
<Row gutter={24}>

View File

@ -18,7 +18,7 @@ const breadcrumbNameMap = {
[ROUTES.ERROR_DETAIL]: 'Errors',
[ROUTES.LIST_ALL_ALERT]: 'Alerts',
[ROUTES.ALL_DASHBOARD]: 'Dashboard',
[ROUTES.LOGS]: 'Logs'
[ROUTES.LOGS]: 'Logs',
};
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {

View File

@ -1,3 +1,4 @@
/* eslint-disable sonarjs/no-duplicate-string */
/* eslint no-useless-escape: 0 */
const logqlQueries = [

View File

@ -1,5 +1,5 @@
import { logqlQueries } from 'lib/__fixtures__/logql';
import reverseParser from 'lib/logql/reverseParser';
import { reverseParser } from 'lib/logql/reverseParser';
describe('lib/logql/reverseParser', () => {
test('reverse parse valid queries', () => {

View File

@ -1,5 +1,6 @@
import splitter from 'lib/logql/splitter';
import { logqlQueries } from 'lib/__fixtures__/logql';
import { splitter } from 'lib/logql/splitter';
describe('lib/logql/splitter', () => {
test('splitter valid quereies', () => {
logqlQueries.forEach((queryObject) => {

View File

@ -1,4 +1,11 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
import {
ErrorConvertToFullText,
ErrorInvalidQueryPair,
} from 'lib/logql/errors';
import splitter from 'lib/logql/splitter';
import {
ConditionalOperators,
@ -6,10 +13,6 @@ import {
QueryOperatorsSingleVal,
QueryTypes,
} from 'lib/logql/tokens';
import {
ErrorConvertToFullText,
ErrorInvalidQueryPair,
} from 'lib/logql/errors';
const validateMultiValue = (queryToken: string): boolean => {
const queryValues = [];

View File

@ -1,3 +1,5 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
export const reverseParser = (

View File

@ -1,7 +1,9 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
export const splitter = (queryString) => {
const splittedParts = [];
export const splitter = (queryString: string): string[] => {
const splittedParts: string[] = [];
let start = 0;
let isBracketStart = false;
let isQuoteStart = false;
@ -13,10 +15,6 @@ export const splitter = (queryString) => {
for (let idx = 0; idx < queryString.length; idx += 1) {
const currentChar = queryString[idx];
// if (start === null) {
// start = idx;
// }
if (currentChar === ' ') {
if (!isBracketStart && !isQuoteStart) {
pushPart(idx);
@ -48,7 +46,6 @@ export const splitter = (queryString) => {
pushPart(queryString.length);
}
// console.log(splittedParts.filter(Boolean));
return splittedParts.map((s) => String.raw`${s}`).filter(Boolean);
};

View File

@ -17,7 +17,6 @@ export const ConditionalOperators = {
OR: 'OR',
};
export const QueryTypes = {
QUERY_KEY: 'QUERY_KEY',
QUERY_OPERATOR: 'QUERY_OPERATOR',

View File

@ -1,4 +1,7 @@
export const fieldSearchFilter = (searchSpace = '', currentValue = '') => {
export const fieldSearchFilter = (
searchSpace = '',
currentValue = '',
): boolean => {
if (!currentValue || !searchSpace) {
return true;
}

View File

@ -1,14 +1,14 @@
import { ILog } from 'types/api/logs/log';
export function FlatLogData(log: ILog): Record<string, never> {
let flattenLogObject: Record<string, never> = {};
export function FlatLogData(log: ILog): Record<string, unknown> {
const flattenLogObject: Record<string, unknown> = {};
Object.keys(log).forEach((key: string) => {
if (typeof log[key] !== 'object') {
flattenLogObject[key] = log[key];
Object.keys(log).forEach((key: string): void => {
if (typeof log[key as never] !== 'object') {
flattenLogObject[key] = log[key as never];
} else {
Object.keys(log[key]).forEach((childKey) => {
flattenLogObject[childKey] = log[key][childKey]
Object.keys(log[key as never]).forEach((childKey) => {
flattenLogObject[childKey] = log[key as never][childKey];
});
}
});

View File

@ -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} `;
if (typeof fieldValue === 'number') {
generatedQueryString += `(${fieldValue})`;

View File

@ -1,7 +1,7 @@
import Logs from 'container/Logs';
import React from 'react';
function LogsHome() {
function LogsHome(): JSX.Element {
return <Logs />;
}

View File

@ -1,13 +1,14 @@
import GetSearchFields from 'api/logs/GetSearchFields';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import { GET_FIELDS, SET_FIELDS } from 'types/actions/logs';
import { TraceReducer } from 'types/reducer/trace';
import { SET_FIELDS } from 'types/actions/logs';
export const AddToSelectedField = (): ((dispatch: Dispatch<AppActions>) => void) => {
return async (dispatch): void => {
export const AddToSelectedField = (): ((
dispatch: Dispatch<AppActions>,
) => void) => {
return async (dispatch): Promise<void> => {
const response = await GetSearchFields();
if (response.payload)
dispatch({
type: SET_FIELDS,
payload: response.payload,

View File

@ -1,13 +1,12 @@
import GetSearchFields from 'api/logs/GetSearchFields';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import { GET_FIELDS, SET_FIELDS } from 'types/actions/logs';
import { TraceReducer } from 'types/reducer/trace';
import { SET_FIELDS } from 'types/actions/logs';
const IGNORED_SELECTED_FIELDS = ['timestamp'];
export const GetLogsFields = (): ((dispatch: Dispatch<AppActions>) => void) => {
return async (dispatch): void => {
return async (dispatch): Promise<void> => {
const response = await GetSearchFields();
if (response.payload) {
dispatch({

View File

@ -2,9 +2,12 @@ import GetLogs from 'api/logs/GetLogs';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import { SET_LOADING, SET_LOGS } from 'types/actions/logs';
import { Props } from 'types/api/logs/getLogs';
export const getLogs = (props): ((dispatch: Dispatch<AppActions>) => void) => {
return async (dispatch): void => {
export const getLogs = (
props: Props,
): ((dispatch: Dispatch<AppActions>) => void) => {
return async (dispatch): Promise<void> => {
dispatch({
type: SET_LOADING,
payload: true,

View File

@ -1,19 +1,17 @@
import GetLogs from 'api/logs/GetLogs';
import GetLogsAggregate from 'api/logs/GetLogsAggregate';
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
import {
SET_LOADING,
SET_LOADING_AGGREGATE,
SET_LOGS,
SET_LOGS_AGGREGATE_SERIES,
} from 'types/actions/logs';
import { Props } from 'types/api/logs/getLogsAggregate';
import { ILogsAggregate } from 'types/api/logs/logAggregate';
export const getLogsAggregate = (
props,
props: Props,
): ((dispatch: Dispatch<AppActions>) => void) => {
return async (dispatch): void => {
return async (dispatch): Promise<void> => {
dispatch({
type: SET_LOADING_AGGREGATE,
payload: true,

View File

@ -3,7 +3,7 @@ import { combineReducers } from 'redux';
import appReducer from './app';
import dashboardReducer from './dashboard';
import globalTimeReducer from './global';
import LogsReducer from './logs';
import { LogsReducer } from './logs';
import metricsReducers from './metric';
import { ServiceMapReducer } from './serviceMap';
import traceReducer from './trace';

View File

@ -21,7 +21,7 @@ import {
STOP_LIVE_TAIL,
TOGGLE_LIVE_TAIL,
} from 'types/actions/logs';
import ILogsReducer from 'types/reducer/logs';
import { ILogsReducer } from 'types/reducer/logs';
const initialState: ILogsReducer = {
fields: {
@ -39,33 +39,10 @@ const initialState: ILogsReducer = {
isLoading: false,
isLoadingAggregate: false,
logsAggregate: [],
detailedLog: null,
liveTail: 'STOPPED',
liveTailStartRange: 15,
// detailedLog: {
// timestamp: 1659360016955270100,
// 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: {},
// },
selectedLogId: null,
detailedLog: null,
};
export const LogsReducer = (
@ -123,9 +100,12 @@ export const LogsReducer = (
case ADD_SEARCH_FIELD_QUERY_STRING: {
const updatedQueryString =
state.searchFilter.queryString +
(state.searchFilter.queryString.length > 0 ? ' and ' : '') +
action.payload;
state.searchFilter.queryString ||
`${
state.searchFilter.queryString && state.searchFilter.queryString.length > 0
? ' and '
: ''
}${action.payload}`;
const updatedParsedQuery = parseQuery(updatedQueryString);
return {

View File

@ -123,42 +123,6 @@ export interface SetLiveTailStartTime {
type: typeof SET_LIVE_TAIL_START_TIME;
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 =
| GetFields

View File

@ -6,4 +6,8 @@ export type Props = {
limit: number;
orderBy: string;
order: string;
idGt?: string;
idLt?: string;
timestampStart?: number;
timestampEnd?: number;
};

View File

@ -9,4 +9,7 @@ export type Props = {
timestampStart: number;
timestampEnd: number;
step: number;
q?: string;
idGt?: string;
idLt?: string;
};

View File

@ -17,7 +17,7 @@ export interface ILogsReducer {
isLoading: boolean;
isLoadingAggregate: boolean;
logsAggregate: ILogsAggregate[];
selectedLogId: string;
selectedLogId: string | null;
detailedLog: null | ILog;
liveTail: TLogsLiveTailState;
liveTailStartRange: number; // time in minutes

File diff suppressed because it is too large Load Diff