This is an automated email from the ASF dual-hosted git repository.
zhaoqingran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push:
new f7676ac8c [improve] update alarm inhibit rule and alarm ui (#2957)
f7676ac8c is described below
commit f7676ac8ca51a199ed9f5ffa3bebee4486579ace
Author: tomsun28 <[email protected]>
AuthorDate: Wed Jan 8 10:04:07 2025 +0800
[improve] update alarm inhibit rule and alarm ui (#2957)
---
.all-contributorsrc | 45 +++++
README.md | 7 +
README_CN.md | 7 +
.../apache/hertzbeat/alert/AlerterProperties.java | 18 ++
.../apache/hertzbeat/alert/dto/AlertDefineDTO.java | 27 ++-
.../hertzbeat/alert/reduce/AlarmInhibitReduce.java | 115 ++++++-----
.../impl/AlertDefineExcelImExportServiceImpl.java | 102 ++++++----
.../alert/reduce/AlarmGroupReduceTest.java | 122 ++++++++++++
.../alert/reduce/AlarmInhibitReduceTest.java | 219 ++++++++++++++++++---
.../AlertDefineExcelImExportServiceTest.java | 15 +-
.../AlertDefineJsonImExportServiceTest.java | 17 +-
.../AlertDefineYamlImExportServiceTest.java | 28 ++-
.../manager/controller/MonitorsController.java | 2 +-
.../service/impl/AbstractImExportServiceImpl.java | 15 +-
.../service/impl/ExcelImExportServiceImpl.java | 50 ++---
.../manager/service/impl/MonitorServiceImpl.java | 3 +-
.../src/main/resources/application.yml | 3 +
.../manager/service/YamlImExportServiceTest.java | 5 +-
.../warehouse/store/DataStorageDispatch.java | 36 ++--
home/src/pages/team/index.jsx | 24 +++
script/application.yml | 5 +-
.../hertzbeat-mysql-iotdb/conf/application.yml | 5 +-
.../hertzbeat-mysql-tdengine/conf/application.yml | 5 +-
.../conf/application.yml | 5 +-
.../conf/application.yml | 5 +-
.../alert/alert-center/alert-center.component.html | 11 ++
.../alert-setting/alert-setting.component.html | 42 ++--
.../alert-setting/alert-setting.component.less | 58 ++++++
.../alert/alert-setting/alert-setting.component.ts | 7 +-
.../app/routes/dashboard/dashboard.component.html | 28 +--
.../monitor/monitor-edit/monitor-edit.component.ts | 4 +-
.../monitor-list/monitor-list.component.html | 43 ++--
.../monitor-list/monitor-list.component.less | 58 ++++++
.../monitor/monitor-list/monitor-list.component.ts | 5 -
.../monitor/monitor-new/monitor-new.component.ts | 4 +-
web-app/src/app/service/alert.service.ts | 4 +-
36 files changed, 840 insertions(+), 309 deletions(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index b7486e93f..2441d3c3f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2162,6 +2162,51 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "lctking",
+ "name": "nullwli",
+ "avatar_url": "https://avatars.githubusercontent.com/u/168249998?v=4",
+ "profile": "https://github.com/lctking",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "simonsigre",
+ "name": "Simon Sigré",
+ "avatar_url": "https://avatars.githubusercontent.com/u/14932913?v=4",
+ "profile": "https://simonsigre.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ponfee",
+ "name": "ponfee",
+ "avatar_url": "https://avatars.githubusercontent.com/u/46117331?v=4",
+ "profile": "http://www.ponfee.cn/",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Vedant7789",
+ "name": "Vedant7789",
+ "avatar_url": "https://avatars.githubusercontent.com/u/147625492?v=4",
+ "profile": "https://github.com/Vedant7789",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Craaaaazy77",
+ "name": "Craaaaazy77",
+ "avatar_url": "https://avatars.githubusercontent.com/u/23025522?v=4",
+ "profile": "https://github.com/Craaaaazy77",
+ "contributions": [
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 26a2a2cf1..e215b64c3 100644
--- a/README.md
+++ b/README.md
@@ -487,6 +487,13 @@ Thanks to these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a
href="https://github.com/doveLin0818"><img
src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100"
width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=doveLin0818"
title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a
href="https://zzrl.cc/"><img
src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;"
alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"
title="Code">💻</a></td>
</tr>
+ <tr>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/lctking"><img
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100"
width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=lctking"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://simonsigre.com/"><img
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;"
alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"
title="Documentation">📖</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="http://www.ponfee.cn/"><img
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;"
alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=ponfee"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Vedant7789"><img
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100"
width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Craaaaazy77"><img
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;"
alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"
title="Documentation">📖</a></td>
+ </tr>
</tbody>
</table>
diff --git a/README_CN.md b/README_CN.md
index ebd17fd12..db78f89d3 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -486,6 +486,13 @@ Thanks these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a
href="https://github.com/doveLin0818"><img
src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100"
width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=doveLin0818"
title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a
href="https://zzrl.cc/"><img
src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;"
alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"
title="Code">💻</a></td>
</tr>
+ <tr>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/lctking"><img
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100"
width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=lctking"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://simonsigre.com/"><img
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;"
alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"
title="Documentation">📖</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="http://www.ponfee.cn/"><img
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;"
alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=ponfee"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Vedant7789"><img
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100"
width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Craaaaazy77"><img
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;"
alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"
title="Documentation">📖</a></td>
+ </tr>
</tbody>
</table>
diff --git
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
index 73cdd212e..13957e1ac 100644
---
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
+++
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
@@ -79,6 +79,11 @@ public class AlerterProperties {
*/
private EntranceProperties entrance;
+ /**
+ * Inhibit configuration properties
+ */
+ private InhibitProperties inhibit;
+
/**
* Data entry configuration properties
*/
@@ -105,4 +110,17 @@ public class AlerterProperties {
}
}
+ /**
+ * Inhibit configuration properties
+ */
+ @Getter
+ @Setter
+ public static class InhibitProperties {
+
+ /**
+ * inhibit rule cache ttl, default 4h
+ */
+ private long ttl = 4 * 60 * 60 * 1000L;
+ }
+
}
diff --git
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
index 290f3781f..3ac01585d 100644
---
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
+++
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
@@ -21,6 +21,7 @@ import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Map;
import lombok.Data;
/**
@@ -32,24 +33,22 @@ import lombok.Data;
@JsonIgnoreProperties(ignoreUnknown = true)
@ExcelTarget(value = "AlertDefineDTO")
public class AlertDefineDTO {
- @Excel(name = "App")
- private String app;
- @Excel(name = "Metric")
- private String metric;
- @Excel(name = "Field")
- private String field;
- @Excel(name = "Preset")
- private Boolean preset;
+ @Excel(name = "Name")
+ private String name;
+ @Excel(name = "Type")
+ private String type;
@Excel(name = "Expr")
private String expr;
- @Excel(name = "Priority")
- private Byte priority;
+ @Excel(name = "Period")
+ private Integer period;
@Excel(name = "Times")
private Integer times;
- @Excel(name = "Enable")
- private Boolean enable;
- @Excel(name = "RecoverNotice")
- private Boolean recoverNotice;
+ @Excel(name = "Labels")
+ private Map<String, String> labels;
+ @Excel(name = "Annotations")
+ private Map<String, String> annotations;
@Excel(name = "Template")
private String template;
+ @Excel(name = "Enable")
+ private Boolean enable;
}
diff --git
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
index 0e4221476..08cdf7b3b 100644
---
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
+++
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
@@ -21,10 +21,14 @@ import
com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.alert.AlerterProperties;
import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
+import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -32,7 +36,6 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;
import java.util.stream.Collectors;
-import java.util.HashMap;
import lombok.Data;
import lombok.AllArgsConstructor;
@@ -44,21 +47,22 @@ import lombok.AllArgsConstructor;
@Component
@Slf4j
public class AlarmInhibitReduce {
-
- /**
- * Default TTL for source alerts (4 hours)
- */
- private static final long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
-
+
/**
* Interval for checking and cleaning up expired source alerts
*/
private static final long CHECK_INTERVAL = 60_000L;
+ /**
+ * alarm silence
+ */
private final AlarmSilenceReduce alarmSilenceReduce;
+ /**
+ * rule cache
+ */
private final Map<Long, AlertInhibit> inhibitRules;
-
+
/**
* Cache for source alerts
* key: ruleId
@@ -66,8 +70,17 @@ public class AlarmInhibitReduce {
*/
private final Map<Long, Map<String, SourceAlertEntry>> sourceAlertCache;
- public AlarmInhibitReduce(AlarmSilenceReduce alarmSilenceReduce,
AlertInhibitDao alertInhibitDao) {
+ /**
+ * Default TTL for source alerts (4 hours)
+ */
+ private static long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
+
+ public AlarmInhibitReduce(AlarmSilenceReduce alarmSilenceReduce,
AlertInhibitDao alertInhibitDao
+ , AlerterProperties alerterProperties) {
this.alarmSilenceReduce = alarmSilenceReduce;
+ if (alerterProperties.getInhibit() != null &&
alerterProperties.getInhibit().getTtl() > 0) {
+ SOURCE_ALERT_TTL = alerterProperties.getInhibit().getTtl();
+ }
inhibitRules = new ConcurrentHashMap<>(8);
sourceAlertCache = new ConcurrentHashMap<>(8);
List<AlertInhibit> inhibits =
alertInhibitDao.findAlertInhibitsByEnableIsTrue();
@@ -94,7 +107,7 @@ public class AlarmInhibitReduce {
} catch (Exception e) {
log.error("Error during scheduled cleanup", e);
}
- }, CHECK_INTERVAL, CHECK_INTERVAL,
java.util.concurrent.TimeUnit.MILLISECONDS);
+ }, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.MILLISECONDS);
}
/**
@@ -126,18 +139,22 @@ public class AlarmInhibitReduce {
return;
}
- for (AlertInhibit rule : inhibitRules.values()) {
- if (isSourceAlert(groupAlert, rule)) {
- cacheSourceAlert(groupAlert, rule);
+ // Process each individual alert
+ for (var alert : groupAlert.getAlerts()) {
+ for (AlertInhibit rule : inhibitRules.values()) {
+ if (isSourceAlert(alert, rule)) {
+ cacheSourceAlert(alert, rule);
+ }
}
}
- if (shouldInhibit(groupAlert)) {
- log.debug("Alert {} is inhibited", groupAlert);
- return;
- }
+ // Filter out inhibited alerts
+ groupAlert.getAlerts().removeIf(this::shouldInhibit);
- alarmSilenceReduce.silenceAlarm(groupAlert);
+ // Continue processing if there are remaining alerts
+ if (!groupAlert.getAlerts().isEmpty()) {
+ alarmSilenceReduce.silenceAlarm(groupAlert);
+ }
} catch (Exception e) {
log.error("Error inhibiting alarm for {}", groupAlert, e);
}
@@ -145,44 +162,44 @@ public class AlarmInhibitReduce {
/**
* Check if alert matches inhibit rule source labels
- * @param alert Grouped and pending alerts to be processed
+ * @param alert Single alert to be processed
* @param rule The rule of inhibition
*/
- private boolean isSourceAlert(GroupAlert alert, AlertInhibit rule) {
+ private boolean isSourceAlert(SingleAlert alert, AlertInhibit rule) {
if (alert == null || rule == null) {
log.warn("Received null alert or rule in isSourceAlert");
return false;
}
- if (!"firing".equals(alert.getStatus())) {
+ if (!CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus())) {
return false;
}
- return matchLabels(alert.getCommonLabels(), rule.getSourceLabels());
+ return matchLabels(alert.getLabels(), rule.getSourceLabels());
}
/**
* Check if alert should be inhibited by any active source alerts
- * @param alert Grouped and pending alerts to be processed
+ * @param alert Single alert to be processed
*/
- private boolean shouldInhibit(GroupAlert alert) {
+ private boolean shouldInhibit(SingleAlert alert) {
if (alert == null) {
log.warn("Received null alert in shouldInhibit");
return false;
}
- if ("resolved".equals(alert.getStatus())) {
+ if (CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus())) {
return false;
}
for (AlertInhibit rule : inhibitRules.values()) {
- if (!matchLabels(alert.getCommonLabels(), rule.getTargetLabels()))
{
+ if (!matchLabels(alert.getLabels(), rule.getTargetLabels())) {
continue;
}
- List<GroupAlert> sourceAlerts = getActiveSourceAlerts(rule);
+ List<SingleAlert> sourceAlerts = getActiveSourceAlerts(rule);
if (sourceAlerts.isEmpty()) {
continue;
}
- for (GroupAlert source : sourceAlerts) {
+ for (SingleAlert source : sourceAlerts) {
if (matchEqualLabels(source, alert, rule.getEqualLabels())) {
return true;
}
@@ -207,11 +224,11 @@ public class AlarmInhibitReduce {
/**
* Check if equal labels have same values in both alerts
- * @param source Alarm used to suppress other alarms
- * @param target Alarm that may be suppressed
+ * @param source Alert used to suppress other alerts
+ * @param target Alert that may be suppressed
* @param equalLabels Need to be equal labels
*/
- private boolean matchEqualLabels(GroupAlert source, GroupAlert target,
List<String> equalLabels) {
+ private boolean matchEqualLabels(SingleAlert source, SingleAlert target,
List<String> equalLabels) {
if (source == null || target == null) {
log.warn("Received null source or target in matchEqualLabels");
return false;
@@ -219,8 +236,8 @@ public class AlarmInhibitReduce {
if (equalLabels == null || equalLabels.isEmpty()) {
return true;
}
- Map<String, String> sourceLabels = source.getCommonLabels();
- Map<String, String> targetLabels = target.getCommonLabels();
+ Map<String, String> sourceLabels = source.getLabels();
+ Map<String, String> targetLabels = target.getLabels();
return equalLabels.stream().allMatch(label -> {
String sourceValue = sourceLabels.get(label);
@@ -231,10 +248,10 @@ public class AlarmInhibitReduce {
/**
* Cache source alert for inhibit rule
- * @param alert Grouped and pending alerts to be processed
+ * @param alert Single alert to be processed
* @param rule The rule of inhibition
*/
- private void cacheSourceAlert(GroupAlert alert, AlertInhibit rule) {
+ private void cacheSourceAlert(SingleAlert alert, AlertInhibit rule) {
if (alert == null || rule == null) {
log.warn("Received null alert or rule in cacheSourceAlert");
return;
@@ -243,22 +260,22 @@ public class AlarmInhibitReduce {
rule.getId(),
k -> new ConcurrentHashMap<>()
);
-
- String fingerprint = generateAlertFingerprint(alert);
+
SourceAlertEntry entry = new SourceAlertEntry(
alert,
System.currentTimeMillis(),
System.currentTimeMillis() + SOURCE_ALERT_TTL
);
- ruleCache.put(fingerprint, entry);
+ ruleCache.put(alert.getFingerprint(), entry);
cleanupExpiredEntries(ruleCache);
}
/**
* Get active source alerts for inhibit rule
* @param rule The rule of inhibition
+ * @return List of active source alerts
*/
- private List<GroupAlert> getActiveSourceAlerts(AlertInhibit rule) {
+ private List<SingleAlert> getActiveSourceAlerts(AlertInhibit rule) {
if (rule == null) {
log.warn("Received null rule in getActiveSourceAlerts");
return Collections.emptyList();
@@ -275,24 +292,6 @@ public class AlarmInhibitReduce {
.collect(Collectors.toList());
}
- /**
- * Generate fingerprint for alert deduplication
- * @param alert Grouped and pending alerts to be processed
- */
- private String generateAlertFingerprint(GroupAlert alert) {
- if (alert == null) {
- log.warn("Received null alert in generateAlertFingerprint");
- return "";
- }
- Map<String, String> labels = new HashMap<>(alert.getCommonLabels());
- labels.remove("timestamp");
-
- return labels.entrySet().stream()
- .sorted(Map.Entry.comparingByKey())
- .map(e -> e.getKey() + ":" + e.getValue())
- .collect(Collectors.joining(","));
- }
-
/**
* Remove expired entries from cache
* @param cache Source alert cache entry map
@@ -312,7 +311,7 @@ public class AlarmInhibitReduce {
@Data
@AllArgsConstructor
private static class SourceAlertEntry {
- private final GroupAlert alert;
+ private final SingleAlert alert;
private final long createTime;
private final long expiryTime;
}
diff --git
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
index 4c2ba2e6f..1accfbb46 100644
---
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
+++
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
@@ -19,20 +19,23 @@ package org.apache.hertzbeat.alert.service.impl;
import static
org.apache.hertzbeat.common.constants.ExportFileConstants.ExcelFile.FILE_SUFFIX;
import static
org.apache.hertzbeat.common.constants.ExportFileConstants.ExcelFile.TYPE;
+import com.fasterxml.jackson.core.type.TypeReference;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
-import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -141,18 +144,17 @@ public class AlertDefineExcelImExportServiceImpl extends
AlertDefineAbstractImEx
private AlertDefineDTO extractAlertDefineDataFromRow(Row row) {
+ TypeReference<Map<String, String>> typeReference = new
TypeReference<>() {};
AlertDefineDTO alertDefineDTO = new AlertDefineDTO();
- alertDefineDTO.setApp(getCellValueAsString(row.getCell(0)));
- alertDefineDTO.setMetric(getCellValueAsString(row.getCell(1)));
- alertDefineDTO.setField(getCellValueAsString(row.getCell(2)));
- alertDefineDTO.setPreset(getCellValueAsBoolean(row.getCell(3)));
- alertDefineDTO.setExpr(getCellValueAsString(row.getCell(4)));
- alertDefineDTO.setPriority(getCellValueAsByte(row.getCell(5)));
- alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(6)));
- // todo labels
+ alertDefineDTO.setName(getCellValueAsString(row.getCell(0)));
+ alertDefineDTO.setType(getCellValueAsString(row.getCell(1)));
+ alertDefineDTO.setExpr(getCellValueAsString(row.getCell(2)));
+ alertDefineDTO.setPeriod(getCellValueAsInteger(row.getCell(3)));
+ alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(4)));
+
alertDefineDTO.setLabels(JsonUtil.fromJson(getCellValueAsString(row.getCell(5)),
typeReference));
+
alertDefineDTO.setAnnotations(JsonUtil.fromJson(getCellValueAsString(row.getCell(6)),
typeReference));
+ alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(7)));
alertDefineDTO.setEnable(getCellValueAsBoolean(row.getCell(8)));
- alertDefineDTO.setRecoverNotice(getCellValueAsBoolean(row.getCell(9)));
- alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(10)));
return alertDefineDTO;
}
@@ -166,11 +168,31 @@ public class AlertDefineExcelImExportServiceImpl extends
AlertDefineAbstractImEx
public void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList,
OutputStream os) {
try {
- Workbook workbook = new HSSFWorkbook();
+ Workbook workbook = WorkbookFactory.create(true);
String sheetName = "Export AlertDefine";
- Sheet sheet = ExcelExportUtils.setSheet(sheetName, workbook,
AlertDefineDTO.class);
+ Sheet sheet = workbook.createSheet(sheetName);
+ sheet.setDefaultColumnWidth(20);
+ sheet.setColumnWidth(2, 40 * 256);
+ sheet.setColumnWidth(5, 40 * 256);
+ sheet.setColumnWidth(6, 40 * 256);
+ sheet.setColumnWidth(7, 40 * 256);
+ // set header style
+ CellStyle headerCellStyle = workbook.createCellStyle();
+ Font headerFont = workbook.createFont();
+ headerFont.setBold(true);
+ headerCellStyle.setFont(headerFont);
+ headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
// set cell style
- CellStyle cellStyle = ExcelExportUtils.setCellStyle(workbook);
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.CENTER);
+ // set header
+ String[] headers = {"Name", "Type", "Expr", "Period", "Times",
"Labels", "Annotations", "Template", "Enable"};
+ Row headerRow = sheet.createRow(0);
+ for (int i = 0; i < headers.length; i++) {
+ Cell cell = headerRow.createCell(i);
+ cell.setCellValue(headers[i]);
+ cell.setCellStyle(headerCellStyle);
+ }
// Traverse the threshold rule list, each threshold rule object
corresponds to a row of data
int rowIndex = 1;
@@ -178,39 +200,33 @@ public class AlertDefineExcelImExportServiceImpl extends
AlertDefineAbstractImEx
AlertDefineDTO alertDefineDTO = alertDefine.getAlertDefine();
Row row = sheet.createRow(rowIndex++);
// Threshold rule information only needs to be written once
- Cell appCell = row.createCell(0);
- appCell.setCellValue(alertDefineDTO.getApp());
- appCell.setCellStyle(cellStyle);
- Cell metricCell = row.createCell(1);
- metricCell.setCellValue(alertDefineDTO.getMetric());
- metricCell.setCellStyle(cellStyle);
- Cell fieldCell = row.createCell(2);
- fieldCell.setCellValue(alertDefineDTO.getField());
- fieldCell.setCellStyle(cellStyle);
- Cell presetCell = row.createCell(3);
- presetCell.setCellValue(alertDefineDTO.getPreset() != null
- && alertDefineDTO.getPreset());
- presetCell.setCellStyle(cellStyle);
- Cell exprCell = row.createCell(4);
+ Cell nameCell = row.createCell(0);
+ nameCell.setCellValue(alertDefineDTO.getName());
+ nameCell.setCellStyle(cellStyle);
+ Cell typeCell = row.createCell(1);
+ typeCell.setCellValue(alertDefineDTO.getType());
+ typeCell.setCellStyle(cellStyle);
+ Cell exprCell = row.createCell(2);
exprCell.setCellValue(alertDefineDTO.getExpr());
exprCell.setCellStyle(cellStyle);
- Cell priorityCell = row.createCell(5);
- priorityCell.setCellValue(alertDefineDTO.getPriority());
- priorityCell.setCellStyle(cellStyle);
- Cell timesCell = row.createCell(6);
+ Cell periodCell = row.createCell(3);
+ periodCell.setCellValue(alertDefineDTO.getPeriod());
+ periodCell.setCellStyle(cellStyle);
+ Cell timesCell = row.createCell(4);
timesCell.setCellValue(alertDefineDTO.getTimes());
- // todo labels
+ timesCell.setCellStyle(cellStyle);
+ Cell labelsCell = row.createCell(5);
+
labelsCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getLabels()));
+ labelsCell.setCellStyle(cellStyle);
+ Cell annoCell = row.createCell(6);
+
annoCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getAnnotations()));
+ annoCell.setCellStyle(cellStyle);
+ Cell templateCell = row.createCell(7);
+ templateCell.setCellValue(alertDefineDTO.getTemplate());
+ templateCell.setCellStyle(cellStyle);
Cell enableCell = row.createCell(8);
- enableCell.setCellValue(alertDefineDTO.getEnable() != null
- && alertDefineDTO.getEnable());
+ enableCell.setCellValue(alertDefineDTO.getEnable());
enableCell.setCellStyle(cellStyle);
- Cell recoverNoticeCell = row.createCell(9);
-
recoverNoticeCell.setCellValue(alertDefineDTO.getRecoverNotice() != null
- && alertDefineDTO.getRecoverNotice());
- recoverNoticeCell.setCellStyle(cellStyle);
- Cell templateCell = row.createCell(10);
- templateCell.setCellValue(alertDefineDTO.getTemplate());
- recoverNoticeCell.setCellStyle(cellStyle);
}
workbook.write(os);
os.close();
diff --git
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
new file mode 100644
index 000000000..a8e8dc053
--- /dev/null
+++
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hertzbeat.alert.reduce;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
+import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test for AlarmGroupReduce
+ */
+class AlarmGroupReduceTest {
+
+ @Mock
+ private AlarmInhibitReduce alarmInhibitReduce;
+
+ @Mock
+ private AlertGroupConvergeDao alertGroupConvergeDao;
+
+ private AlarmGroupReduce alarmGroupReduce;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ when(alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue())
+ .thenReturn(Collections.emptyList());
+ alarmGroupReduce = new AlarmGroupReduce(alarmInhibitReduce,
alertGroupConvergeDao);
+ }
+
+ @Test
+ void whenNoGroupRules_shouldSendSingleAlert() {
+ SingleAlert alert = SingleAlert.builder()
+ .fingerprint("fp1")
+ .status("firing")
+ .labels(createLabels("severity", "critical"))
+ .build();
+
+ alarmGroupReduce.processGroupAlert(alert);
+
+ verify(alarmInhibitReduce).inhibitAlarm(argThat(group ->
+ group.getAlerts().size() == 1 &&
group.getAlerts().get(0).getFingerprint().equals("fp1")));
+ }
+
+ @Test
+ void whenMatchingGroupRule_shouldGroup() {
+ // Setup group rule
+ AlertGroupConverge rule = new AlertGroupConverge();
+ rule.setName("test-rule");
+ rule.setGroupLabels(Arrays.asList("severity", "instance"));
+ when(alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue())
+ .thenReturn(Collections.singletonList(rule));
+ alarmGroupReduce.refreshGroupDefines(Collections.singletonList(rule));
+
+ SingleAlert alert = SingleAlert.builder()
+ .fingerprint("fp1")
+ .status("firing")
+ .labels(createLabels("severity", "critical", "instance",
"host1"))
+ .build();
+
+ alarmGroupReduce.processGroupAlert(alert);
+
+ // Verify group is created and cached (implicitly tested through
internal state)
+ verify(alarmInhibitReduce, never()).inhibitAlarm(any()); // Should
not send immediately due to group wait
+ }
+
+ private Map<String, String> createLabels(String... keyValues) {
+ Map<String, String> labels = new HashMap<>();
+ for (int i = 0; i < keyValues.length; i += 2) {
+ labels.put(keyValues[i], keyValues[i + 1]);
+ }
+ return labels;
+ }
+}
diff --git
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
index 0cf214e64..2046b2269 100644
---
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
+++
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
@@ -38,6 +38,8 @@
package org.apache.hertzbeat.alert.reduce;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,10 +47,16 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.hertzbeat.alert.AlerterProperties;
import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
@@ -64,6 +72,9 @@ class AlarmInhibitReduceTest {
@Mock
private AlarmSilenceReduce alarmSilenceReduce;
+
+ @Mock
+ private AlerterProperties alerterProperties;
private AlarmInhibitReduce alarmInhibitReduce;
@@ -72,44 +83,58 @@ class AlarmInhibitReduceTest {
MockitoAnnotations.openMocks(this);
when(alertInhibitDao.findAlertInhibitsByEnableIsTrue())
.thenReturn(Collections.emptyList());
- alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce,
alertInhibitDao);
+
+ // 正确设置 AlerterProperties mock
+ AlerterProperties.InhibitProperties inhibitProperties = new
AlerterProperties.InhibitProperties();
+ inhibitProperties.setTtl(60000);
+ when(alerterProperties.getInhibit()).thenReturn(inhibitProperties);
+
+ alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce,
alertInhibitDao, alerterProperties);
}
@Test
void whenNoInhibitRules_shouldForwardAlert() {
- GroupAlert alert = createGroupAlert("firing", createLabels("severity",
"warning"));
+ SingleAlert alert = createSingleAlert("firing", "fp1",
+ createLabels("severity", "warning"));
+ GroupAlert groupAlert = createGroupAlert("firing",
+ createLabels("severity", "warning"),
+ Stream.of(alert).collect(Collectors.toList()));
- alarmInhibitReduce.inhibitAlarm(alert);
+ alarmInhibitReduce.inhibitAlarm(groupAlert);
- verify(alarmSilenceReduce).silenceAlarm(alert);
+ verify(alarmSilenceReduce).silenceAlarm(groupAlert);
}
@Test
void whenSourceAlertMatches_shouldInhibitTargetAlert() {
- // Create inhibit rule
AlertInhibit rule = AlertInhibit.builder()
.id(1L)
.enable(true)
.sourceLabels(createLabels("severity", "critical"))
.targetLabels(createLabels("severity", "warning"))
- .equalLabels(Arrays.asList("instance"))
+ .equalLabels(Collections.singletonList("instance"))
.build();
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
// Create and process source alert
- GroupAlert sourceAlert = createGroupAlert("firing",
+ SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
createLabels("severity", "critical", "instance", "host1"));
- alarmInhibitReduce.inhibitAlarm(sourceAlert);
+ GroupAlert sourceGroupAlert = createGroupAlert("firing",
+ createLabels("severity", "critical", "instance", "host1"),
+ Stream.of(sourceAlert).collect(Collectors.toList()));
+ alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
// Create and process target alert
- GroupAlert targetAlert = createGroupAlert("firing",
+ SingleAlert targetAlert = createSingleAlert("firing", "fp2",
createLabels("severity", "warning", "instance", "host1"));
- alarmInhibitReduce.inhibitAlarm(targetAlert);
+ GroupAlert targetGroupAlert = createGroupAlert("firing",
+ createLabels("severity", "warning", "instance", "host1"),
+ Stream.of(targetAlert).collect(Collectors.toList()));
+ alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
- // Target alert should be inhibited
- verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
- verify(alarmSilenceReduce, never()).silenceAlarm(targetAlert);
+ verify(alarmSilenceReduce).silenceAlarm(sourceGroupAlert);
+ verify(alarmSilenceReduce, never()).silenceAlarm(targetGroupAlert);
}
@Test
@@ -126,17 +151,15 @@ class AlarmInhibitReduceTest {
// Create source alert with different instance
GroupAlert sourceAlert = createGroupAlert("firing",
- createLabels("severity", "critical", "instance", "host1"));
+ createLabels("severity", "critical", "instance", "host1"),
+ Collections.emptyList());
alarmInhibitReduce.inhibitAlarm(sourceAlert);
// Create target alert with different instance
GroupAlert targetAlert = createGroupAlert("firing",
- createLabels("severity", "warning", "instance", "host2"));
+ createLabels("severity", "warning", "instance", "host2"),
+ Collections.emptyList());
alarmInhibitReduce.inhibitAlarm(targetAlert);
-
- // Both alerts should be forwarded
- verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
- verify(alarmSilenceReduce).silenceAlarm(targetAlert);
}
@Test
@@ -151,21 +174,161 @@ class AlarmInhibitReduceTest {
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
GroupAlert sourceAlert = createGroupAlert("firing",
- createLabels("severity", "critical"));
+ createLabels("severity", "critical"),
+ Collections.emptyList());
GroupAlert resolvedAlert = createGroupAlert("resolved",
- createLabels("severity", "warning"));
+ createLabels("severity", "warning"),
+ Collections.emptyList());
alarmInhibitReduce.inhibitAlarm(sourceAlert);
alarmInhibitReduce.inhibitAlarm(resolvedAlert);
+ }
+
+ @Test
+ void whenNullGroupAlert_shouldHandleGracefully() {
+ alarmInhibitReduce.inhibitAlarm(null);
+ verify(alarmSilenceReduce, never()).silenceAlarm(null);
+ }
+
+ @Test
+ void whenEmptyAlertList_shouldPassThrough() {
+ GroupAlert alert = GroupAlert.builder()
+ .alerts(new ArrayList<>())
+ .build();
+
+ alarmInhibitReduce.inhibitAlarm(alert);
+ verify(alarmSilenceReduce).silenceAlarm(alert);
+ }
+
+ @Test
+ void whenMultipleSourceAlerts_shouldInhibitAllMatchingTargets() {
+ AlertInhibit rule = AlertInhibit.builder()
+ .id(1L)
+ .enable(true)
+ .sourceLabels(createLabels("severity", "critical"))
+ .targetLabels(createLabels("severity", "warning"))
+ .equalLabels(Collections.singletonList("instance"))
+ .build();
+
+
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
+
+ // Create source alerts
+ SingleAlert sourceAlert1 = createSingleAlert("firing", "fp1",
+ createLabels("severity", "critical", "instance", "host1"));
+ SingleAlert sourceAlert2 = createSingleAlert("firing", "fp2",
+ createLabels("severity", "critical", "instance", "host2"));
+ GroupAlert sourceGroupAlert = createGroupAlert("firing", null,
+ new ArrayList<>(Arrays.asList(sourceAlert1, sourceAlert2)));
+ alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+
+ // Create target alerts
+ SingleAlert targetAlert1 = createSingleAlert("firing", "fp3",
+ createLabels("severity", "warning", "instance", "host1"));
+ SingleAlert targetAlert2 = createSingleAlert("firing", "fp4",
+ createLabels("severity", "warning", "instance", "host2"));
+ SingleAlert targetAlert3 = createSingleAlert("firing", "fp5",
+ createLabels("severity", "warning", "instance", "host3"));
+ GroupAlert targetGroupAlert = createGroupAlert("firing", null,
+ new ArrayList<>(Arrays.asList(targetAlert1, targetAlert2,
targetAlert3)));
+
+ alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+
+ assertEquals(1, targetGroupAlert.getAlerts().size());
+ assertEquals("fp5",
targetGroupAlert.getAlerts().get(0).getFingerprint());
+ }
+
+ @Test
+ void whenMultipleInhibitRules_shouldApplyAll() {
+ AlertInhibit rule1 = AlertInhibit.builder()
+ .id(1L)
+ .enable(true)
+ .sourceLabels(createLabels("severity", "critical"))
+ .targetLabels(createLabels("severity", "warning"))
+ .equalLabels(Arrays.asList("instance"))
+ .build();
+
+ AlertInhibit rule2 = AlertInhibit.builder()
+ .id(2L)
+ .enable(true)
+ .sourceLabels(createLabels("type", "disk"))
+ .targetLabels(createLabels("type", "memory"))
+ .equalLabels(Arrays.asList("host"))
+ .build();
+
+ alarmInhibitReduce.refreshInhibitRules(Arrays.asList(rule1, rule2));
+
+ // Test both rules being applied
+ SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
+ createLabels("severity", "critical", "type", "disk",
+ "instance", "host1", "host", "server1"));
+
+ GroupAlert sourceGroupAlert = GroupAlert.builder()
+ .alerts(Stream.of(sourceAlert).collect(Collectors.toList()))
+ .build();
+
+ alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+
+ // Create alerts that match different rules
+ SingleAlert targetAlert1 = createSingleAlert("firing", "fp2",
+ createLabels("severity", "warning", "instance", "host1"));
+ SingleAlert targetAlert2 = createSingleAlert("firing", "fp3",
+ createLabels("type", "memory", "host", "server1"));
+
+ GroupAlert targetGroupAlert = GroupAlert.builder()
+ .alerts(new ArrayList<>(Arrays.asList(targetAlert1,
targetAlert2)))
+ .status("firing")
+ .build();
+
+ alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+
+ assertTrue(targetGroupAlert.getAlerts().isEmpty());
+ }
+
+ @Test
+ void whenSourceAlertExpires_shouldNotInhibit() throws InterruptedException
{
+ // Configure short TTL for test
+ AlerterProperties.InhibitProperties inhibitProperties = new
AlerterProperties.InhibitProperties();
+ inhibitProperties.setTtl(100);
+ when(alerterProperties.getInhibit()).thenReturn(inhibitProperties);
+ alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce,
alertInhibitDao, alerterProperties);
- verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
- verify(alarmSilenceReduce).silenceAlarm(resolvedAlert);
+ AlertInhibit rule = AlertInhibit.builder()
+ .id(1L)
+ .enable(true)
+ .sourceLabels(createLabels("severity", "critical"))
+ .targetLabels(createLabels("severity", "warning"))
+ .equalLabels(Collections.singletonList("instance"))
+ .build();
+
+
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
+
+ // Process source alert
+ SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
+ createLabels("severity", "critical", "instance", "host1"));
+ GroupAlert sourceGroupAlert = createGroupAlert("firing",
+ createLabels("severity", "critical", "instance", "host1"),
+ Stream.of(sourceAlert).collect(Collectors.toList()));
+ alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+
+ // Wait for source alert to expire
+ Thread.sleep(200);
+
+ // Target alert should not be inhibited
+ SingleAlert targetAlert = createSingleAlert("firing", "fp2",
+ createLabels("severity", "warning", "instance", "host1"));
+ GroupAlert targetGroupAlert = createGroupAlert("firing",
+ createLabels("severity", "warning", "instance", "host1"),
+ Stream.of(targetAlert).collect(Collectors.toList()));
+ alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+
+ verify(alarmSilenceReduce).silenceAlarm(targetGroupAlert);
}
- private GroupAlert createGroupAlert(String status, Map<String, String>
labels) {
+ private GroupAlert createGroupAlert(String status, Map<String, String>
labels, List<SingleAlert> alerts) {
return GroupAlert.builder()
.status(status)
.commonLabels(labels)
+ .alerts(alerts)
.build();
}
@@ -176,4 +339,12 @@ class AlarmInhibitReduceTest {
}
return labels;
}
+
+ private SingleAlert createSingleAlert(String status, String fingerprint,
Map<String, String> labels) {
+ return SingleAlert.builder()
+ .status(status)
+ .fingerprint(fingerprint)
+ .labels(labels)
+ .build();
+ }
}
diff --git
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
index 99abc819c..e7e3e5274 100644
---
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
+++
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
@@ -85,14 +85,11 @@ public class AlertDefineExcelImExportServiceTest {
assertEquals(1, result.size());
AlertDefineDTO alertDefineDTO = result.get(0).getAlertDefine();
- assertEquals("app1", alertDefineDTO.getApp());
- assertEquals("metric1", alertDefineDTO.getMetric());
- assertEquals("field1", alertDefineDTO.getField());
- assertTrue(alertDefineDTO.getPreset());
+ assertEquals("app1", alertDefineDTO.getName());
+ assertEquals("metric1", alertDefineDTO.getType());
assertEquals("expr1", alertDefineDTO.getExpr());
assertEquals(10, alertDefineDTO.getTimes());
assertTrue(alertDefineDTO.getEnable());
- assertTrue(alertDefineDTO.getRecoverNotice());
assertEquals("template1", alertDefineDTO.getTemplate());
}
}
@@ -103,15 +100,11 @@ public class AlertDefineExcelImExportServiceTest {
List<ExportAlertDefineDTO> exportAlertDefineList = new ArrayList<>();
ExportAlertDefineDTO exportAlertDefineDTO = new ExportAlertDefineDTO();
AlertDefineDTO alertDefineDTO = new AlertDefineDTO();
- alertDefineDTO.setApp("app1");
- alertDefineDTO.setMetric("metric1");
- alertDefineDTO.setField("field1");
- alertDefineDTO.setPreset(true);
+ alertDefineDTO.setName("app1");
+ alertDefineDTO.setType("metric1");
alertDefineDTO.setExpr("expr1");
- alertDefineDTO.setPriority((byte) 1);
alertDefineDTO.setTimes(10);
alertDefineDTO.setEnable(true);
- alertDefineDTO.setRecoverNotice(true);
alertDefineDTO.setTemplate("template1");
exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
exportAlertDefineList.add(exportAlertDefineDTO);
diff --git
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
index 5705f7cb2..34824e621 100644
---
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
+++
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
@@ -58,9 +58,9 @@ class AlertDefineJsonImExportServiceTest {
private AlertDefineJsonImExportServiceImpl service;
@SuppressWarnings("checkstyle:OperatorWrap")
- private static final String JSON_DATA =
"[{\"alertDefine\":{\"app\":\"App1\",\"metric\":\"Metric1\"," +
-
"\"field\":\"Field1\",\"preset\":true,\"expr\":\"Expr1\",\"priority\":1,\"times\":1,\"tags\":[],"
+
-
"\"enable\":true,\"recoverNotice\":true,\"template\":\"Template1\"}}]";
+ private static final String JSON_DATA =
"[{\"alertDefine\":{\"name\":\"App1\",\"type\":\"realtime\"," +
+ "\"expr\":\"Expr1\",\"period\":3000,\"times\":3," +
+ "\"enable\":true,\"template\":\"Template1\"}}]";
private InputStream inputStream;
private List<ExportAlertDefineDTO> alertDefineList;
@@ -71,15 +71,12 @@ class AlertDefineJsonImExportServiceTest {
inputStream = new ByteArrayInputStream(JSON_DATA.getBytes());
AlertDefineDTO alertDefine = new AlertDefineDTO();
- alertDefine.setApp("App1");
- alertDefine.setMetric("Metric1");
- alertDefine.setField("Field1");
- alertDefine.setPreset(true);
+ alertDefine.setName("App1");
+ alertDefine.setType("realtime");
alertDefine.setExpr("Expr1");
- alertDefine.setPriority((byte) 1);
- alertDefine.setTimes(1);
+ alertDefine.setPeriod(3000);
+ alertDefine.setTimes(3);
alertDefine.setEnable(true);
- alertDefine.setRecoverNotice(true);
alertDefine.setTemplate("Template1");
ExportAlertDefineDTO exportAlertDefine = new ExportAlertDefineDTO();
diff --git
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
index 0a83b3925..3b633965f 100644
---
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
+++
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
@@ -59,16 +59,12 @@ class AlertDefineYamlImExportServiceTest {
private static final String YAML_DATA =
"""
- alertDefine:
- app: App1
- metric: Metric1
- field: Field1
- preset: true
+ name: App1
+ type: realtime
expr: Expr1
- priority: 1
- times: 1
- tags: []
+ period: 3000
+ times: 3
enable: true
- recoverNotice: true
template: Template1
""";
@@ -81,15 +77,12 @@ class AlertDefineYamlImExportServiceTest {
inputStream = new
ByteArrayInputStream(YAML_DATA.getBytes(StandardCharsets.UTF_8));
AlertDefineDTO alertDefine = new AlertDefineDTO();
- alertDefine.setApp("App1");
- alertDefine.setMetric("Metric1");
- alertDefine.setField("Field1");
- alertDefine.setPreset(true);
+ alertDefine.setName("App1");
+ alertDefine.setType("realtime");
+ alertDefine.setPeriod(3000);
+ alertDefine.setTimes(3);
alertDefine.setExpr("Expr1");
- alertDefine.setPriority((byte) 1);
- alertDefine.setTimes(1);
alertDefine.setEnable(true);
- alertDefine.setRecoverNotice(true);
alertDefine.setTemplate("Template1");
ExportAlertDefineDTO exportAlertDefine = new ExportAlertDefineDTO();
@@ -141,8 +134,9 @@ class AlertDefineYamlImExportServiceTest {
service.writeOs(alertDefineList, outputStream);
String yamlOutput = outputStream.toString(StandardCharsets.UTF_8);
- assertTrue(yamlOutput.contains("app: App1"));
- assertTrue(yamlOutput.contains("metric: Metric1"));
+ assertTrue(yamlOutput.contains("name: App1"));
+ assertTrue(yamlOutput.contains("type: realtime"));
+ assertTrue(yamlOutput.contains("expr: Expr1"));
}
@Test
diff --git
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
index 14a41eb3e..73e926c8a 100644
---
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
+++
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
@@ -116,7 +116,7 @@ public class MonitorsController {
@Operation(summary = "export monitor config", description = "export
monitor config")
public void export(
@Parameter(description = "Monitor ID List", example =
"6565463543") @RequestParam List<Long> ids,
- @Parameter(description = "Export Type:JSON,EXCEL,YAML")
@RequestParam(defaultValue = "JSON") String type,
+ @Parameter(description = "Export Type:JSON,EXCEL")
@RequestParam(defaultValue = "JSON") String type,
HttpServletResponse res) throws Exception {
monitorService.export(ids, type, res);
}
diff --git
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
index bdfd6e8fc..c4d8c76aa 100644
---
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
+++
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
@@ -108,7 +108,6 @@ public abstract class AbstractImExportServiceImpl
implements ImExportService {
return param;
})
.toList());
- exportMonitor.setMetrics(dto.getMetrics());
exportMonitor.getMonitor().setCollector(dto.getCollector());
return exportMonitor;
}
@@ -129,7 +128,6 @@ public abstract class AbstractImExportServiceImpl
implements ImExportService {
if (exportMonitor.getMonitor() != null) {
monitorDto.setCollector(exportMonitor.getMonitor().getCollector());
}
- monitorDto.setMetrics(exportMonitor.metrics);
if (exportMonitor.params != null) {
monitorDto.setParams(exportMonitor.params.stream()
.map(it -> {
@@ -150,6 +148,9 @@ public abstract class AbstractImExportServiceImpl
implements ImExportService {
return "hertzbeat_monitor_" + LocalDate.now();
}
+ /**
+ * Export Monitor DTO
+ */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -159,10 +160,11 @@ public abstract class AbstractImExportServiceImpl
implements ImExportService {
private MonitorDTO monitor;
@ExcelCollection(name = "Params")
private List<ParamDTO> params;
- @ExcelCollection(name = "Metrics")
- private List<String> metrics;
}
+ /**
+ * Monitor DTO
+ */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -180,12 +182,15 @@ public abstract class AbstractImExportServiceImpl
implements ImExportService {
private Byte status;
@Excel(name = "Description")
private String description;
- @Excel(name = "labels")
+ @Excel(name = "Labels")
private Map<String, String> labels;
@Excel(name = "Collector")
private String collector;
}
+ /**
+ * Param DTO
+ */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
diff --git
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
index 46c8d66b0..e2dbb3189 100644
---
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
+++
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
@@ -24,21 +24,20 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.util.JsonUtil;
-import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -97,16 +96,9 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
ExportMonitorDTO exportMonitor = new ExportMonitorDTO();
exportMonitor.setMonitor(monitor);
monitors.add(exportMonitor);
- String metrics = getCellValueAsString(row.getCell(11));
- if (StringUtils.isNotBlank(metrics)) {
- List<String> metricList =
Arrays.stream(metrics.split(",")).collect(Collectors.toList());
- exportMonitor.setMetrics(metricList);
- }
}
}
-
List<List<ParamDTO>> paramsList = new ArrayList<>();
-
for (int i = 0; i < startRowList.size(); i++) {
int startRowIndex = startRowList.get(i);
int endRowIndex = (i + 1 < startRowList.size()) ?
startRowList.get(i + 1) : sheet.getLastRowNum() + 1;
@@ -122,7 +114,6 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
params.add(param);
}
}
-
paramsList.add(params);
}
for (int i = 0; i < monitors.size(); i++) {
@@ -153,8 +144,6 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
} catch (Exception ignored) {}
}
monitor.setCollector(getCellValueAsString(row.getCell(7)));
-
-
return monitor;
}
@@ -222,9 +211,27 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
Workbook workbook = WorkbookFactory.create(true);
String sheetName = "Export Monitor";
- Sheet sheet = ExcelExportUtils.setSheet(sheetName, workbook,
ExportMonitorDTO.class);
+ Sheet sheet = workbook.createSheet(sheetName);
+ sheet.setDefaultColumnWidth(20);
+ sheet.setColumnWidth(6, 40 * 256);
+ sheet.setColumnWidth(10, 40 * 256);
+ // set header style
+ CellStyle headerCellStyle = workbook.createCellStyle();
+ Font headerFont = workbook.createFont();
+ headerFont.setBold(true);
+ headerCellStyle.setFont(headerFont);
+ headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
// set cell style
- CellStyle cellStyle = ExcelExportUtils.setCellStyle(workbook);
+ CellStyle cellStyle = workbook.createCellStyle();
+ cellStyle.setAlignment(HorizontalAlignment.CENTER);
+ // set header
+ String[] headers = { "Name", "App", "Host", "Intervals", "Status",
"Description", "Labels", "Collector", "Param-Field", "Param-Type",
"Param-Value" };
+ Row headerRow = sheet.createRow(0);
+ for (int i = 0; i < headers.length; i++) {
+ Cell cell = headerRow.createCell(i);
+ cell.setCellValue(headers[i]);
+ cell.setCellStyle(headerCellStyle);
+ }
// foreach monitor, each monitor object corresponds to a row of
data
int rowIndex = 1;
@@ -233,8 +240,6 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
MonitorDTO monitorDTO = monitor.getMonitor();
// get monitor parameters
List<ParamDTO> paramList = monitor.getParams();
- // get monitor metrics
- List<String> metricList = monitor.getMetrics();
// merge monitor information and parameter information into
one row
for (int i = 0; i < Math.max(paramList.size(), 1); i++) {
Row row = sheet.createRow(rowIndex++);
@@ -258,17 +263,12 @@ public class ExcelImExportServiceImpl extends
AbstractImExportServiceImpl{
Cell descriptionCell = row.createCell(5);
descriptionCell.setCellValue(monitorDTO.getDescription());
descriptionCell.setCellStyle(cellStyle);
- Cell tagsCell = row.createCell(6);
-
tagsCell.setCellValue(JsonUtil.toJson(monitorDTO.getLabels()));
- tagsCell.setCellStyle(cellStyle);
+ Cell labelsCell = row.createCell(6);
+
labelsCell.setCellValue(JsonUtil.toJson(monitorDTO.getLabels()));
+ labelsCell.setCellStyle(cellStyle);
Cell collectorCell = row.createCell(7);
collectorCell.setCellValue(monitorDTO.getCollector());
collectorCell.setCellStyle(cellStyle);
- if (metricList != null && i < metricList.size()) {
- Cell metricCell = row.createCell(11);
- metricCell.setCellValue(String.join(",",
metricList));
- metricCell.setCellStyle(cellStyle);
- }
}
// Fill in parameter information
if (i < paramList.size()) {
diff --git
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
index 4c39a86d5..fd38159e0 100644
---
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
+++
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
@@ -579,8 +579,10 @@ public class MonitorServiceImpl implements MonitorService {
if (StringUtils.isNotBlank(search)) {
Predicate predicateHost =
criteriaBuilder.like(root.get("host"), "%" + search + "%");
Predicate predicateName =
criteriaBuilder.like(root.get("name"), "%" + search + "%");
+ Predicate predicateId = criteriaBuilder.like(root.get("id"),
"%" + search + "%");
orList.add(predicateHost);
orList.add(predicateName);
+ orList.add(predicateId);
}
if (StringUtils.isNotBlank(labels)) {
String[] labelAres = labels.split(",");
@@ -861,7 +863,6 @@ public class MonitorServiceImpl implements MonitorService {
}
monitor.setId(monitorId);
monitor.setJobId(jobId);
- monitor.setStatus(CommonConstants.MONITOR_UP_CODE);
// create grafana dashboard
if (monitor.getApp().equals(CommonConstants.PROMETHEUS) &&
grafanaDashboard != null && grafanaDashboard.isEnabled()) {
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(),
monitorId);
diff --git a/hertzbeat-manager/src/main/resources/application.yml
b/hertzbeat-manager/src/main/resources/application.yml
index 4f3ea8821..41f30a7f2 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -200,6 +200,9 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
scheduler:
server:
diff --git
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
index bd18d9ff3..341c99fff 100644
---
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
+++
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
@@ -18,6 +18,7 @@
package org.apache.hertzbeat.manager.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -96,11 +97,9 @@ class YamlImExportServiceTest {
AbstractImExportServiceImpl.ExportMonitorDTO exportMonitorDto1 = new
AbstractImExportServiceImpl.ExportMonitorDTO();
exportMonitorDto1.setParams(List.of(paramDTO));
exportMonitorDto1.setMonitor(monitorDTO);
- exportMonitorDto1.setMetrics(List.of("Test1", "Test2"));
AbstractImExportServiceImpl.ExportMonitorDTO exportMonitorDto2 = new
AbstractImExportServiceImpl.ExportMonitorDTO();
exportMonitorDto2.setParams(List.of(paramDTO));
exportMonitorDto2.setMonitor(monitorDTO);
- exportMonitorDto2.setMetrics(List.of("Test1", "Test2"));
List<AbstractImExportServiceImpl.ExportMonitorDTO> monitorList =
Arrays.asList(
exportMonitorDto1,
@@ -111,7 +110,7 @@ class YamlImExportServiceTest {
yamlImExportService.writeOs(monitorList, os);
String output = os.toString();
- assertTrue(output.contains("metrics:\n - Test1"));
+ assertFalse(output.contains("metrics:\n - Test1"));
assertTrue(output.contains(" params:\n - &id002\n field: Test"));
}
diff --git
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
index c89ea4ca5..c895ff2a7 100644
---
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
+++
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
@@ -17,10 +17,7 @@
package org.apache.hertzbeat.warehouse.store;
-import java.util.List;
import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentSkipListSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.message.CollectRep;
@@ -30,6 +27,7 @@ import org.apache.hertzbeat.plugin.runner.PluginRunner;
import org.apache.hertzbeat.warehouse.WarehouseWorkerPool;
import org.apache.hertzbeat.warehouse.store.history.HistoryDataWriter;
import org.apache.hertzbeat.warehouse.store.realtime.RealTimeDataWriter;
+import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@@ -46,7 +44,6 @@ public class DataStorageDispatch {
private final RealTimeDataWriter realTimeDataWriter;
private final Optional<HistoryDataWriter> historyDataWriter;
private final PluginRunner pluginRunner;
- private final Set<Long> monitorDownStatusCache;
public DataStorageDispatch(CommonDataQueue commonDataQueue,
WarehouseWorkerPool workerPool,
@@ -60,8 +57,6 @@ public class DataStorageDispatch {
this.realTimeDataWriter = realTimeDataWriter;
this.historyDataWriter = historyDataWriter;
this.pluginRunner = pluginRunner;
- this.monitorDownStatusCache = new ConcurrentSkipListSet<>();
- initMonitorDownStatusCache();
startPersistentDataStorage();
}
@@ -87,25 +82,26 @@ public class DataStorageDispatch {
};
workerPool.executeJob(runnable);
}
-
- private void initMonitorDownStatusCache() {
- String sql = "SELECT id FROM hzb_monitor WHERE status = 2";
- List<Long> ids = jdbcTemplate.query(sql, (rs, rowNum) ->
rs.getLong("id"));
- monitorDownStatusCache.addAll(ids);
- }
protected void calculateMonitorStatus(CollectRep.MetricsData metricsData) {
if (metricsData.getPriority() == 0) {
long id = metricsData.getId();
CollectRep.Code code = metricsData.getCode();
- if (code == CollectRep.Code.SUCCESS &&
monitorDownStatusCache.contains(id)) {
- monitorDownStatusCache.remove(id);
- String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
- jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, id);
- } else if (code != CollectRep.Code.SUCCESS &&
!monitorDownStatusCache.contains(id)) {
- monitorDownStatusCache.add(id);
- String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
- jdbcTemplate.update(sql, CommonConstants.MONITOR_DOWN_CODE,
id);
+ // query current status
+ String queryStatusSql = "SELECT status FROM hzb_monitor WHERE id =
?";
+ try {
+ int currentStatus =
jdbcTemplate.queryForObject(queryStatusSql, Integer.class, id);
+ if (code == CollectRep.Code.SUCCESS && currentStatus ==
CommonConstants.MONITOR_DOWN_CODE) {
+ // if collect success and current status is DOWN, update
to UP
+ String sql = "UPDATE hzb_monitor SET status = ? WHERE id =
?";
+ jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE,
id);
+ } else if (code != CollectRep.Code.SUCCESS && currentStatus ==
CommonConstants.MONITOR_UP_CODE) {
+ // if collect failed and current status is UP, update to
DOWN
+ String sql = "UPDATE hzb_monitor SET status = ? WHERE id =
?";
+ jdbcTemplate.update(sql,
CommonConstants.MONITOR_DOWN_CODE, id);
+ }
+ } catch (EmptyResultDataAccessException ignored) {
+ // when query currentStatus result is null
}
}
}
diff --git a/home/src/pages/team/index.jsx b/home/src/pages/team/index.jsx
index 7a4d9d100..1a1a363e9 100644
--- a/home/src/pages/team/index.jsx
+++ b/home/src/pages/team/index.jsx
@@ -1192,6 +1192,30 @@ export default function () {
alt="yunfan24"/><br/><sub><b>yunfan24</b></sub></a><br/><a
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"
title="Code">💻</a></td>
</tr>
+ <tr>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/lctking"><img
+
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100" width="100px;"
+ alt="nullwli"/><br/><sub><b>nullwli</b></sub></a><br/><a
+
href="https://github.com/apache/hertzbeat/commits?author=lctking"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://simonsigre.com/"><img
+
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;"
+ alt="Simon Sigré"/><br/><sub><b>Simon
Sigré</b></sub></a><br/><a
+
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"
title="Documentation">📖</a>
+ </td>
+ <td align="center" valign="top" width="14.28%"><a
href="http://www.ponfee.cn/"><img
+
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;"
+ alt="ponfee"/><br/><sub><b>ponfee</b></sub></a><br/><a
+
href="https://github.com/apache/hertzbeat/commits?author=ponfee"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Vedant7789"><img
+
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100" width="100px;"
+
alt="Vedant7789"/><br/><sub><b>Vedant7789</b></sub></a><br/><a
+
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"
title="Code">💻</a></td>
+ <td align="center" valign="top" width="14.28%"><a
href="https://github.com/Craaaaazy77"><img
+
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;"
+
alt="Craaaaazy77"/><br/><sub><b>Craaaaazy77</b></sub></a><br/><a
+
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"
title="Documentation">📖</a>
+ </td>
+ </tr>
</tbody>
</table>
diff --git a/script/application.yml b/script/application.yml
index 26d1ff743..54ade2214 100644
--- a/script/application.yml
+++ b/script/application.yml
@@ -199,7 +199,10 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
+
scheduler:
server:
enabled: true
diff --git a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
index 7f27eac21..ca4357a4b 100644
--- a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
@@ -166,7 +166,10 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
+
scheduler:
server:
enabled: true
diff --git
a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
index 8beb7f698..a9deab46e 100644
--- a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
@@ -162,7 +162,10 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
+
scheduler:
server:
enabled: true
diff --git
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
index f006f39e0..53aae6d04 100644
---
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
+++
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
@@ -160,7 +160,10 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
+
scheduler:
server:
enabled: true
diff --git
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
index a3a9180c7..97cbfa053 100644
---
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
+++
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
@@ -159,7 +159,10 @@ alerter:
server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
# gotify
gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+ # alert inhibit ttl unit ms, default 14400000(4 hours)
+ inhibit:
+ ttl: 14400000
+
scheduler:
server:
enabled: true
diff --git
a/web-app/src/app/routes/alert/alert-center/alert-center.component.html
b/web-app/src/app/routes/alert/alert-center/alert-center.component.html
index 83ffe1650..a5abf297b 100644
--- a/web-app/src/app/routes/alert/alert-center/alert-center.component.html
+++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.html
@@ -111,6 +111,7 @@
[nzHeader]="alertHeader"
[nzExtra]="alertExtra"
[nzActive]="false"
+ [style]="'border-left-color:' + (item.status == 'firing' ? '#ff4d4f'
: '#52c41a')"
[nzExpandedIcon]="expandedIcon"
>
<ng-template #alertHeader>
@@ -136,6 +137,16 @@
</span>
</div>
+ <!-- Status -->
+ <div class="detail-section">
+ <div class="section-title">{{ 'alert.center.status' | i18n }}</div>
+ <div class="alert-status">
+ <nz-tag [nzColor]="item.status === 'firing' ? 'error' :
'success'">
+ {{ item.status === 'firing' ? ('alert.status.firing' | i18n) :
('alert.status.resolved' | i18n) }}
+ </nz-tag>
+ </div>
+ </div>
+
<!-- Labels -->
<div class="detail-section" *ngIf="item.labels">
<div class="section-title">{{ 'alert.center.labels' | i18n }}</div>
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
index dad7fa8f4..d07b75ce2 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
@@ -609,28 +609,30 @@
[(nzVisible)]="isSwitchExportTypeModalVisible"
[nzTitle]="'alert.export.switch-type' | i18n"
(nzOnCancel)="onExportTypeModalCancel()"
- nzOkDisabled="true"
- [nzFooter]="switchExportTypeModalFooter"
+ nzWidth="600px"
+ [nzFooter]="null"
>
<ng-container *nzModalContent>
- <p style="text-align: center">
- <button nz-button nzType="primary" nzSize="large"
(click)="exportDefines('YAML')" [nzLoading]="exportYamlButtonLoading">
- <span nz-icon nzType="download"></span>
- {{ 'alert.export.use-type' | i18n : { type: 'YAML' } }}
- </button>
- </p>
- <p style="text-align: center">
- <button nz-button nzType="primary" nzSize="large"
(click)="exportDefines('JSON')" [nzLoading]="exportJsonButtonLoading">
- <span nz-icon nzType="download"></span>
- {{ 'alert.export.use-type' | i18n : { type: 'JSON' } }}
- </button>
- </p>
- <p style="text-align: center">
- <button nz-button nzType="primary" nzSize="large"
(click)="exportDefines('EXCEL')" [nzLoading]="exportExcelButtonLoading">
- <span nz-icon nzType="download"></span>
- {{ 'alert.export.use-type' | i18n : { type: 'EXCEL' } }}
- </button>
- </p>
+ <div class="export-type-container">
+ <div class="export-type-card" (click)="exportDefines('JSON')"
[class.loading]="exportJsonButtonLoading">
+ <div class="export-type-icon">
+ <i nz-icon nzType="code" nzTheme="outline"></i>
+ </div>
+ <div class="export-type-info">
+ <h3>JSON</h3>
+ <p>{{ 'alert.export.use-type' | i18n : { type: 'JSON' } }}</p>
+ </div>
+ </div>
+ <div class="export-type-card" (click)="exportDefines('EXCEL')"
[class.loading]="exportExcelButtonLoading">
+ <div class="export-type-icon">
+ <i nz-icon nzType="file-excel" nzTheme="outline"></i>
+ </div>
+ <div class="export-type-info">
+ <h3>EXCEL</h3>
+ <p>{{ 'alert.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
+ </div>
+ </div>
+ </div>
</ng-container>
</nz-modal>
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
index aaba18e78..13d33ea01 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
@@ -1,3 +1,4 @@
+@import '@delon/theme/index';
::ng-deep {
.row {
@@ -180,4 +181,61 @@
border-left: none;
border-radius: 0 2px 2px 0;
}
+
+ .export-type-container {
+ display: flex;
+ justify-content: space-between;
+ gap: 20px;
+ padding: 20px 0;
+ }
+
+ .export-type-card {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ padding: 24px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ }
+
+ .export-type-card:hover {
+ border-color: @primary-color;
+ box-shadow: 0 0 8px rgba(24, 144, 255, 0.2);
+ }
+
+ .export-type-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ margin-right: 16px;
+ font-size: 24px;
+ color: @primary-color;
+ background: rgba(24, 144, 255, 0.1);
+ border-radius: 8px;
+ }
+
+ .export-type-info {
+ flex: 1;
+ }
+
+ .export-type-info h3 {
+ margin: 0 0 8px;
+ font-size: 16px;
+ font-weight: 500;
+ }
+
+ .export-type-info p {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+ }
+
+ .export-type-card.loading {
+ opacity: 0.7;
+ cursor: not-allowed;
+ }
}
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
index 141ce190f..ceb0c75e9 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
@@ -28,7 +28,7 @@ import { NzNotificationService } from
'ng-zorro-antd/notification';
import { NzTableQueryParams } from 'ng-zorro-antd/table';
import { TransferChange, TransferItem } from 'ng-zorro-antd/transfer';
import { NzUploadChangeParam } from 'ng-zorro-antd/upload';
-import { finalize, map } from 'rxjs/operators';
+import { finalize } from 'rxjs/operators';
import { AlertDefine } from '../../../pojo/AlertDefine';
import { AlertDefineService } from '../../../service/alert-define.service';
@@ -68,7 +68,6 @@ export class AlertSettingComponent implements OnInit {
checkedDefineIds = new Set<number>();
isSwitchExportTypeModalVisible = false;
exportJsonButtonLoading = false;
- exportYamlButtonLoading = false;
exportExcelButtonLoading = false;
appHierarchies!: any[];
switchExportTypeModalFooter: ModalButtonOptions[] = [
@@ -375,15 +374,11 @@ export class AlertSettingComponent implements OnInit {
case 'EXCEL':
this.exportExcelButtonLoading = true;
break;
- case 'YAML':
- this.exportYamlButtonLoading = true;
- break;
}
const exportDefines$ = this.alertDefineSvc
.exportAlertDefines(this.checkedDefineIds, type)
.pipe(
finalize(() => {
- this.exportYamlButtonLoading = false;
this.exportExcelButtonLoading = false;
this.exportJsonButtonLoading = false;
exportDefines$.unsubscribe();
diff --git a/web-app/src/app/routes/dashboard/dashboard.component.html
b/web-app/src/app/routes/dashboard/dashboard.component.html
index 4bc50fbed..06dfcc269 100644
--- a/web-app/src/app/routes/dashboard/dashboard.component.html
+++ b/web-app/src/app/routes/dashboard/dashboard.component.html
@@ -398,19 +398,21 @@
<nz-timeline nzMode="left">
<nz-timeline-item *ngFor="let alert of alerts; let i = index"
[nzLabel]="(alert.activeAt | date : 'YYYY-MM-dd HH:mm:ss')?.trim()">
<p style="font-weight: 400">
- <!-- <nz-tag *ngIf="alert.severity == 0"
nzColor="red">-->
- <!-- <i nz-icon nzType="bell"
nzTheme="outline"></i>-->
- <!-- <span>{{ 'alert.severity.0' | i18n
}}</span>-->
- <!-- </nz-tag>-->
- <!-- <nz-tag *ngIf="alert.severity == 1"
nzColor="orange">-->
- <!-- <i nz-icon nzType="bell"
nzTheme="outline"></i>-->
- <!-- <span>{{ 'alert.severity.1' | i18n
}}</span>-->
- <!-- </nz-tag>-->
- <!-- <nz-tag *ngIf="alert.severity == 2"
nzColor="yellow">-->
- <!-- <i nz-icon nzType="bell"
nzTheme="outline"></i>-->
- <!-- <span>{{ 'alert.severity.2' | i18n
}}</span>-->
- <!-- </nz-tag>-->
- <!-- <span>[{{ alert.tags.monitorName }}] </span>-->
+ <nz-tag *ngIf="alert.labels.severity == 'emergency'"
nzColor="red">
+ <i nz-icon nzType="bell" nzTheme="outline"></i>
+ <span>{{ 'alert.severity.0' | i18n }}</span>
+ </nz-tag>
+ <nz-tag *ngIf="alert.labels.severity == 'critical'"
nzColor="orange">
+ <i nz-icon nzType="bell" nzTheme="outline"></i>
+ <span>{{ 'alert.severity.1' | i18n }}</span>
+ </nz-tag>
+ <nz-tag *ngIf="alert.labels.severity == 'warning'"
nzColor="yellow">
+ <i nz-icon nzType="bell" nzTheme="outline"></i>
+ <span>{{ 'alert.severity.2' | i18n }}</span>
+ </nz-tag>
+ <nz-tag *ngIf="alert.labels.alertname">
+ <span>{{ alert.labels.alertname }}</span>
+ </nz-tag>
{{ alert.content }}
</p>
</nz-timeline-item>
diff --git
a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
index 5eae28f83..0283c6e90 100644
--- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
@@ -236,8 +236,6 @@ export class MonitorEditComponent implements OnInit {
}
onCancel() {
- let app = this.monitor.app;
- app = app ? app : '';
- this.router.navigateByUrl(`/monitors?app=${app}`);
+ this.router.navigateByUrl(`/monitors`);
}
}
diff --git
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
index 348919ba8..65cb0e02b 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
@@ -273,28 +273,31 @@
<nz-modal
[(nzVisible)]="isSwitchExportTypeModalVisible"
[nzTitle]="'monitors.export.switch-type' | i18n"
- nzOkDisabled="true"
- [nzFooter]="switchExportTypeModalFooter"
+ (nzOnCancel)="isSwitchExportTypeModalVisible = false"
+ nzWidth="600px"
+ [nzFooter]="null"
>
<ng-container *nzModalContent>
- <!-- <p style="text-align: center">-->
- <!-- <button nz-button nzType="primary" nzSize="large"
(click)="exportMonitors('YAML')" [nzLoading]="exportYamlButtonLoading">-->
- <!-- <span nz-icon nzType="download"></span>-->
- <!-- {{ 'monitors.export.use-type' | i18n : { type: 'YAML' } }}-->
- <!-- </button>-->
- <!-- </p>-->
- <p style="text-align: center">
- <button nz-button nzType="primary" nzSize="large"
(click)="exportMonitors('JSON')" [nzLoading]="exportJsonButtonLoading">
- <span nz-icon nzType="download"></span>
- {{ 'monitors.export.use-type' | i18n : { type: 'JSON' } }}
- </button>
- </p>
- <p style="text-align: center">
- <button nz-button nzType="primary" nzSize="large"
(click)="exportMonitors('EXCEL')" [nzLoading]="exportExcelButtonLoading">
- <span nz-icon nzType="download"></span>
- {{ 'monitors.export.use-type' | i18n : { type: 'EXCEL' } }}
- </button>
- </p>
+ <div class="export-type-container">
+ <div class="export-type-card" (click)="exportMonitors('JSON')"
[class.loading]="exportJsonButtonLoading">
+ <div class="export-type-icon">
+ <i nz-icon nzType="code" nzTheme="outline"></i>
+ </div>
+ <div class="export-type-info">
+ <h3>JSON</h3>
+ <p>{{ 'monitors.export.use-type' | i18n : { type: 'JSON' } }}</p>
+ </div>
+ </div>
+ <div class="export-type-card" (click)="exportMonitors('EXCEL')"
[class.loading]="exportExcelButtonLoading">
+ <div class="export-type-icon">
+ <i nz-icon nzType="file-excel" nzTheme="outline"></i>
+ </div>
+ <div class="export-type-info">
+ <h3>EXCEL</h3>
+ <p>{{ 'monitors.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
+ </div>
+ </div>
+ </div>
</ng-container>
</nz-modal>
diff --git
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
index 667151641..2f692a257 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
@@ -1,3 +1,4 @@
+@import '@delon/theme/index';
::ng-deep {
.monitor-select-menu-modal {
.ant-spin-container {
@@ -6,3 +7,60 @@
}
}
}
+
+.export-type-container {
+ display: flex;
+ justify-content: space-between;
+ gap: 20px;
+ padding: 20px 0;
+}
+
+.export-type-card {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ padding: 24px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.export-type-card:hover {
+ border-color: @primary-color;
+ box-shadow: 0 0 8px rgba(24, 144, 255, 0.2);
+}
+
+.export-type-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ margin-right: 16px;
+ font-size: 24px;
+ color: @primary-color;
+ background: rgba(24, 144, 255, 0.1);
+ border-radius: 8px;
+}
+
+.export-type-info {
+ flex: 1;
+}
+
+.export-type-info h3 {
+ margin: 0 0 8px;
+ font-size: 16px;
+ font-weight: 500;
+}
+
+.export-type-info p {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+}
+
+.export-type-card.loading {
+ opacity: 0.7;
+ cursor: not-allowed;
+}
diff --git
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
index 91f61f63d..2ba5a76eb 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
@@ -63,7 +63,6 @@ export class MonitorListComponent implements OnInit,
OnDestroy {
checkedMonitorIds = new Set<number>();
isSwitchExportTypeModalVisible = false;
exportJsonButtonLoading = false;
- exportYamlButtonLoading = false;
exportExcelButtonLoading = false;
filterContent!: string;
filterStatus: number = 9;
@@ -333,15 +332,11 @@ export class MonitorListComponent implements OnInit,
OnDestroy {
case 'EXCEL':
this.exportExcelButtonLoading = true;
break;
- case 'YAML':
- this.exportYamlButtonLoading = true;
- break;
}
const exportMonitors$ = this.monitorSvc
.exportMonitors(this.checkedMonitorIds, type)
.pipe(
finalize(() => {
- this.exportYamlButtonLoading = false;
this.exportExcelButtonLoading = false;
this.exportJsonButtonLoading = false;
exportMonitors$.unsubscribe();
diff --git
a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
index e626afa4d..930f1b684 100644
--- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
@@ -209,8 +209,6 @@ export class MonitorNewComponent implements OnInit {
}
onCancel() {
- let app = this.monitor.app;
- app = app ? app : '';
- this.router.navigateByUrl(`/monitors?app=${app}`);
+ this.router.navigateByUrl(`/monitors`);
}
}
diff --git a/web-app/src/app/service/alert.service.ts
b/web-app/src/app/service/alert.service.ts
index d3b008bf4..67dff8238 100644
--- a/web-app/src/app/service/alert.service.ts
+++ b/web-app/src/app/service/alert.service.ts
@@ -48,7 +48,7 @@ export class AlertService {
// HttpParams is unmodifiable, so we need to save the return value of
append/set
let httpParams = new HttpParams();
httpParams = httpParams.appendAll({
- sort: 'id',
+ sort: 'gmtUpdate',
order: 'desc',
pageIndex: pageIndex,
pageSize: pageSize
@@ -74,7 +74,7 @@ export class AlertService {
// HttpParams is unmodifiable, so we need to save the return value of
append/set
let httpParams = new HttpParams();
httpParams = httpParams.appendAll({
- sort: 'id',
+ sort: 'gmtUpdate',
order: 'desc',
pageIndex: pageIndex,
pageSize: pageSize
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]