Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions web/src/components/Incidents/AlertsChart/AlertsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
generateDateArray,
generateAlertsDateArray,
getCurrentTime,
isInTimeWindow,
} from '../utils';
import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime';
import { useTranslation } from 'react-i18next';
Expand All @@ -41,7 +42,7 @@ import { MonitoringState } from '../../../store/store';
import { isEmpty } from 'lodash-es';
import { DataTestIDs } from '../../data-test';

const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => {
const AlertsChart = ({ theme, daysSpan }: { theme: 'light' | 'dark'; daysSpan: number }) => {
const dispatch = useDispatch();
const [chartContainerHeight, setChartContainerHeight] = useState<number>();
const [chartHeight, setChartHeight] = useState<number>();
Expand Down Expand Up @@ -72,8 +73,13 @@ const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => {

const chartData: AlertsChartBar[][] = useMemo(() => {
if (!Array.isArray(alertsData) || alertsData.length === 0) return [];
return alertsData.map((alert) => createAlertsChartBars(alert));
}, [alertsData]);
return alertsData
.filter((alert) => {
const lastTimestamp = alert.values[alert.values.length - 1][0];
return isInTimeWindow(lastTimestamp, daysSpan, currentTime);
})
.map((alert) => createAlertsChartBars(alert));
}, [alertsData, currentTime, daysSpan]);

useEffect(() => {
setChartContainerHeight(chartData?.length < 5 ? 300 : chartData?.length * 55);
Expand Down
14 changes: 11 additions & 3 deletions web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
calculateIncidentsChartDomain,
createIncidentsChartBars,
generateDateArray,
isInTimeWindow,
} from '../utils';
import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -84,10 +85,15 @@ const IncidentsChart = ({
const chartData = useMemo(() => {
if (!Array.isArray(incidentsData) || incidentsData.length === 0) return [];

const filteredIncidents = selectedGroupId
const groupFilteredIncidents = selectedGroupId
? incidentsData.filter((incident) => incident.group_id === selectedGroupId)
: incidentsData;

const filteredIncidents = groupFilteredIncidents.filter((incident) => {
const lastTimestamp = incident.values[incident.values.length - 1][0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the incident.values.length be equal to 0 (similar with alerts)? Should we check that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incident.values.length equal to 0 would mean having some incidents with no datapoints?

return isInTimeWindow(lastTimestamp, chartDays * (60 * 60 * 24 * 1000), currentTime);
});

// Create chart bars and sort by original x values to maintain proper order
const chartBars = filteredIncidents.map((incident) =>
createIncidentsChartBars(incident, dateValues),
Expand All @@ -96,7 +102,7 @@ const IncidentsChart = ({

// Reassign consecutive x values to eliminate gaps between bars
return chartBars.map((bars, index) => bars.map((bar) => ({ ...bar, x: index + 1 })));
}, [incidentsData, dateValues, selectedGroupId]);
}, [incidentsData, dateValues, selectedGroupId, currentTime, chartDays]);

useEffect(() => {
setIsLoading(false);
Expand Down Expand Up @@ -175,7 +181,9 @@ const IncidentsChart = ({
if (datum.nodata) {
return '';
}
const startDate = dateTimeFormatter(i18n.language).format(new Date(datum.y0));
const startDate = dateTimeFormatter(i18n.language).format(
new Date(datum.originalStartDate),
);
const endDate = datum.firing
? '---'
: dateTimeFormatter(i18n.language).format(new Date(datum.y));
Expand Down
11 changes: 8 additions & 3 deletions web/src/components/Incidents/IncidentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ const IncidentsPage = () => {
if (rules && alertsData) {
dispatch(
setAlertsTableData({
alertsTableData: groupAlertsForTable(alertsData, rules),
alertsTableData: groupAlertsForTable(
alertsData,
rules,
incidentsLastRefreshTime,
daysSpan,
),
}),
);
}
Expand Down Expand Up @@ -604,7 +609,7 @@ const IncidentsPage = () => {
<StackItem>
<IncidentsChart
incidentsData={filteredData}
chartDays={timeRanges.length}
chartDays={daysSpan / (60 * 60 * 24 * 1000)} // Convert ms to days
theme={theme}
selectedGroupId={selectedGroupId}
onIncidentClick={handleIncidentChartClick}
Expand All @@ -613,7 +618,7 @@ const IncidentsPage = () => {
/>
</StackItem>
<StackItem>
<AlertsChart theme={theme} />
<AlertsChart theme={theme} daysSpan={daysSpan} />
</StackItem>
</>
)}
Expand Down
13 changes: 12 additions & 1 deletion web/src/components/Incidents/processAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,19 @@ export function convertToAlerts(
export const groupAlertsForTable = (
alerts: Array<Alert>,
alertingRulesData: Array<PrometheusRule>,
currentTime: number,
daysSpan: number,
): Array<GroupedAlert> => {
const groupedAlerts = alerts.reduce((acc: Array<GroupedAlert>, alert) => {
const filteredAlerts = alerts.filter((alert) => {
const endTime = alert.values[alert.values.length - 1][0];
const delta = (currentTime - daysSpan) / 1000;
if (endTime < delta) {
return false;
}
return true;
});

const groupedAlerts = filteredAlerts.reduce((acc: Array<GroupedAlert>, alert) => {
const { component, alertstate, severity, layer, alertname, silenced } = alert;
const existingGroup = acc.find((group) => group.component === component);
const rule = alertingRulesData?.find((rule) => alertname === rule.name);
Expand Down
8 changes: 0 additions & 8 deletions web/src/components/Incidents/processIncidents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,6 @@ describe('getIncidentsTimeRanges', () => {
const now = getCurrentTime();

describe('basic functionality', () => {
it('should return single range for timespan less than one day', () => {
const timespan = 12 * 60 * 60 * 1000; // 12 hours
const result = getIncidentsTimeRanges(timespan, now);

expect(result).toHaveLength(1);
expect(result[0].duration).toBe(ONE_DAY);
});

it('should split longer timespans into daily chunks', () => {
const timespan = 3 * ONE_DAY; // 3 days
const result = getIncidentsTimeRanges(timespan, now);
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/Incidents/processIncidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ export const getIncidentsTimeRanges = (
currentTime: number,
): Array<{ endTime: number; duration: number }> => {
const ONE_DAY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const startTime = currentTime - timespan;
const FIFTEEN_DAYS = 15 * ONE_DAY;
const startTime = currentTime - FIFTEEN_DAYS;
const timeRanges = [{ endTime: startTime + ONE_DAY, duration: ONE_DAY }];

while (timeRanges[timeRanges.length - 1].endTime < currentTime) {
Expand Down
30 changes: 29 additions & 1 deletion web/src/components/Incidents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ export const isResolved = (lastTimestamp: number, currentTime: number): boolean
return delta >= threshold;
};

/**
* Checks if the last timestamp is in the time window.
* @param lastTimestamp - The last timestamp in the incident/alert (in seconds)
* @param daysSpan - The number of days in the time window (in milliseconds).
* @param currentTime - The current time in milliseconds.
* @returns true if the last timestamp is in the time window, false otherwise.
*/
export const isInTimeWindow = (
lastTimestamp: number,
daysSpan: number,
currentTime: number,
): boolean => {
// convert chartDays to ms and then convert the result to seconds
const timeWindow = (currentTime - daysSpan) / 1000;
// if endTime is lower than currentTime-chartDays, return false else true
return lastTimestamp >= timeWindow;
};

/**
* Inserts padding data points to ensure the chart renders correctly.
* This allows the chart to properly render events, especially single data points.
Expand Down Expand Up @@ -273,6 +291,7 @@ export const createIncidentsChartBars = (incident: Incident, dateArray: SpanDate
const groupedData = consolidateAndMergeIntervals(incident, dateArray);

const data: {
originalStartDate: Date;
y0: Date;
y: Date;
x: number;
Expand All @@ -296,8 +315,17 @@ export const createIncidentsChartBars = (incident: Incident, dateArray: SpanDate
const severity = getSeverityName(groupedData[i][2]);
const isLastElement = i === groupedData.length - 1;

let startDateTimestamp = groupedData[i][0];
// if startDateTimestamp is more than 24 hours before the dateArray[0], set startDateTimestamp to 24 hours before the dateArray[0]
// in a way to avoid the chart to be too far in the past
// 86400 is 24 hours in seconds
if (startDateTimestamp < dateArray[0] - 86400) {
startDateTimestamp = dateArray[0] - 86400;
}

data.push({
y0: new Date(groupedData[i][0] * 1000),
originalStartDate: new Date(groupedData[i][0] * 1000),
y0: new Date(startDateTimestamp * 1000),
y: new Date(groupedData[i][1] * 1000),
x: incident.x,
name: severity,
Expand Down