mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 09:05:55 +08:00
feat: create alerts is updated from trace explorer (#2995)
This commit is contained in:
parent
709bfda0cc
commit
cda37b99b4
@ -0,0 +1,27 @@
|
|||||||
|
import { TFunction } from 'i18next';
|
||||||
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
|
||||||
|
import { OptionType } from './types';
|
||||||
|
|
||||||
|
export const getOptionList = (t: TFunction): OptionType[] => [
|
||||||
|
{
|
||||||
|
title: t('metric_based_alert'),
|
||||||
|
selection: AlertTypes.METRICS_BASED_ALERT,
|
||||||
|
description: t('metric_based_alert_desc'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('log_based_alert'),
|
||||||
|
selection: AlertTypes.LOGS_BASED_ALERT,
|
||||||
|
description: t('log_based_alert_desc'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('traces_based_alert'),
|
||||||
|
selection: AlertTypes.TRACES_BASED_ALERT,
|
||||||
|
description: t('traces_based_alert_desc'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('exceptions_based_alert'),
|
||||||
|
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
|
||||||
|
description: t('exceptions_based_alert_desc'),
|
||||||
|
},
|
||||||
|
];
|
@ -1,61 +1,40 @@
|
|||||||
import { Row } from 'antd';
|
import { Row } from 'antd';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
|
||||||
|
import { getOptionList } from './config';
|
||||||
import { AlertTypeCard, SelectTypeContainer } from './styles';
|
import { AlertTypeCard, SelectTypeContainer } from './styles';
|
||||||
|
import { OptionType } from './types';
|
||||||
interface OptionType {
|
|
||||||
title: string;
|
|
||||||
selection: AlertTypes;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
||||||
const { t } = useTranslation(['alerts']);
|
const { t } = useTranslation(['alerts']);
|
||||||
|
|
||||||
const renderOptions = (): JSX.Element => {
|
const optionList = getOptionList(t);
|
||||||
const optionList: OptionType[] = [
|
|
||||||
{
|
const renderOptions = useMemo(
|
||||||
title: t('metric_based_alert'),
|
() => (
|
||||||
selection: AlertTypes.METRICS_BASED_ALERT,
|
|
||||||
description: t('metric_based_alert_desc'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('log_based_alert'),
|
|
||||||
selection: AlertTypes.LOGS_BASED_ALERT,
|
|
||||||
description: t('log_based_alert_desc'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('traces_based_alert'),
|
|
||||||
selection: AlertTypes.TRACES_BASED_ALERT,
|
|
||||||
description: t('traces_based_alert_desc'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('exceptions_based_alert'),
|
|
||||||
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
|
|
||||||
description: t('exceptions_based_alert_desc'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
{optionList.map((o: OptionType) => (
|
{optionList.map((option: OptionType) => (
|
||||||
<AlertTypeCard
|
<AlertTypeCard
|
||||||
key={o.selection}
|
key={option.selection}
|
||||||
title={o.title}
|
title={option.title}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
onSelect(o.selection);
|
onSelect(option.selection);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{o.description}
|
{option.description}
|
||||||
</AlertTypeCard>
|
</AlertTypeCard>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
),
|
||||||
};
|
[onSelect, optionList],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectTypeContainer>
|
<SelectTypeContainer>
|
||||||
<h3> {t('choose_alert_type')} </h3>
|
<h3> {t('choose_alert_type')} </h3>
|
||||||
<Row>{renderOptions()}</Row>
|
<Row>{renderOptions}</Row>
|
||||||
</SelectTypeContainer>
|
</SelectTypeContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
|
||||||
|
export interface OptionType {
|
||||||
|
title: string;
|
||||||
|
selection: AlertTypes;
|
||||||
|
description: string;
|
||||||
|
}
|
8
frontend/src/container/CreateAlertRule/config.ts
Normal file
8
frontend/src/container/CreateAlertRule/config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
export const ALERT_TYPE_VS_SOURCE_MAPPING = {
|
||||||
|
[DataSource.LOGS]: AlertTypes.LOGS_BASED_ALERT,
|
||||||
|
[DataSource.METRICS]: AlertTypes.METRICS_BASED_ALERT,
|
||||||
|
[DataSource.TRACES]: AlertTypes.TRACES_BASED_ALERT,
|
||||||
|
};
|
@ -1,9 +1,11 @@
|
|||||||
import { Form, Row } from 'antd';
|
import { Form, Row } from 'antd';
|
||||||
import FormAlertRules from 'container/FormAlertRules';
|
import FormAlertRules from 'container/FormAlertRules';
|
||||||
import { useState } from 'react';
|
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
import { AlertDef } from 'types/api/alerts/def';
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
|
|
||||||
|
import { ALERT_TYPE_VS_SOURCE_MAPPING } from './config';
|
||||||
import {
|
import {
|
||||||
alertDefaults,
|
alertDefaults,
|
||||||
exceptionAlertDefaults,
|
exceptionAlertDefaults,
|
||||||
@ -17,6 +19,9 @@ function CreateRules(): JSX.Element {
|
|||||||
const [alertType, setAlertType] = useState<AlertTypes>(
|
const [alertType, setAlertType] = useState<AlertTypes>(
|
||||||
AlertTypes.METRICS_BASED_ALERT,
|
AlertTypes.METRICS_BASED_ALERT,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const compositeQuery = useGetCompositeQueryParam();
|
||||||
|
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
|
|
||||||
const onSelectType = (typ: AlertTypes): void => {
|
const onSelectType = (typ: AlertTypes): void => {
|
||||||
@ -36,6 +41,19 @@ function CreateRules(): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!compositeQuery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource;
|
||||||
|
|
||||||
|
const alertType = ALERT_TYPE_VS_SOURCE_MAPPING[dataSource];
|
||||||
|
|
||||||
|
if (alertType) {
|
||||||
|
onSelectType(alertType);
|
||||||
|
}
|
||||||
|
}, [compositeQuery]);
|
||||||
|
|
||||||
if (!initValues) {
|
if (!initValues) {
|
||||||
return (
|
return (
|
||||||
<Row wrap={false}>
|
<Row wrap={false}>
|
||||||
|
@ -1,24 +1,44 @@
|
|||||||
import { Button, Dropdown, MenuProps, Modal } from 'antd';
|
import { Button, Dropdown, MenuProps, Modal } from 'antd';
|
||||||
|
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
import { MENU_KEY, MENU_LABEL } from './config';
|
import { MENU_KEY, MENU_LABEL } from './config';
|
||||||
import ExportPanelContainer from './ExportPanel';
|
import ExportPanelContainer from './ExportPanel';
|
||||||
|
|
||||||
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
function ExportPanel({
|
||||||
|
isLoading,
|
||||||
|
onExport,
|
||||||
|
query,
|
||||||
|
}: ExportPanelProps): JSX.Element {
|
||||||
const [isExport, setIsExport] = useState<boolean>(false);
|
const [isExport, setIsExport] = useState<boolean>(false);
|
||||||
|
|
||||||
const onModalToggle = useCallback((value: boolean) => {
|
const onModalToggle = useCallback((value: boolean) => {
|
||||||
setIsExport(value);
|
setIsExport(value);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onCreateAlertsHandler = useCallback(() => {
|
||||||
|
history.push(
|
||||||
|
`${ROUTES.ALERTS_NEW}?${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||||
|
JSON.stringify(query),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
const onMenuClickHandler: MenuProps['onClick'] = useCallback(
|
const onMenuClickHandler: MenuProps['onClick'] = useCallback(
|
||||||
(e: OnClickProps) => {
|
(e: OnClickProps) => {
|
||||||
if (e.key === MENU_KEY.EXPORT) {
|
if (e.key === MENU_KEY.EXPORT) {
|
||||||
onModalToggle(true);
|
onModalToggle(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.key === MENU_KEY.CREATE_ALERTS) {
|
||||||
|
onCreateAlertsHandler();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[onModalToggle],
|
[onModalToggle, onCreateAlertsHandler],
|
||||||
);
|
);
|
||||||
|
|
||||||
const menu: MenuProps = useMemo(
|
const menu: MenuProps = useMemo(
|
||||||
@ -54,7 +74,11 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
|||||||
open={isExport}
|
open={isExport}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<ExportPanelContainer isLoading={isLoading} onExport={onExport} />
|
<ExportPanelContainer
|
||||||
|
query={query}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onExport={onExport}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -71,6 +95,7 @@ interface OnClickProps {
|
|||||||
export interface ExportPanelProps {
|
export interface ExportPanelProps {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onExport: (dashboard: Dashboard | null) => void;
|
onExport: (dashboard: Dashboard | null) => void;
|
||||||
|
query: Query | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ExportPanel;
|
export default ExportPanel;
|
||||||
|
@ -64,7 +64,7 @@ function WidgetHeader({
|
|||||||
history.push(
|
history.push(
|
||||||
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
|
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
|
||||||
widget.panelTypes
|
widget.panelTypes
|
||||||
}&${COMPOSITE_QUERY}=${JSON.stringify(widget.query)}`,
|
}&${COMPOSITE_QUERY}=${encodeURIComponent(JSON.stringify(widget.query))}`,
|
||||||
);
|
);
|
||||||
}, [widget.id, widget.panelTypes, widget.query]);
|
}, [widget.id, widget.panelTypes, widget.query]);
|
||||||
|
|
||||||
|
@ -77,8 +77,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
history.push(
|
history.push(
|
||||||
`${
|
`${
|
||||||
ROUTES.EDIT_ALERTS
|
ROUTES.EDIT_ALERTS
|
||||||
}?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${JSON.stringify(
|
}?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||||
compositeQuery,
|
JSON.stringify(compositeQuery),
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -47,7 +47,9 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
|
|||||||
history.push(
|
history.push(
|
||||||
`${history.location.pathname}/new?graphType=${name}&widgetId=${
|
`${history.location.pathname}/new?graphType=${name}&widgetId=${
|
||||||
emptyLayout.i
|
emptyLayout.i
|
||||||
}&${COMPOSITE_QUERY}=${JSON.stringify(initialQueriesMap.metrics)}`,
|
}&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||||
|
JSON.stringify(initialQueriesMap.metrics),
|
||||||
|
)}`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
|
@ -8,7 +8,16 @@ export const useGetCompositeQueryParam = (): Query | null => {
|
|||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
|
const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
|
||||||
|
let parsedCompositeQuery: Query | null = null;
|
||||||
|
|
||||||
return compositeQuery ? JSON.parse(compositeQuery) : null;
|
try {
|
||||||
|
if (!compositeQuery) return null;
|
||||||
|
|
||||||
|
parsedCompositeQuery = JSON.parse(decodeURIComponent(compositeQuery));
|
||||||
|
} catch (e) {
|
||||||
|
parsedCompositeQuery = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedCompositeQuery;
|
||||||
}, [urlQuery]);
|
}, [urlQuery]);
|
||||||
};
|
};
|
||||||
|
@ -74,7 +74,9 @@ function TracesExplorer(): JSX.Element {
|
|||||||
dashboardId: data?.payload?.uuid,
|
dashboardId: data?.payload?.uuid,
|
||||||
})}/new?${QueryParams.graphType}=graph&${
|
})}/new?${QueryParams.graphType}=graph&${
|
||||||
QueryParams.widgetId
|
QueryParams.widgetId
|
||||||
}=empty&${COMPOSITE_QUERY}=${JSON.stringify(exportDefaultQuery)}`;
|
}=empty&${COMPOSITE_QUERY}=${encodeURIComponent(
|
||||||
|
JSON.stringify(exportDefaultQuery),
|
||||||
|
)}`;
|
||||||
|
|
||||||
history.push(dashboardEditView);
|
history.push(dashboardEditView);
|
||||||
},
|
},
|
||||||
@ -118,7 +120,11 @@ function TracesExplorer(): JSX.Element {
|
|||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<ActionsWrapper>
|
<ActionsWrapper>
|
||||||
<ExportPanel isLoading={isLoading} onExport={handleExport} />
|
<ExportPanel
|
||||||
|
query={stagedQuery}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onExport={handleExport}
|
||||||
|
/>
|
||||||
</ActionsWrapper>
|
</ActionsWrapper>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
|
@ -460,7 +460,10 @@ export function QueryBuilderProvider({
|
|||||||
id: uuid(),
|
id: uuid(),
|
||||||
};
|
};
|
||||||
|
|
||||||
urlQuery.set(COMPOSITE_QUERY, JSON.stringify(currentGeneratedQuery));
|
urlQuery.set(
|
||||||
|
COMPOSITE_QUERY,
|
||||||
|
encodeURIComponent(JSON.stringify(currentGeneratedQuery)),
|
||||||
|
);
|
||||||
|
|
||||||
if (searchParams) {
|
if (searchParams) {
|
||||||
Object.keys(searchParams).forEach((param) =>
|
Object.keys(searchParams).forEach((param) =>
|
||||||
|
@ -91,7 +91,11 @@ export const SaveDashboard = ({
|
|||||||
const compositeQuery = params.get(COMPOSITE_QUERY);
|
const compositeQuery = params.get(COMPOSITE_QUERY);
|
||||||
const { maxTime, minTime } = store.getState().globalTime;
|
const { maxTime, minTime } = store.getState().globalTime;
|
||||||
const query = compositeQuery
|
const query = compositeQuery
|
||||||
? updateStepInterval(JSON.parse(compositeQuery), maxTime, minTime)
|
? updateStepInterval(
|
||||||
|
JSON.parse(decodeURIComponent(compositeQuery)),
|
||||||
|
maxTime,
|
||||||
|
minTime,
|
||||||
|
)
|
||||||
: updateStepInterval(selectedWidget.query, maxTime, minTime);
|
: updateStepInterval(selectedWidget.query, maxTime, minTime);
|
||||||
|
|
||||||
const response = await updateDashboardApi({
|
const response = await updateDashboardApi({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user