mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-12 17:51:36 +08:00
feat: celery task page restructuring and other misc fixes (#6985)
* feat: celery task page restructuring and other misc fixes * feat: added custom series hook * feat: code fix and typo * feat: added feedback fixes * feat: configured error % graphs * feat: resolved comments
This commit is contained in:
parent
d1e7cc128f
commit
3849ca1ecc
@ -1,14 +1,10 @@
|
|||||||
import './CeleryTaskConfigOptions.styles.scss';
|
import './CeleryTaskConfigOptions.styles.scss';
|
||||||
|
|
||||||
import { Color } from '@signozhq/design-tokens';
|
import { Select, Spin, Typography } from 'antd';
|
||||||
import { Button, Select, Spin, Tooltip, Typography } from 'antd';
|
|
||||||
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';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getValuesFromQueryParams,
|
getValuesFromQueryParams,
|
||||||
@ -23,11 +19,8 @@ function CeleryTaskConfigOptions(): JSX.Element {
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
|
|
||||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="celery-task-filters">
|
<div className="celery-task-filters">
|
||||||
<div className="celery-filters">
|
<div className="celery-filters">
|
||||||
@ -66,25 +59,6 @@ function CeleryTaskConfigOptions(): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { DefaultOptionType } from 'antd/es/select';
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
|
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
|
||||||
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
export interface Filters {
|
export interface Filters {
|
||||||
searchText: string;
|
searchText: string;
|
||||||
@ -31,8 +35,18 @@ export function useGetAllFilters(props: Filters): GetAllFiltersResponse {
|
|||||||
tagType,
|
tagType,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
const { data, isLoading } = useQuery(
|
const { data, isLoading } = useQuery(
|
||||||
['attributesValues', attributeKey, searchText],
|
[
|
||||||
|
REACT_QUERY_KEY.GET_ATTRIBUTE_VALUES,
|
||||||
|
attributeKey,
|
||||||
|
searchText,
|
||||||
|
minTime,
|
||||||
|
maxTime,
|
||||||
|
],
|
||||||
async () => {
|
async () => {
|
||||||
const keys = Array.isArray(attributeKey) ? attributeKey : [attributeKey];
|
const keys = Array.isArray(attributeKey) ? attributeKey : [attributeKey];
|
||||||
|
|
||||||
|
@ -3,14 +3,12 @@ import './CeleryTaskGraph.style.scss';
|
|||||||
import { Color } from '@signozhq/design-tokens';
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { themeColors } from 'constants/theme';
|
|
||||||
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 { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import getLabelName from 'lib/getLabelName';
|
import { isEmpty } from 'lodash-es';
|
||||||
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
|
|
||||||
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';
|
||||||
@ -18,11 +16,10 @@ import { useHistory, useLocation } from 'react-router-dom';
|
|||||||
import { UpdateTimeInterval } from 'store/actions';
|
import { UpdateTimeInterval } from 'store/actions';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { QueryData } from 'types/api/widgets/getQuery';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
||||||
import { paths } from '../CeleryUtils';
|
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
||||||
import {
|
import {
|
||||||
celeryAllStateWidgetData,
|
celeryAllStateWidgetData,
|
||||||
celeryFailedStateWidgetData,
|
celeryFailedStateWidgetData,
|
||||||
@ -114,58 +111,26 @@ function CeleryTaskBar({
|
|||||||
string,
|
string,
|
||||||
];
|
];
|
||||||
|
|
||||||
onClick?.({
|
if (!isEmpty(entity) || !isEmpty(value)) {
|
||||||
entity,
|
onClick?.({
|
||||||
value,
|
entity,
|
||||||
timeRange: [start, end],
|
value,
|
||||||
widgetData,
|
timeRange: [start, end],
|
||||||
});
|
widgetData,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGraphSeries = (color: string, label: string): any => ({
|
const { getCustomSeries } = useGetGraphCustomSeries({
|
||||||
|
isDarkMode,
|
||||||
drawStyle: 'bars',
|
drawStyle: 'bars',
|
||||||
paths,
|
colorMapping: {
|
||||||
lineInterpolation: 'spline',
|
SUCCESS: Color.BG_FOREST_500,
|
||||||
show: true,
|
FAILURE: Color.BG_CHERRY_500,
|
||||||
label,
|
RETRY: Color.BG_AMBER_400,
|
||||||
fill: `${color}90`,
|
|
||||||
stroke: color,
|
|
||||||
width: 2,
|
|
||||||
spanGaps: true,
|
|
||||||
points: {
|
|
||||||
size: 5,
|
|
||||||
show: false,
|
|
||||||
stroke: color,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const customSeries = (data: QueryData[]): uPlot.Series[] => {
|
|
||||||
const configurations: uPlot.Series[] = [
|
|
||||||
{ label: 'Timestamp', stroke: 'purple' },
|
|
||||||
];
|
|
||||||
for (let i = 0; i < data.length; i += 1) {
|
|
||||||
const { metric = {}, queryName = '', legend = '' } = data[i] || {};
|
|
||||||
const label = getLabelName(metric, queryName || '', legend || '');
|
|
||||||
let color = generateColor(
|
|
||||||
label,
|
|
||||||
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
|
|
||||||
);
|
|
||||||
if (label === 'SUCCESS') {
|
|
||||||
color = Color.BG_FOREST_500;
|
|
||||||
}
|
|
||||||
if (label === 'FAILURE') {
|
|
||||||
color = Color.BG_CHERRY_500;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (label === 'RETRY') {
|
|
||||||
color = Color.BG_AMBER_400;
|
|
||||||
}
|
|
||||||
const series = getGraphSeries(color, label);
|
|
||||||
configurations.push(series);
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
@ -183,7 +148,7 @@ function CeleryTaskBar({
|
|||||||
onClickHandler={(...args): void =>
|
onClickHandler={(...args): void =>
|
||||||
onGraphClick(celerySlowestTasksTableWidgetData, ...args)
|
onGraphClick(celerySlowestTasksTableWidgetData, ...args)
|
||||||
}
|
}
|
||||||
customSeries={customSeries}
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Failed && (
|
{barState === CeleryTaskState.Failed && (
|
||||||
@ -195,7 +160,7 @@ function CeleryTaskBar({
|
|||||||
onClickHandler={(...args): void =>
|
onClickHandler={(...args): void =>
|
||||||
onGraphClick(celeryFailedTasksTableWidgetData, ...args)
|
onGraphClick(celeryFailedTasksTableWidgetData, ...args)
|
||||||
}
|
}
|
||||||
customSeries={customSeries}
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Retry && (
|
{barState === CeleryTaskState.Retry && (
|
||||||
@ -207,7 +172,7 @@ function CeleryTaskBar({
|
|||||||
onClickHandler={(...args): void =>
|
onClickHandler={(...args): void =>
|
||||||
onGraphClick(celeryRetryTasksTableWidgetData, ...args)
|
onGraphClick(celeryRetryTasksTableWidgetData, ...args)
|
||||||
}
|
}
|
||||||
customSeries={customSeries}
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{barState === CeleryTaskState.Successful && (
|
{barState === CeleryTaskState.Successful && (
|
||||||
@ -219,7 +184,7 @@ function CeleryTaskBar({
|
|||||||
onClickHandler={(...args): void =>
|
onClickHandler={(...args): void =>
|
||||||
onGraphClick(celerySuccessTasksTableWidgetData, ...args)
|
onGraphClick(celerySuccessTasksTableWidgetData, ...args)
|
||||||
}
|
}
|
||||||
customSeries={customSeries}
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,93 +6,85 @@
|
|||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
.celery-task-graph-grid {
|
.celery-task-graph-bar,
|
||||||
display: grid;
|
.celery-task-graph-task-latency {
|
||||||
grid-template-columns: 60% 40%;
|
height: 380px !important;
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.celery-task-graph {
|
.celery-task-graph-grid-content {
|
||||||
height: 380px !important;
|
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
width: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
}
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
height: calc(100% - 18px);
|
height: calc(100% - 18px);
|
||||||
|
|
||||||
.widget-graph-container {
|
.widget-graph-container {
|
||||||
&.bar {
|
&.bar {
|
||||||
height: calc(100% - 110px);
|
height: calc(100% - 110px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.graph {
|
||||||
|
height: calc(100% - 80px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.celery-task-graph-worker-count {
|
.celery-task-graph-worker-count {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--bg-slate-500);
|
||||||
|
background: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
rgba(171, 189, 255, 0) 0%,
|
||||||
|
rgba(171, 189, 255, 0) 100%
|
||||||
|
),
|
||||||
|
#0b0c0e;
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-count-text-container {
|
||||||
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid var(--bg-slate-500);
|
|
||||||
background: linear-gradient(
|
|
||||||
0deg,
|
|
||||||
rgba(171, 189, 255, 0) 0%,
|
|
||||||
rgba(171, 189, 255, 0) 100%
|
|
||||||
),
|
|
||||||
#0b0c0e;
|
|
||||||
|
|
||||||
.ant-card-body {
|
.celery-task-graph-worker-count-text {
|
||||||
height: 100%;
|
font-size: 2.5vw;
|
||||||
width: 100%;
|
text-align: center;
|
||||||
}
|
|
||||||
|
|
||||||
.worker-count-text-container {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.celery-task-graph-worker-count-text {
|
|
||||||
font-size: 2.5vw;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.worker-count-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: start;
|
|
||||||
align-items: start;
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.celery-task-graph-bar,
|
.worker-count-header {
|
||||||
.celery-task-graph-task-latency {
|
display: flex;
|
||||||
height: 380px !important;
|
justify-content: start;
|
||||||
|
align-items: start;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.celery-task-graph-grid-content {
|
.celery-task-graph {
|
||||||
padding: 6px;
|
height: 380px !important;
|
||||||
height: 100%;
|
padding: 6px;
|
||||||
}
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
height: calc(100% - 18px);
|
height: calc(100% - 18px);
|
||||||
|
|
||||||
.widget-graph-container {
|
.widget-graph-container {
|
||||||
&.bar {
|
&.bar {
|
||||||
height: calc(100% - 110px);
|
height: calc(100% - 110px);
|
||||||
}
|
|
||||||
|
|
||||||
&.graph {
|
|
||||||
height: calc(100% - 80px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,6 +108,85 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.metric-based-graphs,
|
||||||
|
.trace-based-graphs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.metric-page-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.celery-task-graph {
|
||||||
|
height: 280px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trace-based-graphs {
|
||||||
|
.trace-based-graphs-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.configure-option-Info {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: max-content 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.configure-option-Info-text {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-panel {
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(18, 19, 23, 0.4);
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
height: 48px !important;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.ant-typography {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-panel-section {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.row-icon {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.celery-task-states {
|
.celery-task-states {
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import './CeleryTaskGraph.style.scss';
|
import './CeleryTaskGraph.style.scss';
|
||||||
|
|
||||||
import { Card, Typography } from 'antd';
|
import { Card, Typography } from 'antd';
|
||||||
import { useMemo } from 'react';
|
import { CardContainer } from 'container/GridCardLayout/styles';
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
@ -23,9 +26,11 @@ import CeleryTaskLatencyGraph from './CeleryTaskLatencyGraph';
|
|||||||
export default function CeleryTaskGraphGrid({
|
export default function CeleryTaskGraphGrid({
|
||||||
onClick,
|
onClick,
|
||||||
queryEnabled,
|
queryEnabled,
|
||||||
|
configureOptionComponent,
|
||||||
}: {
|
}: {
|
||||||
onClick: (task: CaptureDataProps) => void;
|
onClick: (task: CaptureDataProps) => void;
|
||||||
queryEnabled: boolean;
|
queryEnabled: boolean;
|
||||||
|
configureOptionComponent?: React.ReactNode;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
@ -71,44 +76,123 @@ export default function CeleryTaskGraphGrid({
|
|||||||
DataTypes.String,
|
DataTypes.String,
|
||||||
'tag',
|
'tag',
|
||||||
);
|
);
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
|
const [collapsedSections, setCollapsedSections] = useState<{
|
||||||
|
[key: string]: boolean;
|
||||||
|
}>({
|
||||||
|
metricBasedGraphs: false,
|
||||||
|
traceBasedGraphs: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleCollapse = (key: string): void => {
|
||||||
|
setCollapsedSections((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: !prev[key],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="celery-task-graph-grid-container">
|
<div className="celery-task-graph-grid-container">
|
||||||
<div className="celery-task-graph-grid">
|
<div className="metric-based-graphs">
|
||||||
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||||
<Card className="celery-task-graph-worker-count">
|
<div className="row-panel">
|
||||||
<div className="worker-count-header">
|
<div className="row-panel-section">
|
||||||
<Typography.Text className="worker-count-header-text">
|
<Typography.Text className="section-title">
|
||||||
Worker Count
|
Flower Metrics
|
||||||
|
</Typography.Text>
|
||||||
|
{collapsedSections.metricBasedGraphs ? (
|
||||||
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
onClick={(): void => toggleCollapse('metricBasedGraphs')}
|
||||||
|
className="row-icon"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronUp
|
||||||
|
size={14}
|
||||||
|
onClick={(): void => toggleCollapse('metricBasedGraphs')}
|
||||||
|
className="row-icon"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContainer>
|
||||||
|
{!collapsedSections.metricBasedGraphs && (
|
||||||
|
<div className="metric-page-grid">
|
||||||
|
<CeleryTaskGraph
|
||||||
|
key={celeryActiveTasksData.id}
|
||||||
|
widgetData={celeryActiveTasksData}
|
||||||
|
queryEnabled={queryEnabled}
|
||||||
|
/>
|
||||||
|
<Card className="celery-task-graph-worker-count">
|
||||||
|
<div className="worker-count-header">
|
||||||
|
<Typography.Text className="worker-count-header-text">
|
||||||
|
Worker Count
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<div className="worker-count-text-container">
|
||||||
|
<Typography.Text className="celery-task-graph-worker-count-text">
|
||||||
|
{options.filter((option) => option.value).length}
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="trace-based-graphs">
|
||||||
|
<div className="trace-based-graphs-header">
|
||||||
|
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||||
|
<div className="row-panel">
|
||||||
|
<div className="row-panel-section">
|
||||||
|
<Typography.Text className="section-title">
|
||||||
|
Span Based Stats
|
||||||
|
</Typography.Text>
|
||||||
|
{collapsedSections.traceBasedGraphs ? (
|
||||||
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
onClick={(): void => toggleCollapse('traceBasedGraphs')}
|
||||||
|
className="row-icon"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronUp
|
||||||
|
size={14}
|
||||||
|
onClick={(): void => toggleCollapse('traceBasedGraphs')}
|
||||||
|
className="row-icon"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContainer>
|
||||||
|
<div className="configure-option-Info">
|
||||||
|
{configureOptionComponent}
|
||||||
|
<Typography.Text className="configure-option-Info-text">
|
||||||
|
Click on a graph co-ordinate to see more details
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="worker-count-text-container">
|
</div>
|
||||||
<Typography.Text className="celery-task-graph-worker-count-text">
|
{!collapsedSections.traceBasedGraphs && (
|
||||||
{options.filter((option) => option.value).length}
|
<>
|
||||||
</Typography.Text>
|
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
||||||
</div>
|
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
||||||
</Card>
|
<div className="celery-task-graph-grid-bottom">
|
||||||
</div>
|
{bottomWidgetData.map((widgetData, index) => (
|
||||||
<div className="celery-task-graph-grid">
|
<CeleryTaskGraph
|
||||||
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
key={widgetData.id}
|
||||||
<CeleryTaskGraph
|
widgetData={widgetData}
|
||||||
key={celeryActiveTasksData.id}
|
onClick={onClick}
|
||||||
widgetData={celeryActiveTasksData}
|
queryEnabled={queryEnabled}
|
||||||
queryEnabled={queryEnabled}
|
rightPanelTitle={rightPanelTitle[index]}
|
||||||
/>
|
applyCeleryTaskFilter
|
||||||
</div>
|
/>
|
||||||
<div className="celery-task-graph-grid-bottom">
|
))}
|
||||||
{bottomWidgetData.map((widgetData, index) => (
|
</div>
|
||||||
<CeleryTaskGraph
|
</>
|
||||||
key={widgetData.id}
|
)}
|
||||||
widgetData={widgetData}
|
|
||||||
onClick={onClick}
|
|
||||||
queryEnabled={queryEnabled}
|
|
||||||
rightPanelTitle={rightPanelTitle[index]}
|
|
||||||
applyCeleryTaskFilter
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CeleryTaskGraphGrid.defaultProps = {
|
||||||
|
configureOptionComponent: null,
|
||||||
|
};
|
||||||
|
@ -332,6 +332,7 @@ export const celeryTasksByWorkerWidgetData = (
|
|||||||
timeAggregation: 'rate',
|
timeAggregation: 'rate',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
yAxisUnit: 'cps',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -345,44 +346,103 @@ export const celeryErrorByWorkerWidgetData = (
|
|||||||
description: 'Represents the number of errors by each worker.',
|
description: 'Represents the number of errors by each worker.',
|
||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
queryName: 'A',
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
aggregateAttribute: {
|
aggregateAttribute: {
|
||||||
dataType: DataTypes.String,
|
dataType: 'string',
|
||||||
id: '------false',
|
id: 'span_id--string----true',
|
||||||
isColumn: false,
|
isColumn: true,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: '',
|
key: 'span_id',
|
||||||
type: '',
|
type: '',
|
||||||
},
|
},
|
||||||
aggregateOperator: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
spaceAggregation: 'sum',
|
||||||
disabled: false,
|
functions: [],
|
||||||
expression: 'A',
|
|
||||||
filters: {
|
filters: {
|
||||||
items: [],
|
items: [
|
||||||
|
{
|
||||||
|
id: uuidv4(),
|
||||||
|
key: {
|
||||||
|
dataType: DataTypes.bool,
|
||||||
|
id: 'has_error--bool----true',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
key: 'has_error',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
op: '=',
|
||||||
|
value: 'true',
|
||||||
|
},
|
||||||
|
],
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
expression: 'A',
|
||||||
|
disabled: true,
|
||||||
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
|
having: [],
|
||||||
|
limit: null,
|
||||||
|
orderBy: [],
|
||||||
groupBy: [
|
groupBy: [
|
||||||
{
|
{
|
||||||
dataType: DataTypes.String,
|
dataType: DataTypes.String,
|
||||||
id: 'celery.hostname--string--tag--false',
|
|
||||||
isColumn: false,
|
isColumn: false,
|
||||||
isJSON: false,
|
isJSON: false,
|
||||||
key: 'celery.hostname',
|
key: 'celery.hostname',
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
|
id: 'celery.hostname--string--tag--false',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
having: [],
|
legend: '',
|
||||||
legend: '{{celery.hostname}}',
|
|
||||||
limit: 10,
|
|
||||||
orderBy: [],
|
|
||||||
queryName: 'A',
|
|
||||||
reduceTo: 'avg',
|
reduceTo: 'avg',
|
||||||
spaceAggregation: 'sum',
|
|
||||||
stepInterval: getStepInterval(startTime, endTime),
|
|
||||||
timeAggregation: 'rate',
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
dataSource: 'traces',
|
||||||
|
queryName: 'B',
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
|
aggregateAttribute: {
|
||||||
|
dataType: 'string',
|
||||||
|
id: 'span_id--string----true',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
key: 'span_id',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
timeAggregation: 'count_distinct',
|
||||||
|
spaceAggregation: 'sum',
|
||||||
|
functions: [],
|
||||||
|
filters: {
|
||||||
|
items: [],
|
||||||
|
op: 'AND',
|
||||||
|
},
|
||||||
|
expression: 'B',
|
||||||
|
disabled: true,
|
||||||
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
|
having: [],
|
||||||
|
limit: null,
|
||||||
|
orderBy: [],
|
||||||
|
groupBy: [
|
||||||
|
{
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
isColumn: false,
|
||||||
|
isJSON: false,
|
||||||
|
key: 'celery.hostname',
|
||||||
|
type: 'tag',
|
||||||
|
id: 'celery.hostname--string--tag--false',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
legend: '',
|
||||||
|
reduceTo: 'avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
queryName: 'F1',
|
||||||
|
expression: '(A/B)*100',
|
||||||
|
disabled: false,
|
||||||
|
legend: '{{celery.hostname}}',
|
||||||
|
} as any,
|
||||||
],
|
],
|
||||||
|
yAxisUnit: 'percent',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -392,7 +452,7 @@ export const celeryLatencyByWorkerWidgetData = (
|
|||||||
): Widgets =>
|
): Widgets =>
|
||||||
getWidgetQueryBuilder(
|
getWidgetQueryBuilder(
|
||||||
getWidgetQuery({
|
getWidgetQuery({
|
||||||
title: 'Latency by Worker',
|
title: 'Latency by worker',
|
||||||
description: 'Represents the latency of tasks by each worker.',
|
description: 'Represents the latency of tasks by each worker.',
|
||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
@ -444,7 +504,7 @@ export const celeryActiveTasksWidgetData = (
|
|||||||
): Widgets =>
|
): Widgets =>
|
||||||
getWidgetQueryBuilder(
|
getWidgetQueryBuilder(
|
||||||
getWidgetQuery({
|
getWidgetQuery({
|
||||||
title: 'Tasks/ worker (Active tasks)',
|
title: 'Active Tasks by worker',
|
||||||
description: 'Represents the number of active tasks.',
|
description: 'Represents the number of active tasks.',
|
||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
@ -487,6 +547,7 @@ export const celeryActiveTasksWidgetData = (
|
|||||||
timeAggregation: 'latest',
|
timeAggregation: 'latest',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
yAxisUnit: 'cps',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import { Color } from '@signozhq/design-tokens';
|
||||||
|
import { themeColors } from 'constants/theme';
|
||||||
|
import getLabelName from 'lib/getLabelName';
|
||||||
|
import { drawStyles } from 'lib/uPlotLib/utils/constants';
|
||||||
|
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
|
||||||
|
import { QueryData } from 'types/api/widgets/getQuery';
|
||||||
|
|
||||||
|
import { paths } from './CeleryUtils';
|
||||||
|
|
||||||
|
interface UseGetGraphCustomSeriesProps {
|
||||||
|
isDarkMode: boolean;
|
||||||
|
drawStyle?: typeof drawStyles[keyof typeof drawStyles];
|
||||||
|
colorMapping?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultColorMapping = {
|
||||||
|
SUCCESS: Color.BG_FOREST_500,
|
||||||
|
FAILURE: Color.BG_CHERRY_500,
|
||||||
|
RETRY: Color.BG_AMBER_400,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetGraphCustomSeries = ({
|
||||||
|
isDarkMode,
|
||||||
|
drawStyle = 'bars',
|
||||||
|
colorMapping = defaultColorMapping,
|
||||||
|
}: UseGetGraphCustomSeriesProps): {
|
||||||
|
getCustomSeries: (data: QueryData[]) => uPlot.Series[];
|
||||||
|
} => {
|
||||||
|
const getGraphSeries = (color: string, label: string): any => ({
|
||||||
|
drawStyle,
|
||||||
|
paths,
|
||||||
|
lineInterpolation: 'spline',
|
||||||
|
show: true,
|
||||||
|
label,
|
||||||
|
fill: `${color}90`,
|
||||||
|
stroke: color,
|
||||||
|
width: 2,
|
||||||
|
spanGaps: true,
|
||||||
|
points: {
|
||||||
|
size: 5,
|
||||||
|
show: false,
|
||||||
|
stroke: color,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCustomSeries = (data: QueryData[]): uPlot.Series[] => {
|
||||||
|
const configurations: uPlot.Series[] = [
|
||||||
|
{ label: 'Timestamp', stroke: 'purple' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i += 1) {
|
||||||
|
const { metric = {}, queryName = '', legend = '' } = data[i] || {};
|
||||||
|
const label = getLabelName(metric, queryName || '', legend || '');
|
||||||
|
|
||||||
|
// Check if label exists in colorMapping
|
||||||
|
const color =
|
||||||
|
colorMapping[label] ||
|
||||||
|
generateColor(
|
||||||
|
label,
|
||||||
|
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
const series = getGraphSeries(color, label);
|
||||||
|
configurations.push(series);
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { getCustomSeries };
|
||||||
|
};
|
@ -32,4 +32,5 @@ export const REACT_QUERY_KEY = {
|
|||||||
GET_JOB_LIST: 'GET_JOB_LIST',
|
GET_JOB_LIST: 'GET_JOB_LIST',
|
||||||
GET_DAEMONSET_LIST: 'GET_DAEMONSET_LIST,',
|
GET_DAEMONSET_LIST: 'GET_DAEMONSET_LIST,',
|
||||||
GET_VOLUME_LIST: 'GET_VOLUME_LIST',
|
GET_VOLUME_LIST: 'GET_VOLUME_LIST',
|
||||||
|
GET_ATTRIBUTE_VALUES: 'GET_ATTRIBUTE_VALUES',
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
|
import { useGetGraphCustomSeries } from 'components/CeleryTask/useGetGraphCustomSeries';
|
||||||
import { useNavigateToTraces } from 'components/CeleryTask/useNavigateToTraces';
|
import { useNavigateToTraces } from 'components/CeleryTask/useNavigateToTraces';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||||
@ -109,6 +111,18 @@ export default function OverviewRightPanelGraph({
|
|||||||
},
|
},
|
||||||
[navigateToTraces, filters, selectedTimeStamp],
|
[navigateToTraces, filters, selectedTimeStamp],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { getCustomSeries } = useGetGraphCustomSeries({
|
||||||
|
isDarkMode: false,
|
||||||
|
drawStyle: 'bars',
|
||||||
|
colorMapping: {
|
||||||
|
False: Color.BG_CHERRY_500,
|
||||||
|
True: Color.BG_FOREST_400,
|
||||||
|
None: Color.BG_SLATE_200,
|
||||||
|
'Request Rate': Color.BG_ROBIN_500,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="overview-right-panel-graph-card">
|
<Card className="overview-right-panel-graph-card">
|
||||||
<div className="request-rate-card">
|
<div className="request-rate-card">
|
||||||
@ -125,6 +139,7 @@ export default function OverviewRightPanelGraph({
|
|||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
onClickHandler={handleGraphClick('Celery_request_rate')}
|
onClickHandler={handleGraphClick('Celery_request_rate')}
|
||||||
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="error-rate-card">
|
<div className="error-rate-card">
|
||||||
@ -141,6 +156,7 @@ export default function OverviewRightPanelGraph({
|
|||||||
headerMenuList={[...ViewMenuAction]}
|
headerMenuList={[...ViewMenuAction]}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
onClickHandler={handleGraphClick('Celery_error_rate')}
|
onClickHandler={handleGraphClick('Celery_error_rate')}
|
||||||
|
customSeries={getCustomSeries}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="avg-latency-card">
|
<div className="avg-latency-card">
|
||||||
|
@ -195,6 +195,7 @@ export const celeryOverviewRequestRateGraphData = (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
panelTypes: PANEL_TYPES.BAR,
|
panelTypes: PANEL_TYPES.BAR,
|
||||||
|
yAxisUnit: 'reqps',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -206,22 +207,24 @@ export const celeryOverviewErrorRateGraphData = (
|
|||||||
): Widgets =>
|
): Widgets =>
|
||||||
getWidgetQueryBuilder(
|
getWidgetQueryBuilder(
|
||||||
getWidgetQuery({
|
getWidgetQuery({
|
||||||
title: 'Error',
|
title: 'Error %',
|
||||||
description: 'Represents Error in the service',
|
description: 'Represents Error in the service',
|
||||||
queryData: [
|
queryData: [
|
||||||
{
|
{
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
queryName: 'A',
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
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: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
spaceAggregation: 'sum',
|
||||||
disabled: false,
|
functions: [],
|
||||||
expression: 'A',
|
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
...(filters ?? []),
|
...(filters ?? []),
|
||||||
@ -241,31 +244,31 @@ export const celeryOverviewErrorRateGraphData = (
|
|||||||
],
|
],
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
expression: 'A',
|
||||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
disabled: true,
|
||||||
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
having: [],
|
having: [],
|
||||||
legend: 'True',
|
|
||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'A',
|
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||||
|
legend: '',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'avg',
|
||||||
spaceAggregation: 'sum',
|
|
||||||
stepInterval: getStepInterval(startTime, endTime),
|
|
||||||
timeAggregation: 'rate',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
queryName: 'B',
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
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: 'rate',
|
timeAggregation: 'count_distinct',
|
||||||
dataSource: DataSource.TRACES,
|
spaceAggregation: 'sum',
|
||||||
disabled: false,
|
functions: [],
|
||||||
expression: 'B',
|
|
||||||
filters: {
|
filters: {
|
||||||
items: [
|
items: [
|
||||||
...(filters ?? []),
|
...(filters ?? []),
|
||||||
@ -285,20 +288,60 @@ export const celeryOverviewErrorRateGraphData = (
|
|||||||
],
|
],
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
},
|
},
|
||||||
functions: [],
|
expression: 'B',
|
||||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
disabled: true,
|
||||||
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
having: [],
|
having: [],
|
||||||
legend: 'False',
|
|
||||||
limit: null,
|
limit: null,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
queryName: 'B',
|
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||||
|
legend: '',
|
||||||
reduceTo: 'avg',
|
reduceTo: 'avg',
|
||||||
spaceAggregation: 'sum',
|
|
||||||
stepInterval: getStepInterval(startTime, endTime),
|
|
||||||
timeAggregation: 'rate',
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
queryName: 'C',
|
||||||
|
aggregateOperator: 'count_distinct',
|
||||||
|
aggregateAttribute: {
|
||||||
|
dataType: DataTypes.String,
|
||||||
|
id: 'span_id--string----true',
|
||||||
|
isColumn: true,
|
||||||
|
isJSON: false,
|
||||||
|
key: 'span_id',
|
||||||
|
type: '',
|
||||||
|
},
|
||||||
|
timeAggregation: 'count_distinct',
|
||||||
|
spaceAggregation: 'sum',
|
||||||
|
functions: [],
|
||||||
|
filters: {
|
||||||
|
items: filters ?? [],
|
||||||
|
op: 'AND',
|
||||||
|
},
|
||||||
|
expression: 'C',
|
||||||
|
disabled: true,
|
||||||
|
stepInterval: getStepInterval(startTime, endTime),
|
||||||
|
having: [],
|
||||||
|
limit: null,
|
||||||
|
orderBy: [],
|
||||||
|
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||||
|
legend: '',
|
||||||
|
reduceTo: 'avg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
queryName: 'F1',
|
||||||
|
expression: '(A/C)*100',
|
||||||
|
disabled: false,
|
||||||
|
legend: 'True',
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
queryName: 'F2',
|
||||||
|
expression: '(B/C)*100',
|
||||||
|
disabled: false,
|
||||||
|
legend: 'False',
|
||||||
|
} as any,
|
||||||
],
|
],
|
||||||
panelTypes: PANEL_TYPES.BAR,
|
panelTypes: PANEL_TYPES.BAR,
|
||||||
|
yAxisUnit: 'percent',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.celery-content-header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
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);
|
||||||
@ -15,15 +19,43 @@ 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 Task</p>
|
<p className="celery-content-header-title">Celery</p>
|
||||||
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
<div className="celery-content-header-right">
|
||||||
|
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
||||||
|
<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>
|
||||||
<CeleryTaskConfigOptions />
|
<CeleryTaskGraphGrid
|
||||||
<CeleryTaskGraphGrid onClick={onTaskClick} queryEnabled={!task} />
|
onClick={onTaskClick}
|
||||||
|
queryEnabled={!task}
|
||||||
|
configureOptionComponent={<CeleryTaskConfigOptions />}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!!task && (
|
{!!task && (
|
||||||
<CeleryTaskDetail
|
<CeleryTaskDetail
|
||||||
|
@ -38,7 +38,7 @@ export const Celery: TabRoutes = {
|
|||||||
Component: CeleryTask,
|
Component: CeleryTask,
|
||||||
name: (
|
name: (
|
||||||
<div className="tab-item">
|
<div className="tab-item">
|
||||||
<Rows3 size={16} /> Celery Task
|
<Rows3 size={16} /> Celery
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
route: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
|
route: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user