diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/NumUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/NumUtil.java index 20e3b1e1..df4a8529 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/NumUtil.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/NumUtil.java @@ -1,11 +1,14 @@ package org.jeecg.common.util; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.HashMap; +import java.util.Map; import static org.jeecg.modules.base.enums.Op.*; import static org.jeecg.modules.base.enums.Op.LE; @@ -126,6 +129,34 @@ public class NumUtil { } } + public static Map compare(Map current, Double threshold, String op){ + boolean cNull = MapUtil.isEmpty(current); + boolean tNull = ObjectUtil.isNull(threshold); + + Map result = new HashMap<>(); + if (cNull || tNull) return result; + + double thresholdV = threshold; + for (Map.Entry entry : current.entrySet()) { + String key = entry.getKey(); + double currentV = entry.getValue(); + if (EQ.getOp().equals(op)){ + result.put(key, currentV == thresholdV); + } else if (GT.getOp().equals(op)) { + result.put(key, currentV > thresholdV); + } else if (GE.getOp().equals(op)) { + result.put(key, currentV >= thresholdV); + } else if (LT.getOp().equals(op)) { + result.put(key, currentV < thresholdV); + } else if (LE.getOp().equals(op)) { + result.put(key, currentV <= thresholdV); + }else { + result.put(key, false); + } + } + return result; + } + public static String kb2Gb(String kb, int scale){ if (StrUtil.isBlank(kb)) return null; diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java index c82afb12..32214f11 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java @@ -7,8 +7,8 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum Item { - EMAIL_CONN("1", "邮箱服务连接情况"), - DATABASE_CONN("2", "数据源连接情况"); + EMAIL_CONN("1", "Connection Status"), + TABLESPACE_USAGE("2", "TableSpace Usage"); private String value; diff --git a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/IAlarmItemService.java b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/IAlarmItemService.java index acdee0d7..aa44f032 100644 --- a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/IAlarmItemService.java +++ b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/IAlarmItemService.java @@ -12,7 +12,7 @@ public interface IAlarmItemService extends IService { List alarmItemsServer(String sourceId); - List alarmItemsDatabase(String sourceId); + List alarmItemsDatabase(String sourceType, String sourceId); List allItems(String sourceType, String sourceId); } diff --git a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmItemServiceImpl.java b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmItemServiceImpl.java index 4ce8875d..6442d529 100644 --- a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmItemServiceImpl.java +++ b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmItemServiceImpl.java @@ -1,15 +1,20 @@ package org.jeecg.modules.service.impl; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.constant.enums.DbType; import org.jeecg.modules.base.dto.ItemDto; import org.jeecg.modules.base.entity.postgre.AlarmItem; +import org.jeecg.modules.base.entity.postgre.AlarmItemDe; +import org.jeecg.modules.base.entity.postgre.SysDatabase; import org.jeecg.modules.base.enums.SourceType; import org.jeecg.modules.mapper.AlarmItemMapper; import org.jeecg.modules.service.IAlarmItemDeService; import org.jeecg.modules.service.IAlarmItemService; +import org.jeecg.modules.service.ISysDatabaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -22,6 +27,9 @@ import java.util.stream.Collectors; @Slf4j public class AlarmItemServiceImpl extends ServiceImpl implements IAlarmItemService { + @Autowired + private ISysDatabaseService databaseService; + @Autowired private IAlarmItemDeService alarmItemDeService; @@ -44,8 +52,16 @@ public class AlarmItemServiceImpl extends ServiceImpl alarmItemsDatabase(String sourceId) { - return baseMapper.alarmItemsDatabase(sourceId); + public List alarmItemsDatabase(String sourceType, String sourceId) { + List itemDtos = baseMapper.alarmItemsDatabase(sourceId).stream() + .map(ItemDto::new).collect(Collectors.toList()); + SysDatabase database = databaseService.getById(sourceId); + String dbType = database.getDbType(); + // 如果数据库类型是Oracle 则添加额外的监控项:表空间使用率 + if (StrUtil.equals(dbType, DbType.ORACLE.getType())) + itemDtos.addAll(alarmItemDeService.alarmItemDes(sourceType).stream() + .map(ItemDto::new).collect(Collectors.toList())); + return itemDtos; } /* @@ -63,8 +79,7 @@ public class AlarmItemServiceImpl extends ServiceImpl update(AlarmRule alarmRule) { AlarmRule alarmRuleOld = getById(alarmRule.getId()); - if (ObjectUtil.isNull(alarmRuleOld)){ - return Result.error(Prompt.DATA_NOT_EXITS); - } String ruleId = alarmRuleOld.getId(); + Integer enabled = alarmRuleOld.getEnabled(); // 保存oldRule的sourceType 避免修改了sourceType String sourceTypeOld = alarmRuleOld.getSourceType(); Rule rule = alarmRule.getRule(); @@ -105,10 +103,10 @@ public class AlarmRuleServiceImpl extends ServiceImpl add(List nuclideNames,String nuclideType,Integer useType) { + @Transactional + public Result add(List nuclideNames, String nuclideType, Integer useType) { if (CollUtil.isEmpty(nuclideNames)) return Result.error(Prompt.PARAM_NOT_EMPTY); if (StrUtil.isBlank(nuclideType)) return Result.error("nuclideType" + Prompt.PARAM_REQUIRED); if (ObjectUtil.isNull(useType)) return Result.error("useType" + Prompt.PARAM_REQUIRED); + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SysDefaultNuclide::getNuclideType, nuclideType); + wrapper.eq(SysDefaultNuclide::getUseType, useType); + this.remove(wrapper); List SysDefaultNuclides = new ArrayList<>(); for (String nuclideName : nuclideNames) { SysDefaultNuclide sysDefaultNuclide = new SysDefaultNuclide(); @@ -73,7 +80,7 @@ public class SysDefaultNuclideServiceImpl extends ServiceImpl newList = ListUtil.toList("A", "F"); + List oldList = ListUtil.toList("A", "B", "C"); + Collection intersection = CollUtil.intersection(newList, oldList); + newList = newList.stream() + .filter(item -> !CollUtil.contains(intersection, item)) + .collect(Collectors.toList()); + System.out.println("add: " + newList); + + oldList = oldList.stream() + .filter(item -> !CollUtil.contains(intersection, item)) + .collect(Collectors.toList()); + System.out.println("del: " + oldList); + } } diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/feignclient/AbnormalAlarmClient.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/feignclient/AbnormalAlarmClient.java index 653cf6c7..8f8e5e73 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/feignclient/AbnormalAlarmClient.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/feignclient/AbnormalAlarmClient.java @@ -1,6 +1,7 @@ package org.jeecg.modules.feignclient; import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.base.dto.SpaceInfo; import org.jeecg.modules.base.entity.postgre.AlarmLog; import org.jeecg.modules.base.entity.postgre.SysDatabase; import org.jeecg.modules.base.entity.postgre.SysEmail; @@ -36,6 +37,9 @@ public interface AbnormalAlarmClient { @PutMapping("/sysDatabase/updateStatus") void updateDatabase(@RequestBody SysDatabase database); + @GetMapping("/sysDatabase/spaceInfo") + Result> spaceInfo(@RequestParam String sourceId); + /* SysServerController下相关接口 */ @GetMapping("/sysServer/getNameById") String getServerName(@RequestParam String id); diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/message/handle/impl/EmailPushMsgHandle.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/message/handle/impl/EmailPushMsgHandle.java index 7f3a8ba1..a0528728 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/message/handle/impl/EmailPushMsgHandle.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/message/handle/impl/EmailPushMsgHandle.java @@ -61,8 +61,7 @@ public class EmailPushMsgHandle implements ISendMsgHandle { String context = messageDTO.getContent(); String alarmTitle = "邮件发送失败提醒"; StringBuilder alarmContext = new StringBuilder(); - alarmContext.append("系统给您发送的邮件 [标题: ").append(title) - .append(", 内容: ").append(context).append("]发送失败,失败原因: "); + alarmContext.append("系统给您发送的邮件 [标题: ").append(title).append(", 内容: ").append(context).append("]发送失败,失败原因: "); // 循环给每一个用户发送邮件 for (Map.Entry userEmails : messageDTO.getUserEmail().entrySet()) { String username = userEmails.getKey(); @@ -71,9 +70,9 @@ public class EmailPushMsgHandle implements ISendMsgHandle { messageDTO.setToUser(email); emailService.sendMailToOne(messageDTO); } catch (MessagingException e) { - if (e instanceof SMTPAddressFailedException) { // 无效的电子邮箱异常 + if (e instanceof SMTPAddressFailedException) { alarmContext.append("无效的电子邮箱: ").append(email); - } else if (e instanceof SendFailedException) { // 邮件发送失败异常 + } else { alarmContext.append(e.getMessage()); } // 邮件发送失败 给用户发送站内提示消息 diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/DatabaseJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/DatabaseJob.java index 6a2fd875..6de21045 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/DatabaseJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/DatabaseJob.java @@ -1,6 +1,7 @@ package org.jeecg.modules.quartz.jobs; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; @@ -18,6 +19,7 @@ import org.jeecg.modules.base.entity.Rule; import org.jeecg.modules.base.entity.monitor.ItemHistory; import org.jeecg.modules.base.entity.postgre.AlarmLog; import org.jeecg.modules.base.entity.postgre.AlarmRule; +import org.jeecg.modules.base.enums.Item; import org.jeecg.modules.feignclient.ManageUtil; import org.jeecg.modules.quartz.entity.Monitor; import org.quartz.*; @@ -67,6 +69,8 @@ public class DatabaseJob extends Monitor { operator = alarmRule.getOperator(); String ruleId = alarmRule.getId(); String itemId = alarmRule.getItemId(); + // 如果是其它自定义监控项(表空间使用率、...) 则跳过 + if (CollUtil.contains(ListUtil.toList(Item.TABLESPACE_USAGE.getValue()), itemId)) continue; String type = alarmRule.getItemType(); Integer itemType = StrUtil.isBlank(type) ? 0 : Integer.parseInt(type); String silenceKey = prefixSilence + ruleId; @@ -80,18 +84,6 @@ public class DatabaseJob extends Monitor { String sourceId = alarmRule.getSourceId(); String databaseName = getAlarmClient().getDatabaseName(sourceId); - /*// 根据监控项id选择要查询的监控项信息 - Item item = Item.of(itemId); - if (ObjectUtil.isNull(item)) continue; - Number current = null; - switch (item){ - case DATABASE_CONN: // 监控项-2: 测试数据源是否可以连接成功 - current = isConnection(sourceId); - break; - // 追加的监控项... - default: - break; - }*/ // 向运管查询监控项数据 String token = ManageUtil.getToken(); Result result = getMonitorSystem().itemBack(itemId, itemType, start, end, token); @@ -141,16 +133,4 @@ public class DatabaseJob extends Monitor { } destroy(); } - - /* - * 监控项-2: 测试数据源是否可以连接成功 (0:失败 1:成功) - * */ - /*private Integer isConnection(String databaseId){ - int res = 1; - String statusKey = RedisConstant.DATABASE_STATUS; - NameValue nameValue = (NameValue)getRedisUtil().hget(statusKey, databaseId); - if (ObjectUtil.isNull(nameValue) || ObjectUtil.isNull(nameValue.getValue()) || !nameValue.getValue()) - res = 0; - return res; - }*/ } diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/TableSpaceJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/TableSpaceJob.java new file mode 100644 index 00000000..9705aceb --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/jobs/TableSpaceJob.java @@ -0,0 +1,160 @@ +package org.jeecg.modules.quartz.jobs; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.dto.message.MessageDTO; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.DateConstant; +import org.jeecg.common.constant.RedisConstant; +import org.jeecg.common.util.DataTool; +import org.jeecg.common.util.NumUtil; +import org.jeecg.common.util.TemplateUtil; +import org.jeecg.modules.base.dto.SpaceInfo; +import org.jeecg.modules.base.entity.Rule; +import org.jeecg.modules.base.entity.monitor.ItemHistory; +import org.jeecg.modules.base.entity.postgre.AlarmLog; +import org.jeecg.modules.base.entity.postgre.AlarmRule; +import org.jeecg.modules.base.enums.Item; +import org.jeecg.modules.feignclient.ManageUtil; +import org.jeecg.modules.quartz.entity.Monitor; +import org.jeecg.modules.system.service.ISysDataSourceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.jeecg.modules.base.enums.Item.TABLESPACE_USAGE; +import static org.jeecg.modules.base.enums.SourceType.DATABASE; +import static org.jeecg.modules.base.enums.Template.MONITOR_DATABASE; + +@Slf4j +@Component +public class TableSpaceJob extends Monitor { + /** + * 解析Oracle 表空间预警规则 + **/ + @Scheduled(cron = "${task.period-space:0 0/1 * * * ?}") + public void execute(){ + init(); + + // 查询所有Database的报警规则,根据报警规则查询监控项数据 + String pattern = RedisConstant.PREFIX_RULE + DATABASE.getType(); + Set keys = getRedisStreamUtil().keys(pattern); + if (CollUtil.isEmpty(keys)) return; + + String prefixSilence = RedisConstant.PREFIX_SILENCE; + String operator = null; + for (String ruleKey : keys) { + try { + AlarmRule alarmRule = (AlarmRule) getRedisStreamUtil().get(ruleKey); + // 如果报警规则为空,或者在沉默周期内,跳过当前规则 + operator = alarmRule.getOperator(); + String ruleId = alarmRule.getId(); + String itemId = alarmRule.getItemId(); + String silenceKey = prefixSilence + ruleId; + boolean hasKey = getRedisStreamUtil().hasKey(silenceKey); + boolean blank1 = StrUtil.isBlank(operator); + boolean blank2 = StrUtil.isBlank(itemId); + + if (blank1 || blank2 || hasKey) continue; + + // 根据sourceId查询Database信息(缓存) + String sourceId = alarmRule.getSourceId(); + String databaseName = getAlarmClient().getDatabaseName(sourceId); + + // 根据监控项id选择要查询的监控项信息 + Item item = Item.of(itemId); + if (ObjectUtil.isNull(item)) continue; + + // 解析预警规则,判断是否需要报警 + boolean needWarn = false; + Map result = null; + Map spaceMap = null; + ObjectMapper mapper = new ObjectMapper(); + Rule rule = mapper.readValue(operator, Rule.class); + String op = rule.getOperator(); + Double threshold = rule.getThreshold(); + + if (item == TABLESPACE_USAGE){ // 监控项id: 2 Oracle表空间使用率 + List spaceInfos = getAlarmClient().spaceInfo(sourceId).getResult(); + if (CollUtil.isEmpty(spaceInfos)) continue; + spaceMap = spaceInfos.stream().collect(Collectors.toMap(SpaceInfo::getSpaceName, SpaceInfo::getUsed)); + result = NumUtil.compare(spaceMap, threshold, op); + needWarn = result.containsValue(true); + } + + // 如果当前值超过阈值 则需要发送报警信息 + if (needWarn){ + // 记录报警日志 + AlarmLog alarmLog = new AlarmLog(); + alarmLog.setRuleId(ruleId); + alarmLog.setOperator(operator); + alarmLog.setAlarmValue(value(spaceMap, result)); + + String ruleName = alarmRule.getName(); + Map data = DataTool.getInstance(). + put(databaseName).put(ruleName).put(rule.joint()).put(info(spaceMap, result)).get(); + MessageDTO messageDTO = TemplateUtil.parse(MONITOR_DATABASE.getCode(), data); + + alarmLog.setAlarmInfo(messageDTO.getContent()); + getAlarmClient().create(alarmLog); + // 规则触发报警后,设置该规则的沉默周期(如果有) + // 沉默周期失效之前,该规则不会再次被触发 + Long silenceCycle = alarmRule.getSilenceCycle(); + ruleSilence(silenceKey, silenceCycle); + + // 发送报警信息 + String groupId = alarmRule.getContactId(); + String notific = alarmRule.getNotification(); + getSendMessage().send(messageDTO, groupId, notific); + getPushAppUtil().pushToSingle(messageDTO, groupId); + } + } catch (JsonProcessingException e) { + log.error("Database-TableSpace预警规则: {}解析失败,失败原因: {}", operator, e.getMessage()); + }catch (Exception e){ + log.error("Database-TableSpace监控异常: {}", e.getMessage()); + } + } + destroy(); + } + + /* + * 所有的预警值 以,分割 + * */ + private String value(Map sapceMap, Map result){ + List keys = result.entrySet().stream() + .filter(Map.Entry::getValue) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + return sapceMap.entrySet().stream() + .filter(entry -> CollUtil.contains(keys, entry.getKey())) + .map(entry -> String.valueOf(entry.getValue())) + .collect(Collectors.joining(", ")); + } + + /* + * [表空间名1: 预警值1, 表空间名2: 预警值2] + * */ + private String info(Map sapceMap, Map result){ + List keys = result.entrySet().stream() + .filter(Map.Entry::getValue) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + return "[" + sapceMap.entrySet().stream() + .filter(entry -> CollUtil.contains(keys, entry.getKey())) + .map(entry -> entry.getKey() + ": " + entry.getValue()) + .collect(Collectors.joining(", ")) + + "]"; + } +}