mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-08 16:49:01 +08:00
[Fix]: threshold in alerts (#4074)
This commit is contained in:
parent
aad44a1037
commit
0b991331d7
@ -2,6 +2,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
|
|||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||||
|
import { getFormatNameByOptionId } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
@ -19,7 +20,7 @@ import { EQueryType } from 'types/common/dashboard';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { ChartContainer, FailedMessageContainer } from './styles';
|
import { ChartContainer, FailedMessageContainer } from './styles';
|
||||||
import { covertIntoDataFormats } from './utils';
|
import { getThresholdLabel } from './utils';
|
||||||
|
|
||||||
export interface ChartPreviewProps {
|
export interface ChartPreviewProps {
|
||||||
name: string;
|
name: string;
|
||||||
@ -50,12 +51,6 @@ function ChartPreview({
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const thresholdValue = covertIntoDataFormats({
|
|
||||||
value: threshold,
|
|
||||||
sourceUnit: alertDef?.condition.targetUnit,
|
|
||||||
targetUnit: query?.unit,
|
|
||||||
});
|
|
||||||
|
|
||||||
const canQuery = useMemo((): boolean => {
|
const canQuery = useMemo((): boolean => {
|
||||||
if (!query || query == null) {
|
if (!query || query == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -110,6 +105,9 @@ function ChartPreview({
|
|||||||
|
|
||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
|
const optionName =
|
||||||
|
getFormatNameByOptionId(alertDef?.condition.targetUnit || '') || '';
|
||||||
|
|
||||||
const options = useMemo(
|
const options = useMemo(
|
||||||
() =>
|
() =>
|
||||||
getUPlotChartOptions({
|
getUPlotChartOptions({
|
||||||
@ -124,10 +122,16 @@ function ChartPreview({
|
|||||||
keyIndex: 0,
|
keyIndex: 0,
|
||||||
moveThreshold: (): void => {},
|
moveThreshold: (): void => {},
|
||||||
selectedGraph: PANEL_TYPES.TIME_SERIES, // no impact
|
selectedGraph: PANEL_TYPES.TIME_SERIES, // no impact
|
||||||
thresholdValue,
|
thresholdValue: threshold,
|
||||||
thresholdLabel: `${t(
|
thresholdLabel: `${t(
|
||||||
'preview_chart_threshold_label',
|
'preview_chart_threshold_label',
|
||||||
)} (y=${thresholdValue} ${query?.unit || ''})`,
|
)} (y=${getThresholdLabel(
|
||||||
|
optionName,
|
||||||
|
threshold,
|
||||||
|
alertDef?.condition.targetUnit,
|
||||||
|
query?.unit,
|
||||||
|
)})`,
|
||||||
|
thresholdUnit: alertDef?.condition.targetUnit,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -136,8 +140,10 @@ function ChartPreview({
|
|||||||
queryResponse?.data?.payload,
|
queryResponse?.data?.payload,
|
||||||
containerDimensions,
|
containerDimensions,
|
||||||
isDarkMode,
|
isDarkMode,
|
||||||
|
threshold,
|
||||||
t,
|
t,
|
||||||
thresholdValue,
|
optionName,
|
||||||
|
alertDef?.condition.targetUnit,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,6 +51,21 @@ export function covertIntoDataFormats({
|
|||||||
return Number.isNaN(result) ? 0 : result;
|
return Number.isNaN(result) ? 0 : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getThresholdLabel = (
|
||||||
|
optionName: string,
|
||||||
|
value: number,
|
||||||
|
unit?: string,
|
||||||
|
yAxisUnit?: string,
|
||||||
|
): string => {
|
||||||
|
if (
|
||||||
|
unit === MiscellaneousFormats.PercentUnit ||
|
||||||
|
yAxisUnit === MiscellaneousFormats.PercentUnit
|
||||||
|
) {
|
||||||
|
return `${value * 100}%`;
|
||||||
|
}
|
||||||
|
return `${value} ${optionName}`;
|
||||||
|
};
|
||||||
|
|
||||||
interface IUnit {
|
interface IUnit {
|
||||||
value: number;
|
value: number;
|
||||||
sourceUnit?: string;
|
sourceUnit?: string;
|
||||||
|
@ -6,6 +6,8 @@ import {
|
|||||||
CategoryNames,
|
CategoryNames,
|
||||||
DataFormats,
|
DataFormats,
|
||||||
DataRateFormats,
|
DataRateFormats,
|
||||||
|
HelperCategory,
|
||||||
|
HelperFormat,
|
||||||
MiscellaneousFormats,
|
MiscellaneousFormats,
|
||||||
ThroughputFormats,
|
ThroughputFormats,
|
||||||
TimeFormats,
|
TimeFormats,
|
||||||
@ -119,3 +121,10 @@ export const getCategoryByOptionId = (id: string): Category | undefined =>
|
|||||||
|
|
||||||
export const isCategoryName = (name: string): name is CategoryNames =>
|
export const isCategoryName = (name: string): name is CategoryNames =>
|
||||||
alertsCategory.some((category) => category.name === name);
|
alertsCategory.some((category) => category.name === name);
|
||||||
|
|
||||||
|
const allFormats: HelperFormat[] = alertsCategory.flatMap(
|
||||||
|
(category: HelperCategory) => category.formats,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getFormatNameByOptionId = (id: string): string | undefined =>
|
||||||
|
allFormats.find((format) => format.id === id)?.name;
|
||||||
|
@ -362,3 +362,13 @@ export type Category = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DataTypeCategories = Category[];
|
export type DataTypeCategories = Category[];
|
||||||
|
|
||||||
|
export interface HelperFormat {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HelperCategory {
|
||||||
|
name: string;
|
||||||
|
formats: Format[];
|
||||||
|
}
|
||||||
|
@ -265,10 +265,15 @@ function findUnitObject(
|
|||||||
|
|
||||||
export function convertValue(
|
export function convertValue(
|
||||||
value: number,
|
value: number,
|
||||||
currentUnit: string,
|
currentUnit?: string,
|
||||||
targetUnit: string,
|
targetUnit?: string,
|
||||||
): number | null {
|
): number | null {
|
||||||
if (targetUnit === 'none') {
|
if (
|
||||||
|
targetUnit === 'none' ||
|
||||||
|
!currentUnit ||
|
||||||
|
!targetUnit ||
|
||||||
|
currentUnit === targetUnit
|
||||||
|
) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
const currentUnitObj = findUnitObject(currentUnit);
|
const currentUnitObj = findUnitObject(currentUnit);
|
||||||
|
@ -15,6 +15,7 @@ import onClickPlugin, { OnClickPluginOpts } from './plugins/onClickPlugin';
|
|||||||
import tooltipPlugin from './plugins/tooltipPlugin';
|
import tooltipPlugin from './plugins/tooltipPlugin';
|
||||||
import getAxes from './utils/getAxes';
|
import getAxes from './utils/getAxes';
|
||||||
import getSeries from './utils/getSeriesData';
|
import getSeries from './utils/getSeriesData';
|
||||||
|
import { getYAxisScale } from './utils/getYAxisScale';
|
||||||
|
|
||||||
interface GetUPlotChartOptions {
|
interface GetUPlotChartOptions {
|
||||||
id?: string;
|
id?: string;
|
||||||
@ -79,7 +80,11 @@ export const getUPlotChartOptions = ({
|
|||||||
auto: true, // Automatically adjust scale range
|
auto: true, // Automatically adjust scale range
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
auto: true,
|
...getYAxisScale(
|
||||||
|
thresholds,
|
||||||
|
apiResponse?.data.newResult.data.result,
|
||||||
|
yAxisUnit,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
83
frontend/src/lib/uPlotLib/utils/getYAxisScale.ts
Normal file
83
frontend/src/lib/uPlotLib/utils/getYAxisScale.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||||
|
import { convertValue } from 'lib/getConvertedValue';
|
||||||
|
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||||
|
|
||||||
|
function findMinMaxValues(data: QueryDataV3[]): [number, number] {
|
||||||
|
let min = 0;
|
||||||
|
let max = 0;
|
||||||
|
data?.forEach((entry) => {
|
||||||
|
entry.series?.forEach((series) => {
|
||||||
|
series.values.forEach((valueObj) => {
|
||||||
|
const value = parseFloat(valueObj.value);
|
||||||
|
if (!value) return;
|
||||||
|
min = Math.min(min, value);
|
||||||
|
max = Math.max(max, value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [min, max];
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMinMaxThresholdValues(
|
||||||
|
thresholds: ThresholdProps[],
|
||||||
|
yAxisUnit?: string,
|
||||||
|
): [number, number] {
|
||||||
|
let minThresholdValue = 0;
|
||||||
|
let maxThresholdValue = 0;
|
||||||
|
|
||||||
|
thresholds.forEach((entry) => {
|
||||||
|
const { thresholdValue, thresholdUnit } = entry;
|
||||||
|
if (thresholdValue === undefined) return;
|
||||||
|
minThresholdValue = Math.min(
|
||||||
|
minThresholdValue,
|
||||||
|
convertValue(thresholdValue, thresholdUnit, yAxisUnit) || 0,
|
||||||
|
);
|
||||||
|
maxThresholdValue = Math.max(
|
||||||
|
maxThresholdValue,
|
||||||
|
convertValue(thresholdValue, thresholdUnit, yAxisUnit) || 0,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [minThresholdValue, maxThresholdValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRange(
|
||||||
|
thresholds: ThresholdProps[],
|
||||||
|
series: QueryDataV3[],
|
||||||
|
yAxisUnit?: string,
|
||||||
|
): [number, number] {
|
||||||
|
const [minThresholdValue, maxThresholdValue] = findMinMaxThresholdValues(
|
||||||
|
thresholds,
|
||||||
|
yAxisUnit,
|
||||||
|
);
|
||||||
|
const [minSeriesValue, maxSeriesValue] = findMinMaxValues(series);
|
||||||
|
|
||||||
|
const min = Math.min(minThresholdValue, minSeriesValue);
|
||||||
|
const max = Math.max(maxThresholdValue, maxSeriesValue);
|
||||||
|
|
||||||
|
return [min, max];
|
||||||
|
}
|
||||||
|
|
||||||
|
function areAllSeriesEmpty(series: QueryDataV3[]): boolean {
|
||||||
|
return series.every((entry) => {
|
||||||
|
if (!entry.series) return true;
|
||||||
|
return entry.series.every((series) => series.values.length === 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getYAxisScale = (
|
||||||
|
thresholds?: ThresholdProps[],
|
||||||
|
series?: QueryDataV3[],
|
||||||
|
yAxisUnit?: string,
|
||||||
|
): {
|
||||||
|
auto: boolean;
|
||||||
|
range?: [number, number];
|
||||||
|
} => {
|
||||||
|
if (!thresholds || !series) return { auto: true };
|
||||||
|
|
||||||
|
if (areAllSeriesEmpty(series)) return { auto: true };
|
||||||
|
|
||||||
|
const [min, max] = getRange(thresholds, series, yAxisUnit);
|
||||||
|
return { auto: false, range: [min, max] };
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user