From 05076968c9e7105cb01f6eaacc6acb925136b514 Mon Sep 17 00:00:00 2001 From: Kolesnyk Anton <50579516+KolesnikAntony@users.noreply.github.com> Date: Wed, 15 Feb 2023 07:20:39 +0200 Subject: [PATCH 01/20] fix: it has been fixed of difficult to click on metrics graph points (#2207) * fix: it has been fixed of difficult to click on metrics graph points * fix: resolve conflict * fix: changed hover point & memoized the passed props * fix: memo from develop * fix: add condition for end and start stamps * chore: type position is updated --------- Co-authored-by: palashgdev --- .../MetricsApplication/Tabs/Overview.tsx | 156 ++++++++++-------- .../container/MetricsApplication/Tabs/util.ts | 8 +- 2 files changed, 91 insertions(+), 73 deletions(-) diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index d51dae088a..8ecd0c9cba 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -1,3 +1,4 @@ +import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import Graph from 'components/Graph'; import { METRICS_PAGE_QUERY_PARAM } from 'constants/query'; import ROUTES from 'constants/routes'; @@ -29,7 +30,29 @@ import { onGraphClickHandler, onViewTracePopupClick } from './util'; function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const { servicename } = useParams<{ servicename?: string }>(); const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); + + const handleSetTimeStamp = useCallback((selectTime: number) => { + setSelectedTimeStamp(selectTime); + }, []); + const dispatch = useDispatch(); + const handleGraphClick = useCallback( + (type: string): ClickHandlerType => ( + ChartEvent: ChartEvent, + activeElements: ActiveElement[], + chart: Chart, + data: ChartData, + ): void => { + onGraphClickHandler(handleSetTimeStamp)( + ChartEvent, + activeElements, + chart, + data, + type, + ); + }, + [handleSetTimeStamp], + ); const { topOperations, @@ -82,14 +105,16 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); - dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + if (startTimestamp !== endTimestamp) { + dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + } }, [dispatch], ); const onErrorTrackHandler = (timestamp: number): void => { const currentTime = timestamp; - const tPlusOne = timestamp + 1 * 60 * 1000; + const tPlusOne = timestamp + 60 * 1000; const urlParams = new URLSearchParams(); urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); @@ -102,6 +127,52 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { ); }; + const generalChartDataProperties = useCallback( + (title: string, colorIndex: number) => ({ + borderColor: colors[colorIndex], + label: title, + showLine: true, + borderWidth: 1.5, + spanGaps: true, + pointRadius: 2, + pointHoverRadius: 4, + }), + [], + ); + + const dataSets = useMemo( + () => [ + { + data: serviceOverview.map((e) => + parseFloat(convertToNanoSecondsToSecond(e.p99)), + ), + ...generalChartDataProperties('p99 Latency', 0), + }, + { + data: serviceOverview.map((e) => + parseFloat(convertToNanoSecondsToSecond(e.p95)), + ), + ...generalChartDataProperties('p95 Latency', 1), + }, + { + data: serviceOverview.map((e) => + parseFloat(convertToNanoSecondsToSecond(e.p50)), + ), + ...generalChartDataProperties('p50 Latency', 2), + }, + ], + [generalChartDataProperties, serviceOverview], + ); + + const data = useMemo( + () => ({ + datasets: dataSets, + labels: serviceOverview.map( + (e) => new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))), + ), + }), + [serviceOverview, dataSets], + ); return ( <> @@ -122,58 +193,11 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { Latency { - onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, - 'Service', - ); - }} + animate={false} + onClickHandler={handleGraphClick('Service')} name="service_latency" type="line" - data={{ - datasets: [ - { - data: serviceOverview.map((e) => - parseFloat(convertToNanoSecondsToSecond(e.p99)), - ), - borderColor: colors[0], - label: 'p99 Latency', - showLine: true, - borderWidth: 1.5, - spanGaps: true, - pointRadius: 1.5, - }, - { - data: serviceOverview.map((e) => - parseFloat(convertToNanoSecondsToSecond(e.p95)), - ), - borderColor: colors[1], - label: 'p95 Latency', - showLine: true, - borderWidth: 1.5, - spanGaps: true, - pointRadius: 1.5, - }, - { - data: serviceOverview.map((e) => - parseFloat(convertToNanoSecondsToSecond(e.p50)), - ), - borderColor: colors[2], - label: 'p50 Latency', - showLine: true, - borderWidth: 1.5, - spanGaps: true, - pointRadius: 1.5, - }, - ], - labels: serviceOverview.map( - (e) => - new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))), - ), - }} + data={data} yAxisUnit="ms" onDragSelect={onDragSelect} /> @@ -200,15 +224,7 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { { - onGraphClickHandler(setSelectedTimeStamp)( - event, - element, - chart, - data, - 'Rate', - ); - }} + onClickHandler={handleGraphClick('Rate')} widget={operationPerSecWidget} yAxisUnit="ops" onDragSelect={onDragSelect} @@ -236,15 +252,7 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { { - onGraphClickHandler(setSelectedTimeStamp)( - ChartEvent, - activeElements, - chart, - data, - 'Error', - ); - }} + onClickHandler={handleGraphClick('Error')} widget={errorPercentageWidget} yAxisUnit="%" onDragSelect={onDragSelect} @@ -267,4 +275,12 @@ interface DashboardProps { getWidgetQueryBuilder: (query: Widgets['query']) => Widgets; } +type ClickHandlerType = ( + ChartEvent: ChartEvent, + activeElements: ActiveElement[], + chart: Chart, + data: ChartData, + type?: string, +) => void; + export default Application; diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index a0e29e60fa..c030492aff 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -21,7 +21,7 @@ export function onViewTracePopupClick( ): VoidFunction { return (): void => { const currentTime = timestamp; - const tPlusOne = timestamp + 1 * 60 * 1000; + const tPlusOne = timestamp + 60 * 1000; const urlParams = new URLSearchParams(); urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString()); @@ -36,7 +36,9 @@ export function onViewTracePopupClick( } export function onGraphClickHandler( - setSelectedTimeStamp: React.Dispatch>, + setSelectedTimeStamp: ( + n: number, + ) => void | React.Dispatch>, ) { return async ( event: ChartEvent, @@ -49,7 +51,7 @@ export function onGraphClickHandler( const points = chart.getElementsAtEventForMode( event.native, 'nearest', - { intersect: true }, + { intersect: false }, true, ); const id = `${from}_button`; From bad80def907186b7cf67c9bb070dd24f24e9d15c Mon Sep 17 00:00:00 2001 From: volodfast Date: Wed, 15 Feb 2023 11:25:15 +0200 Subject: [PATCH 02/20] feat: add list and table views for logs (#2163) * feat: add list and table views for logs * chore: some of the changes are updated * chore: some of the refactoring is done * chore: px to updated to rem * chore: constant is moved to local storage * refactor: some of the refactoring is updated * chore: some of the changes are updated * fix: resize log table issue * chore: logs is updated * chore: resize header is updated * chore: font observer is added in package json and hook is added for same * chore: no logs text is updated * chore: no logs text is updated * chore: updated some feedback in raw logs line * chore: types is added --------- Co-authored-by: Palash Gupta Co-authored-by: Pranay Prateek Co-authored-by: Vishal Sharma Co-authored-by: Chintan Sudani --- frontend/package.json | 2 + .../Logs/{LogItem => ListLogView}/index.tsx | 13 ++- .../Logs/{LogItem => ListLogView}/styles.ts | 0 .../Logs/{LogItem => ListLogView}/util.ts | 0 .../src/components/Logs/RawLogView/config.ts | 5 + .../src/components/Logs/RawLogView/index.tsx | 48 ++++++++ .../src/components/Logs/RawLogView/styles.ts | 24 ++++ .../src/components/Logs/TableView/config.ts | 12 ++ .../src/components/Logs/TableView/index.tsx | 106 ++++++++++++++++++ .../ResizeTable/ResizableHeader.tsx | 9 +- frontend/src/constants/localStorage.ts | 2 + frontend/src/container/LogControls/index.tsx | 32 ++++-- frontend/src/container/LogsFilters/index.tsx | 8 +- frontend/src/container/LogsFilters/styles.ts | 8 +- .../src/container/LogsSearchFilter/index.tsx | 1 + frontend/src/container/LogsTable/index.tsx | 102 ++++++++++++++--- frontend/src/container/LogsTable/styles.ts | 6 +- frontend/src/hooks/useFontObserver.tsx | 69 ++++++++++++ frontend/src/index.html.ejs | 8 +- frontend/src/pages/Logs/PopoverContent.tsx | 28 +++++ frontend/src/pages/Logs/config.ts | 25 +++++ frontend/src/pages/Logs/hooks.ts | 78 +++++++++++++ frontend/src/pages/Logs/index.tsx | 99 +++++++++++++++- frontend/src/pages/Logs/types.ts | 17 +++ frontend/src/pages/Logs/utils.ts | 7 ++ .../src/store/actions/logs/setLInesPerRow.ts | 14 +++ .../src/store/actions/logs/setViewMode.ts | 15 +++ frontend/src/store/reducers/logs.ts | 18 +++ frontend/src/types/actions/logs.ts | 14 +++ frontend/src/types/reducer/logs.ts | 3 + 30 files changed, 714 insertions(+), 59 deletions(-) rename frontend/src/components/Logs/{LogItem => ListLogView}/index.tsx (95%) rename frontend/src/components/Logs/{LogItem => ListLogView}/styles.ts (100%) rename frontend/src/components/Logs/{LogItem => ListLogView}/util.ts (100%) create mode 100644 frontend/src/components/Logs/RawLogView/config.ts create mode 100644 frontend/src/components/Logs/RawLogView/index.tsx create mode 100644 frontend/src/components/Logs/RawLogView/styles.ts create mode 100644 frontend/src/components/Logs/TableView/config.ts create mode 100644 frontend/src/components/Logs/TableView/index.tsx create mode 100644 frontend/src/hooks/useFontObserver.tsx create mode 100644 frontend/src/pages/Logs/PopoverContent.tsx create mode 100644 frontend/src/pages/Logs/config.ts create mode 100644 frontend/src/pages/Logs/hooks.ts create mode 100644 frontend/src/pages/Logs/types.ts create mode 100644 frontend/src/pages/Logs/utils.ts create mode 100644 frontend/src/store/actions/logs/setLInesPerRow.ts create mode 100644 frontend/src/store/actions/logs/setViewMode.ts diff --git a/frontend/package.json b/frontend/package.json index 0486067372..ba9e1d3e05 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -55,6 +55,7 @@ "event-source-polyfill": "1.0.31", "file-loader": "6.1.1", "flat": "^5.0.2", + "fontfaceobserver": "2.3.0", "history": "4.10.1", "html-webpack-plugin": "5.1.0", "i18next": "^21.6.12", @@ -127,6 +128,7 @@ "@types/d3-tip": "^3.5.5", "@types/event-source-polyfill": "^1.0.0", "@types/flat": "^5.0.2", + "@types/fontfaceobserver": "2.1.0", "@types/jest": "^27.5.1", "@types/lodash-es": "^4.17.4", "@types/mini-css-extract-plugin": "^2.5.1", diff --git a/frontend/src/components/Logs/LogItem/index.tsx b/frontend/src/components/Logs/ListLogView/index.tsx similarity index 95% rename from frontend/src/components/Logs/LogItem/index.tsx rename to frontend/src/components/Logs/ListLogView/index.tsx index e09f6c4ba7..d8108062aa 100644 --- a/frontend/src/components/Logs/LogItem/index.tsx +++ b/frontend/src/components/Logs/ListLogView/index.tsx @@ -4,17 +4,21 @@ import { Button, Divider, Row, Typography } from 'antd'; import { map } from 'd3'; import dayjs from 'dayjs'; import { useNotifications } from 'hooks/useNotifications'; +// utils import { FlatLogData } from 'lib/logs/flatLogData'; import React, { useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useCopyToClipboard } from 'react-use'; +// interfaces import { AppState } from 'store/reducers'; import { SET_DETAILED_LOG_DATA } from 'types/actions/logs'; import { ILog } from 'types/api/logs/log'; import { ILogsReducer } from 'types/reducer/logs'; +// components import AddToQueryHOC from '../AddToQueryHOC'; import CopyClipboardHOC from '../CopyClipboardHOC'; +// styles import { Container, LogContainer, Text, TextContainer } from './styles'; import { isValidLogField } from './util'; @@ -37,6 +41,7 @@ function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element { ); } + function LogSelectedField({ fieldKey = '', fieldValue = '', @@ -70,15 +75,17 @@ function LogSelectedField({ ); } -interface LogItemProps { +interface ListLogViewProps { logData: ILog; } -function LogItem({ logData }: LogItemProps): JSX.Element { +function ListLogView({ logData }: ListLogViewProps): JSX.Element { const { fields: { selected }, } = useSelector((state) => state.logs); + const dispatch = useDispatch(); const flattenLogData = useMemo(() => FlatLogData(logData), [logData]); + const [, setCopy] = useCopyToClipboard(); const { notifications } = useNotifications(); @@ -152,4 +159,4 @@ function LogItem({ logData }: LogItemProps): JSX.Element { ); } -export default LogItem; +export default ListLogView; diff --git a/frontend/src/components/Logs/LogItem/styles.ts b/frontend/src/components/Logs/ListLogView/styles.ts similarity index 100% rename from frontend/src/components/Logs/LogItem/styles.ts rename to frontend/src/components/Logs/ListLogView/styles.ts diff --git a/frontend/src/components/Logs/LogItem/util.ts b/frontend/src/components/Logs/ListLogView/util.ts similarity index 100% rename from frontend/src/components/Logs/LogItem/util.ts rename to frontend/src/components/Logs/ListLogView/util.ts diff --git a/frontend/src/components/Logs/RawLogView/config.ts b/frontend/src/components/Logs/RawLogView/config.ts new file mode 100644 index 0000000000..66adeae252 --- /dev/null +++ b/frontend/src/components/Logs/RawLogView/config.ts @@ -0,0 +1,5 @@ +export const rawLineStyle: React.CSSProperties = { + marginBottom: 0, + fontFamily: "'Fira Code', monospace", + fontWeight: 300, +}; diff --git a/frontend/src/components/Logs/RawLogView/index.tsx b/frontend/src/components/Logs/RawLogView/index.tsx new file mode 100644 index 0000000000..9cfafc5bd3 --- /dev/null +++ b/frontend/src/components/Logs/RawLogView/index.tsx @@ -0,0 +1,48 @@ +import { ExpandAltOutlined } from '@ant-design/icons'; +import { Typography } from 'antd'; +import dayjs from 'dayjs'; +// hooks +import { useIsDarkMode } from 'hooks/useDarkMode'; +import React, { useCallback, useMemo } from 'react'; +// interfaces +import { ILog } from 'types/api/logs/log'; + +import { rawLineStyle } from './config'; +// styles +import { ExpandIconWrapper, RawLogViewContainer } from './styles'; + +interface RawLogViewProps { + data: ILog; + linesPerRow: number; + onClickExpand: (log: ILog) => void; +} + +function RawLogView(props: RawLogViewProps): JSX.Element { + const { data, linesPerRow, onClickExpand } = props; + + const isDarkMode = useIsDarkMode(); + + const text = useMemo( + () => `${dayjs(data.timestamp / 1e6).format()} | ${data.body}`, + [data.timestamp, data.body], + ); + + const ellipsis = useMemo(() => ({ rows: linesPerRow }), [linesPerRow]); + + const handleClickExpand = useCallback(() => { + onClickExpand(data); + }, [onClickExpand, data]); + + return ( + + + + + + {text} + + + ); +} + +export default RawLogView; diff --git a/frontend/src/components/Logs/RawLogView/styles.ts b/frontend/src/components/Logs/RawLogView/styles.ts new file mode 100644 index 0000000000..19163adc07 --- /dev/null +++ b/frontend/src/components/Logs/RawLogView/styles.ts @@ -0,0 +1,24 @@ +import { blue } from '@ant-design/colors'; +import { Col, Row } from 'antd'; +import styled from 'styled-components'; + +export const RawLogViewContainer = styled(Row)<{ $isDarkMode: boolean }>` + width: 100%; + font-weight: 700; + font-size: 0.625rem; + line-height: 1.25rem; + + transition: background-color 0.2s ease-in; + + &:hover { + background-color: ${({ $isDarkMode }): string => + $isDarkMode ? 'rgba(255,255,255,0.1)' : 'rgba(0, 0, 0, 0.1)'}; + } +`; + +export const ExpandIconWrapper = styled(Col)` + color: ${blue[6]}; + padding: 0.25rem 0.375rem; + cursor: pointer; + font-size: 12px; +`; diff --git a/frontend/src/components/Logs/TableView/config.ts b/frontend/src/components/Logs/TableView/config.ts new file mode 100644 index 0000000000..f68dcbfebf --- /dev/null +++ b/frontend/src/components/Logs/TableView/config.ts @@ -0,0 +1,12 @@ +import { TableProps } from 'antd'; + +export const defaultCellStyle: React.CSSProperties = { + paddingTop: 4, + paddingBottom: 6, + paddingRight: 8, + paddingLeft: 8, +}; + +export const tableScroll: TableProps>['scroll'] = { + x: true, +}; diff --git a/frontend/src/components/Logs/TableView/index.tsx b/frontend/src/components/Logs/TableView/index.tsx new file mode 100644 index 0000000000..3cb6d9a041 --- /dev/null +++ b/frontend/src/components/Logs/TableView/index.tsx @@ -0,0 +1,106 @@ +import { ExpandAltOutlined } from '@ant-design/icons'; +import { Table, Typography } from 'antd'; +import { ColumnsType, ColumnType } from 'antd/es/table'; +import dayjs from 'dayjs'; +// utils +import { FlatLogData } from 'lib/logs/flatLogData'; +import React, { useMemo } from 'react'; +import { IField } from 'types/api/logs/fields'; +// interfaces +import { ILog } from 'types/api/logs/log'; + +// styles +import { ExpandIconWrapper } from '../RawLogView/styles'; +// config +import { defaultCellStyle, tableScroll } from './config'; + +type ColumnTypeRender = ReturnType< + NonNullable['render']> +>; + +type LogsTableViewProps = { + logs: ILog[]; + fields: IField[]; + linesPerRow: number; + onClickExpand: (log: ILog) => void; +}; + +function LogsTableView(props: LogsTableViewProps): JSX.Element { + const { logs, fields, linesPerRow, onClickExpand } = props; + + const flattenLogData = useMemo(() => logs.map((log) => FlatLogData(log)), [ + logs, + ]); + + const columns: ColumnsType> = useMemo(() => { + const fieldColumns: ColumnsType> = fields.map( + ({ name }) => ({ + title: name, + dataIndex: name, + key: name, + render: (field): ColumnTypeRender> => ({ + props: { + style: defaultCellStyle, + }, + children: ( + + {field} + + ), + }), + }), + ); + + return [ + { + title: '', + dataIndex: 'id', + key: 'expand', + // https://github.com/ant-design/ant-design/discussions/36886 + render: (_, item): ColumnTypeRender> => ({ + props: { + style: defaultCellStyle, + }, + children: ( + { + onClickExpand((item as unknown) as ILog); + }} + > + + + ), + }), + }, + { + title: 'Timestamp', + dataIndex: 'timestamp', + key: 'timestamp', + // https://github.com/ant-design/ant-design/discussions/36886 + render: (field): ColumnTypeRender> => { + const date = dayjs(field / 1e6).format(); + return { + props: { + style: defaultCellStyle, + }, + children: {date}, + }; + }, + }, + ...fieldColumns, + ]; + }, [fields, linesPerRow, onClickExpand]); + + return ( + + ); +} + +export default LogsTableView; diff --git a/frontend/src/components/ResizeTable/ResizableHeader.tsx b/frontend/src/components/ResizeTable/ResizableHeader.tsx index 48f904b05a..b3119c0c1a 100644 --- a/frontend/src/components/ResizeTable/ResizableHeader.tsx +++ b/frontend/src/components/ResizeTable/ResizableHeader.tsx @@ -17,13 +17,6 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element { [], ); - const draggableOpts = useMemo( - () => ({ - enableUserSelectHack, - }), - [], - ); - if (!width) { // eslint-disable-next-line react/jsx-props-no-spreading return - + ); } diff --git a/frontend/src/container/LogsFilters/styles.ts b/frontend/src/container/LogsFilters/styles.ts index 38aca7fe1e..f6e9e69bff 100644 --- a/frontend/src/container/LogsFilters/styles.ts +++ b/frontend/src/container/LogsFilters/styles.ts @@ -1,13 +1,7 @@ import { blue, grey } from '@ant-design/colors'; -import { Col, Typography } from 'antd'; +import { Typography } from 'antd'; import styled from 'styled-components'; -export const Container = styled(Col)` - padding-top: 0.3rem; - min-width: 15.625rem; - max-width: 21.875rem; -`; - export const CategoryContainer = styled.div` margin: 1rem 0; padding-left: 0.2rem; diff --git a/frontend/src/container/LogsSearchFilter/index.tsx b/frontend/src/container/LogsSearchFilter/index.tsx index 098d6c60ed..b124ab6c1a 100644 --- a/frontend/src/container/LogsSearchFilter/index.tsx +++ b/frontend/src/container/LogsSearchFilter/index.tsx @@ -48,6 +48,7 @@ function SearchFilter({ AppState, ILogsReducer >((state) => state.logs); + const globalTime = useSelector( (state) => state.globalTime, ); diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx index c1f6f7c690..2b0013035d 100644 --- a/frontend/src/container/LogsTable/index.tsx +++ b/frontend/src/container/LogsTable/index.tsx @@ -1,19 +1,53 @@ -import { Typography } from 'antd'; -import LogItem from 'components/Logs/LogItem'; +import { Card, Typography } from 'antd'; +// components +import ListLogView from 'components/Logs/ListLogView'; +import RawLogView from 'components/Logs/RawLogView'; +import LogsTableView from 'components/Logs/TableView'; import Spinner from 'components/Spinner'; +import { contentStyle } from 'container/Trace/Search/config'; +import useFontFaceObserver from 'hooks/useFontObserver'; import React, { memo, useCallback, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { Virtuoso } from 'react-virtuoso'; +// interfaces import { AppState } from 'store/reducers'; +import { ILog } from 'types/api/logs/log'; import { ILogsReducer } from 'types/reducer/logs'; +// styles import { Container, Heading } from './styles'; -function LogsTable(): JSX.Element { - const { logs, isLoading, liveTail } = useSelector( - (state) => state.logs, +export type LogViewMode = 'raw' | 'table' | 'list'; + +type LogsTableProps = { + viewMode: LogViewMode; + linesPerRow: number; + onClickExpand: (logData: ILog) => void; +}; + +function LogsTable(props: LogsTableProps): JSX.Element { + const { viewMode, onClickExpand, linesPerRow } = props; + + useFontFaceObserver( + [ + { + family: 'Fira Code', + weight: '300', + }, + ], + viewMode === 'raw', + { + timeout: 5000, + }, ); + const { + logs, + fields: { selected }, + isLoading, + liveTail, + } = useSelector((state) => state.logs); + const isLiveTail = useMemo(() => logs.length === 0 && liveTail === 'PLAYING', [ logs?.length, liveTail, @@ -27,29 +61,63 @@ function LogsTable(): JSX.Element { const getItemContent = useCallback( (index: number): JSX.Element => { const log = logs[index]; - return ; + + if (viewMode === 'raw') { + return ( + + ); + } + + return ; }, - [logs], + [logs, linesPerRow, viewMode, onClickExpand], ); + const renderContent = useMemo(() => { + if (viewMode === 'table') { + return ( + + ); + } + + return ( + + + + ); + }, [getItemContent, linesPerRow, logs, onClickExpand, selected, viewMode]); + if (isLoading) { return ; } return ( - - - Event - + + {viewMode !== 'table' && ( + + Event + + )} + {isLiveTail && Getting live logs...} - {isNoLogs && No log lines found} + {isNoLogs && No logs lines found} - + {renderContent} ); } diff --git a/frontend/src/container/LogsTable/styles.ts b/frontend/src/container/LogsTable/styles.ts index cf42d3df9b..a7c843fa51 100644 --- a/frontend/src/container/LogsTable/styles.ts +++ b/frontend/src/container/LogsTable/styles.ts @@ -1,14 +1,16 @@ -import { Card, Col } from 'antd'; +import { Card } from 'antd'; import styled from 'styled-components'; -export const Container = styled(Col)` +export const Container = styled.div` overflow-x: hidden; width: 100%; margin-bottom: 1rem; + margin-top: 0.5rem; `; export const Heading = styled(Card)` margin-bottom: 0.1rem; + height: 32px; .ant-card-body { padding: 0.3rem 0.5rem; } diff --git a/frontend/src/hooks/useFontObserver.tsx b/frontend/src/hooks/useFontObserver.tsx new file mode 100644 index 0000000000..7292a8ee28 --- /dev/null +++ b/frontend/src/hooks/useFontObserver.tsx @@ -0,0 +1,69 @@ +import FontFaceObserver from 'fontfaceobserver'; +import { useEffect, useState } from 'react'; + +export interface FontFace { + family: string; + weight?: + | `light` + | `normal` + | `bold` + | `bolder` + | `100` + | `200` + | `300` + | `400` + | `500` + | `600` + | `700` + | `800` + | `900`; + style?: `normal` | `italic` | `oblique`; + stretch?: + | `normal` + | `ultra-condensed` + | `extra-condensed` + | `condensed` + | `semi-condensed` + | `semi-expanded` + | `expanded` + | `extra-expanded` + | `ultra-expanded`; +} + +export interface Options { + testString?: string; + timeout?: number; +} + +export interface Config { + showErrors: boolean; +} + +function useFontFaceObserver( + fontFaces: FontFace[] = [], + isEnabled = true, + { testString, timeout }: Options = {}, + { showErrors }: Config = { showErrors: false }, +): boolean { + const [isResolved, setIsResolved] = useState(false); + const fontFacesString = JSON.stringify(fontFaces); + + useEffect(() => { + if (isEnabled) { + const promises = JSON.parse(fontFacesString).map( + ({ family, weight, style, stretch }: FontFace) => + new FontFaceObserver(family, { + weight, + style, + stretch, + }).load(testString, timeout), + ); + + Promise.all(promises).then(() => setIsResolved(true)); + } + }, [fontFacesString, testString, timeout, showErrors, isEnabled]); + + return isResolved; +} + +export default useFontFaceObserver; diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs index 60deae1901..6d7c037e1f 100644 --- a/frontend/src/index.html.ejs +++ b/frontend/src/index.html.ejs @@ -50,8 +50,14 @@ + + + - +
diff --git a/frontend/src/pages/Logs/PopoverContent.tsx b/frontend/src/pages/Logs/PopoverContent.tsx new file mode 100644 index 0000000000..8dcf819d3a --- /dev/null +++ b/frontend/src/pages/Logs/PopoverContent.tsx @@ -0,0 +1,28 @@ +import { InputNumber, Row, Space, Typography } from 'antd'; +import React from 'react'; + +interface PopoverContentProps { + linesPerRow: number; + handleLinesPerRowChange: (l: unknown) => void; +} + +function PopoverContent({ + linesPerRow, + handleLinesPerRowChange, +}: PopoverContentProps): JSX.Element { + return ( + + + Max lines per Row + + + + ); +} + +export default PopoverContent; diff --git a/frontend/src/pages/Logs/config.ts b/frontend/src/pages/Logs/config.ts new file mode 100644 index 0000000000..dca2a3b937 --- /dev/null +++ b/frontend/src/pages/Logs/config.ts @@ -0,0 +1,25 @@ +import { ViewModeOption } from './types'; + +export const viewModeOptionList: ViewModeOption[] = [ + { + key: 'raw', + label: 'Raw', + value: 'raw', + }, + { + key: 'table', + label: 'Table', + value: 'table', + }, + { + key: 'list', + label: 'List', + value: 'list', + }, +]; + +export const logsOptions = ['raw', 'table']; + +export const defaultSelectStyle: React.CSSProperties = { + minWidth: '6rem', +}; diff --git a/frontend/src/pages/Logs/hooks.ts b/frontend/src/pages/Logs/hooks.ts new file mode 100644 index 0000000000..7093147ce3 --- /dev/null +++ b/frontend/src/pages/Logs/hooks.ts @@ -0,0 +1,78 @@ +// utils +import get from 'api/browser/localstorage/get'; +import { LOCALSTORAGE } from 'constants/localStorage'; +// interfaces +import { LogViewMode } from 'container/LogsTable'; +import { useCallback, useLayoutEffect, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { setLinesPerRow } from 'store/actions/logs/setLInesPerRow'; +// actions +import { setViewMode } from 'store/actions/logs/setViewMode'; +import { AppState } from 'store/reducers'; + +import { viewModeOptionList } from './config'; +import { SelectedLogViewData } from './types'; +import { isLogViewMode } from './utils'; + +export const useSelectedLogView = (): SelectedLogViewData => { + const dispatch = useDispatch(); + const viewMode = useSelector( + (state) => state.logs.viewMode, + ); + const linesPerRow = useSelector( + (state) => state.logs.linesPerRow, + ); + + const viewModeOption = useMemo( + () => + viewModeOptionList.find( + (viewModeOption) => viewModeOption.value === viewMode, + ) ?? viewModeOptionList[0], + [viewMode], + ); + + const handleViewModeChange = useCallback( + (selectedViewMode: LogViewMode) => { + dispatch(setViewMode(selectedViewMode)); + }, + [dispatch], + ); + + const handleViewModeOptionChange = useCallback( + ({ key }: { key: string }) => { + if (isLogViewMode(key)) handleViewModeChange(key); + }, + [handleViewModeChange], + ); + + const handleLinesPerRowChange = useCallback( + (selectedLinesPerRow: unknown) => { + if (typeof selectedLinesPerRow === 'number') { + dispatch(setLinesPerRow(selectedLinesPerRow)); + } + }, + [dispatch], + ); + + useLayoutEffect(() => { + const storedViewMode = get(LOCALSTORAGE.LOGS_VIEW_MODE); + if (storedViewMode) { + handleViewModeChange(storedViewMode as LogViewMode); + } + + const storedLinesPerRow = get(LOCALSTORAGE.LOGS_LINES_PER_ROW); + if (storedLinesPerRow) { + handleLinesPerRowChange(+storedLinesPerRow); + } + }, [handleViewModeChange, handleLinesPerRowChange]); + + return { + viewModeOptionList, + viewModeOption, + viewMode, + handleViewModeChange, + handleViewModeOptionChange, + linesPerRow, + handleLinesPerRowChange, + }; +}; diff --git a/frontend/src/pages/Logs/index.tsx b/frontend/src/pages/Logs/index.tsx index 15cc757ab3..d92c45708e 100644 --- a/frontend/src/pages/Logs/index.tsx +++ b/frontend/src/pages/Logs/index.tsx @@ -1,4 +1,4 @@ -import { Divider, Row } from 'antd'; +import { Button, Col, Divider, Popover, Row, Select, Space } from 'antd'; import LogControls from 'container/LogControls'; import LogDetailedView from 'container/LogDetailedView'; import LogLiveTail from 'container/LogLiveTail'; @@ -6,11 +6,67 @@ import LogsAggregate from 'container/LogsAggregate'; import LogsFilters from 'container/LogsFilters'; import LogsSearchFilter from 'container/LogsSearchFilter'; import LogsTable from 'container/LogsTable'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; +import AppActions from 'types/actions'; +import { SET_DETAILED_LOG_DATA } from 'types/actions/logs'; +import { ILog } from 'types/api/logs/log'; +import { defaultSelectStyle, logsOptions } from './config'; +import { useSelectedLogView } from './hooks'; +import PopoverContent from './PopoverContent'; import SpaceContainer from './styles'; function Logs(): JSX.Element { + const dispatch = useDispatch>(); + + const showExpandedLog = useCallback( + (logData: ILog) => { + dispatch({ + type: SET_DETAILED_LOG_DATA, + payload: logData, + }); + }, + [dispatch], + ); + + const { + viewModeOptionList, + viewModeOption, + viewMode, + handleViewModeOptionChange, + linesPerRow, + handleLinesPerRowChange, + } = useSelectedLogView(); + + const renderPopoverContent = useCallback( + () => ( + + ), + [linesPerRow, handleLinesPerRowChange], + ); + + const isFormatButtonVisible = useMemo(() => logsOptions.includes(viewMode), [ + viewMode, + ]); + + const selectedViewModeOption = useMemo(() => viewModeOption.value.toString(), [ + viewModeOption.value, + ]); + + const onChangeVeiwMode = useCallback( + (key: string) => { + handleViewModeOptionChange({ + key, + }); + }, + [handleViewModeOptionChange], + ); + return ( <> - - + - - +
+ + + + + + {isFormatButtonVisible && ( + + + + )} + + + + + + + + + + + ); diff --git a/frontend/src/pages/Logs/types.ts b/frontend/src/pages/Logs/types.ts new file mode 100644 index 0000000000..c4bbfc6042 --- /dev/null +++ b/frontend/src/pages/Logs/types.ts @@ -0,0 +1,17 @@ +import { ItemType } from 'antd/es/menu/hooks/useItems'; +import { LogViewMode } from 'container/LogsTable'; + +export type ViewModeOption = ItemType & { + label: string; + value: LogViewMode; +}; + +export type SelectedLogViewData = { + viewModeOptionList: ViewModeOption[]; + viewModeOption: ViewModeOption; + viewMode: LogViewMode; + handleViewModeChange: (s: LogViewMode) => void; + handleViewModeOptionChange: ({ key }: { key: string }) => void; + linesPerRow: number; + handleLinesPerRowChange: (l: unknown) => void; +}; diff --git a/frontend/src/pages/Logs/utils.ts b/frontend/src/pages/Logs/utils.ts new file mode 100644 index 0000000000..2d8f317a9a --- /dev/null +++ b/frontend/src/pages/Logs/utils.ts @@ -0,0 +1,7 @@ +import { LogViewMode } from 'container/LogsTable'; + +import { viewModeOptionList } from './config'; + +export const isLogViewMode = (value: unknown): value is LogViewMode => + typeof value === 'string' && + viewModeOptionList.some((option) => option.key === value); diff --git a/frontend/src/store/actions/logs/setLInesPerRow.ts b/frontend/src/store/actions/logs/setLInesPerRow.ts new file mode 100644 index 0000000000..3931053574 --- /dev/null +++ b/frontend/src/store/actions/logs/setLInesPerRow.ts @@ -0,0 +1,14 @@ +import set from 'api/browser/localstorage/set'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import { SET_LINES_PER_ROW } from 'types/actions/logs'; + +type ActionSetLinesPerRow = { type: typeof SET_LINES_PER_ROW; payload: number }; + +export function setLinesPerRow(lines: number): ActionSetLinesPerRow { + set(LOCALSTORAGE.LOGS_LINES_PER_ROW, lines.toString()); + + return { + type: SET_LINES_PER_ROW, + payload: lines, + }; +} diff --git a/frontend/src/store/actions/logs/setViewMode.ts b/frontend/src/store/actions/logs/setViewMode.ts new file mode 100644 index 0000000000..4f5bb3fb1d --- /dev/null +++ b/frontend/src/store/actions/logs/setViewMode.ts @@ -0,0 +1,15 @@ +import set from 'api/browser/localstorage/set'; +import { LOCALSTORAGE } from 'constants/localStorage'; +import { LogViewMode } from 'container/LogsTable'; +import { SET_VIEW_MODE } from 'types/actions/logs'; + +type ActionSetViewMode = { type: typeof SET_VIEW_MODE; payload: LogViewMode }; + +export function setViewMode(viewMode: LogViewMode): ActionSetViewMode { + set(LOCALSTORAGE.LOGS_VIEW_MODE, viewMode); + + return { + type: SET_VIEW_MODE, + payload: viewMode, + }; +} diff --git a/frontend/src/store/reducers/logs.ts b/frontend/src/store/reducers/logs.ts index e920d68a6e..06c6b41b70 100644 --- a/frontend/src/store/reducers/logs.ts +++ b/frontend/src/store/reducers/logs.ts @@ -10,6 +10,7 @@ import { RESET_ID_START_AND_END, SET_DETAILED_LOG_DATA, SET_FIELDS, + SET_LINES_PER_ROW, SET_LIVE_TAIL_START_TIME, SET_LOADING, SET_LOADING_AGGREGATE, @@ -18,6 +19,7 @@ import { SET_LOGS_AGGREGATE_SERIES, SET_SEARCH_QUERY_PARSED_PAYLOAD, SET_SEARCH_QUERY_STRING, + SET_VIEW_MODE, STOP_LIVE_TAIL, TOGGLE_LIVE_TAIL, UPDATE_INTERESTING_FIELDS, @@ -36,6 +38,8 @@ const initialState: ILogsReducer = { }, logs: [], logLinesPerPage: 25, + linesPerRow: 2, + viewMode: 'raw', idEnd: '', idStart: '', isLoading: false, @@ -205,6 +209,20 @@ export const LogsReducer = ( }; } + case SET_LINES_PER_ROW: { + return { + ...state, + linesPerRow: action.payload, + }; + } + + case SET_VIEW_MODE: { + return { + ...state, + viewMode: action.payload, + }; + } + case UPDATE_INTERESTING_FIELDS: { return { ...state, diff --git a/frontend/src/types/actions/logs.ts b/frontend/src/types/actions/logs.ts index b680b70233..bd400f9204 100644 --- a/frontend/src/types/actions/logs.ts +++ b/frontend/src/types/actions/logs.ts @@ -1,3 +1,4 @@ +import { LogViewMode } from 'container/LogsTable'; import { ILogQLParsedQueryItem } from 'lib/logql/types'; import { IField, IFieldMoveToSelected, IFields } from 'types/api/logs/fields'; import { TLogsLiveTailState } from 'types/api/logs/liveTail'; @@ -28,6 +29,8 @@ export const PUSH_LIVE_TAIL_EVENT = 'LOGS_PUSH_LIVE_TAIL_EVENT'; export const STOP_LIVE_TAIL = 'LOGS_STOP_LIVE_TAIL'; export const FLUSH_LOGS = 'LOGS_FLUSH_LOGS'; export const SET_LIVE_TAIL_START_TIME = 'LOGS_SET_LIVE_TAIL_START_TIME'; +export const SET_LINES_PER_ROW = 'SET_LINES_PER_ROW'; +export const SET_VIEW_MODE = 'SET_VIEW_MODE'; export const UPDATE_SELECTED_FIELDS = 'LOGS_UPDATE_SELECTED_FIELDS'; export const UPDATE_INTERESTING_FIELDS = 'LOGS_UPDATE_INTERESTING_FIELDS'; @@ -118,6 +121,15 @@ export interface SetLiveTailStartTime { payload: number; } +export interface SetLinesPerRow { + type: typeof SET_LINES_PER_ROW; + payload: number; +} + +export interface SetViewMode { + type: typeof SET_VIEW_MODE; + payload: LogViewMode; +} type IFieldType = 'interesting' | 'selected'; export interface UpdateSelectedInterestFields { @@ -149,4 +161,6 @@ export type LogsActions = | StopLiveTail | FlushLogs | SetLiveTailStartTime + | SetLinesPerRow + | SetViewMode | UpdateSelectedInterestFields; diff --git a/frontend/src/types/reducer/logs.ts b/frontend/src/types/reducer/logs.ts index 43b3c6f7fa..762afc199c 100644 --- a/frontend/src/types/reducer/logs.ts +++ b/frontend/src/types/reducer/logs.ts @@ -1,3 +1,4 @@ +import { LogViewMode } from 'container/LogsTable'; import { ILogQLParsedQueryItem } from 'lib/logql/types'; import { IFields } from 'types/api/logs/fields'; import { TLogsLiveTailState } from 'types/api/logs/liveTail'; @@ -12,6 +13,8 @@ export interface ILogsReducer { }; logs: ILog[]; logLinesPerPage: number; + linesPerRow: number; + viewMode: LogViewMode; idEnd: string; idStart: string; isLoading: boolean; From dca0b11acdd62cc02d454ab8cd0d2e67fc2898a2 Mon Sep 17 00:00:00 2001 From: palashgdev Date: Wed, 15 Feb 2023 15:49:24 +0530 Subject: [PATCH 03/20] fix: onSearch callback is updated (#2266) --- frontend/src/container/LogsSearchFilter/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/container/LogsSearchFilter/index.tsx b/frontend/src/container/LogsSearchFilter/index.tsx index b124ab6c1a..3751b91487 100644 --- a/frontend/src/container/LogsSearchFilter/index.tsx +++ b/frontend/src/container/LogsSearchFilter/index.tsx @@ -186,8 +186,8 @@ function SearchFilter({ onChange={(e): void => { const { value } = e.target; setSearchText(value); - debouncedupdateQueryString(value); }} + onSearch={debouncedupdateQueryString} allowClear /> From 2fa265ff2e8ccfac60aeec41db150705615b6e1f Mon Sep 17 00:00:00 2001 From: palashgdev Date: Wed, 15 Feb 2023 16:03:53 +0530 Subject: [PATCH 04/20] fix: onDrag is updated (#2267) --- frontend/src/container/GridGraphLayout/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/GridGraphLayout/index.tsx b/frontend/src/container/GridGraphLayout/index.tsx index 8b85557bfe..2d57c98eda 100644 --- a/frontend/src/container/GridGraphLayout/index.tsx +++ b/frontend/src/container/GridGraphLayout/index.tsx @@ -77,7 +77,9 @@ function GridGraph(props: Props): JSX.Element { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); - dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + if (startTimestamp !== endTimestamp) { + dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + } }, [dispatch], ); From 28142764af35a85233aa147a358a35ccd1f4bd9b Mon Sep 17 00:00:00 2001 From: palashgdev Date: Wed, 15 Feb 2023 18:32:40 +0530 Subject: [PATCH 05/20] chore: testMatch is updated (#2270) --- frontend/jest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/jest.config.ts b/frontend/jest.config.ts index 9021421c78..51d6776579 100644 --- a/frontend/jest.config.ts +++ b/frontend/jest.config.ts @@ -15,7 +15,7 @@ const config: Config.InitialOptions = { useESM: true, }, }, - testMatch: ['/src/**/?(*.)(test).(ts|js)?(x)'], + testMatch: ['/src/**/*?(*.)(test).(ts|js)?(x)'], preset: 'ts-jest/presets/js-with-ts-esm', transform: { '^.+\\.(ts|tsx)?$': 'ts-jest', From c18fff6ae87d25123b06b65236b0f68eb3a94f45 Mon Sep 17 00:00:00 2001 From: GitStart <1501599+gitstart@users.noreply.github.com> Date: Fri, 17 Feb 2023 06:34:51 +0100 Subject: [PATCH 06/20] FE: remove @types/redux (#2274) * FE: remove @types/redux * Update package.json --------- Co-authored-by: gitstart --- frontend/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index ba9e1d3e05..0f731c45f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -139,7 +139,6 @@ "@types/react-redux": "^7.1.11", "@types/react-resizable": "3.0.3", "@types/react-router-dom": "^5.1.6", - "@types/redux": "^3.6.0", "@types/styled-components": "^5.1.4", "@types/uuid": "^8.3.1", "@types/vis": "^4.21.21", From c657f96032a3889f35075603dae6aebee511eede Mon Sep 17 00:00:00 2001 From: GitStart <1501599+gitstart@users.noreply.github.com> Date: Fri, 17 Feb 2023 06:58:09 +0100 Subject: [PATCH 07/20] fix: overflowing last timestamp (#2271) Co-authored-by: gitstart Co-authored-by: palashgdev --- frontend/src/container/Timeline/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/Timeline/index.tsx b/frontend/src/container/Timeline/index.tsx index 5d9a6465f6..9deeaaa261 100644 --- a/frontend/src/container/Timeline/index.tsx +++ b/frontend/src/container/Timeline/index.tsx @@ -76,7 +76,11 @@ function Timeline({ },0)`} key={`${interval.label + interval.percentage + index}`} > - + {interval.label} Date: Mon, 20 Feb 2023 17:28:38 +0530 Subject: [PATCH 08/20] fix: width is added for the max content (#2292) --- .../src/container/GantChart/SpanLength/styles.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/frontend/src/container/GantChart/SpanLength/styles.ts b/frontend/src/container/GantChart/SpanLength/styles.ts index 0af7b0a234..e3d4be03a1 100644 --- a/frontend/src/container/GantChart/SpanLength/styles.ts +++ b/frontend/src/container/GantChart/SpanLength/styles.ts @@ -16,6 +16,7 @@ export const SpanLine = styled.div` top: 50%; position: absolute; `; + export const SpanBorder = styled.div` background: ${({ bgColor }): string => bgColor}; border-radius: 5px; @@ -34,27 +35,18 @@ export const SpanWrapper = styled.div` position: relative; z-index: 2; min-height: 2rem; - - /* &:before { - display: inline-block; - content: ''; - border-bottom: 1px solid #303030; - position: absolute; - left: -30px; - width: 30px; - z-index: 0; - } */ `; interface SpanTextProps extends Pick { isDarkMode: boolean; } -export const SpanText = styled(Typography)` +export const SpanText = styled(Typography.Paragraph)` &&& { left: ${({ leftOffset }): string => `${leftOffset}%`}; top: 65%; position: absolute; - color: ${({ isDarkMode }): string => (isDarkMode ? '##ACACAC' : '#666')}; + width: max-content; + color: ${({ isDarkMode }): string => (isDarkMode ? '#ACACAC' : '#666')}; font-size: 0.75rem; } `; From 2624ce400721e5ed1811f829626c315f83154e5a Mon Sep 17 00:00:00 2001 From: palashgdev Date: Mon, 20 Feb 2023 19:12:54 +0530 Subject: [PATCH 09/20] feat(FE): span Kind is added in the trace filter page (#2281) --- frontend/src/api/trace/getFilters.ts | 1 + frontend/src/api/trace/getSpans.ts | 1 + frontend/src/api/trace/getSpansAggregate.ts | 1 + frontend/src/api/trace/getTagFilter.ts | 1 + frontend/src/api/trace/getTagValue.ts | 1 + .../MetricsPageQueries/ExternalQueries.ts | 2 +- .../MetricsApplication/Tabs/DBCall.tsx | 12 +- .../MetricsApplication/Tabs/External.tsx | 118 ++++++++++++++++-- .../MetricsApplication/Tabs/Overview.tsx | 12 +- .../MetricsApplication/Tabs/constant.ts | 3 + .../container/MetricsApplication/Tabs/util.ts | 21 +++- .../Panel/PanelBody/Common/Checkbox.tsx | 3 + .../Panel/PanelBody/Duration/index.tsx | 3 + .../Panel/PanelBody/SearchTraceID/index.tsx | 3 + .../Filters/Panel/PanelHeading/index.tsx | 6 + .../Trace/Search/AllTags/Tag/TagKey.tsx | 2 + .../Trace/Search/AllTags/Tag/TagValue.tsx | 12 +- frontend/src/container/Trace/Search/index.tsx | 1 + .../Trace/TraceGraphFilter/index.tsx | 3 + frontend/src/pages/Trace/index.tsx | 5 + .../store/actions/trace/getInitialFilter.ts | 5 + .../actions/trace/getInitialSpansAggregate.ts | 2 + frontend/src/store/actions/trace/getSpans.ts | 1 + .../trace/parseFilter/parseSpanKind.ts | 39 ++++++ frontend/src/store/reducers/trace.ts | 3 + frontend/src/types/actions/trace.ts | 1 + frontend/src/types/api/trace/getFilters.ts | 1 + .../src/types/api/trace/getSpanAggregate.ts | 1 + frontend/src/types/api/trace/getSpans.ts | 1 + frontend/src/types/api/trace/getTagFilters.ts | 1 + frontend/src/types/api/trace/getTagValue.ts | 2 + frontend/src/types/reducer/trace.ts | 1 + .../app/clickhouseReader/reader.go | 21 +++- pkg/query-service/model/queryParams.go | 6 +- 34 files changed, 263 insertions(+), 33 deletions(-) create mode 100644 frontend/src/container/MetricsApplication/Tabs/constant.ts create mode 100644 frontend/src/store/actions/trace/parseFilter/parseSpanKind.ts diff --git a/frontend/src/api/trace/getFilters.ts b/frontend/src/api/trace/getFilters.ts index 1173fde10c..7eeb9404ba 100644 --- a/frontend/src/api/trace/getFilters.ts +++ b/frontend/src/api/trace/getFilters.ts @@ -32,6 +32,7 @@ const getFilters = async ( maxDuration: String((duration.duration || [])[0] || ''), minDuration: String((duration.duration || [])[1] || ''), exclude, + spanKind: props.spanKind, }); return { diff --git a/frontend/src/api/trace/getSpans.ts b/frontend/src/api/trace/getSpans.ts index 06555d20fe..8b56caa46d 100644 --- a/frontend/src/api/trace/getSpans.ts +++ b/frontend/src/api/trace/getSpans.ts @@ -44,6 +44,7 @@ const getSpans = async ( maxDuration: String((duration.duration || [])[0] || ''), minDuration: String((duration.duration || [])[1] || ''), exclude, + spanKind: props.spanKind, }, ); diff --git a/frontend/src/api/trace/getSpansAggregate.ts b/frontend/src/api/trace/getSpansAggregate.ts index 373e677ab1..cfa1f7e31f 100644 --- a/frontend/src/api/trace/getSpansAggregate.ts +++ b/frontend/src/api/trace/getSpansAggregate.ts @@ -48,6 +48,7 @@ const getSpanAggregate = async ( maxDuration: String((duration.duration || [])[0] || ''), minDuration: String((duration.duration || [])[1] || ''), exclude, + spanKind: props.spanKind, }); return { diff --git a/frontend/src/api/trace/getTagFilter.ts b/frontend/src/api/trace/getTagFilter.ts index e4cadb710c..2f53ab5a6d 100644 --- a/frontend/src/api/trace/getTagFilter.ts +++ b/frontend/src/api/trace/getTagFilter.ts @@ -32,6 +32,7 @@ const getTagFilters = async ( maxDuration: String((duration.duration || [])[0] || ''), minDuration: String((duration.duration || [])[1] || ''), exclude, + spanKind: props.spanKind, }); return { diff --git a/frontend/src/api/trace/getTagValue.ts b/frontend/src/api/trace/getTagValue.ts index a330262049..2519c7a758 100644 --- a/frontend/src/api/trace/getTagValue.ts +++ b/frontend/src/api/trace/getTagValue.ts @@ -15,6 +15,7 @@ const getTagValue = async ( Key: props.tagKey.Key, Type: props.tagKey.Type, }, + spanKind: props.spanKind, }); return { statusCode: 200, diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts index d364315ab9..7980619427 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts @@ -161,7 +161,7 @@ export const externalCallDurationByAddress = ({ }; interface ExternalCallDurationByAddressProps extends ExternalCallProps { - legend: '{{address}}'; + legend: string; } export interface ExternalCallProps { diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index bef9779f54..37b77ad81c 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -79,11 +79,11 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element { type="default" size="small" id="database_call_rps_button" - onClick={onViewTracePopupClick( + onClick={onViewTracePopupClick({ servicename, selectedTraceTags, - selectedTimeStamp, - )} + timestamp: selectedTimeStamp, + })} > View Traces @@ -114,11 +114,11 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element { type="default" size="small" id="database_call_avg_duration_button" - onClick={onViewTracePopupClick( + onClick={onViewTracePopupClick({ servicename, selectedTraceTags, - selectedTimeStamp, - )} + timestamp: selectedTimeStamp, + })} > View Traces diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index a0bd62bb29..f6d2d31fbb 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -6,8 +6,11 @@ import { externalCallErrorPercent, externalCallRpsByAddress, } from 'container/MetricsApplication/MetricsPageQueries/ExternalQueries'; -import { resourceAttributesToTagFilterItems } from 'lib/resourceAttributes'; -import React, { useMemo } from 'react'; +import { + convertRawQueriesToTraceSelectedTags, + resourceAttributesToTagFilterItems, +} from 'lib/resourceAttributes'; +import React, { useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import { AppState } from 'store/reducers'; @@ -15,8 +18,13 @@ import { Widgets } from 'types/api/dashboard/getAll'; import MetricReducer from 'types/reducer/metrics'; import { Card, GraphContainer, GraphTitle, Row } from '../styles'; +import { legend } from './constant'; +import { Button } from './styles'; +import { onGraphClickHandler, onViewTracePopupClick } from './util'; function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { + const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); + const { servicename } = useParams<{ servicename?: string }>(); const { resourceAttributeQueries } = useSelector( (state) => state.metrics, @@ -27,8 +35,6 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { [resourceAttributeQueries], ); - const legend = '{{address}}'; - const externalCallErrorWidget = useMemo( () => getWidgetQueryBuilder({ @@ -36,7 +42,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { promQL: [], metricsBuilder: externalCallErrorPercent({ servicename, - legend, + legend: legend.address, tagFilterItems, }), clickHouse: [], @@ -44,6 +50,14 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { [getWidgetQueryBuilder, servicename, tagFilterItems], ); + const selectedTraceTags = useMemo( + () => + JSON.stringify( + convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [], + ), + [resourceAttributeQueries], + ); + const externalCallDurationWidget = useMemo( () => getWidgetQueryBuilder({ @@ -65,7 +79,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { promQL: [], metricsBuilder: externalCallRpsByAddress({ servicename, - legend, + legend: legend.address, tagFilterItems, }), clickHouse: [], @@ -80,7 +94,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { promQL: [], metricsBuilder: externalCallDurationByAddress({ servicename, - legend, + legend: legend.address, tagFilterItems, }), clickHouse: [], @@ -92,6 +106,19 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { <> + External Call Error Percentage @@ -100,12 +127,35 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { fullViewOptions={false} widget={externalCallErrorWidget} yAxisUnit="%" + onClickHandler={(ChartEvent, activeElements, chart, data): void => { + onGraphClickHandler(setSelectedTimeStamp)( + ChartEvent, + activeElements, + chart, + data, + 'external_call_error_percentage', + ); + }} /> + + External Call duration @@ -114,6 +164,15 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { fullViewOptions={false} widget={externalCallDurationWidget} yAxisUnit="ms" + onClickHandler={(ChartEvent, activeElements, chart, data): void => { + onGraphClickHandler(setSelectedTimeStamp)( + ChartEvent, + activeElements, + chart, + data, + 'external_call_duration', + ); + }} /> @@ -122,6 +181,19 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { + External Call RPS(by Address) @@ -130,12 +202,35 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { fullViewOptions={false} widget={externalCallRPSWidget} yAxisUnit="reqps" + onClickHandler={(ChartEvent, activeElements, chart, data): void => { + onGraphClickHandler(setSelectedTimeStamp)( + ChartEvent, + activeElements, + chart, + data, + 'external_call_rps_by_address', + ); + }} /> + + External Call duration(by Address) @@ -144,6 +239,15 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { fullViewOptions={false} widget={externalCallDurationAddressWidget} yAxisUnit="ms" + onClickHandler={(ChartEvent, activeElements, chart, data): void => { + onGraphClickHandler(setSelectedTimeStamp)( + ChartEvent, + activeElements, + chart, + data, + 'external_call_duration_by_address', + ); + }} /> diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 8ecd0c9cba..de15029408 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -181,11 +181,11 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { type="default" size="small" id="Service_button" - onClick={onViewTracePopupClick( + onClick={onViewTracePopupClick({ servicename, selectedTraceTags, - selectedTimeStamp, - )} + timestamp: selectedTimeStamp, + })} > View Traces @@ -210,11 +210,11 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { type="default" size="small" id="Rate_button" - onClick={onViewTracePopupClick( + onClick={onViewTracePopupClick({ servicename, selectedTraceTags, - selectedTimeStamp, - )} + timestamp: selectedTimeStamp, + })} > View Traces diff --git a/frontend/src/container/MetricsApplication/Tabs/constant.ts b/frontend/src/container/MetricsApplication/Tabs/constant.ts new file mode 100644 index 0000000000..4931667e6e --- /dev/null +++ b/frontend/src/container/MetricsApplication/Tabs/constant.ts @@ -0,0 +1,3 @@ +export const legend = { + address: '{{address}}', +}; diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index c030492aff..beba6da3f3 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -14,11 +14,18 @@ export const dbSystemTags: Tags[] = [ }, ]; -export function onViewTracePopupClick( - servicename: string | undefined, - selectedTraceTags: string, - timestamp: number, -): VoidFunction { +interface OnViewTracePopupClickProps { + servicename: string | undefined; + selectedTraceTags: string; + timestamp: number; + isExternalCall?: boolean; +} +export function onViewTracePopupClick({ + selectedTraceTags, + servicename, + timestamp, + isExternalCall, +}: OnViewTracePopupClickProps): VoidFunction { return (): void => { const currentTime = timestamp; const tPlusOne = timestamp + 60 * 1000; @@ -30,7 +37,9 @@ export function onViewTracePopupClick( history.replace( `${ 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`, + }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1${ + isExternalCall ? '&spanKind=3' : '' + }`, ); }; } diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx index a413671d18..ee42069fe3 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx @@ -24,6 +24,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { filter, userSelectedFilter, isFilterExclude, + spanKind, } = useSelector((state) => state.traces); const globalTime = useSelector( @@ -91,6 +92,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { start: String(globalTime.minTime), getFilters: filterToFetchData.filter((e) => e !== name), isFilterExclude: preIsFilterExclude, + spanKind, }); if (response.statusCode === 200) { @@ -125,6 +127,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { order: spansAggregate.order, orderParam: spansAggregate.orderParam, pageSize: spansAggregate.pageSize, + spanKind, }, }); diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx index bec1e5260e..6fa6c9ec53 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx @@ -30,6 +30,7 @@ function Duration(): JSX.Element { selectedTags, userSelectedFilter, isFilterExclude, + spanKind, } = useSelector((state) => state.traces); const dispatch = useDispatch>(); @@ -88,6 +89,7 @@ function Duration(): JSX.Element { other: Object.fromEntries(preSelectedFilter), start: String(globalTime.minTime), isFilterExclude, + spanKind, }); if (response.statusCode === 200) { @@ -113,6 +115,7 @@ function Duration(): JSX.Element { order: spansAggregate.order, pageSize: spansAggregate.pageSize, orderParam: spansAggregate.orderParam, + spanKind, }, }); diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/SearchTraceID/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/SearchTraceID/index.tsx index cbd72ebb95..eef14987e1 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/SearchTraceID/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/SearchTraceID/index.tsx @@ -22,6 +22,7 @@ function TraceID(): JSX.Element { selectedTags, userSelectedFilter, isFilterExclude, + spanKind, } = useSelector((state) => state.traces); const dispatch = useDispatch>(); const globalTime = useSelector( @@ -52,6 +53,7 @@ function TraceID(): JSX.Element { start: String(globalTime.minTime), getFilters: filterToFetchData, isFilterExclude, + spanKind, }); if (response.statusCode === 200) { @@ -77,6 +79,7 @@ function TraceID(): JSX.Element { order: spansAggregate.order, pageSize: spansAggregate.pageSize, orderParam: spansAggregate.orderParam, + spanKind, }, }); diff --git a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx index 310596451c..cf272257f9 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx @@ -37,6 +37,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { filter, isFilterExclude, userSelectedFilter, + spanKind, } = useSelector((state) => state.traces); const { name: PanelName, isOpen: IsPanelOpen } = props; @@ -75,6 +76,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { getFilters: updatedFilterData, other: Object.fromEntries(getprepdatedSelectedFilter), isFilterExclude, + spanKind, }); if (response.statusCode === 200) { @@ -107,6 +109,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { order: spansAggregate.order, pageSize: spansAggregate.pageSize, orderParam: spansAggregate.orderParam, + spanKind, }, }); @@ -160,6 +163,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { order: spansAggregate.order, pageSize: spansAggregate.pageSize, orderParam: spansAggregate.orderParam, + spanKind, }, }); @@ -195,6 +199,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { getFilters: filterToFetchData, other: Object.fromEntries(updatedFilter), isFilterExclude: postIsFilterExclude, + spanKind, }); if (response.statusCode === 200 && response.payload) { @@ -213,6 +218,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { order: spansAggregate.order, pageSize: spansAggregate.pageSize, orderParam: spansAggregate.orderParam, + spanKind, }, }); diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagKey.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagKey.tsx index 5d241efec0..f14a35e0bd 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/TagKey.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagKey.tsx @@ -27,6 +27,7 @@ function TagsKey(props: TagsKeysProps): JSX.Element { globalTime.maxTime, traces.selectedFilter, traces.isFilterExclude, + traces.spanKind, ], { queryFn: () => @@ -35,6 +36,7 @@ function TagsKey(props: TagsKeysProps): JSX.Element { end: globalTime.maxTime, other: Object.fromEntries(traces.selectedFilter), isFilterExclude: traces.isFilterExclude, + spanKind: traces.spanKind, }), cacheTime: 120000, }, diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx index af5b51cfe2..22abbe9df7 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx @@ -30,6 +30,8 @@ function TagValue(props: TagValueProps): JSX.Element { BoolValues: selectedBoolValues, } = tag; + const traces = useSelector((state) => state.traces); + const [localTagValue, setLocalTagValue] = useState( getInitialLocalValue( selectedNumberValues, @@ -45,7 +47,14 @@ function TagValue(props: TagValueProps): JSX.Element { const tagType = useMemo(() => extractTagType(tagKey), [tagKey]); const { isLoading, data } = useQuery( - ['tagKey', globalReducer.minTime, globalReducer.maxTime, tagKey, tagType], + [ + 'tagKey', + globalReducer.minTime, + globalReducer.maxTime, + tagKey, + tagType, + traces.spanKind, + ], { queryFn: () => getTagValue({ @@ -55,6 +64,7 @@ function TagValue(props: TagValueProps): JSX.Element { Key: extractTagKey(tagKey), Type: tagType, }, + spanKind: traces.spanKind, }), }, ); diff --git a/frontend/src/container/Trace/Search/index.tsx b/frontend/src/container/Trace/Search/index.tsx index adcb59f6eb..d9b4741082 100644 --- a/frontend/src/container/Trace/Search/index.tsx +++ b/frontend/src/container/Trace/Search/index.tsx @@ -67,6 +67,7 @@ function Search({ order: traces.spansAggregate.order, pageSize: traces.spansAggregate.pageSize, orderParam: traces.spansAggregate.orderParam, + spanKind: traces.spanKind, }, }); diff --git a/frontend/src/container/Trace/TraceGraphFilter/index.tsx b/frontend/src/container/Trace/TraceGraphFilter/index.tsx index 52811fef6f..48f6666101 100644 --- a/frontend/src/container/Trace/TraceGraphFilter/index.tsx +++ b/frontend/src/container/Trace/TraceGraphFilter/index.tsx @@ -40,6 +40,7 @@ function TraceGraphFilter(): JSX.Element { globalTime.maxTime, traces.selectedFilter, traces.isFilterExclude, + traces.spanKind, ], { queryFn: () => @@ -48,8 +49,10 @@ function TraceGraphFilter(): JSX.Element { end: globalTime.maxTime, other: Object.fromEntries(traces.selectedFilter), isFilterExclude: traces.isFilterExclude, + spanKind: traces.spanKind, }), cacheTime: 120000, + enabled: traces.filter.size > 0, }, ); diff --git a/frontend/src/pages/Trace/index.tsx b/frontend/src/pages/Trace/index.tsx index 2de31ecd53..fba0cb2afc 100644 --- a/frontend/src/pages/Trace/index.tsx +++ b/frontend/src/pages/Trace/index.tsx @@ -52,6 +52,7 @@ function Trace({ selectedFunction, selectedGroupBy, isFilterExclude, + spanKind, } = useSelector((state) => state.traces); const { notifications } = useNotifications(); @@ -71,6 +72,7 @@ function Trace({ selectedTags, order: spansAggregate.order, orderParam: spansAggregate.orderParam, + spanKind, }, notifications, ); @@ -85,6 +87,7 @@ function Trace({ spansAggregate.order, spansAggregate.orderParam, notifications, + spanKind, ]); useEffect(() => { @@ -98,6 +101,7 @@ function Trace({ start: minTime, step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }), isFilterExclude, + spanKind, }, notifications, ); @@ -111,6 +115,7 @@ function Trace({ getSpans, isFilterExclude, notifications, + spanKind, ]); useEffect( diff --git a/frontend/src/store/actions/trace/getInitialFilter.ts b/frontend/src/store/actions/trace/getInitialFilter.ts index 7737b3887e..5ff04b1eff 100644 --- a/frontend/src/store/actions/trace/getInitialFilter.ts +++ b/frontend/src/store/actions/trace/getInitialFilter.ts @@ -12,6 +12,7 @@ import { import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace'; +import { parseQueryIntoSpanKind } from './parseFilter/parseSpanKind'; import { isTraceFilterEnum, parseAggregateOrderParams, @@ -55,6 +56,8 @@ export const GetInitialTraceFilter = ( traces.filterToFetchData, ); + const parsedSpanKind = parseQueryIntoSpanKind(query, traces.spanKind); + const getUserSelected = parseSelectedFilter(query, traces.userSelectedFilter); const getIsFilterExcluded = parseFilterExclude(query, traces.isFilterExclude); @@ -102,6 +105,7 @@ export const GetInitialTraceFilter = ( start: String(minTime), other: Object.fromEntries(getSelectedFilter.currentValue), isFilterExclude: getIsFilterExcluded.currentValue, + spanKind: parsedSpanKind.currentValue, }); const preSelectedFilter: Map = new Map( @@ -164,6 +168,7 @@ export const GetInitialTraceFilter = ( order: parsedQueryOrder.currentValue, pageSize: parsedPageSize.currentValue, orderParam: parsedOrderParams.currentValue, + spanKind: parsedSpanKind.currentValue, }, }); } else { diff --git a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts index c8a07b99de..acd7e18d5e 100644 --- a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts +++ b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts @@ -61,6 +61,7 @@ export const GetSpansAggregate = ( isFilterExclude: traces.isFilterExclude, order, orderParam: props.orderParam, + spanKind: props.spanKind, }); if (response.statusCode === 200) { @@ -140,4 +141,5 @@ export interface GetSpansAggregateProps { selectedTags: TraceReducer['selectedTags']; order: GetSpanAggregateProps['order']; orderParam: GetSpanAggregateProps['orderParam']; + spanKind: TraceReducer['spanKind']; } diff --git a/frontend/src/store/actions/trace/getSpans.ts b/frontend/src/store/actions/trace/getSpans.ts index a2336bd91d..4b2787e7a9 100644 --- a/frontend/src/store/actions/trace/getSpans.ts +++ b/frontend/src/store/actions/trace/getSpans.ts @@ -63,6 +63,7 @@ export const GetSpans = ( start, step: props.step, isFilterExclude: props.isFilterExclude, + spanKind: props.spanKind, }); if (response.statusCode === 200) { diff --git a/frontend/src/store/actions/trace/parseFilter/parseSpanKind.ts b/frontend/src/store/actions/trace/parseFilter/parseSpanKind.ts new file mode 100644 index 0000000000..c4626b44f7 --- /dev/null +++ b/frontend/src/store/actions/trace/parseFilter/parseSpanKind.ts @@ -0,0 +1,39 @@ +import { TraceReducer } from 'types/reducer/trace'; + +import { ParsedUrl } from '../util'; + +export const parseQueryIntoSpanKind = ( + query: string, + stateCurrent: TraceReducer['spanKind'], +): ParsedUrl => { + const url = new URLSearchParams(query); + + let current = ''; + + const selected = url.get('spanKind'); + + if (selected) { + try { + const parsedValue = selected; + + if (parsedValue && typeof parsedValue === 'string') { + current = parsedValue; + } + } catch (error) { + console.log(error); + console.log('error while parsing json'); + } + } + + if (selected) { + return { + currentValue: current, + urlValue: current, + }; + } + + return { + currentValue: stateCurrent, + urlValue: current, + }; +}; diff --git a/frontend/src/store/reducers/trace.ts b/frontend/src/store/reducers/trace.ts index ea6b9a7671..5545f25eb8 100644 --- a/frontend/src/store/reducers/trace.ts +++ b/frontend/src/store/reducers/trace.ts @@ -70,6 +70,7 @@ const initialValue: TraceReducer = { ['status', INITIAL_FILTER_VALUE], ['traceID', INITIAL_FILTER_VALUE], ]), + spanKind: undefined, }; const traceReducer = ( @@ -97,6 +98,7 @@ const traceReducer = ( order, pageSize, orderParam, + spanKind, } = payload; return { @@ -114,6 +116,7 @@ const traceReducer = ( order, orderParam, }, + spanKind, }; } diff --git a/frontend/src/types/actions/trace.ts b/frontend/src/types/actions/trace.ts index f043926142..f4336427f4 100644 --- a/frontend/src/types/actions/trace.ts +++ b/frontend/src/types/actions/trace.ts @@ -82,6 +82,7 @@ export interface UpdateAllFilters { order: TraceReducer['spansAggregate']['order']; pageSize: TraceReducer['spansAggregate']['pageSize']; orderParam: TraceReducer['spansAggregate']['orderParam']; + spanKind?: TraceReducer['spanKind']; }; } diff --git a/frontend/src/types/api/trace/getFilters.ts b/frontend/src/types/api/trace/getFilters.ts index 422e360831..3edb9166c0 100644 --- a/frontend/src/types/api/trace/getFilters.ts +++ b/frontend/src/types/api/trace/getFilters.ts @@ -8,6 +8,7 @@ export interface Props { [k: string]: string[]; }; isFilterExclude: TraceReducer['isFilterExclude']; + spanKind?: TraceReducer['spanKind']; } export interface PayloadProps { diff --git a/frontend/src/types/api/trace/getSpanAggregate.ts b/frontend/src/types/api/trace/getSpanAggregate.ts index f5bcbdc6c5..97d813aeeb 100644 --- a/frontend/src/types/api/trace/getSpanAggregate.ts +++ b/frontend/src/types/api/trace/getSpanAggregate.ts @@ -10,6 +10,7 @@ export interface Props { order?: TraceReducer['spansAggregate']['order']; isFilterExclude: TraceReducer['isFilterExclude']; orderParam: TraceReducer['spansAggregate']['orderParam']; + spanKind?: TraceReducer['spanKind']; } export type PayloadProps = { diff --git a/frontend/src/types/api/trace/getSpans.ts b/frontend/src/types/api/trace/getSpans.ts index 9143f2a693..7928908da9 100644 --- a/frontend/src/types/api/trace/getSpans.ts +++ b/frontend/src/types/api/trace/getSpans.ts @@ -9,6 +9,7 @@ export interface Props { selectedFilter: TraceReducer['selectedFilter']; selectedTags: TraceReducer['selectedTags']; isFilterExclude: TraceReducer['isFilterExclude']; + spanKind?: TraceReducer['spanKind']; } export interface PayloadProps { diff --git a/frontend/src/types/api/trace/getTagFilters.ts b/frontend/src/types/api/trace/getTagFilters.ts index eeee055e75..d1029f308f 100644 --- a/frontend/src/types/api/trace/getTagFilters.ts +++ b/frontend/src/types/api/trace/getTagFilters.ts @@ -7,6 +7,7 @@ export interface Props { [k: string]: string[]; }; isFilterExclude: TraceReducer['isFilterExclude']; + spanKind?: TraceReducer['spanKind']; } export interface PayloadProps { diff --git a/frontend/src/types/api/trace/getTagValue.ts b/frontend/src/types/api/trace/getTagValue.ts index ee10041e56..5a6af76fac 100644 --- a/frontend/src/types/api/trace/getTagValue.ts +++ b/frontend/src/types/api/trace/getTagValue.ts @@ -1,4 +1,5 @@ import { GlobalReducer } from 'types/reducer/globalTime'; +import { TraceReducer } from 'types/reducer/trace'; export interface Props { start: GlobalReducer['minTime']; @@ -7,6 +8,7 @@ export interface Props { Key: string; Type: string; }; + spanKind?: TraceReducer['spanKind']; } export interface PayloadProps { diff --git a/frontend/src/types/reducer/trace.ts b/frontend/src/types/reducer/trace.ts index 701f27efa6..cc35cbc44a 100644 --- a/frontend/src/types/reducer/trace.ts +++ b/frontend/src/types/reducer/trace.ts @@ -33,6 +33,7 @@ export interface TraceReducer { }; yAxisUnit: string | undefined; filterDisplayValue: Map; + spanKind?: string; } interface SpansAggregateData { diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index a1b2ceabab..4f3e7043a4 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1001,6 +1001,11 @@ func (r *ClickHouseReader) GetSpanFilters(ctx context.Context, queryParams *mode args = append(args, clickhouse.Named("durationNanoMax", queryParams.MaxDuration)) } + if len(queryParams.SpanKind) != 0 { + query = query + " AND kind = @kind" + args = append(args, clickhouse.Named("kind", queryParams.SpanKind)) + } + query = getStatusFilters(query, queryParams.Status, excludeMap) traceFilterReponse := model.SpanFiltersResponse{ @@ -1364,9 +1369,9 @@ func (r *ClickHouseReader) GetFilteredSpans(ctx context.Context, queryParams *mo } query = getStatusFilters(query, queryParams.Status, excludeMap) - if len(queryParams.Kind) != 0 { + if len(queryParams.SpanKind) != 0 { query = query + " AND kind = @kind" - args = append(args, clickhouse.Named("kind", queryParams.Kind)) + args = append(args, clickhouse.Named("kind", queryParams.SpanKind)) } // create TagQuery from TagQueryParams @@ -1666,6 +1671,10 @@ func (r *ClickHouseReader) GetTagFilters(ctx context.Context, queryParams *model query = query + " AND durationNano <= @durationNanoMax" args = append(args, clickhouse.Named("durationNanoMax", queryParams.MaxDuration)) } + if len(queryParams.SpanKind) != 0 { + query = query + " AND kind = @kind" + args = append(args, clickhouse.Named("kind", queryParams.SpanKind)) + } query = getStatusFilters(query, queryParams.Status, excludeMap) @@ -1781,6 +1790,10 @@ func (r *ClickHouseReader) GetTagValues(ctx context.Context, queryParams *model. query = query + " AND durationNano <= @durationNanoMax" args = append(args, clickhouse.Named("durationNanoMax", queryParams.MaxDuration)) } + if len(queryParams.SpanKind) != 0 { + query = query + " AND kind = @kind" + args = append(args, clickhouse.Named("kind", queryParams.SpanKind)) + } query = getStatusFilters(query, queryParams.Status, excludeMap) @@ -2113,9 +2126,9 @@ func (r *ClickHouseReader) GetFilteredSpansAggregates(ctx context.Context, query } query = getStatusFilters(query, queryParams.Status, excludeMap) - if len(queryParams.Kind) != 0 { + if len(queryParams.SpanKind) != 0 { query = query + " AND kind = @kind" - args = append(args, clickhouse.Named("kind", queryParams.Kind)) + args = append(args, clickhouse.Named("kind", queryParams.SpanKind)) } // create TagQuery from TagQueryParams tags := createTagQueryFromTagQueryParams(queryParams.Tags) diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 1f7e2daed8..4730b1fabf 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -312,7 +312,7 @@ type GetFilteredSpansParams struct { TraceID []string `json:"traceID"` ServiceName []string `json:"serviceName"` Operation []string `json:"operation"` - Kind string `json:"kind"` + SpanKind string `json:"spanKind"` Status []string `json:"status"` HttpRoute []string `json:"httpRoute"` HttpCode []string `json:"httpCode"` @@ -340,7 +340,7 @@ type GetFilteredSpanAggregatesParams struct { TraceID []string `json:"traceID"` ServiceName []string `json:"serviceName"` Operation []string `json:"operation"` - Kind string `json:"kind"` + SpanKind string `json:"spanKind"` Status []string `json:"status"` HttpRoute []string `json:"httpRoute"` HttpCode []string `json:"httpCode"` @@ -369,6 +369,7 @@ type SpanFilterParams struct { TraceID []string `json:"traceID"` Status []string `json:"status"` ServiceName []string `json:"serviceName"` + SpanKind string `json:"spanKind"` HttpRoute []string `json:"httpRoute"` HttpCode []string `json:"httpCode"` HttpUrl []string `json:"httpUrl"` @@ -394,6 +395,7 @@ type TagFilterParams struct { ServiceName []string `json:"serviceName"` HttpRoute []string `json:"httpRoute"` HttpCode []string `json:"httpCode"` + SpanKind string `json:"spanKind"` HttpUrl []string `json:"httpUrl"` HttpHost []string `json:"httpHost"` HttpMethod []string `json:"httpMethod"` From 23d628759478c133cba620f7b91028ee4c247616 Mon Sep 17 00:00:00 2001 From: nityanandagohain Date: Tue, 21 Feb 2023 10:52:03 +0530 Subject: [PATCH 10/20] fix: attribute name corrected in logs database --- pkg/query-service/app/clickhouseReader/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/query-service/app/clickhouseReader/options.go b/pkg/query-service/app/clickhouseReader/options.go index 5836070410..da5a87ecd2 100644 --- a/pkg/query-service/app/clickhouseReader/options.go +++ b/pkg/query-service/app/clickhouseReader/options.go @@ -31,7 +31,7 @@ const ( defaultLogsDB string = "signoz_logs" defaultLogsTable string = "distributed_logs" defaultLogsLocalTable string = "logs" - defaultLogAttributeKeysTable string = "distributed_logs_atrribute_keys" + defaultLogAttributeKeysTable string = "distributed_logs_attribute_keys" defaultLogResourceKeysTable string = "distributed_logs_resource_keys" defaultLiveTailRefreshSeconds int = 10 defaultWriteBatchDelay time.Duration = 5 * time.Second From 23490ca7f881c7cdbf58dba7da52ac078b23b26f Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 22 Feb 2023 12:10:32 +0530 Subject: [PATCH 11/20] fix: operator should be IN for top level operations (#2304) --- .../MetricsPageQueries/OverviewQueries.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts index 77182c789c..8c5f95f943 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts @@ -26,7 +26,7 @@ export const operationPerSec = ({ { id: '', key: 'operation', - op: 'MATCH', + op: 'IN', value: topLevelOperations, }, ...tagFilterItems, @@ -56,7 +56,7 @@ export const errorPercentage = ({ { id: '', key: 'operation', - op: 'MATCH', + op: 'IN', value: topLevelOperations, }, { @@ -78,7 +78,7 @@ export const errorPercentage = ({ { id: '', key: 'operation', - op: 'MATCH', + op: 'IN', value: topLevelOperations, }, ...tagFilterItems, From e183cace75f658858e8efbfa96fd223ff972e18d Mon Sep 17 00:00:00 2001 From: Chintan Sudani <46838508+techchintan@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:22:02 +0530 Subject: [PATCH 12/20] fix: null value handle on create dashboard (#2320) * fix: Removed Strict mode to stop render twice * fix: null value handle on create dashboard --- frontend/src/container/GridGraphLayout/Graph/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/GridGraphLayout/Graph/index.tsx b/frontend/src/container/GridGraphLayout/Graph/index.tsx index a9dee6b018..212a2b1e8c 100644 --- a/frontend/src/container/GridGraphLayout/Graph/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/index.tsx @@ -67,7 +67,7 @@ function GridCardGraph({ const queryResponse = useQuery( [ - `GetMetricsQueryRange-${widget.timePreferance}-${globalSelectedInterval}-${widget.id}`, + `GetMetricsQueryRange-${widget?.timePreferance}-${globalSelectedInterval}-${widget.id}`, { widget, maxTime, @@ -78,7 +78,7 @@ function GridCardGraph({ ], () => GetMetricQueryRange({ - selectedTime: widget.timePreferance, + selectedTime: widget?.timePreferance, graphType: widget.panelTypes, query: widget.query, globalSelectedInterval, From a3731e4c4e50a8a17520c34124b4dd02ea17f47c Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 23 Feb 2023 11:15:14 +0530 Subject: [PATCH 13/20] fix: error rate as a percentage of range 0-100% (#2311) --- pkg/query-service/app/clickhouseReader/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 4f3e7043a4..7eb8a65674 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1994,7 +1994,7 @@ func (r *ClickHouseReader) GetDependencyGraph(ctx context.Context, queryParams * result[5] AS p99, sum(total_count) as callCount, sum(total_count)/ @duration AS callRate, - sum(error_count)/sum(total_count) as errorRate + sum(error_count)/sum(total_count) * 100 as errorRate FROM %s.%s WHERE toUInt64(toDateTime(timestamp)) >= @start AND toUInt64(toDateTime(timestamp)) <= @end GROUP BY From 06a55ccdd64364f61a887220578c32d8edc6f3b6 Mon Sep 17 00:00:00 2001 From: palashgdev Date: Thu, 23 Feb 2023 13:10:41 +0530 Subject: [PATCH 14/20] chore: linebreak style is updated (#2277) Co-authored-by: Srikanth Chekuri --- frontend/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 44cfdcb441..37c7b17c45 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -58,7 +58,7 @@ module.exports = { 'react/no-array-index-key': 'error', 'linebreak-style': [ 'error', - process.platform === 'win32' ? 'windows' : 'unix', + process.env.platform === 'win32' ? 'windows' : 'unix', ], '@typescript-eslint/default-param-last': 'off', From 174fc107c22fc60969222771bda9cf0e4c58d026 Mon Sep 17 00:00:00 2001 From: Chintan Sudani <46838508+techchintan@users.noreply.github.com> Date: Thu, 23 Feb 2023 17:06:57 +0530 Subject: [PATCH 15/20] fix: scrollbar issue on widget (#2359) * fix: Removed Strict mode to stop render twice * fix: scrollbar issue on widget --- frontend/src/components/Graph/styles.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Graph/styles.ts b/frontend/src/components/Graph/styles.ts index 2cbe302815..371aa9d317 100644 --- a/frontend/src/components/Graph/styles.ts +++ b/frontend/src/components/Graph/styles.ts @@ -6,7 +6,10 @@ export const LegendsContainer = styled.div` * { ::-webkit-scrollbar { - width: 0.5rem; + width: 0.3rem; + } + ::-webkit-scrollbar:horizontal { + height: 0.3rem; } ::-webkit-scrollbar-track { background: transparent; @@ -18,5 +21,8 @@ export const LegendsContainer = styled.div` ::-webkit-scrollbar-thumb:hover { background: ${themeColors.matterhornGrey}; } + ::-webkit-scrollbar-corner { + background: transparent; + } } `; From 1f44f089e02733d14b5dba3ed413e3a58cbed002 Mon Sep 17 00:00:00 2001 From: palashgdev Date: Thu, 23 Feb 2023 23:54:16 +0530 Subject: [PATCH 16/20] feat: multiple values can be selected (#2365) * feat: multiple values can be selected * chore: tag value is updated * fix: handle few edge cases --------- Co-authored-by: makeavish --- .../Trace/Search/AllTags/Tag/TagValue.tsx | 87 +++++-------------- .../Trace/Search/AllTags/Tag/utils.ts | 81 +++++++++++------ 2 files changed, 77 insertions(+), 91 deletions(-) diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx index 22abbe9df7..a74463d5a4 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx @@ -1,7 +1,7 @@ import { Select } from 'antd'; import { BaseOptionType } from 'antd/es/select'; import getTagValue from 'api/trace/getTagValue'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -17,6 +17,7 @@ import { getTagValueOptions, onTagValueChange, selectOptions, + separateTagValues, TagValueTypes, } from './utils'; @@ -81,66 +82,27 @@ function TagValue(props: TagValueProps): JSX.Element { [index, selectedKey, selectedOperator, setLocalSelectedTags], ); - const onSetLocalValue = useCallback(() => { - setLocalTagValue([]); - }, []); - - const onSelectedHandler = useCallback( - (value: unknown) => { - if ( - typeof value === 'number' || - (typeof value === 'string' && !Number.isNaN(Number(value)) && value !== ' ') - ) { - setLocalTagValue([value]); - setLocalSelectedTags((tags) => [ - ...tags.slice(0, index), - { - Key: selectedKey, - Operator: selectedOperator, - StringValues: [], - NumberValues: [Number(value)], - BoolValues: [], - }, - ...tags.slice(index + 1, tags.length), - ]); - } else if ( - typeof value === 'boolean' || - value === 'true' || - value === 'false' - ) { - setLocalTagValue([value]); - setLocalSelectedTags((tags) => [ - ...tags.slice(0, index), - { - Key: selectedKey, - Operator: selectedOperator, - StringValues: [], - NumberValues: [], - BoolValues: [value === 'true' || value === true], - }, - ...tags.slice(index + 1, tags.length), - ]); - } else if (typeof value === 'string') { - setLocalTagValue([value]); - setLocalSelectedTags((tags) => [ - ...tags.slice(0, index), - { - Key: selectedKey, - Operator: selectedOperator, - StringValues: [value], - NumberValues: [], - BoolValues: [], - }, - ...tags.slice(index + 1, tags.length), - ]); - } - }, - [index, selectedKey, selectedOperator, setLocalSelectedTags], - ); - const onChangeHandler = useCallback( - (value: unknown) => onTagValueChange(value, setLocalTagValue), - [], + (value: unknown) => { + const updatedValues = onTagValueChange(value); + setLocalTagValue(updatedValues); + const { boolValues, numberValues, stringValues } = separateTagValues( + updatedValues, + selectedKey, + ); + + setLocalSelectedTags((tags) => [ + ...tags.slice(0, index), + { + ...tags[index], + BoolValues: boolValues, + NumberValues: numberValues, + StringValues: stringValues, + }, + ...tags.slice(index + 1), + ]); + }, + [index, setLocalSelectedTags, selectedKey], ); const getFilterOptions = useCallback( @@ -159,14 +121,11 @@ function TagValue(props: TagValueProps): JSX.Element { options={getTagValueOptions(data?.payload, tagType)} mode="tags" allowClear - onClear={onSetLocalValue} - onDeselect={onSetLocalValue} showSearch filterOption={getFilterOptions} disabled={isLoading || tagValueDisabled} value={localTagValue} onChange={onChangeHandler} - onSelect={onSelectedHandler} > {selectOptions(data?.payload, tagType)?.map((suggestion) => ( @@ -186,4 +145,4 @@ interface TagValueProps { tagKey: string; } -export default TagValue; +export default memo(TagValue); diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/utils.ts b/frontend/src/container/Trace/Search/AllTags/Tag/utils.ts index 0ac195c3d1..a9367b9c0f 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/utils.ts +++ b/frontend/src/container/Trace/Search/AllTags/Tag/utils.ts @@ -50,23 +50,56 @@ export const extractTagKey = (tagKey: string): string => { return ''; }; -export function onTagValueChange( - values: unknown, - setLocalValue: React.Dispatch>, -): void { - if (Array.isArray(values) && values.length > 0) { - if (typeof values[0] === 'number' || typeof values[0] === 'boolean') { - setLocalValue(values); - } else if (typeof values[0] === 'string') { - if (values[0] === 'true' || values[0] === 'false') { - setLocalValue([values[0] === 'true']); - } else if (values[0] !== ' ' && !Number.isNaN(Number(values[0]))) { - setLocalValue([Number(values[0])]); - } else { - setLocalValue([values[0]]); - } - } +export function onTagValueChange(values: unknown): TagValueTypes[] { + const stringValues = values as string[]; + + if (!Array.isArray(stringValues) || stringValues.length === 0) { + return []; } + + return values as TagValueTypes[]; +} + +export function separateTagValues( + values: TagValueTypes[], + selectedKey: string, +): { boolValues: boolean[]; numberValues: number[]; stringValues: string[] } { + if (selectedKey.includes('.(bool)')) { + const boolValues = values.filter( + (value) => typeof value === 'boolean', + ) as boolean[]; + + return { + boolValues, + numberValues: [], + stringValues: [], + }; + } + + if (selectedKey.includes('.(number)')) { + const numberValues = values + .filter((value) => typeof value === 'number' || !Number.isNaN(Number(value))) + .map((value) => Number(value)) as number[]; + return { + boolValues: [], + numberValues, + stringValues: [], + }; + } + + const stringValues = values.filter( + (value) => + typeof value === 'string' && + value !== 'true' && + value !== 'false' && + Number.isNaN(Number(value)), + ) as string[]; + + return { + boolValues: [], + numberValues: [], + stringValues, + }; } export function disableTagValue( @@ -93,22 +126,16 @@ export function disableTagValue( } return false; } - export function getInitialLocalValue( selectedNumberValues: number[], selectedBoolValues: boolean[], selectedStringValues: string[], ): TagValueTypes[] { - if (selectedStringValues && selectedStringValues.length > 0) { - return selectedStringValues; - } - if (selectedNumberValues && selectedNumberValues.length > 0) { - return selectedNumberValues; - } - if (selectedBoolValues && selectedBoolValues.length > 0) { - return selectedBoolValues; - } - return selectedStringValues; + return [ + ...selectedBoolValues, + ...selectedNumberValues, + ...selectedStringValues, + ]; } export function getTagValueOptions( From ab514cc0f2baf47eb88890e3a11e5674acfc887d Mon Sep 17 00:00:00 2001 From: Amol Umbark Date: Fri, 24 Feb 2023 14:57:07 +0530 Subject: [PATCH 17/20] fix: changed ask admin message (#2215) --- ee/query-service/app/api/api.go | 11 +++++-- ee/query-service/app/api/auth.go | 29 +++++++++-------- frontend/src/container/AppLayout/index.tsx | 2 ++ frontend/src/container/Login/index.tsx | 36 +++++++++++++--------- frontend/src/store/reducers/app.ts | 4 +++ frontend/src/types/actions/app.ts | 2 ++ frontend/src/types/api/user/getVersion.ts | 3 +- frontend/src/types/reducer/app.ts | 2 ++ pkg/query-service/app/http_handler.go | 32 ++++++++++++++++++- pkg/query-service/auth/auth.go | 2 +- pkg/query-service/auth/utils.go | 2 +- pkg/query-service/dao/interface.go | 1 + pkg/query-service/dao/sqlite/rbac.go | 9 ++++++ pkg/query-service/model/response.go | 6 ++++ 14 files changed, 109 insertions(+), 32 deletions(-) diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index fd44fba29a..601bed7714 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -9,6 +9,7 @@ import ( "go.signoz.io/signoz/ee/query-service/license" baseapp "go.signoz.io/signoz/pkg/query-service/app" baseint "go.signoz.io/signoz/pkg/query-service/interfaces" + basemodel "go.signoz.io/signoz/pkg/query-service/model" rules "go.signoz.io/signoz/pkg/query-service/rules" "go.signoz.io/signoz/pkg/query-service/version" ) @@ -96,7 +97,7 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router) { router.HandleFunc("/api/v1/complete/google", baseapp.OpenAccess(ah.receiveGoogleAuth)). Methods(http.MethodGet) - + router.HandleFunc("/api/v1/orgs/{orgId}/domains", baseapp.AdminAccess(ah.listDomainsByOrg)). Methods(http.MethodGet) @@ -127,5 +128,11 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router) { func (ah *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) { version := version.GetVersion() - ah.WriteJSON(w, r, map[string]string{"version": version, "ee": "Y"}) + versionResponse := basemodel.GetVersionResponse{ + Version: version, + EE: "Y", + SetupCompleted: ah.SetupCompleted, + } + + ah.WriteJSON(w, r, versionResponse) } diff --git a/ee/query-service/app/api/auth.go b/ee/query-service/app/api/auth.go index 2e622408be..8d96320778 100644 --- a/ee/query-service/app/api/auth.go +++ b/ee/query-service/app/api/auth.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "net/url" + "github.com/gorilla/mux" "go.signoz.io/signoz/ee/query-service/constants" "go.signoz.io/signoz/ee/query-service/model" @@ -87,9 +88,16 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { // get invite object invite, err := baseauth.ValidateInvite(ctx, req) - if err != nil || invite == nil { + if err != nil { zap.S().Errorf("failed to validate invite token", err) + RespondError(w, model.BadRequest(err), nil) + return + } + + if invite == nil { + zap.S().Errorf("failed to validate invite token: it is either empty or invalid", err) RespondError(w, model.BadRequest(basemodel.ErrSignupFailed{}), nil) + return } // get auth domain from email domain @@ -190,7 +198,7 @@ func handleSsoError(w http.ResponseWriter, r *http.Request, redirectURL string) } // receiveGoogleAuth completes google OAuth response and forwards a request -// to front-end to sign user in +// to front-end to sign user in func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) { redirectUri := constants.GetDefaultSiteURL() ctx := context.Background() @@ -221,15 +229,15 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) // upgrade redirect url from the relay state for better accuracy redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login") - // fetch domain by parsing relay state. + // fetch domain by parsing relay state. domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState) if err != nil { handleSsoError(w, r, redirectUri) return } - // now that we have domain, use domain to fetch sso settings. - // prepare google callback handler using parsedState - + // now that we have domain, use domain to fetch sso settings. + // prepare google callback handler using parsedState - // which contains redirect URL (front-end endpoint) callbackHandler, err := domain.PrepareGoogleOAuthProvider(parsedState) @@ -239,7 +247,7 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) handleSsoError(w, r, redirectUri) return } - + nextPage, err := ah.AppDao().PrepareSsoRedirect(ctx, redirectUri, identity.Email) if err != nil { zap.S().Errorf("[receiveGoogleAuth] failed to generate redirect URI after successful login ", domain.String(), zap.Error(err)) @@ -250,15 +258,12 @@ func (ah *APIHandler) receiveGoogleAuth(w http.ResponseWriter, r *http.Request) http.Redirect(w, r, nextPage, http.StatusSeeOther) } - - // receiveSAML completes a SAML request and gets user logged in func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { // this is the source url that initiated the login request redirectUri := constants.GetDefaultSiteURL() ctx := context.Background() - if !ah.CheckFeature(model.SSO) { zap.S().Errorf("[receiveSAML] sso requested but feature unavailable %s in org domain %s", model.SSO) http.Redirect(w, r, fmt.Sprintf("%s?ssoerror=%s", redirectUri, "feature unavailable, please upgrade your billing plan to access this feature"), http.StatusMovedPermanently) @@ -287,13 +292,13 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { // upgrade redirect url from the relay state for better accuracy redirectUri = fmt.Sprintf("%s://%s%s", parsedState.Scheme, parsedState.Host, "/login") - // fetch domain by parsing relay state. + // fetch domain by parsing relay state. domain, err := ah.AppDao().GetDomainFromSsoResponse(ctx, parsedState) if err != nil { handleSsoError(w, r, redirectUri) return } - + sp, err := domain.PrepareSamlRequest(parsedState) if err != nil { zap.S().Errorf("[receiveSAML] failed to prepare saml request for domain (%s): %v", domain.String(), err) @@ -327,6 +332,6 @@ func (ah *APIHandler) receiveSAML(w http.ResponseWriter, r *http.Request) { handleSsoError(w, r, redirectUri) return } - + http.Redirect(w, r, nextPage, http.StatusSeeOther) } diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 640a7340a5..c31a2bf6f9 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -153,6 +153,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element { type: UPDATE_CURRENT_VERSION, payload: { currentVersion: getUserVersionResponse.data.payload.version, + ee: getUserVersionResponse.data.payload.ee, + setupCompleted: getUserVersionResponse.data.payload.setupCompleted, }, }); } diff --git a/frontend/src/container/Login/index.tsx b/frontend/src/container/Login/index.tsx index e8c25f79f0..a8fda93908 100644 --- a/frontend/src/container/Login/index.tsx +++ b/frontend/src/container/Login/index.tsx @@ -1,4 +1,5 @@ import { Button, Input, Space, Tooltip, Typography } from 'antd'; +import getUserVersion from 'api/user/getVersion'; import loginApi from 'api/user/login'; import loginPrecheckApi from 'api/user/loginPrecheck'; import afterLogin from 'AppRoutes/utils'; @@ -7,6 +8,7 @@ import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; import { PayloadProps as PrecheckResultType } from 'types/api/user/loginPrecheck'; import { FormContainer, FormWrapper, Label, ParentContainer } from './styles'; @@ -45,6 +47,26 @@ function Login({ const { notifications } = useNotifications(); + const getUserVersionResponse = useQuery({ + queryFn: getUserVersion, + queryKey: 'getUserVersion', + enabled: true, + }); + + useEffect(() => { + if ( + getUserVersionResponse.isFetched && + getUserVersionResponse.data && + getUserVersionResponse.data.payload + ) { + const { setupCompleted } = getUserVersionResponse.data.payload; + if (!setupCompleted) { + // no org account registered yet, re-route user to sign up first + history.push(ROUTES.SIGN_UP); + } + } + }, [getUserVersionResponse]); + useEffect(() => { if (withPassword === 'Y') { setPrecheckComplete(true); @@ -255,20 +277,6 @@ function Login({ )} - {!canSelfRegister && ( - - {t('prompt_create_account')}{' '} - { - history.push(ROUTES.SIGN_UP); - }} - style={{ fontWeight: 700 }} - > - {t('create_an_account')} - - - )} - {canSelfRegister && ( {t('prompt_if_admin')}{' '} diff --git a/frontend/src/store/reducers/app.ts b/frontend/src/store/reducers/app.ts index 343ed35941..94ccbc9687 100644 --- a/frontend/src/store/reducers/app.ts +++ b/frontend/src/store/reducers/app.ts @@ -57,6 +57,8 @@ const InitialValue: InitialValueTypes = { role: null, configs: {}, userFlags: {}, + ee: 'Y', + setupCompleted: true, }; const appReducer = ( @@ -89,6 +91,8 @@ const appReducer = ( return { ...state, currentVersion: action.payload.currentVersion, + ee: action.payload.ee, + setupCompleted: action.payload.setupCompleted, }; } diff --git a/frontend/src/types/actions/app.ts b/frontend/src/types/actions/app.ts index 9b0df64afb..9efd43c45e 100644 --- a/frontend/src/types/actions/app.ts +++ b/frontend/src/types/actions/app.ts @@ -46,6 +46,8 @@ export interface UpdateAppVersion { type: typeof UPDATE_CURRENT_VERSION; payload: { currentVersion: AppReducer['currentVersion']; + ee: AppReducer['ee']; + setupCompleted: AppReducer['setupCompleted']; }; } diff --git a/frontend/src/types/api/user/getVersion.ts b/frontend/src/types/api/user/getVersion.ts index a729875bb6..5419a038c1 100644 --- a/frontend/src/types/api/user/getVersion.ts +++ b/frontend/src/types/api/user/getVersion.ts @@ -1,4 +1,5 @@ export interface PayloadProps { version: string; - ee: string; + ee: 'Y' | 'N'; + setupCompleted: boolean; } diff --git a/frontend/src/types/reducer/app.ts b/frontend/src/types/reducer/app.ts index a89f6cb7ae..89c4724310 100644 --- a/frontend/src/types/reducer/app.ts +++ b/frontend/src/types/reducer/app.ts @@ -29,4 +29,6 @@ export default interface AppReducer { featureFlags: null | FeatureFlagPayload; configs: ConfigPayload; userFlags: null | UserFlags; + ee: 'Y' | 'N'; + setupCompleted: boolean; } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 8d49dbb0ad..130d11e368 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -61,6 +61,11 @@ type APIHandler struct { ruleManager *rules.Manager featureFlags interfaces.FeatureLookup ready func(http.HandlerFunc) http.HandlerFunc + + // SetupCompleted indicates if SigNoz is ready for general use. + // at the moment, we mark the app ready when the first user + // is registers. + SetupCompleted bool } type APIHandlerOpts struct { @@ -100,6 +105,19 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) { // if errReadingDashboards != nil { // return nil, errReadingDashboards // } + + // check if at least one user is created + hasUsers, err := aH.appDao.GetUsersWithOpts(context.Background(), 1) + if err.Error() != "" { + // raise warning but no panic as this is a recoverable condition + zap.S().Warnf("unexpected error while fetch user count while initializing base api handler", err.Error()) + } + if len(hasUsers) != 0 { + // first user is already created, we can mark the app ready for general use. + // this means, we disable self-registration and expect new users + // to signup signoz through invite link only. + aH.SetupCompleted = true + } return aH, nil } @@ -1645,7 +1663,13 @@ func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) { func (aH *APIHandler) getVersion(w http.ResponseWriter, r *http.Request) { version := version.GetVersion() - aH.WriteJSON(w, r, map[string]string{"version": version, "ee": "N"}) + versionResponse := model.GetVersionResponse{ + Version: version, + EE: "Y", + SetupCompleted: aH.SetupCompleted, + } + + aH.WriteJSON(w, r, versionResponse) } func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) { @@ -1777,6 +1801,12 @@ func (aH *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) { return } + if !aH.SetupCompleted { + // since the first user is now created, we can disable self-registration as + // from here onwards, we expect admin (owner) to invite other users. + aH.SetupCompleted = true + } + aH.Respond(w, nil) } diff --git a/pkg/query-service/auth/auth.go b/pkg/query-service/auth/auth.go index ef83cb7fb7..b7fc34e1ed 100644 --- a/pkg/query-service/auth/auth.go +++ b/pkg/query-service/auth/auth.go @@ -267,7 +267,7 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User, func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*model.User, *model.ApiError) { if req.InviteToken == "" { - return nil, model.BadRequest(fmt.Errorf("invite token is required")) + return nil, model.BadRequest(ErrorAskAdmin) } if !nopassword && req.Password == "" { diff --git a/pkg/query-service/auth/utils.go b/pkg/query-service/auth/utils.go index df96057229..d76beca5c4 100644 --- a/pkg/query-service/auth/utils.go +++ b/pkg/query-service/auth/utils.go @@ -15,7 +15,7 @@ var ( ErrorInvalidRole = errors.New("Invalid role") ErrorInvalidInviteToken = errors.New("Invalid invite token") - ErrorAskAdmin = errors.New("You are not allowed to create an account. Please ask your admin to send an invite link") + ErrorAskAdmin = errors.New("An invitation is needed to create an account. Please ask your admin (the person who has first installed SIgNoz) to send an invite.") ) func randomHex(sz int) (string, error) { diff --git a/pkg/query-service/dao/interface.go b/pkg/query-service/dao/interface.go index 974b313bf0..ceece7faef 100644 --- a/pkg/query-service/dao/interface.go +++ b/pkg/query-service/dao/interface.go @@ -19,6 +19,7 @@ type Queries interface { GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError) GetUserByEmail(ctx context.Context, email string) (*model.UserPayload, *model.ApiError) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) + GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) GetGroup(ctx context.Context, id string) (*model.Group, *model.ApiError) GetGroupByName(ctx context.Context, name string) (*model.Group, *model.ApiError) diff --git a/pkg/query-service/dao/sqlite/rbac.go b/pkg/query-service/dao/sqlite/rbac.go index 11bed020a2..64ff7ed8ae 100644 --- a/pkg/query-service/dao/sqlite/rbac.go +++ b/pkg/query-service/dao/sqlite/rbac.go @@ -345,7 +345,13 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context, return &users[0], nil } +// GetUsers fetches total user count func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) { + return mds.GetUsersWithOpts(ctx, 0) +} + +// GetUsersWithOpts fetches users and supports additional search options +func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) { users := []model.UserPayload{} query := `select @@ -364,6 +370,9 @@ func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, * g.id = u.group_id and o.id = u.org_id` + if limit > 0 { + query = fmt.Sprintf("%s LIMIT %d", query, limit) + } err := mds.db.Select(&users, query) if err != nil { diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 01afb950e4..8aa815bc19 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -585,3 +585,9 @@ func (ci *ClusterInfo) GetMapFromStruct() map[string]interface{} { json.Unmarshal(data, &clusterInfoMap) return clusterInfoMap } + +type GetVersionResponse struct { + Version string `json:"version"` + EE string `json:"ee"` + SetupCompleted bool `json:"setupCompleted"` +} From 9d1305f174618f1fb25d08c453b292b68c9c854e Mon Sep 17 00:00:00 2001 From: Amol Umbark Date: Fri, 24 Feb 2023 15:09:30 +0530 Subject: [PATCH 18/20] fix: resolves alert charts issue with 1 hr timerame (#2377) --- frontend/src/container/FormAlertRules/RuleOptions.tsx | 2 +- frontend/src/container/FormAlertRules/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/FormAlertRules/RuleOptions.tsx b/frontend/src/container/FormAlertRules/RuleOptions.tsx index d0ce0b2746..071ff84d7f 100644 --- a/frontend/src/container/FormAlertRules/RuleOptions.tsx +++ b/frontend/src/container/FormAlertRules/RuleOptions.tsx @@ -104,7 +104,7 @@ function RuleOptions({ - + diff --git a/frontend/src/container/FormAlertRules/utils.ts b/frontend/src/container/FormAlertRules/utils.ts index 35a5eb158c..a3978a9007 100644 --- a/frontend/src/container/FormAlertRules/utils.ts +++ b/frontend/src/container/FormAlertRules/utils.ts @@ -132,7 +132,7 @@ export const toChartInterval = (evalWindow: string | undefined): Time => { return '15min'; case '30m0s': return '30min'; - case '60m0s': + case '1h0m0s': return '1hr'; case '4h0m0s': return '4hr'; From a26ebb742a3e5d25a7928f49d4c78983e2baf7c0 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 24 Feb 2023 17:34:00 +0530 Subject: [PATCH 19/20] chore: bump signoz/signoz-otel-collector version (#2378) Merged on recommendation of @srikanthccv * chore: bump signoz/signoz-otel-collector version * chore: bump everywhere --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 4 ++-- deploy/docker/clickhouse-setup/docker-compose-core.yaml | 4 ++-- deploy/docker/clickhouse-setup/docker-compose.yaml | 4 ++-- pkg/query-service/tests/test-deploy/docker-compose.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 6792b3acce..2afa69e590 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -179,7 +179,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-config.yaml"] user: root # required for reading docker container logs volumes: @@ -208,7 +208,7 @@ services: <<: *clickhouse-depend otel-collector-metrics: - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-metrics-config.yaml"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index febd11a13d..3edfecb12f 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -41,7 +41,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: otel-collector - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-config.yaml"] # user: root # required for reading docker container logs volumes: @@ -67,7 +67,7 @@ services: otel-collector-metrics: container_name: otel-collector-metrics - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-metrics-config.yaml"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 586b77c1f6..e48efb85fe 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -193,7 +193,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.4} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.5} command: ["--config=/etc/otel-collector-config.yaml"] user: root # required for reading docker container logs volumes: @@ -219,7 +219,7 @@ services: <<: *clickhouse-depend otel-collector-metrics: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.4} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.66.5} command: ["--config=/etc/otel-collector-metrics-config.yaml"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 8f3c7267b2..3bde538611 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -169,7 +169,7 @@ services: <<: *clickhouse-depends otel-collector: - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-config.yaml"] user: root # required for reading docker container logs volumes: @@ -195,7 +195,7 @@ services: <<: *clickhouse-depends otel-collector-metrics: - image: signoz/signoz-otel-collector:0.66.4 + image: signoz/signoz-otel-collector:0.66.5 command: ["--config=/etc/otel-collector-metrics-config.yaml"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml From e7e0f5b96a68c1a8ed91027b80c0aad062c1e254 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 24 Feb 2023 18:14:25 +0530 Subject: [PATCH 20/20] chore: pin version: SigNoz 0.16.2 --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 4 ++-- deploy/docker/clickhouse-setup/docker-compose.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 2afa69e590..bee9c700f5 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -137,7 +137,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.16.1 + image: signoz/query-service:0.16.2 command: ["-config=/root/config/prometheus.yml"] # ports: # - "6060:6060" # pprof port @@ -166,7 +166,7 @@ services: <<: *clickhouse-depend frontend: - image: signoz/frontend:0.16.1 + image: signoz/frontend:0.16.2 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index e48efb85fe..37bc2c40c1 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -153,7 +153,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.16.1} + image: signoz/query-service:${DOCKER_TAG:-0.16.2} container_name: query-service command: ["-config=/root/config/prometheus.yml"] # ports: @@ -181,7 +181,7 @@ services: <<: *clickhouse-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.16.1} + image: signoz/frontend:${DOCKER_TAG:-0.16.2} container_name: frontend restart: on-failure depends_on:
; @@ -35,7 +28,7 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element { height={0} handle={handle} onResize={onResize} - draggableOpts={draggableOpts} + draggableOpts={enableUserSelectHack} > {/* eslint-disable-next-line react/jsx-props-no-spreading */} diff --git a/frontend/src/constants/localStorage.ts b/frontend/src/constants/localStorage.ts index ab8b01a388..d2988c9be5 100644 --- a/frontend/src/constants/localStorage.ts +++ b/frontend/src/constants/localStorage.ts @@ -4,4 +4,6 @@ export enum LOCALSTORAGE { AUTH_TOKEN = 'AUTH_TOKEN', REFRESH_AUTH_TOKEN = 'REFRESH_AUTH_TOKEN', THEME = 'THEME', + LOGS_VIEW_MODE = 'LOGS_VIEW_MODE', + LOGS_LINES_PER_ROW = 'LOGS_LINES_PER_ROW', } diff --git a/frontend/src/container/LogControls/index.tsx b/frontend/src/container/LogControls/index.tsx index db427624c3..41bfeafb53 100644 --- a/frontend/src/container/LogControls/index.tsx +++ b/frontend/src/container/LogControls/index.tsx @@ -6,9 +6,12 @@ import { import { Button, Divider, Select } from 'antd'; import { getGlobalTime } from 'container/LogsSearchFilter/utils'; import { getMinMax } from 'container/TopNav/AutoRefresh/config'; +import { defaultSelectStyle } from 'pages/Logs/config'; import React, { memo, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; import { AppState } from 'store/reducers'; +import AppActions from 'types/actions'; import { GET_NEXT_LOG_LINES, GET_PREVIOUS_LOG_LINES, @@ -21,8 +24,6 @@ import { ILogsReducer } from 'types/reducer/logs'; import { ITEMS_PER_PAGE_OPTIONS } from './config'; import { Container } from './styles'; -const { Option } = Select; - function LogControls(): JSX.Element | null { const { logLinesPerPage, @@ -34,13 +35,14 @@ function LogControls(): JSX.Element | null { const globalTime = useSelector( (state) => state.globalTime, ); - const dispatch = useDispatch(); + + const dispatch = useDispatch>(); const handleLogLinesPerPageChange = (e: number): void => { dispatch({ type: SET_LOG_LINES_PER_PAGE, payload: { - logLinesPerPage: e, + logsLinesPerPage: e, }, }); }; @@ -52,13 +54,17 @@ function LogControls(): JSX.Element | null { globalTime.maxTime, ); - dispatch({ - type: RESET_ID_START_AND_END, - payload: getGlobalTime(globalTime.selectedTime, { - maxTime, - minTime, - }), + const updatedGlobalTime = getGlobalTime(globalTime.selectedTime, { + maxTime, + minTime, }); + + if (updatedGlobalTime) { + dispatch({ + type: RESET_ID_START_AND_END, + payload: updatedGlobalTime, + }); + } }; const handleNavigatePrevious = (): void => { @@ -117,12 +123,16 @@ function LogControls(): JSX.Element | null { Next diff --git a/frontend/src/container/LogsFilters/index.tsx b/frontend/src/container/LogsFilters/index.tsx index bede522e61..d5a673395a 100644 --- a/frontend/src/container/LogsFilters/index.tsx +++ b/frontend/src/container/LogsFilters/index.tsx @@ -1,5 +1,5 @@ import { CloseOutlined, PlusCircleFilled } from '@ant-design/icons'; -import { Input } from 'antd'; +import { Col, Input } from 'antd'; import CategoryHeading from 'components/Logs/CategoryHeading'; import { fieldSearchFilter } from 'lib/logs/fieldSearch'; import React, { useCallback, useState } from 'react'; @@ -9,7 +9,7 @@ import { ILogsReducer } from 'types/reducer/logs'; import { ICON_STYLE } from './config'; import FieldItem from './FieldItem'; -import { CategoryContainer, Container, FieldContainer } from './styles'; +import { CategoryContainer, FieldContainer } from './styles'; import { IHandleInterestProps, IHandleRemoveInterestProps } from './types'; import { onHandleAddInterest, onHandleRemoveInterest } from './utils'; @@ -58,7 +58,7 @@ function LogsFilters(): JSX.Element { ); return ( - +