chore: add analytics for metrics explorer

This commit is contained in:
amlannandy 2025-05-31 13:54:32 +07:00
parent 61b2f8cb31
commit d838d83b12
17 changed files with 273 additions and 21 deletions

View File

@ -24,6 +24,10 @@ import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import ExportPanelContainer from 'container/ExportPanel/ExportPanelContainer'; import ExportPanelContainer from 'container/ExportPanel/ExportPanelContainer';
import {
MetricsExplorerEventKeys,
MetricsExplorerEvents,
} from 'container/MetricsExplorer/events';
import { useOptionsMenu } from 'container/OptionsMenu'; import { useOptionsMenu } from 'container/OptionsMenu';
import { import {
defaultLogsSelectedColumns, defaultLogsSelectedColumns,
@ -140,7 +144,9 @@ function ExplorerOptions({
panelType, panelType,
}); });
} else if (isMetricsExplorer) { } else if (isMetricsExplorer) {
logEvent('Metrics Explorer: Save view clicked', { logEvent(MetricsExplorerEvents.SaveViewClicked, {
[MetricsExplorerEventKeys.Tab]: 'explorer',
[MetricsExplorerEventKeys.OneChartPerQueryEnabled]: isOneChartPerQuery,
panelType, panelType,
}); });
} }
@ -184,8 +190,10 @@ function ExplorerOptions({
panelType, panelType,
}); });
} else if (isMetricsExplorer) { } else if (isMetricsExplorer) {
logEvent('Metrics Explorer: Create alert', { logEvent(MetricsExplorerEvents.AddToAlertClicked, {
panelType, panelType,
[MetricsExplorerEventKeys.Tab]: 'explorer',
[MetricsExplorerEventKeys.OneChartPerQueryEnabled]: isOneChartPerQuery,
}); });
} }
@ -218,11 +226,14 @@ function ExplorerOptions({
panelType, panelType,
}); });
} else if (isMetricsExplorer) { } else if (isMetricsExplorer) {
logEvent('Metrics Explorer: Add to dashboard clicked', { logEvent(MetricsExplorerEvents.AddToDashboardClicked, {
panelType, panelType,
[MetricsExplorerEventKeys.Tab]: 'explorer',
[MetricsExplorerEventKeys.OneChartPerQueryEnabled]: isOneChartPerQuery,
}); });
} }
setIsExport(true); setIsExport(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLogsExplorer, isMetricsExplorer, panelType, setIsExport, sourcepage]); }, [isLogsExplorer, isMetricsExplorer, panelType, setIsExport, sourcepage]);
const { const {

View File

@ -2,6 +2,9 @@ import './Explorer.styles.scss';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { Switch } from 'antd'; import { Switch } from 'antd';
import logEvent from 'api/common/logEvent';
import axios from 'axios';
import { LOCALSTORAGE } from 'constants/localStorage';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper'; import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions'; import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
@ -10,7 +13,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import { useSafeNavigate } from 'hooks/useSafeNavigate'; import { useSafeNavigate } from 'hooks/useSafeNavigate';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom-v5-compat'; import { useSearchParams } from 'react-router-dom-v5-compat';
import { Dashboard } from 'types/api/dashboard/getAll'; import { Dashboard } from 'types/api/dashboard/getAll';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
@ -18,6 +21,7 @@ import { DataSource } from 'types/common/queryBuilder';
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink'; import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import QuerySection from './QuerySection'; import QuerySection from './QuerySection';
import TimeSeries from './TimeSeries'; import TimeSeries from './TimeSeries';
import { ExplorerTabs } from './types'; import { ExplorerTabs } from './types';
@ -93,6 +97,12 @@ function Explorer(): JSX.Element {
[stagedQuery], [stagedQuery],
); );
useEffect(() => {
logEvent(MetricsExplorerEvents.TabChanged, {
[MetricsExplorerEventKeys.Tab]: 'explorer',
});
}, []);
return ( return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}> <Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<div className="metrics-explorer-explore-container"> <div className="metrics-explorer-explore-container">

View File

@ -1,4 +1,5 @@
import { Button } from 'antd'; import { Button } from 'antd';
import logEvent from 'api/common/logEvent';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { QueryBuilder } from 'container/QueryBuilder'; import { QueryBuilder } from 'container/QueryBuilder';
import { ButtonWrapper } from 'container/TracesExplorer/QuerySection/styles'; import { ButtonWrapper } from 'container/TracesExplorer/QuerySection/styles';
@ -6,6 +7,8 @@ import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQ
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
function QuerySection(): JSX.Element { function QuerySection(): JSX.Element {
const { handleRunQuery } = useQueryBuilder(); const { handleRunQuery } = useQueryBuilder();
@ -19,7 +22,15 @@ function QuerySection(): JSX.Element {
version="v4" version="v4"
actions={ actions={
<ButtonWrapper> <ButtonWrapper>
<Button onClick={(): void => handleRunQuery()} type="primary"> <Button
onClick={(): void => {
handleRunQuery();
logEvent(MetricsExplorerEvents.QueryBuilderQueryChanged, {
[MetricsExplorerEventKeys.Tab]: 'explorer',
});
}}
type="primary"
>
Run Query Run Query
</Button> </Button>
</ButtonWrapper> </ButtonWrapper>

View File

@ -4,6 +4,7 @@
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Card, Tooltip, Typography } from 'antd'; import { Card, Tooltip, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from 'antd/es/table';
import logEvent from 'api/common/logEvent';
import { InspectMetricsSeries } from 'api/metricsExplorer/getInspectMetricsDetails'; import { InspectMetricsSeries } from 'api/metricsExplorer/getInspectMetricsDetails';
import classNames from 'classnames'; import classNames from 'classnames';
import ResizeTable from 'components/ResizeTable/ResizeTable'; import ResizeTable from 'components/ResizeTable/ResizeTable';
@ -11,6 +12,7 @@ import { DataType } from 'container/LogDetailedView/TableView';
import { ArrowDownCircle, ArrowRightCircle, Focus } from 'lucide-react'; import { ArrowDownCircle, ArrowRightCircle, Focus } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { import {
SPACE_AGGREGATION_OPTIONS_FOR_EXPANDED_VIEW, SPACE_AGGREGATION_OPTIONS_FOR_EXPANDED_VIEW,
TIME_AGGREGATION_OPTIONS, TIME_AGGREGATION_OPTIONS,
@ -39,6 +41,21 @@ function ExpandedView({
setSelectedTimeSeries, setSelectedTimeSeries,
] = useState<InspectMetricsSeries | null>(null); ] = useState<InspectMetricsSeries | null>(null);
useEffect(() => {
logEvent(MetricsExplorerEvents.InspectPointClicked, {
[MetricsExplorerEventKeys.Modal]: 'inspect',
[MetricsExplorerEventKeys.Filters]: metricInspectionOptions.filters,
[MetricsExplorerEventKeys.TimeAggregationInterval]:
metricInspectionOptions.timeAggregationInterval,
[MetricsExplorerEventKeys.TimeAggregationOption]:
metricInspectionOptions.timeAggregationOption,
[MetricsExplorerEventKeys.SpaceAggregationOption]:
metricInspectionOptions.spaceAggregationOption,
[MetricsExplorerEventKeys.SpaceAggregationLabels]:
metricInspectionOptions.spaceAggregationLabels,
});
}, [metricInspectionOptions]);
useEffect(() => { useEffect(() => {
if (step !== InspectionStep.COMPLETED) { if (step !== InspectionStep.COMPLETED) {
setSelectedTimeSeries(options?.timeSeries ?? null); setSelectedTimeSeries(options?.timeSeries ?? null);

View File

@ -1,5 +1,6 @@
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Button, Skeleton, Switch, Typography } from 'antd'; import { Button, Skeleton, Switch, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import Uplot from 'components/Uplot'; import Uplot from 'components/Uplot';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions'; import { useResizeObserver } from 'hooks/useDimensions';
@ -8,6 +9,7 @@ import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { formatNumberIntoHumanReadableFormat } from '../Summary/utils'; import { formatNumberIntoHumanReadableFormat } from '../Summary/utils';
import { METRIC_TYPE_TO_COLOR_MAP, METRIC_TYPE_TO_ICON_MAP } from './constants'; import { METRIC_TYPE_TO_COLOR_MAP, METRIC_TYPE_TO_ICON_MAP } from './constants';
import GraphPopover from './GraphPopover'; import GraphPopover from './GraphPopover';
@ -203,7 +205,14 @@ function GraphView({
<div className="view-toggle-button"> <div className="view-toggle-button">
<Switch <Switch
checked={viewType === 'graph'} checked={viewType === 'graph'}
onChange={(checked): void => setViewType(checked ? 'graph' : 'table')} onChange={(checked): void => {
const newViewType = checked ? 'graph' : 'table';
setViewType(newViewType);
logEvent(MetricsExplorerEvents.InspectViewChanged, {
[MetricsExplorerEventKeys.Tab]: 'inspect',
[MetricsExplorerEventKeys.InspectView]: newViewType,
});
}}
/> />
<Typography.Text> <Typography.Text>
{viewType === 'graph' ? 'Graph View' : 'Table View'} {viewType === 'graph' ? 'Graph View' : 'Table View'}

View File

@ -3,6 +3,7 @@ import './Inspect.styles.scss';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { Color } from '@signozhq/design-tokens'; import { Color } from '@signozhq/design-tokens';
import { Button, Drawer, Empty, Skeleton, Typography } from 'antd'; import { Button, Drawer, Empty, Skeleton, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails'; import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations'; import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
@ -11,11 +12,16 @@ import { Compass } from 'lucide-react';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import ExpandedView from './ExpandedView'; import ExpandedView from './ExpandedView';
import GraphView from './GraphView'; import GraphView from './GraphView';
import QueryBuilder from './QueryBuilder'; import QueryBuilder from './QueryBuilder';
import Stepper from './Stepper'; import Stepper from './Stepper';
import { GraphPopoverOptions, InspectProps } from './types'; import {
GraphPopoverOptions,
InspectProps,
MetricInspectionAction,
} from './types';
import { useInspectMetrics } from './useInspectMetrics'; import { useInspectMetrics } from './useInspectMetrics';
function Inspect({ function Inspect({
@ -92,6 +98,25 @@ function Inspect({
reset, reset,
} = useInspectMetrics(metricName); } = useInspectMetrics(metricName);
const handleDispatchMetricInspectionOptions = useCallback(
(action: MetricInspectionAction): void => {
dispatchMetricInspectionOptions(action);
logEvent(MetricsExplorerEvents.InspectQueryChanged, {
[MetricsExplorerEventKeys.Modal]: 'inspect',
[MetricsExplorerEventKeys.Filters]: metricInspectionOptions.filters,
[MetricsExplorerEventKeys.TimeAggregationInterval]:
metricInspectionOptions.timeAggregationInterval,
[MetricsExplorerEventKeys.TimeAggregationOption]:
metricInspectionOptions.timeAggregationOption,
[MetricsExplorerEventKeys.SpaceAggregationOption]:
metricInspectionOptions.spaceAggregationOption,
[MetricsExplorerEventKeys.SpaceAggregationLabels]:
metricInspectionOptions.spaceAggregationLabels,
});
},
[dispatchMetricInspectionOptions, metricInspectionOptions],
);
const selectedMetricType = useMemo( const selectedMetricType = useMemo(
() => metricDetailsData?.payload?.data?.metadata?.metric_type, () => metricDetailsData?.payload?.data?.metadata?.metric_type,
[metricDetailsData], [metricDetailsData],
@ -186,7 +211,7 @@ function Inspect({
setMetricName={setMetricName} setMetricName={setMetricName}
spaceAggregationLabels={spaceAggregationLabels} spaceAggregationLabels={spaceAggregationLabels}
metricInspectionOptions={metricInspectionOptions} metricInspectionOptions={metricInspectionOptions}
dispatchMetricInspectionOptions={dispatchMetricInspectionOptions} dispatchMetricInspectionOptions={handleDispatchMetricInspectionOptions}
inspectionStep={inspectionStep} inspectionStep={inspectionStep}
inspectMetricsTimeSeries={inspectMetricsTimeSeries} inspectMetricsTimeSeries={inspectMetricsTimeSeries}
searchQuery={searchQuery} searchQuery={searchQuery}
@ -227,12 +252,18 @@ function Inspect({
popoverOptions, popoverOptions,
metricInspectionOptions, metricInspectionOptions,
spaceAggregationLabels, spaceAggregationLabels,
dispatchMetricInspectionOptions, handleDispatchMetricInspectionOptions,
searchQuery, searchQuery,
expandedViewOptions, expandedViewOptions,
timeAggregatedSeriesMap, timeAggregatedSeriesMap,
]); ]);
useEffect(() => {
logEvent(MetricsExplorerEvents.ModalOpened, {
[MetricsExplorerEventKeys.Modal]: 'inspect',
});
}, []);
return ( return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}> <Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<Drawer <Drawer

View File

@ -1,5 +1,6 @@
/* eslint-disable no-nested-ternary */ /* eslint-disable no-nested-ternary */
import { Card, Input, Select, Typography } from 'antd'; import { Card, Input, Select, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import { InspectMetricsSeries } from 'api/metricsExplorer/getInspectMetricsDetails'; import { InspectMetricsSeries } from 'api/metricsExplorer/getInspectMetricsDetails';
import { MetricType } from 'api/metricsExplorer/getMetricsList'; import { MetricType } from 'api/metricsExplorer/getMetricsList';
import classNames from 'classnames'; import classNames from 'classnames';
@ -16,6 +17,7 @@ import {
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { import {
SPACE_AGGREGATION_OPTIONS, SPACE_AGGREGATION_OPTIONS,
TIME_AGGREGATION_OPTIONS, TIME_AGGREGATION_OPTIONS,
@ -135,6 +137,9 @@ export function MetricFilters({
}} }}
onChange={(value): void => { onChange={(value): void => {
handleChangeQueryData('filters', value); handleChangeQueryData('filters', value);
logEvent(MetricsExplorerEvents.FilterApplied, {
[MetricsExplorerEventKeys.Modal]: 'inspect',
});
dispatchMetricInspectionOptions({ dispatchMetricInspectionOptions({
type: 'SET_FILTERS', type: 'SET_FILTERS',
payload: value, payload: value,

View File

@ -1,5 +1,6 @@
import { Button, Collapse, Input, Menu, Popover, Typography } from 'antd'; import { Button, Collapse, Input, Menu, Popover, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from 'antd/es/table';
import logEvent from 'api/common/logEvent';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import { DataType } from 'container/LogDetailedView/TableView'; import { DataType } from 'container/LogDetailedView/TableView';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';
@ -10,6 +11,7 @@ import { useCopyToClipboard } from 'react-use';
import { PANEL_TYPES } from '../../../constants/queryBuilder'; import { PANEL_TYPES } from '../../../constants/queryBuilder';
import ROUTES from '../../../constants/routes'; import ROUTES from '../../../constants/routes';
import { useHandleExplorerTabChange } from '../../../hooks/useHandleExplorerTabChange'; import { useHandleExplorerTabChange } from '../../../hooks/useHandleExplorerTabChange';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { AllAttributesProps, AllAttributesValueProps } from './types'; import { AllAttributesProps, AllAttributesValueProps } from './types';
import { getMetricDetailsQuery } from './utils'; import { getMetricDetailsQuery } from './utils';
@ -135,9 +137,16 @@ function AllAttributes({
}, },
ROUTES.METRICS_EXPLORER_EXPLORER, ROUTES.METRICS_EXPLORER_EXPLORER,
); );
logEvent(MetricsExplorerEvents.OpenInExplorerClicked, {
[MetricsExplorerEventKeys.MetricName]: metricName,
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.Modal]: 'metric-details',
[MetricsExplorerEventKeys.AttributeKey]: groupBy,
});
}, },
[metricName, metricType, handleExplorerTabChange], [metricName, metricType, handleExplorerTabChange],
); );
const goToMetricsExploreWithAppliedAttribute = useCallback( const goToMetricsExploreWithAppliedAttribute = useCallback(
(key: string, value: string) => { (key: string, value: string) => {
const compositeQuery = getMetricDetailsQuery(metricName, metricType, { const compositeQuery = getMetricDetailsQuery(metricName, metricType, {
@ -153,6 +162,13 @@ function AllAttributes({
}, },
ROUTES.METRICS_EXPLORER_EXPLORER, ROUTES.METRICS_EXPLORER_EXPLORER,
); );
logEvent(MetricsExplorerEvents.OpenInExplorerClicked, {
[MetricsExplorerEventKeys.MetricName]: metricName,
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.Modal]: 'metric-details',
[MetricsExplorerEventKeys.AttributeKey]: key,
[MetricsExplorerEventKeys.AttributeValue]: value,
});
}, },
[metricName, metricType, handleExplorerTabChange], [metricName, metricType, handleExplorerTabChange],
); );

View File

@ -1,5 +1,6 @@
import { Button, Collapse, Input, Select, Typography } from 'antd'; import { Button, Collapse, Input, Select, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from 'antd/es/table';
import logEvent from 'api/common/logEvent';
import { Temporality } from 'api/metricsExplorer/getMetricDetails'; import { Temporality } from 'api/metricsExplorer/getMetricDetails';
import { MetricType } from 'api/metricsExplorer/getMetricsList'; import { MetricType } from 'api/metricsExplorer/getMetricsList';
import { UpdateMetricMetadataProps } from 'api/metricsExplorer/updateMetricMetadata'; import { UpdateMetricMetadataProps } from 'api/metricsExplorer/updateMetricMetadata';
@ -11,6 +12,7 @@ import { useNotifications } from 'hooks/useNotifications';
import { Edit2, Save, X } from 'lucide-react'; import { Edit2, Save, X } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { import {
METRIC_TYPE_LABEL_MAP, METRIC_TYPE_LABEL_MAP,
METRIC_TYPE_VALUES_MAP, METRIC_TYPE_VALUES_MAP,
@ -170,6 +172,11 @@ function Metadata({
{ {
onSuccess: (response): void => { onSuccess: (response): void => {
if (response?.statusCode === 200) { if (response?.statusCode === 200) {
logEvent(MetricsExplorerEvents.MetricMetadataUpdated, {
[MetricsExplorerEventKeys.MetricName]: metricName,
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.Modal]: 'metric-details',
});
notifications.success({ notifications.success({
message: 'Metadata updated successfully', message: 'Metadata updated successfully',
}); });

View File

@ -11,14 +11,16 @@ import {
Tooltip, Tooltip,
Typography, Typography,
} from 'antd'; } from 'antd';
import logEvent from 'api/common/logEvent';
import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails'; import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import { Compass, Crosshair, X } from 'lucide-react'; import { Compass, Crosshair, X } from 'lucide-react';
import { useCallback, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { PANEL_TYPES } from '../../../constants/queryBuilder'; import { PANEL_TYPES } from '../../../constants/queryBuilder';
import ROUTES from '../../../constants/routes'; import ROUTES from '../../../constants/routes';
import { useHandleExplorerTabChange } from '../../../hooks/useHandleExplorerTabChange'; import { useHandleExplorerTabChange } from '../../../hooks/useHandleExplorerTabChange';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import { isInspectEnabled } from '../Inspect/utils'; import { isInspectEnabled } from '../Inspect/utils';
import { formatNumberIntoHumanReadableFormat } from '../Summary/utils'; import { formatNumberIntoHumanReadableFormat } from '../Summary/utils';
import AllAttributes from './AllAttributes'; import AllAttributes from './AllAttributes';
@ -95,11 +97,22 @@ function MetricDetails({
}, },
ROUTES.METRICS_EXPLORER_EXPLORER, ROUTES.METRICS_EXPLORER_EXPLORER,
); );
logEvent(MetricsExplorerEvents.OpenInExplorerClicked, {
[MetricsExplorerEventKeys.MetricName]: metricName,
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.Modal]: 'metric-details',
});
} }
}, [metricName, handleExplorerTabChange, metric?.metadata?.metric_type]); }, [metricName, handleExplorerTabChange, metric?.metadata?.metric_type]);
const isMetricDetailsError = metricDetailsError || !metric; const isMetricDetailsError = metricDetailsError || !metric;
useEffect(() => {
logEvent(MetricsExplorerEvents.ModalOpened, {
[MetricsExplorerEventKeys.Modal]: 'metric-details',
});
}, []);
return ( return (
<Drawer <Drawer
width="60%" width="60%"

View File

@ -107,7 +107,7 @@ function MetricsTable({
total: totalCount, total: totalCount,
}} }}
onRow={(record): { onClick: () => void; className: string } => ({ onRow={(record): { onClick: () => void; className: string } => ({
onClick: (): void => openMetricDetails(record.key), onClick: (): void => openMetricDetails(record.key, 'list'),
className: 'clickable-row', className: 'clickable-row',
})} })}
/> />

View File

@ -154,7 +154,7 @@ function MetricsTreemap({
<foreignObject <foreignObject
width={nodeWidth} width={nodeWidth}
height={nodeHeight} height={nodeHeight}
onClick={(): void => openMetricDetails(node.data.id)} onClick={(): void => openMetricDetails(node.data.id, 'treemap')}
> >
<div <div
style={{ style={{

View File

@ -1,6 +1,7 @@
import './Summary.styles.scss'; import './Summary.styles.scss';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import logEvent from 'api/common/logEvent';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { usePageSize } from 'container/InfraMonitoringK8s/utils'; import { usePageSize } from 'container/InfraMonitoringK8s/utils';
import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList'; import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList';
@ -9,7 +10,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations'; import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useSearchParams } from 'react-router-dom-v5-compat'; import { useSearchParams } from 'react-router-dom-v5-compat';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
@ -17,6 +18,7 @@ import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
import InspectModal from '../Inspect'; import InspectModal from '../Inspect';
import MetricDetails from '../MetricDetails'; import MetricDetails from '../MetricDetails';
import { import {
@ -44,7 +46,7 @@ function Summary(): JSX.Element {
const { pageSize, setPageSize } = usePageSize('metricsExplorer'); const { pageSize, setPageSize } = usePageSize('metricsExplorer');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [orderBy, setOrderBy] = useState<OrderByPayload>(DEFAULT_ORDER_BY); const [orderBy, setOrderBy] = useState<OrderByPayload>(DEFAULT_ORDER_BY);
const [heatmapView, setHeatmapView] = useState<TreemapViewType>( const [heatmapView, sexHeatmapView] = useState<TreemapViewType>(
TreemapViewType.TIMESERIES, TreemapViewType.TIMESERIES,
); );
@ -63,6 +65,12 @@ function Summary(): JSX.Element {
(state) => state.globalTime, (state) => state.globalTime,
); );
useEffect(() => {
logEvent(MetricsExplorerEvents.TimeUpdated, {
[MetricsExplorerEventKeys.Tab]: 'summary',
});
}, [maxTime, minTime]);
const { currentQuery, updateAllQueriesOperators } = useQueryBuilder(); const { currentQuery, updateAllQueriesOperators } = useQueryBuilder();
const defaultQuery = useMemo(() => { const defaultQuery = useMemo(() => {
@ -88,6 +96,12 @@ function Summary(): JSX.Element {
useShareBuilderUrl(defaultQuery); useShareBuilderUrl(defaultQuery);
useEffect(() => {
logEvent(MetricsExplorerEvents.TabChanged, {
[MetricsExplorerEventKeys.Tab]: 'summary',
});
}, []);
// This is used to avoid the filters from being serialized with the id // This is used to avoid the filters from being serialized with the id
const currentQueryFiltersString = useMemo(() => { const currentQueryFiltersString = useMemo(() => {
const filters = currentQuery?.builder?.queryData[0]?.filters; const filters = currentQuery?.builder?.queryData[0]?.filters;
@ -186,6 +200,9 @@ function Summary(): JSX.Element {
[COMPOSITE_QUERY_KEY]: JSON.stringify(compositeQuery), [COMPOSITE_QUERY_KEY]: JSON.stringify(compositeQuery),
}); });
setCurrentPage(1); setCurrentPage(1);
logEvent(MetricsExplorerEvents.FilterApplied, {
[MetricsExplorerEventKeys.Tab]: 'summary',
});
}, },
[handleChangeQueryData, currentQuery, setSearchParams], [handleChangeQueryData, currentQuery, setSearchParams],
); );
@ -214,6 +231,14 @@ function Summary(): JSX.Element {
const onPaginationChange = (page: number, pageSize: number): void => { const onPaginationChange = (page: number, pageSize: number): void => {
setCurrentPage(page); setCurrentPage(page);
setPageSize(pageSize); setPageSize(pageSize);
logEvent(MetricsExplorerEvents.PageNumberChanged, {
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.PageNumber]: page,
});
logEvent(MetricsExplorerEvents.PageSizeChanged, {
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.PageSize]: pageSize,
});
}; };
const formattedMetricsData = useMemo( const formattedMetricsData = useMemo(
@ -221,13 +246,20 @@ function Summary(): JSX.Element {
[metricsData], [metricsData],
); );
const openMetricDetails = (metricName: string): void => { const openMetricDetails = (
metricName: string,
view: 'list' | 'treemap',
): void => {
setSelectedMetricName(metricName); setSelectedMetricName(metricName);
setIsMetricDetailsOpen(true); setIsMetricDetailsOpen(true);
setSearchParams({ setSearchParams({
[IS_METRIC_DETAILS_OPEN_KEY]: 'true', [IS_METRIC_DETAILS_OPEN_KEY]: 'true',
[SELECTED_METRIC_NAME_KEY]: metricName, [SELECTED_METRIC_NAME_KEY]: metricName,
}); });
logEvent(MetricsExplorerEvents.MetricClicked, {
[MetricsExplorerEventKeys.MetricName]: metricName,
[MetricsExplorerEventKeys.View]: view,
});
}; };
const closeMetricDetails = (): void => { const closeMetricDetails = (): void => {
@ -262,6 +294,22 @@ function Summary(): JSX.Element {
}); });
}; };
const handleSetHeatmapView = (view: TreemapViewType): void => {
sexHeatmapView(view);
logEvent(MetricsExplorerEvents.TreemapViewChanged, {
[MetricsExplorerEventKeys.ViewType]: view,
});
};
const handleSetOrderBy = (orderBy: OrderByPayload): void => {
setOrderBy(orderBy);
logEvent(MetricsExplorerEvents.OrderByApplied, {
[MetricsExplorerEventKeys.Tab]: 'summary',
[MetricsExplorerEventKeys.ColumnName]: orderBy.columnName,
[MetricsExplorerEventKeys.Order]: orderBy.order,
});
};
return ( return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}> <Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<div className="metrics-explorer-summary-tab"> <div className="metrics-explorer-summary-tab">
@ -272,7 +320,7 @@ function Summary(): JSX.Element {
isError={isProportionViewError} isError={isProportionViewError}
viewType={heatmapView} viewType={heatmapView}
openMetricDetails={openMetricDetails} openMetricDetails={openMetricDetails}
setHeatmapView={setHeatmapView} setHeatmapView={handleSetHeatmapView}
/> />
<MetricsTable <MetricsTable
isLoading={isMetricsLoading || isMetricsFetching} isLoading={isMetricsLoading || isMetricsFetching}
@ -281,7 +329,7 @@ function Summary(): JSX.Element {
pageSize={pageSize} pageSize={pageSize}
currentPage={currentPage} currentPage={currentPage}
onPaginationChange={onPaginationChange} onPaginationChange={onPaginationChange}
setOrderBy={setOrderBy} setOrderBy={handleSetOrderBy}
totalCount={metricsData?.payload?.data?.total || 0} totalCount={metricsData?.payload?.data?.total || 0}
openMetricDetails={openMetricDetails} openMetricDetails={openMetricDetails}
/> />

View File

@ -1,5 +1,5 @@
import { MetricsTreeMapResponse } from 'api/metricsExplorer/getMetricsTreeMap'; import { MetricsTreeMapResponse } from 'api/metricsExplorer/getMetricsTreeMap';
import React, { Dispatch, SetStateAction } from 'react'; import React from 'react';
import { import {
IBuilderQuery, IBuilderQuery,
TagFilter, TagFilter,
@ -12,9 +12,9 @@ export interface MetricsTableProps {
pageSize: number; pageSize: number;
currentPage: number; currentPage: number;
onPaginationChange: (page: number, pageSize: number) => void; onPaginationChange: (page: number, pageSize: number) => void;
setOrderBy: Dispatch<SetStateAction<OrderByPayload>>; setOrderBy: (orderBy: OrderByPayload) => void;
totalCount: number; totalCount: number;
openMetricDetails: (metricName: string) => void; openMetricDetails: (metricName: string, view: 'list' | 'treemap') => void;
} }
export interface MetricsSearchProps { export interface MetricsSearchProps {
@ -27,7 +27,7 @@ export interface MetricsTreemapProps {
isLoading: boolean; isLoading: boolean;
isError: boolean; isError: boolean;
viewType: TreemapViewType; viewType: TreemapViewType;
openMetricDetails: (metricName: string) => void; openMetricDetails: (metricName: string, view: 'list' | 'treemap') => void;
setHeatmapView: (value: TreemapViewType) => void; setHeatmapView: (value: TreemapViewType) => void;
} }

View File

@ -0,0 +1,51 @@
/**
* This file contains all analytics events for the Metrics Explorer.
*/
export enum MetricsExplorerEvents {
TabChanged = 'Metrics Explorer: Tab visited',
ModalOpened = 'Metrics Explorer: Modal opened',
MetricClicked = 'Metrics Explorer: Metric clicked',
FilterApplied = 'Metrics Explorer: Filter applied',
TimeUpdated = 'Metrics Explorer: Time updated',
TreemapViewChanged = 'Metrics Explorer: Treemap view changed',
PageNumberChanged = 'Metrics Explorer: Page number changed',
PageSizeChanged = 'Metrics Explorer: Page size changed',
OrderByApplied = 'Metrics Explorer: Order by applied',
MetricMetadataUpdated = 'Metrics Explorer: Metric metadata updated',
OpenInExplorerClicked = 'Metric Explorer: Open in explorer clicked',
InspectViewChanged = 'Metrics Explorer: Inspect view changed',
InspectQueryChanged = 'Metrics Explorer: Inspect query changed',
InspectPointClicked = 'Metrics Explorer: Inspect point clicked',
QueryBuilderQueryChanged = 'Metrics Explorer: QueryBuilder query changed',
YAxisUnitApplied = 'Metrics Explorer: Y axis unit applied',
AddToAlertClicked = 'Metrics Explorer: Add to alert clicked',
AddToDashboardClicked = 'Metrics Explorer: Add to dashboard clicked',
SaveViewClicked = 'Metrics Explorer: Save view clicked',
SearchApplied = 'Metrics Explorer: Search applied',
ViewEdited = 'Metrics Explorer: View edited',
ViewDeleted = 'Metrics Explorer: View deleted',
}
export enum MetricsExplorerEventKeys {
Tab = 'tab',
Modal = 'modal',
View = 'view',
Interval = 'interval',
ViewType = 'viewType',
PageNumber = 'pageNumber',
PageSize = 'pageSize',
ColumnName = 'columnName',
Order = 'order',
AttributeKey = 'attributeKey',
AttributeValue = 'attributeValue',
MetricName = 'metricName',
InspectView = 'inspectView',
TimeAggregationOption = 'timeAggregationOption',
TimeAggregationInterval = 'timeAggregationInterval',
SpaceAggregationOption = 'spaceAggregationOption',
SpaceAggregationLabels = 'spaceAggregationLabels',
OneChartPerQueryEnabled = 'oneChartPerQueryEnabled',
YAxisUnit = 'yAxisUnit',
ViewName = 'viewName',
Filters = 'filters',
}

View File

@ -17,6 +17,10 @@ import {
} from 'components/ExplorerCard/utils'; } from 'components/ExplorerCard/utils';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats'; import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { getRandomColor } from 'container/ExplorerOptions/utils'; import { getRandomColor } from 'container/ExplorerOptions/utils';
import {
MetricsExplorerEventKeys,
MetricsExplorerEvents,
} from 'container/MetricsExplorer/events';
import { useDeleteView } from 'hooks/saveViews/useDeleteView'; import { useDeleteView } from 'hooks/saveViews/useDeleteView';
import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; import { useGetAllViews } from 'hooks/saveViews/useGetAllViews';
import { useUpdateView } from 'hooks/saveViews/useUpdateView'; import { useUpdateView } from 'hooks/saveViews/useUpdateView';
@ -155,6 +159,10 @@ function SaveView(): JSX.Element {
logEvent('Logs Views: Views visited', { logEvent('Logs Views: Views visited', {
number: viewsData?.data?.data?.length, number: viewsData?.data?.data?.length,
}); });
} else if (sourcepage === DataSource.METRICS) {
logEvent(MetricsExplorerEvents.TabChanged, {
[MetricsExplorerEventKeys.Tab]: 'views',
});
} }
logEventCalledRef.current = true; logEventCalledRef.current = true;
} }
@ -176,6 +184,9 @@ function SaveView(): JSX.Element {
}); });
hideEditViewModal(); hideEditViewModal();
refetchAllView(); refetchAllView();
logEvent(MetricsExplorerEvents.ViewEdited, {
[MetricsExplorerEventKeys.Tab]: 'views',
});
}, },
onError: (err) => { onError: (err) => {
showErrorNotification(notifications, err); showErrorNotification(notifications, err);
@ -204,6 +215,10 @@ function SaveView(): JSX.Element {
}, },
SOURCEPAGE_VS_ROUTES[sourcepage], SOURCEPAGE_VS_ROUTES[sourcepage],
); );
logEvent(MetricsExplorerEvents.OpenInExplorerClicked, {
[MetricsExplorerEventKeys.Tab]: 'views',
[MetricsExplorerEventKeys.ViewName]: name,
});
} }
}; };

View File

@ -1,6 +1,11 @@
import { NotificationInstance } from 'antd/es/notification/interface'; import { NotificationInstance } from 'antd/es/notification/interface';
import logEvent from 'api/common/logEvent';
import { MenuItemLabelGeneratorProps } from 'components/ExplorerCard/types'; import { MenuItemLabelGeneratorProps } from 'components/ExplorerCard/types';
import { showErrorNotification } from 'components/ExplorerCard/utils'; import { showErrorNotification } from 'components/ExplorerCard/utils';
import {
MetricsExplorerEventKeys,
MetricsExplorerEvents,
} from 'container/MetricsExplorer/events';
import { UseMutateAsyncFunction } from 'react-query'; import { UseMutateAsyncFunction } from 'react-query';
import { DeleteViewPayloadProps } from 'types/api/saveViews/types'; import { DeleteViewPayloadProps } from 'types/api/saveViews/types';
@ -29,6 +34,9 @@ export const deleteViewHandler = ({
message: 'View Deleted Successfully', message: 'View Deleted Successfully',
}); });
refetchAllView(); refetchAllView();
logEvent(MetricsExplorerEvents.ViewDeleted, {
[MetricsExplorerEventKeys.Tab]: 'views',
});
}, },
onError: (err) => { onError: (err) => {
showErrorNotification(notifications, err); showErrorNotification(notifications, err);