diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/CalculateAlarm.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/CalculateAlarm.java index fe704c82351..2fdd33986a3 100644 --- a/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/CalculateAlarm.java +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/CalculateAlarm.java @@ -22,8 +22,8 @@ import com.googlecode.aviator.exception.CompileExpressionErrorException; import com.googlecode.aviator.exception.ExpressionRuntimeException; import com.googlecode.aviator.exception.ExpressionSyntaxErrorException; -import org.dromara.hertzbeat.alert.AlerterProperties; import org.dromara.hertzbeat.alert.AlerterWorkerPool; +import org.dromara.hertzbeat.alert.reduce.AlarmCommonReduce; import org.dromara.hertzbeat.common.queue.CommonDataQueue; import org.dromara.hertzbeat.alert.dao.AlertMonitorDao; import org.dromara.hertzbeat.common.entity.alerter.Alert; @@ -59,24 +59,20 @@ public class CalculateAlarm { * key - monitorId 为监控状态可用性可达性告警 | Indicates the monitoring status availability reachability alarm */ public Map triggeredAlertMap; - public Set unAvailableMonitors; - private final AlerterWorkerPool workerPool; private final CommonDataQueue dataQueue; private final AlertDefineService alertDefineService; - private final AlerterProperties alerterProperties; - private final SilenceAlarm silenceAlarm; + private final AlarmCommonReduce alarmCommonReduce; private final ResourceBundle bundle; - public CalculateAlarm (AlerterWorkerPool workerPool, CommonDataQueue dataQueue, SilenceAlarm silenceAlarm, + public CalculateAlarm (AlerterWorkerPool workerPool, CommonDataQueue dataQueue, AlertDefineService alertDefineService, AlertMonitorDao monitorDao, - AlerterProperties alerterProperties) { + AlarmCommonReduce alarmCommonReduce) { this.workerPool = workerPool; this.dataQueue = dataQueue; - this.silenceAlarm = silenceAlarm; + this.alarmCommonReduce = alarmCommonReduce; this.alertDefineService = alertDefineService; - this.alerterProperties = alerterProperties; this.bundle = ResourceBundleUtil.getBundle("alerter"); this.triggeredAlertMap = new ConcurrentHashMap<>(128); this.unAvailableMonitors = Collections.synchronizedSet(new HashSet<>(16)); @@ -172,13 +168,14 @@ private void calculate(CollectRep.MetricsData metricsData) { String monitorAlertKey = String.valueOf(monitorId) + define.getId(); Alert triggeredAlert = triggeredAlertMap.get(monitorAlertKey); if (triggeredAlert != null) { - int times = triggeredAlert.getTimes() + 1; - triggeredAlert.setTimes(times); - triggeredAlert.setLastTriggerTime(currentTimeMilli); + int times = triggeredAlert.getTriggerTimes() + 1; + triggeredAlert.setTriggerTimes(times); + triggeredAlert.setFirstAlarmTime(currentTimeMilli); + triggeredAlert.setLastAlarmTime(currentTimeMilli); int defineTimes = define.getTimes() == null ? 1 : define.getTimes(); if (times >= defineTimes) { triggeredAlertMap.remove(monitorAlertKey); - silenceAlarm.filterSilenceAndSendData(triggeredAlert); + alarmCommonReduce.reduceAndSendAlarm(triggeredAlert); } } else { fieldValueMap.put("app", app); @@ -193,16 +190,16 @@ private void calculate(CollectRep.MetricsData metricsData) { .priority(define.getPriority()) .status(CommonConstants.ALERT_STATUS_CODE_PENDING) .target(app + "." + metrics + "." + define.getField()) - .times(1) - .firstTriggerTime(currentTimeMilli) - .lastTriggerTime(currentTimeMilli) + .triggerTimes(1) + .firstAlarmTime(currentTimeMilli) + .lastAlarmTime(currentTimeMilli) // Keyword matching and substitution in the template // 模板中关键字匹配替换 .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) .build(); int defineTimes = define.getTimes() == null ? 1 : define.getTimes(); if (1 >= defineTimes) { - silenceAlarm.filterSilenceAndSendData(alert); + alarmCommonReduce.reduceAndSendAlarm(alert); } else { triggeredAlertMap.put(monitorAlertKey, alert); } @@ -261,11 +258,11 @@ private void handlerAvailableMetrics(long monitorId, String app, String metrics, .content(content) .priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING) .status(CommonConstants.ALERT_STATUS_CODE_RESTORED) - .firstTriggerTime(currentTimeMilli) - .lastTriggerTime(currentTimeMilli) - .times(1) + .firstAlarmTime(currentTimeMilli) + .lastAlarmTime(currentTimeMilli) + .triggerTimes(1) .build(); - silenceAlarm.filterSilenceAndSendData(resumeAlert); + alarmCommonReduce.reduceAndSendAlarm(resumeAlert); } } } @@ -291,30 +288,30 @@ private void handlerMonitorAvailableAlert(long monitorId, String app, CollectRep .status(CommonConstants.ALERT_STATUS_CODE_PENDING) .target(CommonConstants.AVAILABILITY) .content(AlertTemplateUtil.render(avaAlertDefine.getTemplate(), valueMap)) - .firstTriggerTime(currentTimeMill) - .lastTriggerTime(currentTimeMill) - .nextEvalInterval(alerterProperties.getAlertEvalIntervalBase()) - .times(1); + .firstAlarmTime(currentTimeMill) + .lastAlarmTime(currentTimeMill) + .triggerTimes(1); if (avaAlertDefine.getTimes() == null || avaAlertDefine.getTimes() <= 1) { - silenceAlarm.filterSilenceAndSendData(alertBuilder.build().clone()); + alarmCommonReduce.reduceAndSendAlarm(alertBuilder.build().clone()); unAvailableMonitors.add(monitorId); } else { alertBuilder.status(CommonConstants.ALERT_STATUS_CODE_NOT_REACH); } triggeredAlertMap.put(String.valueOf(monitorId), alertBuilder.build()); } else { - int times = preAlert.getTimes() + 1; + int times = preAlert.getTriggerTimes() + 1; if (preAlert.getStatus() == CommonConstants.ALERT_STATUS_CODE_PENDING) { times = 1; preAlert.setContent(AlertTemplateUtil.render(avaAlertDefine.getTemplate(), valueMap)); preAlert.setTags(tags); } - preAlert.setTimes(times); - preAlert.setLastTriggerTime(currentTimeMill); + preAlert.setTriggerTimes(times); + preAlert.setFirstAlarmTime(currentTimeMill); + preAlert.setLastAlarmTime(currentTimeMill); int defineTimes = avaAlertDefine.getTimes() == null ? 1 : avaAlertDefine.getTimes(); if (times >= defineTimes) { preAlert.setStatus(CommonConstants.ALERT_STATUS_CODE_PENDING); - silenceAlarm.filterSilenceAndSendData(preAlert); + alarmCommonReduce.reduceAndSendAlarm(preAlert); unAvailableMonitors.add(monitorId); } else { preAlert.setStatus(CommonConstants.ALERT_STATUS_CODE_NOT_REACH); diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergeController.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergeController.java new file mode 100644 index 00000000000..06485446c7b --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergeController.java @@ -0,0 +1,80 @@ +/* + * 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.dromara.hertzbeat.alert.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.dromara.hertzbeat.alert.service.AlertConvergeService; +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.dromara.hertzbeat.common.entity.dto.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +import static org.dromara.hertzbeat.common.constants.CommonConstants.MONITOR_NOT_EXIST_CODE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * Alarm Converge management API + * 告警收敛管理API + * @author tom + * + */ +@Tag(name = "Alert Converge API | 告警收敛管理API") +@RestController +@RequestMapping(path = "/api/alert/converge", produces = {APPLICATION_JSON_VALUE}) +public class AlertConvergeController { + + @Autowired + private AlertConvergeService alertConvergeService; + + @PostMapping + @Operation(summary = "New Alarm Converge | 新增告警收敛", description = "Added an alarm Converge | 新增一个告警收敛") + public ResponseEntity> addNewAlertConverge(@Valid @RequestBody AlertConverge alertConverge) { + alertConvergeService.validate(alertConverge, false); + alertConvergeService.addAlertConverge(alertConverge); + return ResponseEntity.ok(new Message<>("Add success")); + } + + @PutMapping + @Operation(summary = "Modifying an Alarm Converge | 修改告警收敛", description = "Modify an existing alarm Converge | 修改一个已存在告警收敛") + public ResponseEntity> modifyAlertConverge(@Valid @RequestBody AlertConverge alertConverge) { + alertConvergeService.validate(alertConverge, true); + alertConvergeService.modifyAlertConverge(alertConverge); + return ResponseEntity.ok(new Message<>("Modify success")); + } + + @GetMapping(path = "/{id}") + @Operation(summary = "Querying Alarm Converge | 查询告警收敛", + description = "You can obtain alarm Converge information based on the alarm Converge ID | 根据告警收敛ID获取告警收敛信息") + public ResponseEntity> getAlertConverge( + @Parameter(description = "Alarm Converge ID | 告警收敛ID", example = "6565463543") @PathVariable("id") long id) { + AlertConverge alertConverge = alertConvergeService.getAlertConverge(id); + Message.MessageBuilder messageBuilder = Message.builder(); + if (alertConverge == null) { + messageBuilder.code(MONITOR_NOT_EXIST_CODE).msg("AlertConverge not exist."); + } else { + messageBuilder.data(alertConverge); + } + return ResponseEntity.ok(messageBuilder.build()); + } + +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergesController.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergesController.java new file mode 100644 index 00000000000..cf60dd94788 --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/controller/AlertConvergesController.java @@ -0,0 +1,98 @@ +/* + * 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.dromara.hertzbeat.alert.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.dromara.hertzbeat.alert.service.AlertConvergeService; +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.dromara.hertzbeat.common.entity.dto.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * Converge the batch API for alarms + * 收敛告警批量API + * @author tom + * + */ +@Tag(name = "Alert Converge Batch API | 告警收敛管理API") +@RestController +@RequestMapping(path = "/api/alert/converges", produces = {APPLICATION_JSON_VALUE}) +public class AlertConvergesController { + + @Autowired + private AlertConvergeService alertConvergeService; + + @GetMapping + @Operation(summary = "Query the alarm converge list | 查询告警收敛列表", + description = "You can obtain the list of alarm converge by querying filter items | 根据查询过滤项获取告警收敛信息列表") + public ResponseEntity>> getAlertConverges( + @Parameter(description = "Alarm Converge ID | 告警收敛ID", example = "6565463543") @RequestParam(required = false) List ids, + @Parameter(description = "Sort field, default id | 排序字段,默认id", example = "id") @RequestParam(defaultValue = "id") String sort, + @Parameter(description = "Sort mode: asc: ascending, desc: descending | 排序方式,asc:升序,desc:降序", example = "desc") @RequestParam(defaultValue = "desc") String order, + @Parameter(description = "List current page | 列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex, + @Parameter(description = "Number of list pages | 列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) { + + Specification specification = (root, query, criteriaBuilder) -> { + List andList = new ArrayList<>(); + if (ids != null && !ids.isEmpty()) { + CriteriaBuilder.In inPredicate= criteriaBuilder.in(root.get("id")); + for (long id : ids) { + inPredicate.value(id); + } + andList.add(inPredicate); + } + Predicate[] predicates = new Predicate[andList.size()]; + return criteriaBuilder.and(andList.toArray(predicates)); + }; + Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort)); + PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp); + Page alertConvergePage = alertConvergeService.getAlertConverges(specification,pageRequest); + Message> message = new Message<>(alertConvergePage); + return ResponseEntity.ok(message); + } + + @DeleteMapping + @Operation(summary = "Delete alarm converge in batches | 批量删除告警收敛", + description = "Delete alarm converge in batches based on the alarm converge ID list | 根据告警收敛ID列表批量删除告警收敛") + public ResponseEntity> deleteAlertDefines( + @Parameter(description = "Alarm Converge IDs | 告警收敛IDs", example = "6565463543") @RequestParam(required = false) List ids + ) { + if (ids != null && !ids.isEmpty()) { + alertConvergeService.deleteAlertConverges(new HashSet<>(ids)); + } + Message message = new Message<>(); + return ResponseEntity.ok(message); + } + +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/dao/AlertConvergeDao.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/dao/AlertConvergeDao.java new file mode 100644 index 00000000000..e4f7e11cd28 --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/dao/AlertConvergeDao.java @@ -0,0 +1,40 @@ +/* + * 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.dromara.hertzbeat.alert.dao; + +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; + +import java.util.Set; + +/** + * AlertConverge 数据库操作 + * @author tom + * + */ +public interface AlertConvergeDao extends JpaRepository, JpaSpecificationExecutor { + + /** + * Delete alarm converge based on the ID list + * @param convergeIds alert converge id list + */ + @Modifying + void deleteAlertConvergesByIdIn(Set convergeIds); +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmCommonReduce.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmCommonReduce.java new file mode 100644 index 00000000000..97ce2d38979 --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmCommonReduce.java @@ -0,0 +1,31 @@ +package org.dromara.hertzbeat.alert.reduce; + +import lombok.RequiredArgsConstructor; +import org.dromara.hertzbeat.common.entity.alerter.Alert; +import org.dromara.hertzbeat.common.queue.CommonDataQueue; +import org.springframework.stereotype.Service; + +/** + * reduce alarm and send alert data + * + * @author tom + */ +@Service +@RequiredArgsConstructor +public class AlarmCommonReduce { + + private final AlarmSilenceReduce alarmSilenceReduce; + + private final AlarmConvergeReduce alarmConvergeReduce; + + private final CommonDataQueue dataQueue; + + public void reduceAndSendAlarm(Alert alert) { + alert.setTimes(1); + // converge -> silence + if (alarmConvergeReduce.filterConverge(alert) && alarmSilenceReduce.filterSilence(alert)) { + dataQueue.sendAlertsData(alert); + } + } + +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmConvergeReduce.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmConvergeReduce.java new file mode 100644 index 00000000000..658690e250d --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmConvergeReduce.java @@ -0,0 +1,118 @@ +package org.dromara.hertzbeat.alert.reduce; + +import org.dromara.hertzbeat.alert.dao.AlertConvergeDao; +import org.dromara.hertzbeat.common.cache.CacheFactory; +import org.dromara.hertzbeat.common.cache.ICacheService; +import org.dromara.hertzbeat.common.constants.CommonConstants; +import org.dromara.hertzbeat.common.entity.alerter.Alert; +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.dromara.hertzbeat.common.entity.manager.TagItem; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * alarm converge + * 告警收敛 + * @author tom + */ +@Service +public class AlarmConvergeReduce { + + private final AlertConvergeDao alertConvergeDao; + + private final Map converageAlertMap; + + public AlarmConvergeReduce(AlertConvergeDao alertConvergeDao) { + this.alertConvergeDao = alertConvergeDao; + this.converageAlertMap = new ConcurrentHashMap<>(16); + } + + /** + * currentAlert converge filter data + * @param currentAlert currentAlert + * @return true when not filter + */ + @SuppressWarnings("unchecked") + public boolean filterConverge(Alert currentAlert) { + // ignore ALERT_STATUS_CODE_RESTORED + if (currentAlert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) { + return true; + } + ICacheService convergeCache = CacheFactory.getAlertConvergeCache(); + List alertConvergeList = (List) convergeCache.get(CommonConstants.CACHE_ALERT_CONVERGE); + if (alertConvergeList == null) { + alertConvergeList = alertConvergeDao.findAll(); + // matchAll is in the last + alertConvergeList.sort((item1, item2) -> { + if (item1.isMatchAll()) { + return 1; + } else if (item2.isMatchAll()) { + return -1; + } else { + return 0; + } + }); + convergeCache.put(CommonConstants.CACHE_ALERT_CONVERGE, alertConvergeList); + } + for (AlertConverge alertConverge : alertConvergeList) { + if (!alertConverge.isEnable()) { + continue; + } + boolean match = alertConverge.isMatchAll(); + if (!match) { + List tags = alertConverge.getTags(); + if (currentAlert.getTags() != null && !currentAlert.getTags().isEmpty()) { + Map alertTagMap = currentAlert.getTags(); + match = tags.stream().anyMatch(item -> { + if (alertTagMap.containsKey(item.getName())) { + String tagValue = alertTagMap.get(item.getName()); + if (tagValue == null && item.getValue() == null) { + return true; + } else { + return tagValue != null && tagValue.equals(item.getValue()); + } + } else { + return false; + } + }); + } + if (match && alertConverge.getPriorities() != null && !alertConverge.getPriorities().isEmpty()) { + match = alertConverge.getPriorities().stream().anyMatch(item -> item != null && item == currentAlert.getPriority()); + } + } + if (match) { + long evalInterval = alertConverge.getEvalInterval() * 1000; + long now = System.currentTimeMillis(); + if (evalInterval <= 0) { + return true; + } + int alertHash = Objects.hash(currentAlert.getPriority(), currentAlert.getTags()); + Alert preAlert = converageAlertMap.get(alertHash); + if (preAlert == null) { + currentAlert.setTimes(1); + currentAlert.setFirstAlarmTime(now); + currentAlert.setLastAlarmTime(now); + converageAlertMap.put(alertHash, currentAlert); + return true; + } else { + if (now - preAlert.getFirstAlarmTime() < evalInterval) { + preAlert.setTimes(preAlert.getTimes() + 1); + preAlert.setLastAlarmTime(now); + return false; + } else { + currentAlert.setTimes(preAlert.getTimes() + 1); + currentAlert.setFirstAlarmTime(preAlert.getFirstAlarmTime()); + currentAlert.setLastAlarmTime(now); + converageAlertMap.remove(alertHash); + return true; + } + } + } + } + return true; + } +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/SilenceAlarm.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmSilenceReduce.java similarity index 91% rename from alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/SilenceAlarm.java rename to alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmSilenceReduce.java index 62939ef94dd..4f8ba7c612f 100644 --- a/alerter/src/main/java/org/dromara/hertzbeat/alert/calculate/SilenceAlarm.java +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/reduce/AlarmSilenceReduce.java @@ -1,4 +1,4 @@ -package org.dromara.hertzbeat.alert.calculate; +package org.dromara.hertzbeat.alert.reduce; import lombok.RequiredArgsConstructor; import org.dromara.hertzbeat.alert.dao.AlertSilenceDao; @@ -8,7 +8,6 @@ import org.dromara.hertzbeat.common.entity.alerter.Alert; import org.dromara.hertzbeat.common.entity.alerter.AlertSilence; import org.dromara.hertzbeat.common.entity.manager.TagItem; -import org.dromara.hertzbeat.common.queue.CommonDataQueue; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -23,14 +22,17 @@ */ @Service @RequiredArgsConstructor -public class SilenceAlarm { +public class AlarmSilenceReduce { private final AlertSilenceDao alertSilenceDao; - private final CommonDataQueue dataQueue; - + /** + * alert silence filter data + * @param alert alert + * @return true when not filter + */ @SuppressWarnings("unchecked") - public void filterSilenceAndSendData(Alert alert) { + public boolean filterSilence(Alert alert) { ICacheService silenceCache = CacheFactory.getAlertSilenceCache(); List alertSilenceList = (List) silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE); if (alertSilenceList == null) { @@ -38,6 +40,9 @@ public void filterSilenceAndSendData(Alert alert) { silenceCache.put(CommonConstants.CACHE_ALERT_SILENCE, alertSilenceList); } for (AlertSilence alertSilence : alertSilenceList) { + if (!alertSilence.isEnable()) { + continue; + } // if match the silence rule, return boolean match = alertSilence.isMatchAll(); if (!match) { @@ -71,7 +76,7 @@ public void filterSilenceAndSendData(Alert alert) { int times = alertSilence.getTimes() == null ? 0 : alertSilence.getTimes(); alertSilence.setTimes(times + 1); alertSilenceDao.save(alertSilence); - return; + return false; } } } else if (alertSilence.getType() == 1) { @@ -87,13 +92,13 @@ public void filterSilenceAndSendData(Alert alert) { int times = alertSilence.getTimes() == null ? 0 : alertSilence.getTimes(); alertSilence.setTimes(times + 1); alertSilenceDao.save(alertSilence); - return; + return false; } } } } } } - dataQueue.sendAlertsData(alert); + return true; } } diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/AlertConvergeService.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/AlertConvergeService.java new file mode 100644 index 00000000000..acf08a4f540 --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/AlertConvergeService.java @@ -0,0 +1,63 @@ +package org.dromara.hertzbeat.alert.service; + +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; + +import java.util.Set; + +/** + * management interface service for alert converge + * @author tom + * + */ +public interface AlertConvergeService { + /** + * Verify the correctness of the request data parameters + * 校验请求数据参数正确性 + * @param alertConverge AlertConverge + * @param isModify 是否是修改配置 + * @throws IllegalArgumentException A checksum parameter error is thrown | 校验参数错误抛出 + */ + void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException; + + /** + * New AlertConverge + * @param alertConverge AlertConverge Entity | 收敛策略实体 + * @throws RuntimeException Added procedure exception throwing | 新增过程异常抛出 + */ + void addAlertConverge(AlertConverge alertConverge) throws RuntimeException; + + /** + * Modifying an AlertConverge | 修改收敛策略 + * @param alertConverge Alarm definition Entity | 收敛策略实体 + * @throws RuntimeException Exception thrown during modification | 修改过程中异常抛出 + */ + void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException; + + /** + * Obtain AlertConverge information + * @param convergeId AlertConverge ID + * @return AlertConverge + * @throws RuntimeException An exception was thrown during the query | 查询过程中异常抛出 + */ + AlertConverge getAlertConverge(long convergeId) throws RuntimeException; + + + /** + * Delete AlertConverge in batches | 批量删除收敛策略 + * @param convergeIds AlertConverge IDs | 收敛策略IDs + * @throws RuntimeException Exception thrown during deletion | 删除过程中异常抛出 + */ + void deleteAlertConverges(Set convergeIds) throws RuntimeException; + + /** + * Dynamic conditional query + * 动态条件查询 + * @param specification Query conditions | 查询条件 + * @param pageRequest Paging parameters | 分页参数 + * @return The query results | 查询结果 + */ + Page getAlertConverges(Specification specification, PageRequest pageRequest); +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertConvergeServiceImpl.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertConvergeServiceImpl.java new file mode 100644 index 00000000000..643ac87cb20 --- /dev/null +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertConvergeServiceImpl.java @@ -0,0 +1,69 @@ +package org.dromara.hertzbeat.alert.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.hertzbeat.alert.dao.AlertConvergeDao; +import org.dromara.hertzbeat.alert.service.AlertConvergeService; +import org.dromara.hertzbeat.common.cache.CacheFactory; +import org.dromara.hertzbeat.common.cache.ICacheService; +import org.dromara.hertzbeat.common.constants.CommonConstants; +import org.dromara.hertzbeat.common.entity.alerter.AlertConverge; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Set; + +/** + * implement for alert converge service + * + * @author tom + */ +@Service +@Transactional(rollbackFor = Exception.class) +@Slf4j +public class AlertConvergeServiceImpl implements AlertConvergeService { + + @Autowired + private AlertConvergeDao alertConvergeDao; + + @Override + public void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException { + // todo + } + + @Override + public void addAlertConverge(AlertConverge alertConverge) throws RuntimeException { + alertConvergeDao.save(alertConverge); + clearAlertConvergesCache(); + } + + @Override + public void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException { + alertConvergeDao.save(alertConverge); + clearAlertConvergesCache(); + } + + @Override + public AlertConverge getAlertConverge(long convergeId) throws RuntimeException { + return alertConvergeDao.findById(convergeId).orElse(null); + } + + @Override + public void deleteAlertConverges(Set convergeIds) throws RuntimeException { + alertConvergeDao.deleteAlertConvergesByIdIn(convergeIds); + clearAlertConvergesCache(); + } + + @Override + public Page getAlertConverges(Specification specification, PageRequest pageRequest) { + return alertConvergeDao.findAll(specification, pageRequest); + } + + private void clearAlertConvergesCache() { + ICacheService convergeCache = CacheFactory.getAlertConvergeCache(); + convergeCache.remove(CommonConstants.CACHE_ALERT_CONVERGE); + } +} diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertServiceImpl.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertServiceImpl.java index ee62a4cf1fa..189d0672ef6 100644 --- a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertServiceImpl.java +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertServiceImpl.java @@ -17,7 +17,7 @@ package org.dromara.hertzbeat.alert.service.impl; -import org.dromara.hertzbeat.common.queue.CommonDataQueue; +import org.dromara.hertzbeat.alert.reduce.AlarmCommonReduce; import org.dromara.hertzbeat.alert.dao.AlertDao; import org.dromara.hertzbeat.alert.dto.AlertPriorityNum; import org.dromara.hertzbeat.alert.dto.AlertSummary; @@ -55,9 +55,9 @@ public class AlertServiceImpl implements AlertService { @Autowired private AlertDao alertDao; - + @Autowired - private CommonDataQueue commonDataQueue; + private AlarmCommonReduce alarmCommonReduce; @Override public void addAlert(Alert alert) throws RuntimeException { @@ -130,7 +130,7 @@ public AlertSummary getAlertsSummary() { @Override public void addNewAlertReport(AlertReport alertReport) { - commonDataQueue.sendAlertsData(buildAlertData(alertReport)); + alarmCommonReduce.reduceAndSendAlarm(buildAlertData(alertReport)); } /** @@ -156,7 +156,7 @@ private Alert buildAlertData(AlertReport alertReport){ .status(CommonConstants.ALERT_STATUS_CODE_PENDING) .tags(alertReport.getLabels()) .target(alertReport.getAlertName()) - .times(3) + .triggerTimes(1) .gmtCreate(LocalDateTime.ofInstant(Instant.ofEpochMilli(alertReport.getAlertTime()), ZoneId.systemDefault())) .build(); } diff --git a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertSilenceServiceImpl.java b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertSilenceServiceImpl.java index 70aff8660b7..07e891baeed 100644 --- a/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertSilenceServiceImpl.java +++ b/alerter/src/main/java/org/dromara/hertzbeat/alert/service/impl/AlertSilenceServiceImpl.java @@ -31,7 +31,7 @@ public class AlertSilenceServiceImpl implements AlertSilenceService { @Override public void validate(AlertSilence alertSilence, boolean isModify) throws IllegalArgumentException { - // todo + // todo } @Override diff --git a/common/src/main/java/org/dromara/hertzbeat/common/cache/CacheFactory.java b/common/src/main/java/org/dromara/hertzbeat/common/cache/CacheFactory.java index af4e07e2ace..8aac0256146 100644 --- a/common/src/main/java/org/dromara/hertzbeat/common/cache/CacheFactory.java +++ b/common/src/main/java/org/dromara/hertzbeat/common/cache/CacheFactory.java @@ -32,6 +32,9 @@ private CacheFactory() {} private static final ICacheService ALERT_SILENCE_CACHE = new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false); + private static final ICacheService ALERT_CONVERGE_CACHE = + new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false); + /** * 获取notice模块的cache * @return caffeine cache @@ -47,4 +50,8 @@ public static ICacheService getNoticeCache() { public static ICacheService getAlertSilenceCache() { return ALERT_SILENCE_CACHE; } + + public static ICacheService getAlertConvergeCache() { + return ALERT_CONVERGE_CACHE; + } } diff --git a/common/src/main/java/org/dromara/hertzbeat/common/constants/CommonConstants.java b/common/src/main/java/org/dromara/hertzbeat/common/constants/CommonConstants.java index b149a7cd2e5..4a5dedf756d 100644 --- a/common/src/main/java/org/dromara/hertzbeat/common/constants/CommonConstants.java +++ b/common/src/main/java/org/dromara/hertzbeat/common/constants/CommonConstants.java @@ -274,4 +274,9 @@ public interface CommonConstants { * cache key alert silence */ String CACHE_ALERT_SILENCE = "alert_silence"; + + /** + * cache key alert converge + */ + String CACHE_ALERT_CONVERGE = "alert_converge"; } diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/Alert.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/Alert.java index 09c6ff889ba..7c5b2c081a6 100644 --- a/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/Alert.java +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/Alert.java @@ -95,27 +95,27 @@ public class Alert { @Min(0) @Max(3) private byte status; - - @Schema(title = "Alarm threshold trigger times", - description = "告警阈值触发次数", + + @Schema(title = "Alarm times", + description = "告警次数", example = "3", accessMode = READ_WRITE) - @Min(0) private Integer times; - + @Schema(title = "Alarm trigger time (timestamp in milliseconds)", - description = "首次告警触发时间(毫秒时间戳)", + description = "首次告警时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY) - private Long firstTriggerTime; - + private Long firstAlarmTime; + @Schema(title = "Alarm trigger time (timestamp in milliseconds)", - description = "最近告警触发时间(毫秒时间戳)", + description = "最近告警时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY) - private Long lastTriggerTime; + private Long lastAlarmTime; - @Schema(title = "Alarm evaluation interval (milliseconds)", - description = "告警评估时间间隔(单位毫秒)", - example = "2000", accessMode = READ_ONLY) - private Long nextEvalInterval; + @Schema(title = "Alarm threshold trigger times", + description = "告警阈值触发次数", + example = "3", accessMode = READ_WRITE) + @Transient + private Integer triggerTimes; @Schema(description = "告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{key1:value1}", accessMode = READ_WRITE) @Convert(converter = JsonMapAttributeConverter.class) diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/AlertConverge.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/AlertConverge.java new file mode 100644 index 00000000000..0a752f5a643 --- /dev/null +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/alerter/AlertConverge.java @@ -0,0 +1,115 @@ +/* + * 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.dromara.hertzbeat.common.entity.alerter; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.hertzbeat.common.entity.manager.JsonByteListAttributeConverter; +import org.dromara.hertzbeat.common.entity.manager.JsonTagListAttributeConverter; +import org.dromara.hertzbeat.common.entity.manager.TagItem; +import org.hibernate.validator.constraints.Length; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY; +import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE; + +/** + * Alert Converge strategy entity + * 告警收敛策略 + * @author tomsun28 + * + */ +@Entity +@Table(name = "hzb_alert_converge") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "Alert Converge Policy Entity | 告警收敛策略实体") +@EntityListeners(AuditingEntityListener.class) +public class AlertConverge { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Schema(title = "Alert Converge Policy Entity Primary Key Index ID", + description = "告警收敛策略实体主键索引ID", + example = "87584674384", accessMode = READ_ONLY) + private Long id; + + @Schema(title = "Policy name", description = "策略名称", + example = "converge-1", accessMode = READ_WRITE) + @Length(max = 100) + @NotNull + private String name; + + @Schema(title = "Whether to enable this policy", description = "是否启用此策略", + example = "true", accessMode = READ_WRITE) + private boolean enable = true; + + @Schema(title = "Whether to match all", description = "是否应用匹配所有", + example = "true", accessMode = READ_WRITE) + private boolean matchAll = true; + + @Schema(title = "匹配告警级别,空为全部告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色", + example = "[1]", accessMode = READ_WRITE) + @Convert(converter = JsonByteListAttributeConverter.class) + private List priorities; + + @Schema(description = "匹配告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{name: key1, value: value1}", + accessMode = READ_WRITE) + @Convert(converter = JsonTagListAttributeConverter.class) + @Column(length = 2048) + private List tags; + + @Schema(title = "Repeat Alert Converge Time Range, unit s", example = "600", accessMode = READ_WRITE) + @Min(0) + private Integer evalInterval; + + @Schema(title = "The creator of this record", description = "此条记录创建者", example = "tom", accessMode = READ_ONLY) + @CreatedBy + private String creator; + + @Schema(title = "This record was last modified by", + description = "此条记录最新修改者", + example = "tom", accessMode = READ_ONLY) + @LastModifiedBy + private String modifier; + + @Schema(title = "This record creation time (millisecond timestamp)", + description = "记录创建时间", accessMode = READ_ONLY) + @CreatedDate + private LocalDateTime gmtCreate; + + @Schema(title = "Record the latest modification time (timestamp in milliseconds)", + description = "记录最新修改时间", accessMode = READ_ONLY) + @LastModifiedDate + private LocalDateTime gmtUpdate; +} diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/component/alerter/impl/AbstractAlertNotifyHandlerImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/component/alerter/impl/AbstractAlertNotifyHandlerImpl.java index a98d58bf40f..7092034c7e3 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/component/alerter/impl/AbstractAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/component/alerter/impl/AbstractAlertNotifyHandlerImpl.java @@ -66,7 +66,7 @@ protected String renderContent(Alert alert) { context.setVariable("priority", bundle.getString("alerter.priority." + alert.getPriority())); context.setVariable("triggerTimeLabel", bundle.getString("alerter.notify.triggerTime")); - context.setVariable("triggerTime", DTF.format(Instant.ofEpochMilli(alert.getLastTriggerTime()).atZone(ZoneId.systemDefault()).toLocalDateTime())); + context.setVariable("triggerTime", DTF.format(Instant.ofEpochMilli(alert.getLastAlarmTime()).atZone(ZoneId.systemDefault()).toLocalDateTime())); context.setVariable("contentLabel", bundle.getString("alerter.notify.content")); context.setVariable("content", alert.getContent()); diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MailServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MailServiceImpl.java index 56563091065..cedbd94569e 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MailServiceImpl.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MailServiceImpl.java @@ -80,8 +80,8 @@ public String buildAlertHtmlTemplate(final Alert alert) { context.setVariable("content", alert.getContent()); context.setVariable("consoleUrl", alerterProperties.getConsoleUrl()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - String triggerTime = simpleDateFormat.format(new Date(alert.getLastTriggerTime())); - context.setVariable("lastTriggerTime", triggerTime); + String alarmTime = simpleDateFormat.format(new Date(alert.getLastAlarmTime())); + context.setVariable("lastTriggerTime", alarmTime); return templateEngine.process("mailAlarm", context); } } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java index de89339c959..013018179b9 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/NoticeConfigServiceImpl.java @@ -185,9 +185,9 @@ public boolean sendTestMsg(NoticeReceiver noticeReceiver) { Alert alert = new Alert(); alert.setTarget(ALERT_TEST_TARGET); alert.setContent(ALERT_TEST_CONTENT); - alert.setTimes(1); - alert.setFirstTriggerTime(System.currentTimeMillis()); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setTriggerTimes(1); + alert.setFirstAlarmTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); alert.setPriority(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL); return dispatcherAlarm.sendNoticeMsg(noticeReceiver, alert); } diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImplTest.java index 558f48218a0..d2b586b7900 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImplTest.java @@ -41,9 +41,9 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); dingTalkRobotAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DiscordBotAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DiscordBotAlertNotifyHandlerImplTest.java index e475a527df3..b717ecf081e 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DiscordBotAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/DiscordBotAlertNotifyHandlerImplTest.java @@ -46,8 +46,8 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); discordBotAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImplTest.java index c3ea7e9a67d..232598f58f7 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImplTest.java @@ -41,9 +41,9 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); flyBookAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/HuaweiCloudSmnAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/HuaweiCloudSmnAlertNotifyHandlerImplTest.java index 5b59291e806..9478a184d27 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/HuaweiCloudSmnAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/HuaweiCloudSmnAlertNotifyHandlerImplTest.java @@ -81,7 +81,7 @@ void send() throws InterruptedException { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); huaweiyunSmnAlertNotifyHandler.send(receiver, alert); } diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/SlackAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/SlackAlertNotifyHandlerImplTest.java index 5ced52844e9..3d0c0d025f8 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/SlackAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/SlackAlertNotifyHandlerImplTest.java @@ -45,8 +45,8 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); slackAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/TelegramBotAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/TelegramBotAlertNotifyHandlerImplTest.java index 76102c76deb..e094a0da421 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/TelegramBotAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/TelegramBotAlertNotifyHandlerImplTest.java @@ -45,8 +45,8 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); telegramBotAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeChatAppAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeChatAppAlertNotifyHandlerImplTest.java index 26465d5195b..49f4dd798ee 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeChatAppAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeChatAppAlertNotifyHandlerImplTest.java @@ -50,7 +50,7 @@ public void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); weChatAppAlertNotifyHandler.send(receiver, alert); } diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImplTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImplTest.java index 60fe8bd00d0..cd401520a79 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImplTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImplTest.java @@ -41,9 +41,9 @@ void send() { alert.setTags(map); alert.setContent("mock content"); alert.setPriority((byte) 0); - alert.setLastTriggerTime(System.currentTimeMillis()); + alert.setLastAlarmTime(System.currentTimeMillis()); weWorkRobotAlertNotifyHandler.send(receiver, alert); } -} \ No newline at end of file +} diff --git a/script/sql/schema.sql b/script/sql/schema.sql index e49ed2203cf..ca5a83b4360 100644 --- a/script/sql/schema.sql +++ b/script/sql/schema.sql @@ -181,6 +181,26 @@ CREATE TABLE hzb_alert_silence primary key (id) ) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4; +-- ---------------------------- +-- Table structure for hzb_alert_silence +-- ---------------------------- +DROP TABLE IF EXISTS hzb_alert_converge ; +CREATE TABLE hzb_alert_converge +( + id bigint not null auto_increment comment '告警静默主键索引ID', + name varchar(100) not null comment '静默策略名称', + enable boolean not null default true comment '是否启用此策略', + match_all boolean not null default true comment '是否应用匹配所有', + priorities varchar(100) comment '匹配告警级别,空为全部告警级别', + tags varchar(4000) comment '匹配告警信息标签(monitorId:xxx,monitorName:xxx)', + eval_interval int default 0 comment 'Repeat Alert Converge Time Range, unit s', + creator varchar(100) comment '创建者', + modifier varchar(100) comment '最新修改者', + gmt_create timestamp default current_timestamp comment 'create time', + gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', + primary key (id) +) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4; + -- ---------------------------- -- Table structure for alert -- ---------------------------- @@ -193,10 +213,9 @@ CREATE TABLE hzb_alert priority tinyint not null default 0 comment '告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色', content varchar(4000) not null comment '告警通知实际内容', status tinyint not null default 0 comment '告警状态: 0-正常告警(待处理) 1-阈值触发但未达到告警次数 2-恢复告警 3-已处理', - times int not null comment '触发次数,即达到告警定义的触发阈值次数要求后才会发告警', - first_trigger_time bigint comment '首次告警触发时间(毫秒时间戳)', - last_trigger_time bigint comment '最近告警触发时间(毫秒时间戳)', - next_eval_interval bigint comment '告警评估时间间隔(单位毫秒)', + times int not null comment '告警次数', + first_alarm_time bigint comment '首次告警时间(毫秒时间戳)', + last_alarm_time bigint comment '最近告警时间(毫秒时间戳)', tags varchar(4000) comment '告警信息标签(monitorId:xxx,monitorName:xxx)', creator varchar(100) comment '创建者', modifier varchar(100) comment '最新修改者', diff --git a/web-app/src/app/layout/basic/widgets/notify.component.ts b/web-app/src/app/layout/basic/widgets/notify.component.ts index 7c5f517d155..87546e72869 100644 --- a/web-app/src/app/layout/basic/widgets/notify.component.ts +++ b/web-app/src/app/layout/basic/widgets/notify.component.ts @@ -78,7 +78,7 @@ export class HeaderNotifyComponent implements OnInit { id: alert.id, avatar: '/assets/img/notification.svg', title: `${alert.tags?.monitorName}--${this.i18nSvc.fanyi(`alert.priority.${alert.priority}`)}`, - datetime: new Date(alert.lastTriggerTime).toLocaleString(), + datetime: new Date(alert.lastAlarmTime).toLocaleString(), color: 'blue', type: this.i18nSvc.fanyi('dashboard.alerts.title-no') }; diff --git a/web-app/src/app/pojo/Alert.ts b/web-app/src/app/pojo/Alert.ts index 68f93204b9b..4d3833c3f93 100644 --- a/web-app/src/app/pojo/Alert.ts +++ b/web-app/src/app/pojo/Alert.ts @@ -8,10 +8,10 @@ export class Alert { // 告警状态: 0-正常告警(待处理) 1-阈值触发但未达到告警次数 2-恢复告警 3-已处理 status!: number; content!: string; + // alarm times times!: number; - firstTriggerTime!: number; - lastTriggerTime!: number; - nextEvalInterval!: number; + firstAlarmTime!: number; + lastAlarmTime!: number; tags!: Record; gmtCreate!: number; gmtUpdate!: number; diff --git a/web-app/src/app/pojo/AlertConverge.ts b/web-app/src/app/pojo/AlertConverge.ts new file mode 100644 index 00000000000..b92c1e0a269 --- /dev/null +++ b/web-app/src/app/pojo/AlertConverge.ts @@ -0,0 +1,15 @@ +import { TagItem } from './NoticeRule'; + +export class AlertConverge { + id!: number; + name!: string; + enable: boolean = true; + matchAll: boolean = true; + priorities!: number[]; + tags!: TagItem[]; + evalInterval: number = 14400; + creator!: string; + modifier!: string; + gmtCreate!: number; + gmtUpdate!: number; +} 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 c8ee888c3e2..26a6edcc19b 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 @@ -137,7 +137,27 @@ {{ 'alert.status.' + data.status | i18n }} - {{ data.lastTriggerTime | date : 'YYYY-MM-dd HH:mm:ss' }} + + + {{ 'alert.center.first-time' | i18n }} : {{ data.firstAlarmTime | date : 'YYYY-MM-dd HH:mm:ss' }} + + + + {{ 'alert.center.last-time' | i18n }} : {{ data.lastAlarmTime | date : 'YYYY-MM-dd HH:mm:ss' }} + + + + + + + +
+ + + +
+ + + + + + {{ 'alert.converge.name' | i18n }} + {{ 'alert.converge.tags' | i18n }} + {{ 'alert.converge.eval-interval' | i18n }} + {{ 'alert.converge.enable' | i18n }} + {{ 'common.edit-time' | i18n }} + {{ 'common.edit' | i18n }} + + + + + + + {{ data.name }} + + + + {{ sliceTagName(tag) }} + + + + + {{ 'alert.converge.match-all' | i18n }} + + + + + + {{ data.evalInterval == undefined ? 0 : data.evalInterval }} + + + + + + {{ (data.gmtUpdate ? data.gmtUpdate : data.gmtCreate) | date : 'YYYY-MM-dd HH:mm:ss' }} + + + + + + + + + {{ 'common.total' | i18n }} {{ total }} + + + +
+
+ + {{ 'alert.converge.name' | i18n }} + + + + + + {{ 'alert.converge.match-all' | i18n }} + + + + + + {{ 'alert.converge.tags' | i18n }} + + + + + + + {{ 'alert.converge.priority' | i18n }} + + + + + + + + + + + {{ 'alert.converge.repeat' | i18n }} + + + + {{ 'alert.converge.repeat-rule' | i18n }} + + + + + {{ 'alert.converge.eval-interval' | i18n }} + + + + + + {{ 'alert.notice.rule.enable' | i18n }} + + + + +
+
+
diff --git a/web-app/src/app/routes/alert/alert-converge/alert-converge.component.less b/web-app/src/app/routes/alert/alert-converge/alert-converge.component.less new file mode 100644 index 00000000000..e69de29bb2d diff --git a/web-app/src/app/routes/alert/alert-converge/alert-converge.component.spec.ts b/web-app/src/app/routes/alert/alert-converge/alert-converge.component.spec.ts new file mode 100644 index 00000000000..346a212008d --- /dev/null +++ b/web-app/src/app/routes/alert/alert-converge/alert-converge.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AlertConvergeComponent } from './alert-converge.component'; + +describe('AlertConvergeComponent', () => { + let component: AlertConvergeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AlertConvergeComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(AlertConvergeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/alert/alert-converge/alert-converge.component.ts b/web-app/src/app/routes/alert/alert-converge/alert-converge.component.ts new file mode 100644 index 00000000000..894cafd539d --- /dev/null +++ b/web-app/src/app/routes/alert/alert-converge/alert-converge.component.ts @@ -0,0 +1,377 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { I18NService } from '@core'; +import { ALAIN_I18N_TOKEN, SettingsService } from '@delon/theme'; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { NzNotificationService } from 'ng-zorro-antd/notification'; +import { NzTableQueryParams } from 'ng-zorro-antd/table'; +import { finalize } from 'rxjs/operators'; + +import { AlertConverge } from '../../../pojo/AlertConverge'; +import { TagItem } from '../../../pojo/NoticeRule'; +import { AlertConvergeService } from '../../../service/alert-converge.service'; +import { TagService } from '../../../service/tag.service'; + +@Component({ + selector: 'app-alert-converge', + templateUrl: './alert-converge.component.html', + styleUrls: ['./alert-converge.component.less'] +}) +export class AlertConvergeComponent implements OnInit { + constructor( + private modal: NzModalService, + private notifySvc: NzNotificationService, + private alertConvergeService: AlertConvergeService, + private settingsSvc: SettingsService, + private tagService: TagService, + @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService + ) {} + + pageIndex: number = 1; + pageSize: number = 8; + total: number = 0; + converges!: AlertConverge[]; + tableLoading: boolean = true; + checkedConvergeIds = new Set(); + + ngOnInit(): void { + this.loadAlertConvergeTable(); + } + + sync() { + this.loadAlertConvergeTable(); + } + + loadAlertConvergeTable() { + this.tableLoading = true; + let alertDefineInit$ = this.alertConvergeService.getAlertConverges(this.pageIndex - 1, this.pageSize).subscribe( + message => { + this.tableLoading = false; + this.checkedAll = false; + this.checkedConvergeIds.clear(); + if (message.code === 0) { + let page = message.data; + this.converges = page.content; + this.pageIndex = page.number + 1; + this.total = page.totalElements; + } else { + console.warn(message.msg); + } + alertDefineInit$.unsubscribe(); + }, + error => { + this.tableLoading = false; + alertDefineInit$.unsubscribe(); + } + ); + } + + updateAlertConverge(alertConverge: AlertConverge) { + this.tableLoading = true; + const updateDefine$ = this.alertConvergeService + .editAlertConverge(alertConverge) + .pipe( + finalize(() => { + updateDefine$.unsubscribe(); + this.tableLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.edit-success'), ''); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), message.msg); + } + this.loadAlertConvergeTable(); + this.tableLoading = false; + }, + error => { + this.tableLoading = false; + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), error.msg); + } + ); + } + + onDeleteAlertConverges() { + if (this.checkedConvergeIds == null || this.checkedConvergeIds.size === 0) { + this.notifySvc.warning(this.i18nSvc.fanyi('common.notify.no-select-delete'), ''); + return; + } + this.modal.confirm({ + nzTitle: this.i18nSvc.fanyi('common.confirm.delete-batch'), + nzOkText: this.i18nSvc.fanyi('common.button.ok'), + nzCancelText: this.i18nSvc.fanyi('common.button.cancel'), + nzOkDanger: true, + nzOkType: 'primary', + nzClosable: false, + nzOnOk: () => this.deleteAlertConverges(this.checkedConvergeIds) + }); + } + + onDeleteOneAlertConverge(id: number) { + let ids = new Set(); + ids.add(id); + this.modal.confirm({ + nzTitle: this.i18nSvc.fanyi('common.confirm.delete'), + nzOkText: this.i18nSvc.fanyi('common.button.ok'), + nzCancelText: this.i18nSvc.fanyi('common.button.cancel'), + nzOkDanger: true, + nzOkType: 'primary', + nzClosable: false, + nzOnOk: () => this.deleteAlertConverges(ids) + }); + } + + deleteAlertConverges(convergeIds: Set) { + if (convergeIds == null || convergeIds.size == 0) { + this.notifySvc.warning(this.i18nSvc.fanyi('common.notify.no-select-delete'), ''); + return; + } + this.tableLoading = true; + const deleteDefines$ = this.alertConvergeService.deleteAlertConverges(convergeIds).subscribe( + message => { + deleteDefines$.unsubscribe(); + if (message.code === 0) { + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), ''); + this.loadAlertConvergeTable(); + } else { + this.tableLoading = false; + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg); + } + }, + error => { + this.tableLoading = false; + deleteDefines$.unsubscribe(); + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), error.msg); + } + ); + } + + // begin: 列表多选分页逻辑 + checkedAll: boolean = false; + onAllChecked(checked: boolean) { + if (checked) { + this.converges.forEach(item => this.checkedConvergeIds.add(item.id)); + } else { + this.checkedConvergeIds.clear(); + } + } + onItemChecked(id: number, checked: boolean) { + if (checked) { + this.checkedConvergeIds.add(id); + } else { + this.checkedConvergeIds.delete(id); + } + } + /** + * 分页回调 + * + * @param params 页码信息 + */ + onTablePageChange(params: NzTableQueryParams) { + const { pageSize, pageIndex, sort, filter } = params; + this.pageIndex = pageIndex; + this.pageSize = pageSize; + this.loadAlertConvergeTable(); + } + // end: 列表多选逻辑 + + // start 新增修改告警静默 model + isManageModalVisible = false; + isManageModalOkLoading = false; + isManageModalAdd = true; + converge: AlertConverge = new AlertConverge(); + searchTag!: string; + tagsOption: any[] = []; + matchTags: string[] = []; + convergeDates!: Date[]; + dayCheckOptions = [ + { label: this.i18nSvc.fanyi('common.week.7'), value: 7, checked: true }, + { label: this.i18nSvc.fanyi('common.week.1'), value: 1, checked: true }, + { label: this.i18nSvc.fanyi('common.week.2'), value: 2, checked: true }, + { label: this.i18nSvc.fanyi('common.week.3'), value: 3, checked: true }, + { label: this.i18nSvc.fanyi('common.week.4'), value: 4, checked: true }, + { label: this.i18nSvc.fanyi('common.week.5'), value: 5, checked: true }, + { label: this.i18nSvc.fanyi('common.week.6'), value: 6, checked: true } + ]; + + onNewAlertConverge() { + this.converge = new AlertConverge(); + let now = new Date(); + now.setHours(now.getHours() + 6); + this.convergeDates = [new Date(), now]; + this.isManageModalAdd = true; + this.isManageModalVisible = true; + this.isManageModalOkLoading = false; + } + onManageModalCancel() { + this.isManageModalVisible = false; + } + + onEditAlertConverge(convergeId: number) { + if (convergeId == null) { + this.notifySvc.warning(this.i18nSvc.fanyi('common.notify.no-select-edit'), ''); + return; + } + this.editAlertConverge(convergeId); + } + + editAlertConverge(convergeId: number) { + this.isManageModalAdd = false; + this.isManageModalVisible = true; + this.isManageModalOkLoading = false; + const getConverge$ = this.alertConvergeService + .getAlertConverge(convergeId) + .pipe( + finalize(() => { + getConverge$.unsubscribe(); + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.converge = message.data; + this.isManageModalVisible = true; + this.isManageModalAdd = false; + this.matchTags = []; + if (this.converge.tags != undefined) { + this.converge.tags.forEach(item => { + let tag = `${item.name}`; + if (item.value != undefined) { + tag = `${tag}:${item.value}`; + } + this.matchTags.push(tag); + this.tagsOption.push({ + value: tag, + label: tag + }); + }); + } + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), message.msg); + } + }, + error => { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), error.msg); + } + ); + } + onManageModalOk() { + this.converge.tags = []; + this.matchTags.forEach(tag => { + let tmp: string[] = tag.split(':'); + let tagItem = new TagItem(); + if (tmp.length == 1) { + tagItem.name = tmp[0]; + this.converge.tags.push(tagItem); + } else if (tmp.length == 2) { + tagItem.name = tmp[0]; + tagItem.value = tmp[1]; + this.converge.tags.push(tagItem); + } + }); + if (this.converge.priorities != undefined) { + this.converge.priorities = this.converge.priorities.filter(item => item != null && item != 9); + } + this.isManageModalOkLoading = true; + if (this.isManageModalAdd) { + const modalOk$ = this.alertConvergeService + .newAlertConverge(this.converge) + .pipe( + finalize(() => { + modalOk$.unsubscribe(); + this.isManageModalOkLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.isManageModalVisible = false; + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.new-success'), ''); + this.loadAlertConvergeTable(); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.new-fail'), message.msg); + } + }, + error => { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.new-fail'), error.msg); + } + ); + } else { + const modalOk$ = this.alertConvergeService + .editAlertConverge(this.converge) + .pipe( + finalize(() => { + modalOk$.unsubscribe(); + this.isManageModalOkLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.isManageModalVisible = false; + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.edit-success'), ''); + this.loadAlertConvergeTable(); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), message.msg); + } + }, + error => { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), error.msg); + } + ); + } + } + + onPrioritiesChange() { + if (this.converge.priorities != undefined) { + let isAll = false; + this.converge.priorities.forEach(item => { + if (item == 9) { + isAll = true; + } + }); + if (isAll) { + this.converge.priorities = [9, 0, 1, 2]; + } + } + } + + loadTagsOption() { + let tagsInit$ = this.tagService.loadTags(this.searchTag, undefined, 0, 1000).subscribe( + message => { + if (message.code === 0) { + let page = message.data; + this.tagsOption = []; + if (page.content != undefined) { + page.content.forEach(item => { + let tag = `${item.name}`; + if (item.value != undefined) { + tag = `${tag}:${item.value}`; + } + this.tagsOption.push({ + value: tag, + label: tag + }); + }); + } + } else { + console.warn(message.msg); + } + tagsInit$.unsubscribe(); + }, + error => { + tagsInit$.unsubscribe(); + console.error(error.msg); + } + ); + } + + sliceTagName(tag: any): string { + if (tag.value != undefined && tag.value.trim() != '') { + return `${tag.name}:${tag.value}`; + } else { + return tag.name; + } + } +} diff --git a/web-app/src/app/routes/alert/alert-routing.module.ts b/web-app/src/app/routes/alert/alert-routing.module.ts index 4b3dcef0dcd..a79c7c8792d 100644 --- a/web-app/src/app/routes/alert/alert-routing.module.ts +++ b/web-app/src/app/routes/alert/alert-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AlertCenterComponent } from './alert-center/alert-center.component'; +import { AlertConvergeComponent } from './alert-converge/alert-converge.component'; import { AlertNoticeComponent } from './alert-notice/alert-notice.component'; import { AlertSettingComponent } from './alert-setting/alert-setting.component'; import { AlertSilenceComponent } from './alert-silence/alert-silence.component'; @@ -12,6 +13,7 @@ const routes: Routes = [ { path: 'setting', component: AlertSettingComponent }, { path: 'notice', component: AlertNoticeComponent }, { path: 'silence', component: AlertSilenceComponent }, + { path: 'converge', component: AlertConvergeComponent }, { path: '**', component: AlertCenterComponent } ]; diff --git a/web-app/src/app/routes/alert/alert-silence/alert-silence.component.html b/web-app/src/app/routes/alert/alert-silence/alert-silence.component.html index 8c7c10f5899..f811e36499b 100644 --- a/web-app/src/app/routes/alert/alert-silence/alert-silence.component.html +++ b/web-app/src/app/routes/alert/alert-silence/alert-silence.component.html @@ -148,6 +148,7 @@ (nzOpenChange)="loadTagsOption()" [nzOptions]="tagsOption" [nzMaxTagCount]="5" + [nzDropdownMatchSelectWidth]="false" nzShowSearch nzMode="multiple" nzAllowClear diff --git a/web-app/src/app/routes/alert/alert.module.ts b/web-app/src/app/routes/alert/alert.module.ts index 415ab966462..1545dd959e8 100644 --- a/web-app/src/app/routes/alert/alert.module.ts +++ b/web-app/src/app/routes/alert/alert.module.ts @@ -14,12 +14,19 @@ import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; import { NzTransferModule } from 'ng-zorro-antd/transfer'; import { AlertCenterComponent } from './alert-center/alert-center.component'; +import { AlertConvergeComponent } from './alert-converge/alert-converge.component'; import { AlertNoticeComponent } from './alert-notice/alert-notice.component'; import { AlertRoutingModule } from './alert-routing.module'; import { AlertSettingComponent } from './alert-setting/alert-setting.component'; import { AlertSilenceComponent } from './alert-silence/alert-silence.component'; -const COMPONENTS: Array> = [AlertCenterComponent, AlertSettingComponent, AlertNoticeComponent, AlertSilenceComponent]; +const COMPONENTS: Array> = [ + AlertCenterComponent, + AlertSettingComponent, + AlertNoticeComponent, + AlertSilenceComponent, + AlertConvergeComponent +]; @NgModule({ imports: [ diff --git a/web-app/src/app/routes/dashboard/dashboard.component.html b/web-app/src/app/routes/dashboard/dashboard.component.html index 9015fb87e69..61fb169bfa4 100644 --- a/web-app/src/app/routes/dashboard/dashboard.component.html +++ b/web-app/src/app/routes/dashboard/dashboard.component.html @@ -117,7 +117,7 @@

diff --git a/web-app/src/app/service/alert-converge.service.spec.ts b/web-app/src/app/service/alert-converge.service.spec.ts new file mode 100644 index 00000000000..5c7c60d4fbd --- /dev/null +++ b/web-app/src/app/service/alert-converge.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AlertConvergeService } from './alert-converge.service'; + +describe('AlertConvergeService', () => { + let service: AlertConvergeService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AlertConvergeService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/alert-converge.service.ts b/web-app/src/app/service/alert-converge.service.ts new file mode 100644 index 00000000000..75cb4ecf5f6 --- /dev/null +++ b/web-app/src/app/service/alert-converge.service.ts @@ -0,0 +1,55 @@ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { AlertConverge } from '../pojo/AlertConverge'; +import { Message } from '../pojo/Message'; +import { Page } from '../pojo/Page'; + +const alert_converge_uri = '/alert/converge'; +const alert_converges_uri = '/alert/converges'; + +@Injectable({ + providedIn: 'root' +}) +export class AlertConvergeService { + constructor(private http: HttpClient) {} + + public newAlertConverge(body: AlertConverge): Observable> { + return this.http.post>(alert_converge_uri, body); + } + + public editAlertConverge(body: AlertConverge): Observable> { + return this.http.put>(alert_converge_uri, body); + } + + public getAlertConverge(convergeId: number): Observable> { + return this.http.get>(`${alert_converge_uri}/${convergeId}`); + } + + public deleteAlertConverges(convergeIds: Set): Observable> { + let httpParams = new HttpParams(); + convergeIds.forEach(convergeId => { + // 注意HttpParams是不可变对象 需要保存append后返回的对象为最新对象 + // append方法可以叠加同一key, set方法会把key之前的值覆盖只留一个key-value + httpParams = httpParams.append('ids', convergeId); + }); + const options = { params: httpParams }; + return this.http.delete>(alert_converges_uri, options); + } + + public getAlertConverges(pageIndex: number, pageSize: number): Observable>> { + pageIndex = pageIndex ? pageIndex : 0; + pageSize = pageSize ? pageSize : 8; + // 注意HttpParams是不可变对象 需要保存set后返回的对象为最新对象 + let httpParams = new HttpParams(); + httpParams = httpParams.appendAll({ + sort: 'id', + order: 'desc', + pageIndex: pageIndex, + pageSize: pageSize + }); + const options = { params: httpParams }; + return this.http.get>>(alert_converges_uri, options); + } +} diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index bcf667af9af..6c5a0d5518b 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -95,6 +95,12 @@ "icon": "anticon-calculator", "link": "/alert/setting" }, + { + "text": "告警收敛", + "i18n": "menu.alert.converge", + "icon": "anticon-filter", + "link": "/alert/converge" + }, { "text": "告警静默", "i18n": "menu.alert.silence", diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 8494c13f7c3..5d2e143afbe 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -8,7 +8,7 @@ "fullscreen.exit": "Exit", "clear.local.storage": "Clear Local Storage", "monitor": { - "": "Monitor", + "": "Monitoring", "center": "Monitor Center", "service": "Service Monitor", "db": "DB Monitor", @@ -28,10 +28,11 @@ "logout": "Logout" }, "alert": { - "": "Alert", - "center": "Alert Center", + "": "Alerting", + "center": "Alarm Center", + "converge": "Alarm Converge", "setting": "Threshold Rule", - "silence": "Alert Silence", + "silence": "Alarm Silence", "dispatch": "Notification" }, "extras": { @@ -145,10 +146,10 @@ "alert.setting.target.other": "Other metric objects of the row", "alert.setting.target.instance": "Instance of the row", "alert.setting.operator": "Supported operator functions", - "alert.silence.new": "New Silence Rule", - "alert.silence.edit": "Edit Silence Rule", - "alert.silence.delete": "Delete Silence Rule", - "alert.silence.name": "Silence Rule Name", + "alert.silence.new": "New Silence Strategy", + "alert.silence.edit": "Edit Silence Strategy", + "alert.silence.delete": "Delete Silence Strategy", + "alert.silence.name": "Silence Strategy Name", "alert.silence.match-all": "Match All", "alert.silence.priority": "Priority Match", "alert.silence.type.once": "One Time Silence", @@ -158,6 +159,17 @@ "alert.silence.time": "Silence Period", "alert.silence.times": "Silenced Alerts Num", "alert.silence.enable": "Enable Silence", + "alert.converge.new": "New Converge Strategy", + "alert.converge.edit": "Edit Converge Strategy", + "alert.converge.delete": "Delete Converge Strategy", + "alert.converge.name": "Strategy Name", + "alert.converge.match-all": "Match All", + "alert.converge.priority": "Priority Match", + "alert.converge.tags": "Tag Match", + "alert.converge.repeat": "Repeat Alert Criteria", + "alert.converge.repeat-rule": "The alert tags and alert priority are the same", + "alert.converge.eval-interval": "Repeat Alert Converge Interval(s)", + "alert.converge.enable": "Enable Converge", "alert.center.delete": "Delete Alerts", "alert.center.clear": "Clear All", "alert.center.deal": "Mark Processed", @@ -172,6 +184,9 @@ "alert.center.tags": "Tags", "alert.center.status": "Status", "alert.center.time": "Alert Time", + "alert.center.time.tip": "Alerts were triggered {{times}} times during this alert period", + "alert.center.first-time": "First Time", + "alert.center.last-time": "Latest Time", "alert.center.notify.no-delete": "No items selected for deletion!", "alert.center.confirm.delete": "Please confirm whether to delete!", "alert.center.confirm.delete-batch": "Please confirm whether to delete in batch!", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index befba07cba1..9fa64174293 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -30,6 +30,7 @@ "alert": { "": "告警", "center": "告警中心", + "converge": "告警收敛", "silence": "告警静默", "setting": "阈值规则", "dispatch": "消息通知" @@ -158,6 +159,17 @@ "alert.silence.time": "静默时段", "alert.silence.times": "已静默告警数", "alert.silence.enable": "启用静默策略", + "alert.converge.new": "新增收敛策略", + "alert.converge.edit": "编辑收敛策略", + "alert.converge.delete": "删除收敛策略", + "alert.converge.name": "策略名称", + "alert.converge.match-all": "应用所有", + "alert.converge.priority": "匹配级别", + "alert.converge.tags": "匹配标签", + "alert.converge.repeat": "重复告警判定条件", + "alert.converge.repeat-rule": "告警标签与告警级别相同", + "alert.converge.eval-interval": "重复告警收敛周期(秒)", + "alert.converge.enable": "启用收敛策略", "alert.center.delete": "删除告警", "alert.center.clear": "一键清空", "alert.center.deal": "标记已处理", @@ -172,6 +184,9 @@ "alert.center.tags": "标签", "alert.center.status": "状态", "alert.center.time": "告警时间", + "alert.center.time.tip": "此告警期间统计触发 {{times}} 次告警", + "alert.center.first-time": "首次", + "alert.center.last-time": "最新", "alert.center.notify.no-delete": "未选中任何待删除项!", "alert.center.confirm.delete": "请确认是否删除!", "alert.center.confirm.delete-batch": "请确认是否批量删除!", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 754325a3f50..de4da7409c5 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -31,6 +31,7 @@ "": "告警", "center": "告警中心", "setting": "閾值規則", + "converge": "告警收斂", "silence": "告警靜默", "dispatch": "消息通知" }, @@ -158,6 +159,17 @@ "alert.silence.time": "靜默時段", "alert.silence.times": "已靜默告警數", "alert.silence.enable": "啟用靜默策略", + "alert.converge.new": "新增收斂策略", + "alert.converge.edit": "編輯收斂策略", + "alert.converge.delete": "刪除收斂策略", + "alert.converge.name": "策略名稱", + "alert.converge.match-all": "應用所有", + "alert.converge.priority": "匹配級別", + "alert.converge.tags": "匹配標籤", + "alert.converge.repeat": "重複告警判定條件", + "alert.converge.repeat-rule": "告警標籤與告警級別相同", + "alert.converge.eval-interval": "重複告警收斂週期(秒)", + "alert.converge.enable": "啟用收斂策略", "alert.center.delete": "刪除告警", "alert.center.clear": "一鍵清空", "alert.center.deal": "標記已處理", @@ -172,6 +184,9 @@ "alert.center.tags": "標簽", "alert.center.status": "狀態", "alert.center.time": "告警時間", + "alert.center.time.tip": "此告警期間統計觸發 {{times}} 次告警", + "alert.center.first-time": "首次", + "alert.center.last-time": "最新", "alert.center.notify.no-delete": "未選中任何待刪除項!", "alert.center.confirm.delete": "請確認是否刪除!", "alert.center.confirm.delete-batch": "請確認是否批量刪除!",