chore: added mq celery analytics (#7129)

This commit is contained in:
SagarRajput-7 2025-02-17 13:59:16 +05:30 committed by GitHub
parent a6cfb63036
commit 5a107f33f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 95 additions and 5 deletions

View File

@ -2,6 +2,7 @@ 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 logEvent from 'api/common/logEvent';
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';
@ -98,6 +99,12 @@ export default function CeleryTaskDetail({
...rowData, ...rowData,
[taskData.entity]: taskData.value, [taskData.entity]: taskData.value,
}); });
logEvent('MQ Celery: navigation to trace page', {
filters,
startTime,
endTime,
source: widgetData.title,
});
navigateToTrace(filters, startTime, endTime); navigateToTrace(filters, startTime, endTime);
}} }}
start={startTime} start={startTime}

View File

@ -42,10 +42,12 @@ import {
function CeleryTaskBar({ function CeleryTaskBar({
onClick, onClick,
queryEnabled, queryEnabled,
checkIfDataExists,
}: { }: {
onClick?: (task: CaptureDataProps) => void; onClick?: (task: CaptureDataProps) => void;
queryEnabled: boolean; queryEnabled: boolean;
checkIfDataExists?: (isDataAvailable: boolean) => void;
}): JSX.Element { }): JSX.Element {
const history = useHistory(); const history = useHistory();
const { pathname } = useLocation(); const { pathname } = useLocation();
@ -187,6 +189,7 @@ function CeleryTaskBar({
onGraphClick(celerySlowestTasksTableWidgetData, ...args) onGraphClick(celerySlowestTasksTableWidgetData, ...args)
} }
customSeries={getCustomSeries} customSeries={getCustomSeries}
dataAvailable={checkIfDataExists}
/> />
)} )}
{barState === CeleryTaskState.Failed && ( {barState === CeleryTaskState.Failed && (
@ -232,6 +235,7 @@ function CeleryTaskBar({
CeleryTaskBar.defaultProps = { CeleryTaskBar.defaultProps = {
onClick: (): void => {}, onClick: (): void => {},
checkIfDataExists: undefined,
}; };
export default CeleryTaskBar; export default CeleryTaskBar;

View File

@ -35,6 +35,8 @@ function CeleryTaskGraph({
customErrorMessage, customErrorMessage,
start, start,
end, end,
checkIfDataExists,
analyticsEvent,
}: { }: {
widgetData: Widgets; widgetData: Widgets;
onClick?: (task: CaptureDataProps) => void; onClick?: (task: CaptureDataProps) => void;
@ -48,6 +50,8 @@ function CeleryTaskGraph({
customErrorMessage?: string; customErrorMessage?: string;
start?: number; start?: number;
end?: number; end?: number;
checkIfDataExists?: (isDataAvailable: boolean) => void;
analyticsEvent?: string;
}): JSX.Element { }): JSX.Element {
const history = useHistory(); const history = useHistory();
const { pathname } = useLocation(); const { pathname } = useLocation();
@ -125,6 +129,8 @@ function CeleryTaskGraph({
customErrorMessage={customErrorMessage} customErrorMessage={customErrorMessage}
start={start} start={start}
end={end} end={end}
dataAvailable={checkIfDataExists}
analyticsEvent={analyticsEvent}
/> />
</Card> </Card>
); );
@ -141,6 +147,8 @@ CeleryTaskGraph.defaultProps = {
customErrorMessage: undefined, customErrorMessage: undefined,
start: undefined, start: undefined,
end: undefined, end: undefined,
checkIfDataExists: undefined,
analyticsEvent: undefined,
}; };
export default CeleryTaskGraph; export default CeleryTaskGraph;

View File

@ -1,6 +1,7 @@
import './CeleryTaskGraph.style.scss'; import './CeleryTaskGraph.style.scss';
import { Card, Typography } from 'antd'; import { Card, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import { CardContainer } from 'container/GridCardLayout/styles'; import { CardContainer } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import { ChevronDown, ChevronUp } from 'lucide-react'; import { ChevronDown, ChevronUp } from 'lucide-react';
@ -92,6 +93,15 @@ export default function CeleryTaskGraphGrid({
})); }));
}; };
const checkIfDataExists = (isDataAvailable: boolean, title: string): void => {
if (isDataAvailable) {
logEvent(`MQ Celery: ${title} data exists`, {
graph: title,
isDataAvailable,
});
}
};
return ( return (
<div className="celery-task-graph-grid-container"> <div className="celery-task-graph-grid-container">
<div className="metric-based-graphs"> <div className="metric-based-graphs">
@ -124,6 +134,10 @@ export default function CeleryTaskGraphGrid({
widgetData={celeryActiveTasksData} widgetData={celeryActiveTasksData}
queryEnabled={queryEnabled} queryEnabled={queryEnabled}
customErrorMessage="Enable Flower metrics to view this graph" customErrorMessage="Enable Flower metrics to view this graph"
checkIfDataExists={(isDataAvailable): void =>
checkIfDataExists(isDataAvailable, 'Active Tasks by worker')
}
analyticsEvent="MQ Celery: Flower metric not enabled"
/> />
<Card className="celery-task-graph-worker-count"> <Card className="celery-task-graph-worker-count">
<div className="worker-count-header"> <div className="worker-count-header">
@ -173,8 +187,19 @@ export default function CeleryTaskGraphGrid({
</div> </div>
{!collapsedSections.traceBasedGraphs && ( {!collapsedSections.traceBasedGraphs && (
<> <>
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} /> <CeleryTaskBar
<CeleryTaskLatencyGraph queryEnabled={queryEnabled} /> queryEnabled={queryEnabled}
onClick={onClick}
checkIfDataExists={(isDataAvailable): void =>
checkIfDataExists(isDataAvailable, 'State Graph')
}
/>
<CeleryTaskLatencyGraph
queryEnabled={queryEnabled}
checkIfDataExists={(isDataAvailable): void =>
checkIfDataExists(isDataAvailable, 'Task Latency')
}
/>
<div className="celery-task-graph-grid-bottom"> <div className="celery-task-graph-grid-bottom">
{bottomWidgetData.map((widgetData, index) => ( {bottomWidgetData.map((widgetData, index) => (
<CeleryTaskGraph <CeleryTaskGraph
@ -184,6 +209,9 @@ export default function CeleryTaskGraphGrid({
queryEnabled={queryEnabled} queryEnabled={queryEnabled}
rightPanelTitle={rightPanelTitle[index]} rightPanelTitle={rightPanelTitle[index]}
applyCeleryTaskFilter applyCeleryTaskFilter
checkIfDataExists={(isDataAvailable): void =>
checkIfDataExists(isDataAvailable, rightPanelTitle[index])
}
/> />
))} ))}
</div> </div>

View File

@ -1,6 +1,7 @@
import './CeleryTaskGraph.style.scss'; import './CeleryTaskGraph.style.scss';
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import logEvent from 'api/common/logEvent';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { ViewMenuAction } from 'container/GridCardLayout/config'; import { ViewMenuAction } from 'container/GridCardLayout/config';
@ -40,8 +41,10 @@ export enum CeleryTaskGraphState {
function CeleryTaskLatencyGraph({ function CeleryTaskLatencyGraph({
queryEnabled, queryEnabled,
checkIfDataExists,
}: { }: {
queryEnabled: boolean; queryEnabled: boolean;
checkIfDataExists?: (isDataAvailable: boolean) => void;
}): JSX.Element { }): JSX.Element {
const history = useHistory(); const history = useHistory();
const { pathname } = useLocation(); const { pathname } = useLocation();
@ -61,6 +64,10 @@ function CeleryTaskLatencyGraph({
const handleTabClick = (key: CeleryTaskGraphState): void => { const handleTabClick = (key: CeleryTaskGraphState): void => {
setGraphState(key as CeleryTaskGraphState); setGraphState(key as CeleryTaskGraphState);
logEvent('MQ Celery: Task latency graph tab clicked', {
taskName: urlQuery.get(QueryParams.taskName),
graphState: key,
});
}; };
const onDragSelect = useCallback( const onDragSelect = useCallback(
@ -195,6 +202,7 @@ function CeleryTaskLatencyGraph({
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={onGraphClick('Celery_p99_latency')} onClickHandler={onGraphClick('Celery_p99_latency')}
isQueryEnabled={queryEnabled} isQueryEnabled={queryEnabled}
dataAvailable={checkIfDataExists}
/> />
</> </>
)} )}
@ -215,6 +223,7 @@ function CeleryTaskLatencyGraph({
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={onGraphClick('Celery_p95_latency')} onClickHandler={onGraphClick('Celery_p95_latency')}
isQueryEnabled={queryEnabled} isQueryEnabled={queryEnabled}
dataAvailable={checkIfDataExists}
/> />
</> </>
)} )}
@ -234,6 +243,7 @@ function CeleryTaskLatencyGraph({
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={onGraphClick('Celery_p90_latency')} onClickHandler={onGraphClick('Celery_p90_latency')}
isQueryEnabled={queryEnabled} isQueryEnabled={queryEnabled}
dataAvailable={checkIfDataExists}
/> />
</> </>
)} )}
@ -243,3 +253,7 @@ function CeleryTaskLatencyGraph({
} }
export default CeleryTaskLatencyGraph; export default CeleryTaskLatencyGraph;
CeleryTaskLatencyGraph.defaultProps = {
checkIfDataExists: undefined,
};

View File

@ -2,6 +2,7 @@
import './CeleryTaskGraph.style.scss'; import './CeleryTaskGraph.style.scss';
import { Col, Row } from 'antd'; import { Col, Row } from 'antd';
import logEvent from 'api/common/logEvent';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import useUrlQuery from 'hooks/useUrlQuery'; import useUrlQuery from 'hooks/useUrlQuery';
import { Dispatch, SetStateAction, useMemo } from 'react'; import { Dispatch, SetStateAction, useMemo } from 'react';
@ -44,12 +45,16 @@ function CeleryTaskStateGraphConfig({
{ label: 'Successful', key: CeleryTaskState.Successful }, { label: 'Successful', key: CeleryTaskState.Successful },
]; ];
const urlQuery = useUrlQuery();
const handleTabClick = (key: CeleryTaskState): void => { const handleTabClick = (key: CeleryTaskState): void => {
setBarState(key as CeleryTaskState); setBarState(key as CeleryTaskState);
logEvent('MQ Celery: State graph tab clicked', {
taskName: urlQuery.get(QueryParams.taskName),
graphState: key,
});
}; };
const urlQuery = useUrlQuery();
const selectedFilters = useMemo( const selectedFilters = useMemo(
() => () =>
getFiltersFromQueryParams( getFiltersFromQueryParams(

View File

@ -1,3 +1,4 @@
import logEvent from 'api/common/logEvent';
import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
@ -44,6 +45,7 @@ function GridCardGraph({
customErrorMessage, customErrorMessage,
start, start,
end, end,
analyticsEvent,
}: GridCardGraphProps): JSX.Element { }: GridCardGraphProps): JSX.Element {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState<string>(); const [errorMessage, setErrorMessage] = useState<string>();
@ -219,6 +221,11 @@ function GridCardGraph({
setIsInternalServerError( setIsInternalServerError(
String(error.message).includes('API responded with 500'), String(error.message).includes('API responded with 500'),
); );
if (analyticsEvent) {
logEvent(analyticsEvent, {
error: error.message,
});
}
} }
setDashboardQueryRangeCalled(true); setDashboardQueryRangeCalled(true);
}, },
@ -283,6 +290,7 @@ GridCardGraph.defaultProps = {
threshold: undefined, threshold: undefined,
headerMenuList: [MenuItemKeys.View], headerMenuList: [MenuItemKeys.View],
version: 'v3', version: 'v3',
analyticsEvent: undefined,
}; };
export default memo(GridCardGraph); export default memo(GridCardGraph);

View File

@ -58,6 +58,7 @@ export interface GridCardGraphProps {
customErrorMessage?: string; customErrorMessage?: string;
start?: number; start?: number;
end?: number; end?: number;
analyticsEvent?: string;
} }
export interface GetGraphVisibilityStateOnLegendClickProps { export interface GetGraphVisibilityStateOnLegendClickProps {

View File

@ -1,15 +1,30 @@
import './CeleryTask.styles.scss'; import './CeleryTask.styles.scss';
import logEvent from 'api/common/logEvent';
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 { QueryParams } from 'constants/query';
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2'; import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
import { useState } from 'react'; import useUrlQuery from 'hooks/useUrlQuery';
import { useEffect, useRef, useState } from 'react';
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);
const loggedRef = useRef(false);
const taskName = useUrlQuery().get(QueryParams.taskName);
useEffect(() => {
if (taskName && !loggedRef.current) {
logEvent('MQ Celery: Task name filter', {
taskName,
});
loggedRef.current = true;
}
}, [taskName]);
const onTaskClick = (captureData: CaptureDataProps): void => { const onTaskClick = (captureData: CaptureDataProps): void => {
setTask(captureData); setTask(captureData);