mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:39:04 +08:00
chore: add activation events (#5474)
This commit is contained in:
parent
cd07c743b6
commit
df751c7f38
@ -12,6 +12,7 @@ import { ColumnType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { FilterValue, SorterResult } from 'antd/es/table/interface';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { FilterConfirmProps } from 'antd/lib/table/interface';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import getAll from 'api/errors/getAll';
|
||||
import getErrorCounts from 'api/errors/getErrorCounts';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
@ -23,7 +24,8 @@ import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
import history from 'lib/history';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQueries } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
@ -410,6 +412,26 @@ function AllErrors(): JSX.Element {
|
||||
[pathname],
|
||||
);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (
|
||||
!logEventCalledRef.current &&
|
||||
!isUndefined(errorCountResponse.data?.payload)
|
||||
) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === 'resource_deployment_environment',
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('Exception: List page visited', {
|
||||
numberOfExceptions: errorCountResponse.data?.payload,
|
||||
selectedEnvironments,
|
||||
resourceAttributeUsed: !!queries.length,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [errorCountResponse.data?.payload]);
|
||||
|
||||
return (
|
||||
<ResizeTable
|
||||
columns={columns}
|
||||
|
@ -1,8 +1,34 @@
|
||||
import './EmptyLogsSearch.styles.scss';
|
||||
|
||||
import { Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { DataSource, PanelTypeKeys } from 'types/common/queryBuilder';
|
||||
|
||||
export default function EmptyLogsSearch({
|
||||
dataSource,
|
||||
panelType,
|
||||
}: {
|
||||
dataSource: DataSource;
|
||||
panelType: PanelTypeKeys;
|
||||
}): JSX.Element {
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
if (dataSource === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: No results', {
|
||||
panelType,
|
||||
});
|
||||
} else if (dataSource === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: No results', {
|
||||
panelType,
|
||||
});
|
||||
}
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
export default function EmptyLogsSearch(): JSX.Element {
|
||||
return (
|
||||
<div className="empty-logs-search-container">
|
||||
<div className="empty-logs-search-container-content">
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './styles.scss';
|
||||
|
||||
import { Button, Divider, Space, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import getNextPrevId from 'api/errors/getNextPrevId';
|
||||
import Editor from 'components/Editor';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
@ -9,8 +10,9 @@ import dayjs from 'dayjs';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
import history from 'lib/history';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { urlKey } from 'pages/ErrorDetails/utils';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
@ -111,9 +113,29 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
||||
}));
|
||||
|
||||
const onClickTraceHandler = (): void => {
|
||||
logEvent('Exception: Navigate to trace detail page', {
|
||||
groupId: errorDetail.groupID,
|
||||
spanId: errorDetail.spanID,
|
||||
traceId: errorDetail.traceID,
|
||||
exceptionId: errorDetail.errorId,
|
||||
});
|
||||
history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`);
|
||||
};
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||
logEvent('Exception: Detail page visited', {
|
||||
groupId: errorDetail.groupID,
|
||||
spanId: errorDetail.spanID,
|
||||
traceId: errorDetail.traceID,
|
||||
exceptionId: errorDetail.errorId,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>{errorDetail.exceptionType}</Typography>
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import axios from 'axios';
|
||||
import cx from 'classnames';
|
||||
import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils';
|
||||
@ -93,7 +94,23 @@ function ExplorerOptions({
|
||||
setIsExport(value);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
currentQuery,
|
||||
panelType,
|
||||
isStagedQueryUpdated,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
|
||||
const handleSaveViewModalToggle = (): void => {
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Save view clicked', {
|
||||
panelType,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Save view clicked', {
|
||||
panelType,
|
||||
});
|
||||
}
|
||||
setIsSaveModalOpen(!isSaveModalOpen);
|
||||
};
|
||||
|
||||
@ -104,11 +121,21 @@ function ExplorerOptions({
|
||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
const onCreateAlertsHandler = useCallback(() => {
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Create alert', {
|
||||
panelType,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Create alert', {
|
||||
panelType,
|
||||
});
|
||||
}
|
||||
history.push(
|
||||
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||
JSON.stringify(query),
|
||||
)}`,
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [history, query]);
|
||||
|
||||
const onCancel = (value: boolean) => (): void => {
|
||||
@ -116,6 +143,15 @@ function ExplorerOptions({
|
||||
};
|
||||
|
||||
const onAddToDashboard = (): void => {
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Add to dashboard clicked', {
|
||||
panelType,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Add to dashboard clicked', {
|
||||
panelType,
|
||||
});
|
||||
}
|
||||
setIsExport(true);
|
||||
};
|
||||
|
||||
@ -127,13 +163,6 @@ function ExplorerOptions({
|
||||
refetch: refetchAllView,
|
||||
} = useGetAllViews(sourcepage);
|
||||
|
||||
const {
|
||||
currentQuery,
|
||||
panelType,
|
||||
isStagedQueryUpdated,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
|
||||
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
||||
|
||||
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
||||
@ -224,6 +253,17 @@ function ExplorerOptions({
|
||||
onMenuItemSelectHandler({
|
||||
key: option.key,
|
||||
});
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Select view', {
|
||||
panelType,
|
||||
viewName: option.value,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Select view', {
|
||||
panelType,
|
||||
viewName: option.value,
|
||||
});
|
||||
}
|
||||
if (ref.current) {
|
||||
ref.current.blur();
|
||||
}
|
||||
@ -259,6 +299,17 @@ function ExplorerOptions({
|
||||
viewName: newViewName,
|
||||
setNewViewName,
|
||||
});
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Save view successful', {
|
||||
panelType,
|
||||
viewName: newViewName,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Save view successful', {
|
||||
panelType,
|
||||
viewName: newViewName,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Remove this and move this to scss file
|
||||
@ -499,7 +550,7 @@ function ExplorerOptions({
|
||||
|
||||
export interface ExplorerOptionsProps {
|
||||
isLoading?: boolean;
|
||||
onExport: (dashboard: Dashboard | null) => void;
|
||||
onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void;
|
||||
query: Query | null;
|
||||
disabled: boolean;
|
||||
sourcepage: DataSource;
|
||||
|
@ -41,7 +41,7 @@ function ExportPanelContainer({
|
||||
} = useMutation(createDashboard, {
|
||||
onSuccess: (data) => {
|
||||
if (data.payload) {
|
||||
onExport(data?.payload);
|
||||
onExport(data?.payload, true);
|
||||
}
|
||||
refetch();
|
||||
},
|
||||
@ -55,7 +55,7 @@ function ExportPanelContainer({
|
||||
({ uuid }) => uuid === selectedDashboardId,
|
||||
);
|
||||
|
||||
onExport(currentSelectedDashboard || null);
|
||||
onExport(currentSelectedDashboard || null, false);
|
||||
}, [data, selectedDashboardId, onExport]);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
|
@ -40,7 +40,7 @@ function ExportPanel({
|
||||
|
||||
export interface ExportPanelProps {
|
||||
isLoading?: boolean;
|
||||
onExport: (dashboard: Dashboard | null) => void;
|
||||
onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void;
|
||||
query: Query | null;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import './DashboardEmptyState.styles.scss';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
@ -36,6 +37,12 @@ export default function DashboardEmptyState(): JSX.Element {
|
||||
|
||||
const onEmptyWidgetHandler = useCallback(() => {
|
||||
handleToggleDashboardSlider(true);
|
||||
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
dashboardName: selectedDashboard?.data.title,
|
||||
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [handleToggleDashboardSlider]);
|
||||
return (
|
||||
<section className="dashboard-empty-state">
|
||||
|
@ -3,6 +3,7 @@ import './GridCardLayout.styles.scss';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||
import { useForm } from 'antd/es/form/Form';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import cx from 'classnames';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@ -15,7 +16,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import history from 'lib/history';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { defaultTo, isUndefined } from 'lodash-es';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import {
|
||||
Check,
|
||||
@ -27,7 +28,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { sortLayout } from 'providers/Dashboard/util';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FullScreen, FullScreenHandle } from 'react-full-screen';
|
||||
import { ItemCallback, Layout } from 'react-grid-layout';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
@ -126,6 +127,18 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
setDashboardLayout(sortLayout(layouts));
|
||||
}, [layouts]);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||
logEvent('Dashboard Detail: Opened', {
|
||||
dashboardId: data.uuid,
|
||||
dashboardName: data.title,
|
||||
numberOfPanels: data.widgets?.length,
|
||||
numberOfVariables: Object.keys(data?.variables || {}).length || 0,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
}, [data]);
|
||||
const onSaveHandler = (): void => {
|
||||
if (!selectedDashboard) return;
|
||||
|
||||
|
@ -79,7 +79,7 @@ function WidgetHeader({
|
||||
);
|
||||
}, [widget.id, widget.panelTypes, widget.query]);
|
||||
|
||||
const onCreateAlertsHandler = useCreateAlerts(widget);
|
||||
const onCreateAlertsHandler = useCreateAlerts(widget, 'dashboardView');
|
||||
|
||||
const onDownloadHandler = useCallback((): void => {
|
||||
const csv = unparse(tableProcessedDataRef.current);
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { TableProps } from 'antd/lib';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import createDashboard from 'api/dashboard/create';
|
||||
import { AxiosError } from 'axios';
|
||||
import cx from 'classnames';
|
||||
@ -34,7 +35,7 @@ import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import { get, isEmpty } from 'lodash-es';
|
||||
import { get, isEmpty, isUndefined } from 'lodash-es';
|
||||
import {
|
||||
ArrowDownWideNarrow,
|
||||
ArrowUpRight,
|
||||
@ -60,6 +61,7 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -269,6 +271,7 @@ function DashboardsList(): JSX.Element {
|
||||
|
||||
const onNewDashboardHandler = useCallback(async () => {
|
||||
try {
|
||||
logEvent('Dashboard List: Create dashboard clicked', {});
|
||||
setNewDashboardState({
|
||||
...newDashboardState,
|
||||
loading: true,
|
||||
@ -305,6 +308,8 @@ function DashboardsList(): JSX.Element {
|
||||
}, [newDashboardState, t]);
|
||||
|
||||
const onModalHandler = (uploadedGrafana: boolean): void => {
|
||||
logEvent('Dashboard List: Import JSON clicked', {});
|
||||
|
||||
setIsImportJSONModalVisible((state) => !state);
|
||||
setUploadedGrafana(uploadedGrafana);
|
||||
};
|
||||
@ -441,6 +446,10 @@ function DashboardsList(): JSX.Element {
|
||||
} else {
|
||||
history.push(getLink());
|
||||
}
|
||||
logEvent('Dashboard List: Clicked on dashboard', {
|
||||
dashboardId: dashboard.id,
|
||||
dashboardName: dashboard.name,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -619,6 +628,21 @@ function DashboardsList(): JSX.Element {
|
||||
hideOnSinglePage: true,
|
||||
};
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (
|
||||
!logEventCalledRef.current &&
|
||||
!isDashboardListLoading &&
|
||||
!isUndefined(dashboardListResponse)
|
||||
) {
|
||||
logEvent('Dashboard List: Page visited', {
|
||||
number: dashboardListResponse?.length,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isDashboardListLoading]);
|
||||
|
||||
return (
|
||||
<div className="dashboards-list-container">
|
||||
<div className="dashboards-list-view-content">
|
||||
@ -705,6 +729,9 @@ function DashboardsList(): JSX.Element {
|
||||
type="text"
|
||||
className="new-dashboard"
|
||||
icon={<Plus size={14} />}
|
||||
onClick={(): void => {
|
||||
logEvent('Dashboard List: New dashboard clicked', {});
|
||||
}}
|
||||
>
|
||||
New Dashboard
|
||||
</Button>
|
||||
@ -745,6 +772,9 @@ function DashboardsList(): JSX.Element {
|
||||
type="primary"
|
||||
className="periscope-btn primary btn"
|
||||
icon={<Plus size={14} />}
|
||||
onClick={(): void => {
|
||||
logEvent('Dashboard List: New dashboard clicked', {});
|
||||
}}
|
||||
>
|
||||
New dashboard
|
||||
</Button>
|
||||
|
@ -5,6 +5,7 @@ import { ExclamationCircleTwoTone } from '@ant-design/icons';
|
||||
import MEditor, { Monaco } from '@monaco-editor/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import createDashboard from 'api/dashboard/create';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@ -67,6 +68,8 @@ function ImportJSON({
|
||||
const onClickLoadJsonHandler = async (): Promise<void> => {
|
||||
try {
|
||||
setDashboardCreating(true);
|
||||
logEvent('Dashboard List: Import and next clicked', {});
|
||||
|
||||
const dashboardData = JSON.parse(editorValue) as DashboardData;
|
||||
|
||||
if (dashboardData?.layout) {
|
||||
@ -86,6 +89,10 @@ function ImportJSON({
|
||||
dashboardId: response.payload.uuid,
|
||||
}),
|
||||
);
|
||||
logEvent('Dashboard List: New dashboard imported successfully', {
|
||||
dashboardId: response.payload?.uuid,
|
||||
dashboardName: response.payload?.data?.title,
|
||||
});
|
||||
} else if (response.error === 'feature usage exceeded') {
|
||||
setIsFeatureAlert(true);
|
||||
notifications.error({
|
||||
@ -180,6 +187,9 @@ function ImportJSON({
|
||||
type="default"
|
||||
className="periscope-btn"
|
||||
icon={<MonitorDot size={14} />}
|
||||
onClick={(): void => {
|
||||
logEvent('Dashboard List: Upload JSON file clicked', {});
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{t('upload_json_file')}
|
||||
|
@ -172,7 +172,9 @@ function LogsExplorerList({
|
||||
!isFetching &&
|
||||
logs.length === 0 &&
|
||||
!isError &&
|
||||
isFilterApplied && <EmptyLogsSearch />}
|
||||
isFilterApplied && (
|
||||
<EmptyLogsSearch dataSource={DataSource.LOGS} panelType="LIST" />
|
||||
)}
|
||||
|
||||
{isError && !isLoading && !isFetching && <LogsError />}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
import './LogsExplorerViews.styles.scss';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
@ -37,7 +38,14 @@ import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
||||
import { cloneDeep, defaultTo, isEmpty, omit, set } from 'lodash-es';
|
||||
import {
|
||||
cloneDeep,
|
||||
defaultTo,
|
||||
isEmpty,
|
||||
isUndefined,
|
||||
omit,
|
||||
set,
|
||||
} from 'lodash-es';
|
||||
import { Sliders } from 'lucide-react';
|
||||
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
@ -310,6 +318,19 @@ function LogsExplorerViews({
|
||||
],
|
||||
);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(data?.payload)) {
|
||||
const currentData = data?.payload?.data?.newResult?.data?.result || [];
|
||||
logEvent('Logs Explorer: Page visited', {
|
||||
panelType,
|
||||
isEmpty: !currentData?.[0]?.list,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data?.payload]);
|
||||
|
||||
const {
|
||||
mutate: updateDashboard,
|
||||
isLoading: isUpdateDashboardLoading,
|
||||
@ -324,7 +345,7 @@ function LogsExplorerViews({
|
||||
}, [currentQuery]);
|
||||
|
||||
const handleExport = useCallback(
|
||||
(dashboard: Dashboard | null): void => {
|
||||
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
|
||||
if (!dashboard || !panelType) return;
|
||||
|
||||
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
||||
@ -346,6 +367,12 @@ function LogsExplorerViews({
|
||||
options.selectColumns,
|
||||
);
|
||||
|
||||
logEvent('Logs Explorer: Add to dashboard successful', {
|
||||
panelType,
|
||||
isNewDashboard,
|
||||
dashboardName: dashboard?.data?.title,
|
||||
});
|
||||
|
||||
updateDashboard(updatedDashboard, {
|
||||
onSuccess: (data) => {
|
||||
if (data.error) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Col } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Graph from 'container/GridCardLayout/GridCard';
|
||||
@ -11,7 +12,7 @@ import {
|
||||
convertRawQueriesToTraceSelectedTags,
|
||||
resourceAttributesToTagFilterItems,
|
||||
} from 'hooks/useResourceAttribute/utils';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
@ -97,6 +98,24 @@ function DBCall(): JSX.Element {
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === 'resource_deployment_environment',
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
selectedEnvironments,
|
||||
resourceAttributeUsed: !!queries.length,
|
||||
section: 'dbMetrics',
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const apmToTraceQuery = useGetAPMToTracesQueries({
|
||||
servicename,
|
||||
isDBCall: true,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Col } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Graph from 'container/GridCardLayout/GridCard';
|
||||
@ -13,7 +14,7 @@ import {
|
||||
convertRawQueriesToTraceSelectedTags,
|
||||
resourceAttributesToTagFilterItems,
|
||||
} from 'hooks/useResourceAttribute/utils';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
@ -114,6 +115,23 @@ function External(): JSX.Element {
|
||||
],
|
||||
});
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === 'resource_deployment_environment',
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
selectedEnvironments,
|
||||
resourceAttributeUsed: !!queries.length,
|
||||
section: 'externalMetrics',
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const externalCallRPSWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
|
@ -1,3 +1,4 @@
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import getTopLevelOperations, {
|
||||
ServiceDataProps,
|
||||
} from 'api/metrics/getTopLevelOperations';
|
||||
@ -17,7 +18,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import history from 'lib/history';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
@ -81,6 +82,23 @@ function Application(): JSX.Element {
|
||||
[handleSetTimeStamp],
|
||||
);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === 'resource_deployment_environment',
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
selectedEnvironments,
|
||||
resourceAttributeUsed: !!queries.length,
|
||||
section: 'overview',
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const {
|
||||
data: topLevelOperations,
|
||||
error: topLevelOperationsError,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './ComponentSlider.styles.scss';
|
||||
|
||||
import { Card, Modal } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
@ -20,6 +21,13 @@ function DashboardGraphSlider(): JSX.Element {
|
||||
const onClickHandler = (name: PANEL_TYPES) => (): void => {
|
||||
const id = uuid();
|
||||
handleToggleDashboardSlider(false);
|
||||
logEvent('Dashboard Detail: New panel type selected', {
|
||||
// dashboardId: '',
|
||||
// dashboardName: '',
|
||||
// numberOfPanels: 0, // todo - at this point we don't know these attributes
|
||||
panelType: name,
|
||||
widgetId: id,
|
||||
});
|
||||
const queryParamsLog = {
|
||||
graphType: name,
|
||||
widgetId: id,
|
||||
@ -47,7 +55,6 @@ function DashboardGraphSlider(): JSX.Element {
|
||||
PANEL_TYPES_INITIAL_QUERY[name],
|
||||
),
|
||||
};
|
||||
|
||||
if (name === PANEL_TYPES.LIST) {
|
||||
history.push(
|
||||
`${history.location.pathname}/new?${createQueryParams(queryParamsLog)}`,
|
||||
|
@ -2,6 +2,7 @@ import './Description.styles.scss';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
||||
import { dashboardHelpMessage } from 'components/facingIssueBtn/util';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
@ -126,6 +127,12 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
|
||||
const onEmptyWidgetHandler = useCallback(() => {
|
||||
handleToggleDashboardSlider(true);
|
||||
logEvent('Dashboard Detail: Add new panel clicked', {
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
dashboardName: selectedDashboard?.data.title,
|
||||
numberOfPanels: selectedDashboard?.data.widgets?.length,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [handleToggleDashboardSlider]);
|
||||
|
||||
const handleLockDashboardToggle = (): void => {
|
||||
|
@ -2,6 +2,7 @@ import './QuerySection.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import PromQLIcon from 'assets/Dashboard/PromQl';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@ -14,7 +15,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { defaultTo, isUndefined } from 'lodash-es';
|
||||
import { Atom, Play, Terminal } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import {
|
||||
@ -122,6 +123,18 @@ function QuerySection({
|
||||
};
|
||||
|
||||
const handleRunQuery = (): void => {
|
||||
const widgetId = urlQuery.get('widgetId');
|
||||
const isNewPanel = isUndefined(widgets?.find((e) => e.id === widgetId));
|
||||
|
||||
logEvent('Panel Edit: Stage and run query', {
|
||||
dataSource: currentQuery.builder?.queryData?.[0]?.dataSource,
|
||||
panelType: selectedWidget.panelTypes,
|
||||
queryType: currentQuery.queryType,
|
||||
widgetId: selectedWidget.id,
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
dashboardName: selectedDashboard?.data.title,
|
||||
isNewPanel,
|
||||
});
|
||||
handleStageQuery(currentQuery);
|
||||
};
|
||||
|
||||
|
@ -82,7 +82,7 @@ function RightContainer({
|
||||
const selectedGraphType =
|
||||
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
||||
|
||||
const onCreateAlertsHandler = useCreateAlerts(selectedWidget);
|
||||
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
|
||||
|
||||
const allowThreshold = panelTypeVsThreshold[selectedGraph];
|
||||
const allowSoftMinMax = panelTypeVsSoftMinMax[selectedGraph];
|
||||
|
@ -3,6 +3,7 @@ import './NewWidget.styles.scss';
|
||||
|
||||
import { WarningOutlined } from '@ant-design/icons';
|
||||
import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
|
||||
import { chartHelpMessage } from 'components/facingIssueBtn/util';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
@ -31,7 +32,7 @@ import {
|
||||
getPreviousWidgets,
|
||||
getSelectedWidgetIndex,
|
||||
} from 'providers/Dashboard/util';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { generatePath, useParams } from 'react-router-dom';
|
||||
@ -101,6 +102,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
|
||||
const [isNewDashboard, setIsNewDashboard] = useState<boolean>(false);
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const widgetId = query.get('widgetId');
|
||||
const selectedWidget = widgets?.find((e) => e.id === widgetId);
|
||||
@ -108,6 +111,18 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
if (isWidgetNotPresent) {
|
||||
setIsNewDashboard(true);
|
||||
}
|
||||
|
||||
if (!logEventCalledRef.current) {
|
||||
logEvent('Panel Edit: Page visited', {
|
||||
panelType: selectedWidget?.panelTypes,
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
widgetId: selectedWidget?.id,
|
||||
dashboardName: selectedDashboard?.data.title,
|
||||
isNewPanel: !!isWidgetNotPresent,
|
||||
dataSource: currentQuery.builder.queryData?.[0]?.dataSource,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@ -482,7 +497,20 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
};
|
||||
|
||||
const onSaveDashboard = useCallback((): void => {
|
||||
const widgetId = query.get('widgetId');
|
||||
const selectWidget = widgets?.find((e) => e.id === widgetId);
|
||||
|
||||
logEvent('Panel Edit: Save changes', {
|
||||
panelType: selectedWidget.panelTypes,
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
widgetId: selectedWidget.id,
|
||||
dashboardName: selectedDashboard?.data.title,
|
||||
queryType: currentQuery.queryType,
|
||||
isNewPanel: isUndefined(selectWidget),
|
||||
dataSource: currentQuery.builder.queryData?.[0]?.dataSource,
|
||||
});
|
||||
setSaveModal(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const isQueryBuilderActive = useIsFeatureDisabled(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './NoLogs.styles.scss';
|
||||
|
||||
import { Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import { ArrowUpRight } from 'lucide-react';
|
||||
@ -21,6 +22,11 @@ export default function NoLogs({
|
||||
e.stopPropagation();
|
||||
|
||||
if (cloudUser) {
|
||||
if (dataSource === DataSource.TRACES) {
|
||||
logEvent('Traces Explorer: Navigate to onboarding', {});
|
||||
} else if (dataSource === DataSource.LOGS) {
|
||||
logEvent('Logs Explorer: Navigate to onboarding', {});
|
||||
}
|
||||
history.push(
|
||||
dataSource === 'traces'
|
||||
? ROUTES.GET_STARTED_APPLICATION_MONITORING
|
||||
|
@ -3,11 +3,19 @@ import './styles.scss';
|
||||
import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Card, Modal, Table, Typography } from 'antd';
|
||||
import { ExpandableConfig } from 'antd/es/table/interface';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import savePipeline from 'api/pipeline/post';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import cloneDeep from 'lodash-es/cloneDeep';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -466,6 +474,16 @@ function PipelineListsView({
|
||||
getExpandIcon(expanded, onExpand, record),
|
||||
};
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(currPipelineData)) {
|
||||
logEvent('Logs Pipelines: List page visited', {
|
||||
number: currPipelineData?.length,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
}, [currPipelineData]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import localStorageGet from 'api/browser/localstorage/get';
|
||||
import localStorageSet from 'api/browser/localstorage/set';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||
import useErrorNotification from 'hooks/useErrorNotification';
|
||||
import { useQueryService } from 'hooks/useQueryService';
|
||||
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@ -45,6 +47,26 @@ function ServiceTraces(): JSX.Element {
|
||||
setSkipOnboarding(true);
|
||||
};
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === 'resource_deployment_environment',
|
||||
)?.tagValue;
|
||||
|
||||
const rps = data.reduce((total, service) => total + service.callRate, 0);
|
||||
|
||||
logEvent('APM: List page visited', {
|
||||
numberOfServices: data?.length,
|
||||
selectedEnvironments,
|
||||
resourceAttributeUsed: !!queries.length,
|
||||
rps,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data]);
|
||||
|
||||
if (
|
||||
services.length === 0 &&
|
||||
isLoading === false &&
|
||||
|
@ -4,6 +4,7 @@ import './SideNav.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import cx from 'classnames';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import ROUTES from 'constants/routes';
|
||||
@ -179,6 +180,11 @@ function SideNav({
|
||||
};
|
||||
|
||||
const onClickShortcuts = (e: MouseEvent): void => {
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: '/shortcuts',
|
||||
menuLabel: 'Keyboard Shortcuts',
|
||||
});
|
||||
if (isCtrlMetaKey(e)) {
|
||||
openInNewTab('/shortcuts');
|
||||
} else {
|
||||
@ -187,6 +193,10 @@ function SideNav({
|
||||
};
|
||||
|
||||
const onClickGetStarted = (event: MouseEvent): void => {
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: '/get-started',
|
||||
menuLabel: 'Get Started',
|
||||
});
|
||||
if (isCtrlMetaKey(event)) {
|
||||
openInNewTab('/get-started');
|
||||
} else {
|
||||
@ -313,6 +323,10 @@ function SideNav({
|
||||
} else if (item) {
|
||||
onClickHandler(item?.key as string, event);
|
||||
}
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: item.key,
|
||||
menuLabel: item.label,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -440,6 +454,10 @@ function SideNav({
|
||||
isActive={activeMenuKey === item?.key}
|
||||
onClick={(event: MouseEvent): void => {
|
||||
handleUserManagentMenuItemClick(item?.key as string, event);
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: item.key,
|
||||
menuLabel: item.label,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
@ -456,6 +474,10 @@ function SideNav({
|
||||
} else {
|
||||
history.push(`${inviteMemberMenuItem.key}`);
|
||||
}
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: inviteMemberMenuItem.key,
|
||||
menuLabel: inviteMemberMenuItem.label,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@ -470,6 +492,10 @@ function SideNav({
|
||||
userSettingsMenuItem?.key as string,
|
||||
event,
|
||||
);
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
menuRoute: userSettingsMenuItem.key,
|
||||
menuLabel: 'User',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -155,7 +155,9 @@ function TimeSeriesView({
|
||||
chartData[0]?.length === 0 &&
|
||||
!isLoading &&
|
||||
!isError &&
|
||||
isFilterApplied && <EmptyLogsSearch />}
|
||||
isFilterApplied && (
|
||||
<EmptyLogsSearch dataSource={dataSource} panelType="TIME_SERIES" />
|
||||
)}
|
||||
|
||||
{chartData &&
|
||||
chartData[0] &&
|
||||
|
@ -156,7 +156,9 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element {
|
||||
<NoLogs dataSource={DataSource.TRACES} />
|
||||
)}
|
||||
|
||||
{isDataPresent && isFilterApplied && <EmptyLogsSearch />}
|
||||
{isDataPresent && isFilterApplied && (
|
||||
<EmptyLogsSearch dataSource={DataSource.TRACES} panelType="LIST" />
|
||||
)}
|
||||
|
||||
{!isError && transformedQueryTableData.length !== 0 && (
|
||||
<ResizeTable
|
||||
|
@ -105,7 +105,9 @@ function TracesView({ isFilterApplied }: TracesViewProps): JSX.Element {
|
||||
!isFetching &&
|
||||
(tableData || []).length === 0 &&
|
||||
!isError &&
|
||||
isFilterApplied && <EmptyLogsSearch />}
|
||||
isFilterApplied && (
|
||||
<EmptyLogsSearch dataSource={DataSource.TRACES} panelType="TRACE" />
|
||||
)}
|
||||
|
||||
{(tableData || []).length !== 0 && (
|
||||
<ResizeTable
|
||||
|
@ -1,8 +1,10 @@
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { getQueryRangeFormat } from 'api/dashboard/queryRangeFormat';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { MenuItemKeys } from 'container/GridCardLayout/WidgetHeader/contants';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { prepareQueryRangePayload } from 'lib/dashboard/prepareQueryRangePayload';
|
||||
@ -17,7 +19,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
|
||||
const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
||||
const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||
const queryRangeMutation = useMutation(getQueryRangeFormat);
|
||||
|
||||
const { selectedTime: globalSelectedInterval } = useSelector<
|
||||
@ -32,6 +34,24 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
||||
return useCallback(() => {
|
||||
if (!widget) return;
|
||||
|
||||
if (caller === 'panelView') {
|
||||
logEvent('Panel Edit: Create alert', {
|
||||
panelType: widget.panelTypes,
|
||||
dashboardName: selectedDashboard?.data?.title,
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
widgetId: widget.id,
|
||||
queryType: widget.query.queryType,
|
||||
});
|
||||
} else if (caller === 'dashboardView') {
|
||||
logEvent('Dashboard Detail: Panel action', {
|
||||
action: MenuItemKeys.CreateAlerts,
|
||||
panelType: widget.panelTypes,
|
||||
dashboardName: selectedDashboard?.data?.title,
|
||||
dashboardId: selectedDashboard?.uuid,
|
||||
widgetId: widget.id,
|
||||
queryType: widget.query.queryType,
|
||||
});
|
||||
}
|
||||
const { queryPayload } = prepareQueryRangePayload({
|
||||
query: widget.query,
|
||||
globalSelectedInterval,
|
||||
@ -57,6 +77,7 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => {
|
||||
});
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
globalSelectedInterval,
|
||||
notifications,
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
TableProps,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
getViewDetailsUsingViewKey,
|
||||
showErrorNotification,
|
||||
@ -30,7 +31,7 @@ import {
|
||||
Trash2,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
@ -143,6 +144,22 @@ function SaveView(): JSX.Element {
|
||||
viewName: newViewName,
|
||||
});
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isLoading) {
|
||||
if (sourcepage === DataSource.TRACES) {
|
||||
logEvent('Traces Views: Views visited', {
|
||||
number: viewsData?.data.data.length,
|
||||
});
|
||||
} else if (sourcepage === DataSource.LOGS) {
|
||||
logEvent('Logs Views: Views visited', {
|
||||
number: viewsData?.data.data.length,
|
||||
});
|
||||
}
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [viewsData?.data.data, isLoading]);
|
||||
const onUpdateQueryHandler = (): void => {
|
||||
updateViewAsync(
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
VerticalAlignTopOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Flex, Tooltip, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
@ -197,6 +198,11 @@ export function Filter(props: FilterProps): JSX.Element {
|
||||
})),
|
||||
},
|
||||
};
|
||||
if (selectedFilters) {
|
||||
logEvent('Traces Explorer: Sidebar filter used', {
|
||||
selectedFilters,
|
||||
});
|
||||
}
|
||||
redirectWithQueryBuilderData(preparedQuery);
|
||||
},
|
||||
[currentQuery, redirectWithQueryBuilderData, selectedFilters],
|
||||
|
@ -3,6 +3,7 @@ import './TracesExplorer.styles.scss';
|
||||
import { FilterOutlined } from '@ant-design/icons';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Button, Card, Tabs, Tooltip } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import axios from 'axios';
|
||||
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
@ -25,7 +26,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import { cloneDeep, isEmpty, set } from 'lodash-es';
|
||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@ -135,7 +136,7 @@ function TracesExplorer(): JSX.Element {
|
||||
};
|
||||
|
||||
const handleExport = useCallback(
|
||||
(dashboard: Dashboard | null): void => {
|
||||
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
|
||||
if (!dashboard || !panelType) return;
|
||||
|
||||
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
||||
@ -157,6 +158,12 @@ function TracesExplorer(): JSX.Element {
|
||||
options.selectColumns,
|
||||
);
|
||||
|
||||
logEvent('Traces Explorer: Add to dashboard successful', {
|
||||
panelType,
|
||||
isNewDashboard,
|
||||
dashboardName: dashboard?.data?.title,
|
||||
});
|
||||
|
||||
updateDashboard(updatedDashboard, {
|
||||
onSuccess: (data) => {
|
||||
if (data.error) {
|
||||
@ -223,6 +230,13 @@ function TracesExplorer(): JSX.Element {
|
||||
currentPanelType,
|
||||
]);
|
||||
const [isOpen, setOpen] = useState<boolean>(true);
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
logEvent('Traces Explorer: Page visited', {});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
|
Loading…
x
Reference in New Issue
Block a user