mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 15:29:01 +08:00
feat: celery - misc feedback fixes (#7019)
* feat: celery - misc feedback fixes * feat: enabled better sharing and auto-refresh * feat: made task name filter more visible
This commit is contained in:
parent
3b550c485d
commit
cf95b15ba1
@ -1,7 +1,6 @@
|
|||||||
import './CeleryOverviewConfigOptions.styles.scss';
|
import './CeleryOverviewConfigOptions.styles.scss';
|
||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Row, Select, Spin } from 'antd';
|
||||||
import { Button, Row, Select, Spin, Tooltip } from 'antd';
|
|
||||||
import {
|
import {
|
||||||
getValuesFromQueryParams,
|
getValuesFromQueryParams,
|
||||||
setQueryParamsFromOptions,
|
setQueryParamsFromOptions,
|
||||||
@ -10,10 +9,7 @@ import { useCeleryFilterOptions } from 'components/CeleryTask/useCeleryFilterOpt
|
|||||||
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import { Check, Share2 } from 'lucide-react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
|
||||||
|
|
||||||
interface SelectOptionConfig {
|
interface SelectOptionConfig {
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
@ -66,10 +62,6 @@ function FilterSelect({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CeleryOverviewConfigOptions(): JSX.Element {
|
function CeleryOverviewConfigOptions(): JSX.Element {
|
||||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
|
||||||
|
|
||||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
|
||||||
|
|
||||||
const selectConfigs: SelectOptionConfig[] = [
|
const selectConfigs: SelectOptionConfig[] = [
|
||||||
{
|
{
|
||||||
placeholder: 'Service Name',
|
placeholder: 'Service Name',
|
||||||
@ -98,14 +90,6 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleShareURL = (): void => {
|
|
||||||
handleCopyToClipboard(window.location.href);
|
|
||||||
setIsURLCopied(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsURLCopied(false);
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="celery-overview-filters">
|
<div className="celery-overview-filters">
|
||||||
<Row className="celery-filters">
|
<Row className="celery-filters">
|
||||||
@ -118,19 +102,6 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
<Tooltip title="Share this" arrow={false}>
|
|
||||||
<Button
|
|
||||||
className="periscope-btn copy-url-btn"
|
|
||||||
onClick={handleShareURL}
|
|
||||||
icon={
|
|
||||||
isURLCopied ? (
|
|
||||||
<Check size={14} color={Color.BG_FOREST_500} />
|
|
||||||
) : (
|
|
||||||
<Share2 size={14} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 18px; /* 163.636% */
|
line-height: 18px; /* 163.636% */
|
||||||
letter-spacing: 0.44px;
|
letter-spacing: 0.44px;
|
||||||
text-transform: uppercase;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -218,20 +218,26 @@ function getColumns(data: RowData[]): TableColumnsType<RowData> {
|
|||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.error_percentage).localeCompare(String(b.error_percentage)),
|
const aValue = Number(a.error_percentage);
|
||||||
|
const bValue = Number(b.error_percentage);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: ProgressRender,
|
render: ProgressRender,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'LATENCY (P95)',
|
title: 'LATENCY (P95) in ms',
|
||||||
dataIndex: 'p95_latency',
|
dataIndex: 'p95_latency',
|
||||||
key: 'p95_latency',
|
key: 'p95_latency',
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.p95_latency).localeCompare(String(b.p95_latency)),
|
const aValue = Number(a.p95_latency);
|
||||||
|
const bValue = Number(b.p95_latency);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: (value: number | string): string => {
|
render: (value: number | string): string => {
|
||||||
if (!isNumber(value)) return value.toString();
|
if (!isNumber(value)) return value.toString();
|
||||||
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
||||||
@ -245,8 +251,11 @@ function getColumns(data: RowData[]): TableColumnsType<RowData> {
|
|||||||
showTitle: false,
|
showTitle: false,
|
||||||
},
|
},
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: (a: RowData, b: RowData): number =>
|
sorter: (a: RowData, b: RowData): number => {
|
||||||
String(a.throughput).localeCompare(String(b.throughput)),
|
const aValue = Number(a.throughput);
|
||||||
|
const bValue = Number(b.throughput);
|
||||||
|
return aValue - bValue;
|
||||||
|
},
|
||||||
render: (value: number | string): string => {
|
render: (value: number | string): string => {
|
||||||
if (!isNumber(value)) return value.toString();
|
if (!isNumber(value)) return value.toString();
|
||||||
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3);
|
||||||
|
@ -2,24 +2,16 @@ import './CeleryTaskDetail.style.scss';
|
|||||||
|
|
||||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||||
import { Divider, Drawer, Typography } from 'antd';
|
import { Divider, Drawer, Typography } from 'antd';
|
||||||
import { QueryParams } from 'constants/query';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
|
||||||
import { UpdateTimeInterval } from 'store/actions';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
import CeleryTaskGraph from '../CeleryTaskGraph/CeleryTaskGraph';
|
import CeleryTaskGraph from '../CeleryTaskGraph/CeleryTaskGraph';
|
||||||
|
import { createFiltersFromData } from '../CeleryUtils';
|
||||||
import { useNavigateToTraces } from '../useNavigateToTraces';
|
import { useNavigateToTraces } from '../useNavigateToTraces';
|
||||||
|
|
||||||
export type CeleryTaskData = {
|
export type CeleryTaskData = {
|
||||||
@ -39,40 +31,6 @@ export type CeleryTaskDetailProps = {
|
|||||||
drawerOpen: boolean;
|
drawerOpen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createFiltersFromData = (
|
|
||||||
data: Record<string, any>,
|
|
||||||
): Array<{
|
|
||||||
id: string;
|
|
||||||
key: {
|
|
||||||
key: string;
|
|
||||||
dataType: DataTypes;
|
|
||||||
type: string;
|
|
||||||
isColumn: boolean;
|
|
||||||
isJSON: boolean;
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
op: string;
|
|
||||||
value: string;
|
|
||||||
}> => {
|
|
||||||
const excludeKeys = ['A', 'A_without_unit'];
|
|
||||||
|
|
||||||
return Object.entries(data)
|
|
||||||
.filter(([key]) => !excludeKeys.includes(key))
|
|
||||||
.map(([key, value]) => ({
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
key,
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
type: 'tag',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
id: `${key}--string--tag--false`,
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: value.toString(),
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CeleryTaskDetail({
|
export default function CeleryTaskDetail({
|
||||||
widgetData,
|
widgetData,
|
||||||
taskData,
|
taskData,
|
||||||
@ -85,7 +43,7 @@ export default function CeleryTaskDetail({
|
|||||||
!!taskData.entity && !!taskData.timeRange[0] && drawerOpen;
|
!!taskData.entity && !!taskData.timeRange[0] && drawerOpen;
|
||||||
|
|
||||||
const formatTimestamp = (timestamp: number): string =>
|
const formatTimestamp = (timestamp: number): string =>
|
||||||
dayjs(timestamp * 1000).format('MM-DD-YYYY hh:mm A');
|
dayjs(timestamp).format('DD-MM-YYYY hh:mm A');
|
||||||
|
|
||||||
const [totalTask, setTotalTask] = useState(0);
|
const [totalTask, setTotalTask] = useState(0);
|
||||||
|
|
||||||
@ -93,52 +51,9 @@ export default function CeleryTaskDetail({
|
|||||||
setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length);
|
setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
// set time range
|
|
||||||
const { minTime, maxTime, selectedTime } = useSelector<
|
|
||||||
AppState,
|
|
||||||
GlobalReducer
|
|
||||||
>((state) => state.globalTime);
|
|
||||||
|
|
||||||
const startTime = taskData.timeRange[0];
|
const startTime = taskData.timeRange[0];
|
||||||
const endTime = taskData.timeRange[1];
|
const endTime = taskData.timeRange[1];
|
||||||
|
|
||||||
const urlQuery = useUrlQuery();
|
|
||||||
const location = useLocation();
|
|
||||||
const history = useHistory();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
urlQuery.delete(QueryParams.relativeTime);
|
|
||||||
urlQuery.set(QueryParams.startTime, startTime.toString());
|
|
||||||
urlQuery.set(QueryParams.endTime, endTime.toString());
|
|
||||||
|
|
||||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
|
||||||
history.replace(generatedUrl);
|
|
||||||
|
|
||||||
if (startTime !== endTime) {
|
|
||||||
dispatch(UpdateTimeInterval('custom', [startTime, endTime]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (): void => {
|
|
||||||
urlQuery.delete(QueryParams.relativeTime);
|
|
||||||
urlQuery.delete(QueryParams.startTime);
|
|
||||||
urlQuery.delete(QueryParams.endTime);
|
|
||||||
|
|
||||||
if (selectedTime !== 'custom') {
|
|
||||||
dispatch(UpdateTimeInterval(selectedTime));
|
|
||||||
urlQuery.set(QueryParams.relativeTime, selectedTime);
|
|
||||||
} else {
|
|
||||||
dispatch(UpdateTimeInterval('custom', [minTime / 1e6, maxTime / 1e6]));
|
|
||||||
urlQuery.set(QueryParams.startTime, Math.floor(minTime / 1e6).toString());
|
|
||||||
urlQuery.set(QueryParams.endTime, Math.floor(maxTime / 1e6).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
|
||||||
history.replace(generatedUrl);
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const navigateToTrace = useNavigateToTraces();
|
const navigateToTrace = useNavigateToTraces();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -149,10 +64,8 @@ export default function CeleryTaskDetail({
|
|||||||
<Typography.Text className="title">{`Details - ${taskData.entity}`}</Typography.Text>
|
<Typography.Text className="title">{`Details - ${taskData.entity}`}</Typography.Text>
|
||||||
<div>
|
<div>
|
||||||
<Typography.Text className="subtitle">
|
<Typography.Text className="subtitle">
|
||||||
{`${formatTimestamp(taskData.timeRange[0])} ${
|
{`${formatTimestamp(startTime)} ${
|
||||||
taskData.timeRange[1]
|
endTime ? `- ${formatTimestamp(endTime)}` : ''
|
||||||
? `- ${formatTimestamp(taskData.timeRange[1])}`
|
|
||||||
: ''
|
|
||||||
}`}
|
}`}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
@ -185,8 +98,10 @@ export default function CeleryTaskDetail({
|
|||||||
...rowData,
|
...rowData,
|
||||||
[taskData.entity]: taskData.value,
|
[taskData.entity]: taskData.value,
|
||||||
});
|
});
|
||||||
navigateToTrace(filters);
|
navigateToTrace(filters, startTime, endTime);
|
||||||
}}
|
}}
|
||||||
|
start={startTime}
|
||||||
|
end={endTime}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,10 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
||||||
|
import {
|
||||||
|
applyCeleryFilterOnWidgetData,
|
||||||
|
getFiltersFromQueryParams,
|
||||||
|
} from '../CeleryUtils';
|
||||||
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
||||||
import {
|
import {
|
||||||
celeryAllStateWidgetData,
|
celeryAllStateWidgetData,
|
||||||
@ -72,26 +76,60 @@ function CeleryTaskBar({
|
|||||||
|
|
||||||
const [barState, setBarState] = useState<CeleryTaskState>(CeleryTaskState.All);
|
const [barState, setBarState] = useState<CeleryTaskState>(CeleryTaskState.All);
|
||||||
|
|
||||||
|
const selectedFilters = useMemo(
|
||||||
|
() =>
|
||||||
|
getFiltersFromQueryParams(
|
||||||
|
QueryParams.taskName,
|
||||||
|
urlQuery,
|
||||||
|
'celery.task_name',
|
||||||
|
),
|
||||||
|
[urlQuery],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryAllStateData = useMemo(
|
const celeryAllStateData = useMemo(
|
||||||
() => celeryAllStateWidgetData(minTime, maxTime),
|
() => celeryAllStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryAllStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryAllStateData),
|
||||||
|
[selectedFilters, celeryAllStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryFailedStateData = useMemo(
|
const celeryFailedStateData = useMemo(
|
||||||
() => celeryFailedStateWidgetData(minTime, maxTime),
|
() => celeryFailedStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryFailedStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryFailedStateData),
|
||||||
|
[selectedFilters, celeryFailedStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celeryRetryStateData = useMemo(
|
const celeryRetryStateData = useMemo(
|
||||||
() => celeryRetryStateWidgetData(minTime, maxTime),
|
() => celeryRetryStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celeryRetryStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celeryRetryStateData),
|
||||||
|
[selectedFilters, celeryRetryStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const celerySuccessStateData = useMemo(
|
const celerySuccessStateData = useMemo(
|
||||||
() => celerySuccessStateWidgetData(minTime, maxTime),
|
() => celerySuccessStateWidgetData(minTime, maxTime),
|
||||||
[minTime, maxTime],
|
[minTime, maxTime],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const celerySuccessStateFilteredData = useMemo(
|
||||||
|
() =>
|
||||||
|
applyCeleryFilterOnWidgetData(selectedFilters || [], celerySuccessStateData),
|
||||||
|
[selectedFilters, celerySuccessStateData],
|
||||||
|
);
|
||||||
|
|
||||||
const onGraphClick = (
|
const onGraphClick = (
|
||||||
widgetData: Widgets,
|
widgetData: Widgets,
|
||||||
xValue: number,
|
xValue: number,
|
||||||
@ -141,7 +179,7 @@ function CeleryTaskBar({
|
|||||||
<div className="celery-task-graph-grid-content">
|
<div className="celery-task-graph-grid-content">
|
||||||
{barState === CeleryTaskState.All && (
|
{barState === CeleryTaskState.All && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryAllStateData}
|
widget={celeryAllStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@ -153,7 +191,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Failed && (
|
{barState === CeleryTaskState.Failed && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryFailedStateData}
|
widget={celeryFailedStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@ -165,7 +203,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Retry && (
|
{barState === CeleryTaskState.Retry && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celeryRetryStateData}
|
widget={celeryRetryStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
@ -177,7 +215,7 @@ function CeleryTaskBar({
|
|||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Successful && (
|
{barState === CeleryTaskState.Successful && (
|
||||||
<GridCard
|
<GridCard
|
||||||
widget={celerySuccessStateData}
|
widget={celerySuccessStateFilteredData}
|
||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
isQueryEnabled={queryEnabled}
|
isQueryEnabled={queryEnabled}
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
|
|
||||||
.metric-page-grid {
|
.metric-page-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 50% 50%;
|
grid-template-columns: 30% 19%;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -144,6 +144,11 @@
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
border: 1px dashed var(--bg-slate-50);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 24px 6px 12px;
|
||||||
|
width: max-content;
|
||||||
|
|
||||||
.configure-option-Info-text {
|
.configure-option-Info-text {
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -241,11 +246,13 @@
|
|||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.celery-task-graph-grid-container {
|
.celery-task-graph-grid-container {
|
||||||
.celery-task-graph-grid {
|
.celery-task-graph-worker-count {
|
||||||
.celery-task-graph-worker-count {
|
border: 1px solid var(--bg-vanilla-300);
|
||||||
border: 1px solid var(--bg-vanilla-300);
|
background: unset;
|
||||||
background: unset;
|
}
|
||||||
}
|
|
||||||
|
.row-panel .row-panel-section .section-title {
|
||||||
|
color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,4 +281,8 @@
|
|||||||
background-color: var(--bg-ink-400);
|
background-color: var(--bg-ink-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.configure-option-Info {
|
||||||
|
border: 1px dashed var(--bg-robin-400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
applyCeleryTaskFilter,
|
applyCeleryTaskFilter,
|
||||||
|
customErrorMessage,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
}: {
|
}: {
|
||||||
widgetData: Widgets;
|
widgetData: Widgets;
|
||||||
onClick?: (task: CaptureDataProps) => void;
|
onClick?: (task: CaptureDataProps) => void;
|
||||||
@ -42,6 +45,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
applyCeleryTaskFilter?: boolean;
|
applyCeleryTaskFilter?: boolean;
|
||||||
|
customErrorMessage?: string;
|
||||||
|
start?: number;
|
||||||
|
end?: number;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@ -116,6 +122,9 @@ function CeleryTaskGraph({
|
|||||||
openTracesButton={openTracesButton}
|
openTracesButton={openTracesButton}
|
||||||
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
||||||
version={ENTITY_VERSION_V4}
|
version={ENTITY_VERSION_V4}
|
||||||
|
customErrorMessage={customErrorMessage}
|
||||||
|
start={start}
|
||||||
|
end={end}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@ -129,6 +138,9 @@ CeleryTaskGraph.defaultProps = {
|
|||||||
openTracesButton: false,
|
openTracesButton: false,
|
||||||
onOpenTraceBtnClick: undefined,
|
onOpenTraceBtnClick: undefined,
|
||||||
applyCeleryTaskFilter: false,
|
applyCeleryTaskFilter: false,
|
||||||
|
customErrorMessage: undefined,
|
||||||
|
start: undefined,
|
||||||
|
end: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CeleryTaskGraph;
|
export default CeleryTaskGraph;
|
||||||
|
@ -123,11 +123,12 @@ export default function CeleryTaskGraphGrid({
|
|||||||
key={celeryActiveTasksData.id}
|
key={celeryActiveTasksData.id}
|
||||||
widgetData={celeryActiveTasksData}
|
widgetData={celeryActiveTasksData}
|
||||||
queryEnabled={queryEnabled}
|
queryEnabled={queryEnabled}
|
||||||
|
customErrorMessage="Enable Flower metrics to view this graph"
|
||||||
/>
|
/>
|
||||||
<Card className="celery-task-graph-worker-count">
|
<Card className="celery-task-graph-worker-count">
|
||||||
<div className="worker-count-header">
|
<div className="worker-count-header">
|
||||||
<Typography.Text className="worker-count-header-text">
|
<Typography.Text className="worker-count-header-text">
|
||||||
Worker Count
|
Worker Online
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="worker-count-text-container">
|
<div className="worker-count-text-container">
|
||||||
@ -173,7 +174,7 @@ export default function CeleryTaskGraphGrid({
|
|||||||
{!collapsedSections.traceBasedGraphs && (
|
{!collapsedSections.traceBasedGraphs && (
|
||||||
<>
|
<>
|
||||||
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
||||||
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
<CeleryTaskLatencyGraph queryEnabled={queryEnabled} />
|
||||||
<div className="celery-task-graph-grid-bottom">
|
<div className="celery-task-graph-grid-bottom">
|
||||||
{bottomWidgetData.map((widgetData, index) => (
|
{bottomWidgetData.map((widgetData, index) => (
|
||||||
<CeleryTaskGraph
|
<CeleryTaskGraph
|
||||||
|
@ -42,21 +42,7 @@ export const celeryAllStateWidgetData = (
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [],
|
||||||
{
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
id: 'celery.task_name--string--tag--false',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
key: 'celery.task_name',
|
|
||||||
type: 'tag',
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: 'tasks.tasks.divide',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
functions: [],
|
||||||
@ -113,7 +99,7 @@ export const celeryRetryStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '6d97eed3',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -179,7 +165,7 @@ export const celeryFailedStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '5983eae2',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -245,7 +231,7 @@ export const celerySuccessStateWidgetData = (
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '000c5a93',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -602,7 +588,7 @@ export const celeryTaskLatencyWidgetData = (
|
|||||||
reduceTo: 'avg',
|
reduceTo: 'avg',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: getStepInterval(startTime, endTime),
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
timeAggregation: 'p99',
|
timeAggregation: type || 'p99',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
yAxisUnit: 'ns',
|
yAxisUnit: 'ns',
|
||||||
@ -686,7 +672,7 @@ export const celeryRetryTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '9e09c9ed',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -755,7 +741,7 @@ export const celeryFailedTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '2330f906',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -822,7 +808,7 @@ export const celerySuccessTasksTableWidgetData = getWidgetQueryBuilder(
|
|||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'ec3df7b7',
|
id: uuidv4(),
|
||||||
key: {
|
key: {
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.state--string--tag--false',
|
id: 'celery.state--string--tag--false',
|
||||||
@ -945,33 +931,19 @@ export const celeryAllStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [],
|
||||||
{
|
|
||||||
id: uuidv4(),
|
|
||||||
key: {
|
|
||||||
dataType: DataTypes.String,
|
|
||||||
id: 'celery.task_name--string--tag--false',
|
|
||||||
isColumn: false,
|
|
||||||
isJSON: false,
|
|
||||||
key: 'celery.task_name',
|
|
||||||
type: 'tag',
|
|
||||||
},
|
|
||||||
op: '=',
|
|
||||||
value: 'tasks.tasks.divide',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
functions: [],
|
||||||
@ -981,10 +953,10 @@ export const celeryAllStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -998,14 +970,14 @@ export const celerySuccessStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@ -1034,10 +1006,10 @@ export const celerySuccessStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -1051,14 +1023,14 @@ export const celeryFailedStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@ -1087,10 +1059,10 @@ export const celeryFailedStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -1104,13 +1076,13 @@ export const celeryRetryStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.String,
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'count',
|
aggregateOperator: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
dataSource: DataSource.TRACES,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
expression: 'A',
|
expression: 'A',
|
||||||
@ -1139,10 +1111,10 @@ export const celeryRetryStateCountWidgetData = getWidgetQueryBuilder(
|
|||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
queryName: 'A',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'last',
|
||||||
spaceAggregation: 'sum',
|
spaceAggregation: 'sum',
|
||||||
stepInterval: 60,
|
stepInterval: 60,
|
||||||
timeAggregation: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
@ -6,8 +6,11 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
|||||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||||
import GridCard from 'container/GridCardLayout/GridCard';
|
import GridCard from 'container/GridCardLayout/GridCard';
|
||||||
import { Card } from 'container/GridCardLayout/styles';
|
import { Card } from 'container/GridCardLayout/styles';
|
||||||
|
import { Button } from 'container/MetricsApplication/Tabs/styles';
|
||||||
|
import { onGraphClickHandler } from 'container/MetricsApplication/Tabs/util';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||||
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
@ -16,15 +19,13 @@ import { UpdateTimeInterval } from 'store/actions';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
|
||||||
import {
|
import {
|
||||||
applyCeleryFilterOnWidgetData,
|
applyCeleryFilterOnWidgetData,
|
||||||
|
createFiltersFromData,
|
||||||
getFiltersFromQueryParams,
|
getFiltersFromQueryParams,
|
||||||
} from '../CeleryUtils';
|
} from '../CeleryUtils';
|
||||||
import {
|
import { useNavigateToTraces } from '../useNavigateToTraces';
|
||||||
celeryTaskLatencyWidgetData,
|
import { celeryTaskLatencyWidgetData } from './CeleryTaskGraphUtils';
|
||||||
celeryTimeSeriesTablesWidgetData,
|
|
||||||
} from './CeleryTaskGraphUtils';
|
|
||||||
|
|
||||||
interface TabData {
|
interface TabData {
|
||||||
label: string;
|
label: string;
|
||||||
@ -38,10 +39,8 @@ export enum CeleryTaskGraphState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CeleryTaskLatencyGraph({
|
function CeleryTaskLatencyGraph({
|
||||||
onClick,
|
|
||||||
queryEnabled,
|
queryEnabled,
|
||||||
}: {
|
}: {
|
||||||
onClick: (task: CaptureDataProps) => void;
|
|
||||||
queryEnabled: boolean;
|
queryEnabled: boolean;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@ -106,31 +105,51 @@ function CeleryTaskLatencyGraph({
|
|||||||
[celeryTaskLatencyData, selectedFilters],
|
[celeryTaskLatencyData, selectedFilters],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onGraphClick = (
|
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||||
xValue: number,
|
const [entityData, setEntityData] = useState<{
|
||||||
_yValue: number,
|
entity: string;
|
||||||
_mouseX: number,
|
value: string;
|
||||||
_mouseY: number,
|
}>();
|
||||||
data?: {
|
|
||||||
[key: string]: string;
|
const handleSetTimeStamp = useCallback((selectTime: number) => {
|
||||||
|
setSelectedTimeStamp(selectTime);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onGraphClick = useCallback(
|
||||||
|
(type: string): OnClickPluginOpts['onClick'] => (
|
||||||
|
xValue,
|
||||||
|
yValue,
|
||||||
|
mouseX,
|
||||||
|
mouseY,
|
||||||
|
data,
|
||||||
|
): Promise<void> => {
|
||||||
|
const [firstDataPoint] = Object.entries(data || {});
|
||||||
|
const [entity, value] = firstDataPoint;
|
||||||
|
setEntityData({
|
||||||
|
entity,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
return onGraphClickHandler(handleSetTimeStamp)(
|
||||||
|
xValue,
|
||||||
|
yValue,
|
||||||
|
mouseX,
|
||||||
|
mouseY,
|
||||||
|
type,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
): void => {
|
[handleSetTimeStamp],
|
||||||
const { start, end } = getStartAndEndTimesInMilliseconds(xValue);
|
);
|
||||||
|
|
||||||
// Extract entity and value from data
|
const navigateToTraces = useNavigateToTraces();
|
||||||
const [firstDataPoint] = Object.entries(data || {});
|
|
||||||
const [entity, value] = (firstDataPoint || ([] as unknown)) as [
|
|
||||||
string,
|
|
||||||
string,
|
|
||||||
];
|
|
||||||
|
|
||||||
onClick?.({
|
const goToTraces = useCallback(() => {
|
||||||
entity,
|
const { start, end } = getStartAndEndTimesInMilliseconds(selectedTimeStamp);
|
||||||
value,
|
const filters = createFiltersFromData({
|
||||||
timeRange: [start, end],
|
[entityData?.entity as string]: entityData?.value,
|
||||||
widgetData: celeryTimeSeriesTablesWidgetData(entity, value, 'Task Latency'),
|
|
||||||
});
|
});
|
||||||
};
|
navigateToTraces(filters, start, end, true);
|
||||||
|
}, [entityData, navigateToTraces, selectedTimeStamp]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@ -161,32 +180,62 @@ function CeleryTaskLatencyGraph({
|
|||||||
</Row>
|
</Row>
|
||||||
<div className="celery-task-graph-grid-content">
|
<div className="celery-task-graph-grid-content">
|
||||||
{graphState === CeleryTaskGraphState.P99 && (
|
{graphState === CeleryTaskGraphState.P99 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p99_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p99_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{graphState === CeleryTaskGraphState.P95 && (
|
{graphState === CeleryTaskGraphState.P95 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p95_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p95_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{graphState === CeleryTaskGraphState.P90 && (
|
{graphState === CeleryTaskGraphState.P90 && (
|
||||||
<GridCard
|
<>
|
||||||
widget={updatedWidgetData}
|
<Button
|
||||||
headerMenuList={[...ViewMenuAction]}
|
type="default"
|
||||||
onDragSelect={onDragSelect}
|
size="small"
|
||||||
onClickHandler={onGraphClick}
|
id="Celery_p90_latency_button"
|
||||||
isQueryEnabled={queryEnabled}
|
onClick={goToTraces}
|
||||||
/>
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<GridCard
|
||||||
|
widget={updatedWidgetData}
|
||||||
|
headerMenuList={[...ViewMenuAction]}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={onGraphClick('Celery_p90_latency')}
|
||||||
|
isQueryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -2,8 +2,14 @@
|
|||||||
import './CeleryTaskGraph.style.scss';
|
import './CeleryTaskGraph.style.scss';
|
||||||
|
|
||||||
import { Col, Row } from 'antd';
|
import { Col, Row } from 'antd';
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { QueryParams } from 'constants/query';
|
||||||
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyCeleryFilterOnWidgetData,
|
||||||
|
getFiltersFromQueryParams,
|
||||||
|
} from '../CeleryUtils';
|
||||||
import {
|
import {
|
||||||
celeryAllStateCountWidgetData,
|
celeryAllStateCountWidgetData,
|
||||||
celeryFailedStateCountWidgetData,
|
celeryFailedStateCountWidgetData,
|
||||||
@ -42,16 +48,29 @@ function CeleryTaskStateGraphConfig({
|
|||||||
setBarState(key as CeleryTaskState);
|
setBarState(key as CeleryTaskState);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { values, isLoading, isError } = useGetValueFromWidget(
|
const urlQuery = useUrlQuery();
|
||||||
[
|
|
||||||
celeryAllStateCountWidgetData,
|
const selectedFilters = useMemo(
|
||||||
celeryFailedStateCountWidgetData,
|
() =>
|
||||||
celeryRetryStateCountWidgetData,
|
getFiltersFromQueryParams(
|
||||||
celerySuccessStateCountWidgetData,
|
QueryParams.taskName,
|
||||||
],
|
urlQuery,
|
||||||
['celery-task-states'],
|
'celery.task_name',
|
||||||
|
),
|
||||||
|
[urlQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const widgetData = [
|
||||||
|
celeryAllStateCountWidgetData,
|
||||||
|
celeryFailedStateCountWidgetData,
|
||||||
|
celeryRetryStateCountWidgetData,
|
||||||
|
celerySuccessStateCountWidgetData,
|
||||||
|
].map((data) => applyCeleryFilterOnWidgetData(selectedFilters || [], data));
|
||||||
|
|
||||||
|
const { values, isLoading, isError } = useGetValueFromWidget(widgetData, [
|
||||||
|
'celery-task-states',
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="celery-task-states">
|
<Row className="celery-task-states">
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
@ -66,7 +85,13 @@ function CeleryTaskStateGraphConfig({
|
|||||||
<div className="celery-task-states__label-wrapper">
|
<div className="celery-task-states__label-wrapper">
|
||||||
<div className="celery-task-states__label">{tab.label}</div>
|
<div className="celery-task-states__label">{tab.label}</div>
|
||||||
<div className="celery-task-states__value">
|
<div className="celery-task-states__value">
|
||||||
{isLoading ? '-' : isError ? '-' : values[index]}
|
{isLoading
|
||||||
|
? '-'
|
||||||
|
: isError
|
||||||
|
? '-'
|
||||||
|
: Number.isNaN(values[index])
|
||||||
|
? '-'
|
||||||
|
: Math.round(Number(values[index]))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{tab.key === barState && <div className="celery-task-states__indicator" />}
|
{tab.key === barState && <div className="celery-task-states__indicator" />}
|
||||||
|
@ -90,3 +90,40 @@ export const paths = (
|
|||||||
|
|
||||||
return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip);
|
return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createFiltersFromData = (
|
||||||
|
data: Record<string, any>,
|
||||||
|
): Array<{
|
||||||
|
id: string;
|
||||||
|
key: {
|
||||||
|
key: string;
|
||||||
|
dataType: DataTypes;
|
||||||
|
type: string;
|
||||||
|
isColumn: boolean;
|
||||||
|
isJSON: boolean;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
op: string;
|
||||||
|
value: string;
|
||||||
|
}> => {
|
||||||
|
const excludeKeys = ['A', 'A_without_unit'];
|
||||||
|
|
||||||
|
return (
|
||||||
|
Object.entries(data)
|
||||||
|
.filter(([key]) => !excludeKeys.includes(key))
|
||||||
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
id: uuidv4(),
|
||||||
|
key: {
|
||||||
|
key,
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
type: 'tag',
|
||||||
|
isColumn: false,
|
||||||
|
isJSON: false,
|
||||||
|
id: `${key}--string--tag--false`,
|
||||||
|
},
|
||||||
|
op: '=',
|
||||||
|
value: value.toString(),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -12,6 +12,7 @@ export function useNavigateToTraces(): (
|
|||||||
filters: TagFilterItem[],
|
filters: TagFilterItem[],
|
||||||
startTime?: number,
|
startTime?: number,
|
||||||
endTime?: number,
|
endTime?: number,
|
||||||
|
sameTab?: boolean,
|
||||||
) => void {
|
) => void {
|
||||||
const { currentQuery } = useQueryBuilder();
|
const { currentQuery } = useQueryBuilder();
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
@ -38,7 +39,12 @@ export function useNavigateToTraces(): (
|
|||||||
);
|
);
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
(filters: TagFilterItem[], startTime?: number, endTime?: number): void => {
|
(
|
||||||
|
filters: TagFilterItem[],
|
||||||
|
startTime?: number,
|
||||||
|
endTime?: number,
|
||||||
|
sameTab?: boolean,
|
||||||
|
): void => {
|
||||||
const urlParams = new URLSearchParams();
|
const urlParams = new URLSearchParams();
|
||||||
if (startTime && endTime) {
|
if (startTime && endTime) {
|
||||||
urlParams.set(QueryParams.startTime, startTime.toString());
|
urlParams.set(QueryParams.startTime, startTime.toString());
|
||||||
@ -58,7 +64,7 @@ export function useNavigateToTraces(): (
|
|||||||
QueryParams.compositeQuery
|
QueryParams.compositeQuery
|
||||||
}=${JSONCompositeQuery}`;
|
}=${JSONCompositeQuery}`;
|
||||||
|
|
||||||
window.open(newTraceExplorerPath, '_blank');
|
window.open(newTraceExplorerPath, sameTab ? '_self' : '_blank');
|
||||||
},
|
},
|
||||||
[minTime, maxTime, prepareQuery],
|
[minTime, maxTime, prepareQuery],
|
||||||
);
|
);
|
||||||
|
@ -48,6 +48,7 @@ function WidgetGraphComponent({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
customSeries,
|
customSeries,
|
||||||
|
customErrorMessage,
|
||||||
}: WidgetGraphComponentProps): JSX.Element {
|
}: WidgetGraphComponentProps): JSX.Element {
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
@ -317,6 +318,13 @@ function WidgetGraphComponent({
|
|||||||
setSearchTerm={setSearchTerm}
|
setSearchTerm={setSearchTerm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{queryResponse.error && customErrorMessage && (
|
||||||
|
<div className="error-message-container">
|
||||||
|
<Typography.Text type="warning">{customErrorMessage}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
|
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)}
|
)}
|
||||||
|
@ -41,9 +41,15 @@ function GridCardGraph({
|
|||||||
openTracesButton,
|
openTracesButton,
|
||||||
onOpenTraceBtnClick,
|
onOpenTraceBtnClick,
|
||||||
customSeries,
|
customSeries,
|
||||||
|
customErrorMessage,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
}: GridCardGraphProps): JSX.Element {
|
}: GridCardGraphProps): JSX.Element {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
|
const [isInternalServerError, setIsInternalServerError] = useState<boolean>(
|
||||||
|
false,
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
toScrollWidgetId,
|
toScrollWidgetId,
|
||||||
setToScrollWidgetId,
|
setToScrollWidgetId,
|
||||||
@ -178,6 +184,8 @@ function GridCardGraph({
|
|||||||
variables: getDashboardVariables(variables),
|
variables: getDashboardVariables(variables),
|
||||||
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
|
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
version || DEFAULT_ENTITY_VERSION,
|
version || DEFAULT_ENTITY_VERSION,
|
||||||
{
|
{
|
||||||
@ -207,6 +215,11 @@ function GridCardGraph({
|
|||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
setErrorMessage(error.message);
|
setErrorMessage(error.message);
|
||||||
|
if (customErrorMessage) {
|
||||||
|
setIsInternalServerError(
|
||||||
|
String(error.message).includes('API responded with 500'),
|
||||||
|
);
|
||||||
|
}
|
||||||
setDashboardQueryRangeCalled(true);
|
setDashboardQueryRangeCalled(true);
|
||||||
},
|
},
|
||||||
onSettled: (data) => {
|
onSettled: (data) => {
|
||||||
@ -256,6 +269,7 @@ function GridCardGraph({
|
|||||||
openTracesButton={openTracesButton}
|
openTracesButton={openTracesButton}
|
||||||
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
onOpenTraceBtnClick={onOpenTraceBtnClick}
|
||||||
customSeries={customSeries}
|
customSeries={customSeries}
|
||||||
|
customErrorMessage={isInternalServerError ? customErrorMessage : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,6 +37,7 @@ export interface WidgetGraphComponentProps {
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
||||||
|
customErrorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GridCardGraphProps {
|
export interface GridCardGraphProps {
|
||||||
@ -54,6 +55,9 @@ export interface GridCardGraphProps {
|
|||||||
openTracesButton?: boolean;
|
openTracesButton?: boolean;
|
||||||
onOpenTraceBtnClick?: (record: RowData) => void;
|
onOpenTraceBtnClick?: (record: RowData) => void;
|
||||||
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
customSeries?: (data: QueryData[]) => uPlot.Series[];
|
||||||
|
customErrorMessage?: string;
|
||||||
|
start?: number;
|
||||||
|
end?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||||
|
@ -106,6 +106,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-message-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
.row-settings {
|
.row-settings {
|
||||||
.ant-popover-inner {
|
.ant-popover-inner {
|
||||||
width: 191px;
|
width: 191px;
|
||||||
|
@ -23,7 +23,7 @@ export default function CeleryOverview(): JSX.Element {
|
|||||||
<p className="celery-overview-content-header-title">
|
<p className="celery-overview-content-header-title">
|
||||||
Messaging Queue Overview
|
Messaging Queue Overview
|
||||||
</p>
|
</p>
|
||||||
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
<DateTimeSelectionV2 showAutoRefresh hideShareModal={false} />
|
||||||
</div>
|
</div>
|
||||||
<CeleryOverviewConfigOptions />
|
<CeleryOverviewConfigOptions />
|
||||||
<CeleryOverviewTable onRowClick={onRowClick} />
|
<CeleryOverviewTable onRowClick={onRowClick} />
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import './CeleryTask.styles.scss';
|
import './CeleryTask.styles.scss';
|
||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
|
||||||
import { Button, Tooltip } from 'antd';
|
|
||||||
import CeleryTaskConfigOptions from 'components/CeleryTask/CeleryTaskConfigOptions/CeleryTaskConfigOptions';
|
import CeleryTaskConfigOptions from 'components/CeleryTask/CeleryTaskConfigOptions/CeleryTaskConfigOptions';
|
||||||
import CeleryTaskDetail, {
|
import CeleryTaskDetail, {
|
||||||
CaptureDataProps,
|
CaptureDataProps,
|
||||||
} from 'components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail';
|
} from 'components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail';
|
||||||
import CeleryTaskGraphGrid from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid';
|
import CeleryTaskGraphGrid from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid';
|
||||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||||
import { Check, Share2 } from 'lucide-react';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
|
||||||
|
|
||||||
export default function CeleryTask(): JSX.Element {
|
export default function CeleryTask(): JSX.Element {
|
||||||
const [task, setTask] = useState<CaptureDataProps | null>(null);
|
const [task, setTask] = useState<CaptureDataProps | null>(null);
|
||||||
@ -19,36 +15,13 @@ export default function CeleryTask(): JSX.Element {
|
|||||||
setTask(captureData);
|
setTask(captureData);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
|
||||||
|
|
||||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="celery-task-container">
|
<div className="celery-task-container">
|
||||||
<div className="celery-content">
|
<div className="celery-content">
|
||||||
<div className="celery-content-header">
|
<div className="celery-content-header">
|
||||||
<p className="celery-content-header-title">Celery</p>
|
<p className="celery-content-header-title">Celery</p>
|
||||||
<div className="celery-content-header-right">
|
<div className="celery-content-header-right">
|
||||||
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
<DateTimeSelectionV2 showAutoRefresh hideShareModal={false} />
|
||||||
<Tooltip title="Share this" arrow={false}>
|
|
||||||
<Button
|
|
||||||
className="periscope-btn copy-url-btn"
|
|
||||||
onClick={(): void => {
|
|
||||||
handleCopyToClipboard(window.location.href);
|
|
||||||
setIsURLCopied(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsURLCopied(false);
|
|
||||||
}, 1000);
|
|
||||||
}}
|
|
||||||
icon={
|
|
||||||
isURLCopied ? (
|
|
||||||
<Check size={14} color={Color.BG_FOREST_500} />
|
|
||||||
) : (
|
|
||||||
<Share2 size={14} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CeleryTaskGraphGrid
|
<CeleryTaskGraphGrid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user