mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-08 17:08:58 +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 { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Select, Spin, Tooltip, Typography } from 'antd';
|
||||
import { Select, Spin, Typography } from 'antd';
|
||||
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { Check, Share2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import {
|
||||
getValuesFromQueryParams,
|
||||
@ -23,11 +19,8 @@ function CeleryTaskConfigOptions(): JSX.Element {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
||||
const urlQuery = useUrlQuery();
|
||||
|
||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
||||
|
||||
return (
|
||||
<div className="celery-task-filters">
|
||||
<div className="celery-filters">
|
||||
@ -66,25 +59,6 @@ function CeleryTaskConfigOptions(): JSX.Element {
|
||||
}}
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
export interface Filters {
|
||||
searchText: string;
|
||||
@ -31,8 +35,18 @@ export function useGetAllFilters(props: Filters): GetAllFiltersResponse {
|
||||
tagType,
|
||||
} = props;
|
||||
|
||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const { data, isLoading } = useQuery(
|
||||
['attributesValues', attributeKey, searchText],
|
||||
[
|
||||
REACT_QUERY_KEY.GET_ATTRIBUTE_VALUES,
|
||||
attributeKey,
|
||||
searchText,
|
||||
minTime,
|
||||
maxTime,
|
||||
],
|
||||
async () => {
|
||||
const keys = Array.isArray(attributeKey) ? attributeKey : [attributeKey];
|
||||
|
||||
|
@ -3,14 +3,12 @@ import './CeleryTaskGraph.style.scss';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||
import GridCard from 'container/GridCardLayout/GridCard';
|
||||
import { Card } from 'container/GridCardLayout/styles';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import getLabelName from 'lib/getLabelName';
|
||||
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
@ -18,11 +16,10 @@ 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 { QueryData } from 'types/api/widgets/getQuery';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
|
||||
import { paths } from '../CeleryUtils';
|
||||
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
|
||||
import {
|
||||
celeryAllStateWidgetData,
|
||||
celeryFailedStateWidgetData,
|
||||
@ -114,58 +111,26 @@ function CeleryTaskBar({
|
||||
string,
|
||||
];
|
||||
|
||||
onClick?.({
|
||||
entity,
|
||||
value,
|
||||
timeRange: [start, end],
|
||||
widgetData,
|
||||
});
|
||||
if (!isEmpty(entity) || !isEmpty(value)) {
|
||||
onClick?.({
|
||||
entity,
|
||||
value,
|
||||
timeRange: [start, end],
|
||||
widgetData,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getGraphSeries = (color: string, label: string): any => ({
|
||||
const { getCustomSeries } = useGetGraphCustomSeries({
|
||||
isDarkMode,
|
||||
drawStyle: 'bars',
|
||||
paths,
|
||||
lineInterpolation: 'spline',
|
||||
show: true,
|
||||
label,
|
||||
fill: `${color}90`,
|
||||
stroke: color,
|
||||
width: 2,
|
||||
spanGaps: true,
|
||||
points: {
|
||||
size: 5,
|
||||
show: false,
|
||||
stroke: color,
|
||||
colorMapping: {
|
||||
SUCCESS: Color.BG_FOREST_500,
|
||||
FAILURE: Color.BG_CHERRY_500,
|
||||
RETRY: Color.BG_AMBER_400,
|
||||
},
|
||||
});
|
||||
|
||||
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 (
|
||||
<Card
|
||||
isDarkMode={isDarkMode}
|
||||
@ -183,7 +148,7 @@ function CeleryTaskBar({
|
||||
onClickHandler={(...args): void =>
|
||||
onGraphClick(celerySlowestTasksTableWidgetData, ...args)
|
||||
}
|
||||
customSeries={customSeries}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
)}
|
||||
{barState === CeleryTaskState.Failed && (
|
||||
@ -195,7 +160,7 @@ function CeleryTaskBar({
|
||||
onClickHandler={(...args): void =>
|
||||
onGraphClick(celeryFailedTasksTableWidgetData, ...args)
|
||||
}
|
||||
customSeries={customSeries}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
)}
|
||||
{barState === CeleryTaskState.Retry && (
|
||||
@ -207,7 +172,7 @@ function CeleryTaskBar({
|
||||
onClickHandler={(...args): void =>
|
||||
onGraphClick(celeryRetryTasksTableWidgetData, ...args)
|
||||
}
|
||||
customSeries={customSeries}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
)}
|
||||
{barState === CeleryTaskState.Successful && (
|
||||
@ -219,7 +184,7 @@ function CeleryTaskBar({
|
||||
onClickHandler={(...args): void =>
|
||||
onGraphClick(celerySuccessTasksTableWidgetData, ...args)
|
||||
}
|
||||
customSeries={customSeries}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -6,93 +6,85 @@
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.celery-task-graph-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 60% 40%;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
.celery-task-graph-bar,
|
||||
.celery-task-graph-task-latency {
|
||||
height: 380px !important;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.celery-task-graph {
|
||||
height: 380px !important;
|
||||
.celery-task-graph-grid-content {
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
height: calc(100% - 18px);
|
||||
.ant-card-body {
|
||||
height: calc(100% - 18px);
|
||||
|
||||
.widget-graph-container {
|
||||
&.bar {
|
||||
height: calc(100% - 110px);
|
||||
}
|
||||
.widget-graph-container {
|
||||
&.bar {
|
||||
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%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.worker-count-text-container {
|
||||
height: 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%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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-worker-count-text {
|
||||
font-size: 2.5vw;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.celery-task-graph-bar,
|
||||
.celery-task-graph-task-latency {
|
||||
height: 380px !important;
|
||||
.worker-count-header {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: start;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.celery-task-graph-grid-content {
|
||||
padding: 6px;
|
||||
height: 100%;
|
||||
}
|
||||
.celery-task-graph {
|
||||
height: 380px !important;
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.ant-card-body {
|
||||
height: calc(100% - 18px);
|
||||
.ant-card-body {
|
||||
height: calc(100% - 18px);
|
||||
|
||||
.widget-graph-container {
|
||||
&.bar {
|
||||
height: calc(100% - 110px);
|
||||
}
|
||||
|
||||
&.graph {
|
||||
height: calc(100% - 80px);
|
||||
}
|
||||
.widget-graph-container {
|
||||
&.bar {
|
||||
height: calc(100% - 110px);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
@ -1,7 +1,10 @@
|
||||
import './CeleryTaskGraph.style.scss';
|
||||
|
||||
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 { AppState } from 'store/reducers';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
@ -23,9 +26,11 @@ import CeleryTaskLatencyGraph from './CeleryTaskLatencyGraph';
|
||||
export default function CeleryTaskGraphGrid({
|
||||
onClick,
|
||||
queryEnabled,
|
||||
configureOptionComponent,
|
||||
}: {
|
||||
onClick: (task: CaptureDataProps) => void;
|
||||
queryEnabled: boolean;
|
||||
configureOptionComponent?: React.ReactNode;
|
||||
}): JSX.Element {
|
||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
@ -71,44 +76,123 @@ export default function CeleryTaskGraphGrid({
|
||||
DataTypes.String,
|
||||
'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 (
|
||||
<div className="celery-task-graph-grid-container">
|
||||
<div className="celery-task-graph-grid">
|
||||
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
||||
<Card className="celery-task-graph-worker-count">
|
||||
<div className="worker-count-header">
|
||||
<Typography.Text className="worker-count-header-text">
|
||||
Worker Count
|
||||
<div className="metric-based-graphs">
|
||||
<CardContainer className="row-card" isDarkMode={isDarkMode}>
|
||||
<div className="row-panel">
|
||||
<div className="row-panel-section">
|
||||
<Typography.Text className="section-title">
|
||||
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>
|
||||
</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 className="celery-task-graph-grid">
|
||||
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
||||
<CeleryTaskGraph
|
||||
key={celeryActiveTasksData.id}
|
||||
widgetData={celeryActiveTasksData}
|
||||
queryEnabled={queryEnabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="celery-task-graph-grid-bottom">
|
||||
{bottomWidgetData.map((widgetData, index) => (
|
||||
<CeleryTaskGraph
|
||||
key={widgetData.id}
|
||||
widgetData={widgetData}
|
||||
onClick={onClick}
|
||||
queryEnabled={queryEnabled}
|
||||
rightPanelTitle={rightPanelTitle[index]}
|
||||
applyCeleryTaskFilter
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{!collapsedSections.traceBasedGraphs && (
|
||||
<>
|
||||
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
|
||||
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
|
||||
<div className="celery-task-graph-grid-bottom">
|
||||
{bottomWidgetData.map((widgetData, index) => (
|
||||
<CeleryTaskGraph
|
||||
key={widgetData.id}
|
||||
widgetData={widgetData}
|
||||
onClick={onClick}
|
||||
queryEnabled={queryEnabled}
|
||||
rightPanelTitle={rightPanelTitle[index]}
|
||||
applyCeleryTaskFilter
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
CeleryTaskGraphGrid.defaultProps = {
|
||||
configureOptionComponent: null,
|
||||
};
|
||||
|
@ -332,6 +332,7 @@ export const celeryTasksByWorkerWidgetData = (
|
||||
timeAggregation: 'rate',
|
||||
},
|
||||
],
|
||||
yAxisUnit: 'cps',
|
||||
}),
|
||||
);
|
||||
|
||||
@ -345,44 +346,103 @@ export const celeryErrorByWorkerWidgetData = (
|
||||
description: 'Represents the number of errors by each worker.',
|
||||
queryData: [
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'A',
|
||||
aggregateOperator: 'count_distinct',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.String,
|
||||
id: '------false',
|
||||
isColumn: false,
|
||||
dataType: 'string',
|
||||
id: 'span_id--string----true',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
key: '',
|
||||
key: 'span_id',
|
||||
type: '',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
dataSource: DataSource.TRACES,
|
||||
disabled: false,
|
||||
expression: 'A',
|
||||
timeAggregation: 'count_distinct',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
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',
|
||||
},
|
||||
functions: [],
|
||||
expression: 'A',
|
||||
disabled: true,
|
||||
stepInterval: getStepInterval(startTime, endTime),
|
||||
having: [],
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'celery.hostname--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'celery.hostname',
|
||||
type: 'tag',
|
||||
id: 'celery.hostname--string--tag--false',
|
||||
},
|
||||
],
|
||||
having: [],
|
||||
legend: '{{celery.hostname}}',
|
||||
limit: 10,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
legend: '',
|
||||
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 =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
title: 'Latency by Worker',
|
||||
title: 'Latency by worker',
|
||||
description: 'Represents the latency of tasks by each worker.',
|
||||
queryData: [
|
||||
{
|
||||
@ -444,7 +504,7 @@ export const celeryActiveTasksWidgetData = (
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
title: 'Tasks/ worker (Active tasks)',
|
||||
title: 'Active Tasks by worker',
|
||||
description: 'Represents the number of active tasks.',
|
||||
queryData: [
|
||||
{
|
||||
@ -487,6 +547,7 @@ export const celeryActiveTasksWidgetData = (
|
||||
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_DAEMONSET_LIST: 'GET_DAEMONSET_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 { useGetGraphCustomSeries } from 'components/CeleryTask/useGetGraphCustomSeries';
|
||||
import { useNavigateToTraces } from 'components/CeleryTask/useNavigateToTraces';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||
@ -109,6 +111,18 @@ export default function OverviewRightPanelGraph({
|
||||
},
|
||||
[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 (
|
||||
<Card className="overview-right-panel-graph-card">
|
||||
<div className="request-rate-card">
|
||||
@ -125,6 +139,7 @@ export default function OverviewRightPanelGraph({
|
||||
headerMenuList={[...ViewMenuAction]}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('Celery_request_rate')}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
</div>
|
||||
<div className="error-rate-card">
|
||||
@ -141,6 +156,7 @@ export default function OverviewRightPanelGraph({
|
||||
headerMenuList={[...ViewMenuAction]}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('Celery_error_rate')}
|
||||
customSeries={getCustomSeries}
|
||||
/>
|
||||
</div>
|
||||
<div className="avg-latency-card">
|
||||
|
@ -195,6 +195,7 @@ export const celeryOverviewRequestRateGraphData = (
|
||||
},
|
||||
],
|
||||
panelTypes: PANEL_TYPES.BAR,
|
||||
yAxisUnit: 'reqps',
|
||||
}),
|
||||
);
|
||||
|
||||
@ -206,22 +207,24 @@ export const celeryOverviewErrorRateGraphData = (
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
title: 'Error',
|
||||
title: 'Error %',
|
||||
description: 'Represents Error in the service',
|
||||
queryData: [
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'A',
|
||||
aggregateOperator: 'count_distinct',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.EMPTY,
|
||||
id: '------false',
|
||||
isColumn: false,
|
||||
dataType: DataTypes.String,
|
||||
id: 'span_id--string----true',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
key: '',
|
||||
key: 'span_id',
|
||||
type: '',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
dataSource: DataSource.TRACES,
|
||||
disabled: false,
|
||||
expression: 'A',
|
||||
timeAggregation: 'count_distinct',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
...(filters ?? []),
|
||||
@ -241,31 +244,31 @@ export const celeryOverviewErrorRateGraphData = (
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||
expression: 'A',
|
||||
disabled: true,
|
||||
stepInterval: getStepInterval(startTime, endTime),
|
||||
having: [],
|
||||
legend: 'True',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||
legend: '',
|
||||
reduceTo: 'avg',
|
||||
spaceAggregation: 'sum',
|
||||
stepInterval: getStepInterval(startTime, endTime),
|
||||
timeAggregation: 'rate',
|
||||
},
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'B',
|
||||
aggregateOperator: 'count_distinct',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.EMPTY,
|
||||
id: '------false',
|
||||
isColumn: false,
|
||||
dataType: DataTypes.String,
|
||||
id: 'span_id--string----true',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
key: '',
|
||||
key: 'span_id',
|
||||
type: '',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
dataSource: DataSource.TRACES,
|
||||
disabled: false,
|
||||
expression: 'B',
|
||||
timeAggregation: 'count_distinct',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
...(filters ?? []),
|
||||
@ -285,20 +288,60 @@ export const celeryOverviewErrorRateGraphData = (
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||
expression: 'B',
|
||||
disabled: true,
|
||||
stepInterval: getStepInterval(startTime, endTime),
|
||||
having: [],
|
||||
legend: 'False',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'B',
|
||||
groupBy: groupByFilter ? [groupByFilter] : [],
|
||||
legend: '',
|
||||
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,
|
||||
yAxisUnit: 'percent',
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -21,6 +21,12 @@
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.celery-content-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
import './CeleryTask.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import CeleryTaskConfigOptions from 'components/CeleryTask/CeleryTaskConfigOptions/CeleryTaskConfigOptions';
|
||||
import CeleryTaskDetail, {
|
||||
CaptureDataProps,
|
||||
} from 'components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail';
|
||||
import CeleryTaskGraphGrid from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid';
|
||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||
import { Check, Share2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
export default function CeleryTask(): JSX.Element {
|
||||
const [task, setTask] = useState<CaptureDataProps | null>(null);
|
||||
@ -15,15 +19,43 @@ export default function CeleryTask(): JSX.Element {
|
||||
setTask(captureData);
|
||||
};
|
||||
|
||||
const [isURLCopied, setIsURLCopied] = useState(false);
|
||||
|
||||
const [, handleCopyToClipboard] = useCopyToClipboard();
|
||||
|
||||
return (
|
||||
<div className="celery-task-container">
|
||||
<div className="celery-content">
|
||||
<div className="celery-content-header">
|
||||
<p className="celery-content-header-title">Celery Task</p>
|
||||
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
|
||||
<p className="celery-content-header-title">Celery</p>
|
||||
<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>
|
||||
<CeleryTaskConfigOptions />
|
||||
<CeleryTaskGraphGrid onClick={onTaskClick} queryEnabled={!task} />
|
||||
<CeleryTaskGraphGrid
|
||||
onClick={onTaskClick}
|
||||
queryEnabled={!task}
|
||||
configureOptionComponent={<CeleryTaskConfigOptions />}
|
||||
/>
|
||||
</div>
|
||||
{!!task && (
|
||||
<CeleryTaskDetail
|
||||
|
@ -38,7 +38,7 @@ export const Celery: TabRoutes = {
|
||||
Component: CeleryTask,
|
||||
name: (
|
||||
<div className="tab-item">
|
||||
<Rows3 size={16} /> Celery Task
|
||||
<Rows3 size={16} /> Celery
|
||||
</div>
|
||||
),
|
||||
route: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
|
||||
|
Loading…
x
Reference in New Issue
Block a user