🚀 feat(server): 项目监控新增沉默时间配置,避免报警后不再触发报警

This commit is contained in:
小吾立 2024-08-08 14:35:29 +08:00
parent e2cf0b275c
commit e19603b661
7 changed files with 130 additions and 28 deletions

View File

@ -2,6 +2,10 @@
## 2.11.9.1-beta
### 🐣 新增功能
1. 【server】新增 项目监控新增沉默时间配置,避免报警后不再触发报警
### 🐞 解决BUG、优化功能
1. 【all】修复 多会话使用文件跟踪器可能出现多个线程未关闭问题(感谢[@沐剑屏](https://gitee.com/MuJianPing) [Gitee issues IAI0I1](https://gitee.com/dromara/Jpom/issues/IAI0I1)

View File

@ -12,6 +12,8 @@ package org.dromara.jpom.controller.monitor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.keepbx.jpom.IJsonMessage;
import cn.keepbx.jpom.model.JsonMessage;
@ -38,6 +40,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 监控列表
@ -112,6 +115,8 @@ public class MonitorListController extends BaseServerController {
@ValidatorItem(msg = "i18n.configure_monitoring_interval.9741") String execCron,
String notifyUser, String webhook,
String useLanguage,
Integer silenceTime,
String silenceUnit,
HttpServletRequest request) {
String status = getParameter("status");
String autoRestart = getParameter("autoRestart");
@ -155,6 +160,11 @@ public class MonitorListController extends BaseServerController {
monitorModel.setUseLanguage(useLanguage);
monitorModel.notifyUser(notifyUserList);
monitorModel.setName(name);
Integer silenceTime1 = ObjectUtil.defaultIfNull(silenceTime, 0);
Assert.state(silenceTime1 >= 0, "沉默时间不能小于 0");
monitorModel.setSilenceTime(silenceTime1);
TimeUnit timeUnit = EnumUtil.fromString(TimeUnit.class, silenceUnit, TimeUnit.MINUTES);
monitorModel.setSilenceUnit(timeUnit.name());
if (StrUtil.isEmpty(id)) {
//添加监控

View File

@ -73,6 +73,14 @@ public class MonitorModel extends BaseWorkspaceModel {
* 使用语言
*/
private String useLanguage;
/**
* 静默时间
*/
private Integer silenceTime;
/**
* 静默单位
*/
private String silenceUnit;
public String getExecCron() {
if (execCron == null) {

View File

@ -11,6 +11,7 @@ package org.dromara.jpom.monitor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.task.Task;
@ -42,6 +43,7 @@ import org.dromara.jpom.webhook.DefaultWebhookPluginImpl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@ -84,6 +86,7 @@ public class MonitorItem implements Task {
String nodeId = nodeProject.getNode();
NodeModel nodeModel = nodeService.getByKey(nodeId);
if (nodeModel == null) {
log.error("监控项:{} 对应的节点不存在 {}", this.monitorModel.getName(), nodeId);
return true;
}
return this.reqNodeStatus(nodeModel, nodeProject.getProjects());
@ -147,20 +150,30 @@ public class MonitorItem implements Task {
title = StrUtil.format(I18nMessageUtil.get("i18n.node_running_status_abnormal.3160"), nodeModel.getName());
context = ExceptionUtil.stacktraceToString(e);
}
MonitorNotifyLog monitorNotifyLog = new MonitorNotifyLog();
monitorNotifyLog.setStatus(false);
monitorNotifyLog.setTitle(title);
monitorNotifyLog.setContent(context);
monitorNotifyLog.setCreateTime(System.currentTimeMillis());
monitorNotifyLog.setNodeId(nodeModel.getId());
monitorNotifyLog.setProjectId(id);
monitorNotifyLog.setMonitorId(monitorModel.getId());
// 获取上次状态
boolean pre = this.getPreStatus(monitorModel.getId(), nodeModel.getId(), id);
MonitorNotifyLog preData = this.getPreData(monitorModel.getId(), nodeModel.getId(), id);
boolean pre = preData == null || preData.status();
if (pre) {
// 上次正常
MonitorNotifyLog monitorNotifyLog = new MonitorNotifyLog();
monitorNotifyLog.setStatus(false);
monitorNotifyLog.setTitle(title);
monitorNotifyLog.setContent(context);
monitorNotifyLog.setCreateTime(System.currentTimeMillis());
monitorNotifyLog.setNodeId(nodeModel.getId());
monitorNotifyLog.setProjectId(id);
monitorNotifyLog.setMonitorId(monitorModel.getId());
//
this.notifyMsg(nodeModel, monitorNotifyLog);
} else {
Integer silenceTime = this.monitorModel.getSilenceTime();
if (silenceTime != null) {
TimeUnit timeUnit = EnumUtil.fromString(TimeUnit.class, this.monitorModel.getSilenceUnit(), TimeUnit.MINUTES);
long millis = timeUnit.toMillis(this.monitorModel.getSilenceTime());
if (preData.getCreateTime() + millis < System.currentTimeMillis()) {
// 上次不正常并且过了沉默时间
this.notifyMsg(nodeModel, monitorNotifyLog);
}
}
}
return false;
})
@ -180,14 +193,20 @@ public class MonitorItem implements Task {
private boolean checkNotify(MonitorModel monitorModel, NodeModel nodeModel, String id, boolean runStatus, String statusMsg) {
// 获取上次状态
String copyMsg = StrUtil.EMPTY;
boolean pre = this.getPreStatus(monitorModel.getId(), nodeModel.getId(), id);
MonitorNotifyLog preData = this.getPreData(monitorModel.getId(), nodeModel.getId(), id);
boolean pre = preData == null || preData.status();
ProjectInfoCacheModel projectInfoCacheModel = projectInfoCacheService.getData(nodeModel.getId(), id);
String projectName = id;
if (projectInfoCacheModel != null) {
projectName = StrUtil.format("{}/{}", projectInfoCacheModel.getName(), id);
}
String title = null;
String context = null;
//查询项目运行状态
if (runStatus) {
if (!pre) {
// 上次是异常状态
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_resumed_normal_operation.2cbd"), nodeModel.getName(), id, copyMsg);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_resumed_normal_operation.2cbd"), nodeModel.getName(), projectName, copyMsg);
context = "";
}
} else {
@ -199,25 +218,38 @@ public class MonitorItem implements Task {
if (reJson.success()) {
// 重启成功
runStatus = true;
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_successful_restart.603b"), nodeModel.getName(), id, copyMsg);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_successful_restart.603b"), nodeModel.getName(), projectName, copyMsg);
} else {
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_failed_restart.4307"), nodeModel.getName(), id, copyMsg);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_failed_restart.4307"), nodeModel.getName(), projectName, copyMsg);
}
context = I18nMessageUtil.get("i18n.restart_result.253f") + reJson;
} catch (Exception e) {
log.error(I18nMessageUtil.get("i18n.restart_operation.5e3a"), e);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_abnormal_restart.a5c0"), nodeModel.getName(), id, copyMsg);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_stopped_abnormal_restart.a5c0"), nodeModel.getName(), projectName, copyMsg);
context = ExceptionUtil.stacktraceToString(e);
}
} else {
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_not_running.ad89"), nodeModel.getName(), id, copyMsg);
title = StrUtil.format(I18nMessageUtil.get("i18n.node_service_not_running.ad89"), nodeModel.getName(), projectName, copyMsg);
context = I18nMessageUtil.get("i18n.please_check_in_time.3b4f");
}
}
if (!pre && !runStatus) {
// 上一次是异常并且当前还是异常
log.warn("触发报警信息自动忽略,当前处于持续报警中,{}", monitorModel.getName());
return false;
Integer silenceTime = this.monitorModel.getSilenceTime();
if (silenceTime == null) {
log.warn("触发报警信息自动忽略,当前处于持续报警中,{}", monitorModel.getName());
return false;
}
TimeUnit timeUnit = EnumUtil.fromString(TimeUnit.class, this.monitorModel.getSilenceUnit(), TimeUnit.MINUTES);
long millis = timeUnit.toMillis(this.monitorModel.getSilenceTime());
if (preData.getCreateTime() + millis > System.currentTimeMillis()) {
if (preData.getNotifyStatus() != null && preData.getNotifyStatus()) {
// 通知成功
log.warn("触发报警信息自动忽略,上次通知成功并且当前处于持续报警中,{}", monitorModel.getName());
return false;
}
}
}
MonitorNotifyLog monitorNotifyLog = new MonitorNotifyLog();
monitorNotifyLog.setStatus(runStatus);
@ -266,12 +298,20 @@ public class MonitorItem implements Task {
}
private void notifyMsg(NodeModel nodeModel, MonitorNotifyLog monitorNotifyLog) {
ProjectInfoCacheModel projectInfoCacheModel = projectInfoCacheService.getData(nodeModel.getId(), monitorNotifyLog.getProjectId());
this.notifyMsg(nodeModel, monitorNotifyLog, projectInfoCacheModel);
}
private void notifyMsg(NodeModel nodeModel, MonitorNotifyLog monitorNotifyLog, ProjectInfoCacheModel projectInfoCacheModel) {
if (projectInfoCacheModel == null) {
log.error("监控的项目信息丢失不能正常发送监控通知:{} => {}", monitorModel.getName(), monitorNotifyLog.getTitle());
return;
}
List<String> notify = monitorModel.notifyUser();
// 发送通知
if (monitorNotifyLog.getTitle() == null) {
return;
}
ProjectInfoCacheModel projectInfoCacheModel = projectInfoCacheService.getData(nodeModel.getId(), monitorNotifyLog.getProjectId());
monitorNotifyLog.setWorkspaceId(projectInfoCacheModel.getWorkspaceId());
//
notify.forEach(notifyUser -> this.sendNotifyMsgToUser(monitorNotifyLog, notifyUser));

View File

@ -20,3 +20,5 @@ ADD,BUILD_INFO,createUser,String,50,,创建人,false
ADD,SCRIPT_LIBRARY,createUser,String,50,,创建人,false
ALTER,MONITORNOTIFYLOG,title,String,500,,异常描述,false
ADD,MONITOR_INFO,useLanguage,String,20,,使用语言,false
ADD,MONITOR_INFO,silenceTime,Integer,,,沉默时间,false
ADD,MONITOR_INFO,silenceUnit,String,20,,沉默单位,false

1 alterType,tableName,name,type,len,defaultValue,comment,notNull
20 ADD,SCRIPT_LIBRARY,createUser,String,50,,创建人,false
21 ALTER,MONITORNOTIFYLOG,title,String,500,,异常描述,false
22 ADD,MONITOR_INFO,useLanguage,String,20,,使用语言,false
23 ADD,MONITOR_INFO,silenceTime,Integer,,,沉默时间,false
24 ADD,MONITOR_INFO,silenceUnit,String,20,,沉默单位,false

View File

@ -1,7 +1,12 @@
<template>
<div>
<!-- 数据表格 -->
<a-table
<CustomTable
is-show-tools
default-auto-refresh
:auto-refresh-time="30"
table-name="monitor-list"
:active-page="activePage"
:data-source="list"
size="middle"
:columns="columns"
@ -11,6 +16,7 @@
x: 'max-content'
}"
@change="changePage"
@refresh="loadData"
>
<template #title>
<a-space wrap class="search-box">
@ -53,7 +59,7 @@
<a-button type="primary" @click="handleAdd">{{ $t('i18n_66ab5e9f24') }}</a-button>
</a-space>
</template>
<template #bodyCell="{ column, text, record }">
<template #tableBodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'name'">
<a-tooltip placement="topLeft" :title="text">
<span>{{ text }}</span>
@ -96,7 +102,7 @@
</a-space>
</template>
</template>
</a-table>
</CustomTable>
<!-- 编辑区 -->
<CustomModal
v-if="editMonitorVisible"
@ -185,7 +191,7 @@
:disabled="!noFileModes.includes(project.runMode)"
>
<!-- \u3010\u3011 -->
\u3010{{ project.nodeName }}\u3011{{ project.name }} -
{{ `\u3010` }}{{ project.nodeName }}{{ `\u3011` }} {{ project.name }} -
{{ project.runMode }}
</a-select-option>
</a-select-opt-group>
@ -251,6 +257,26 @@
</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="useLanguage">
<template #label>
<a-tooltip>
沉默时间
<QuestionCircleOutlined v-show="!temp.id" />
<template #title>当监控到持续异常时监控通知发送成功后在一段时间内部重复发送报警通知</template>
</a-tooltip>
</template>
<a-input-number v-model:value="temp.silenceTime" placeholder="请输入沉默时间" style="width: 100%">
<template #addonAfter>
<a-select v-model:value="temp.silenceUnit" style="width: 100px" placeholder="选择单位">
<a-select-option value="DAYS"></a-select-option>
<a-select-option value="HOURS">小时</a-select-option>
<a-select-option value="MINUTES">分钟</a-select-option>
<a-select-option value="SECONDS"></a-select-option>
</a-select>
</template>
</a-input-number>
</a-form-item>
</a-form>
</CustomModal>
</div>
@ -358,6 +384,9 @@ export default {
computed: {
pagination() {
return COMPUTED_PAGINATION(this.listQuery)
},
activePage() {
return this.$attrs.routerUrl === this.$route.path
}
},
watch: {},
@ -496,8 +525,8 @@ export default {
status: this.temp.status ? 'on' : 'off',
autoRestart: this.temp.autoRestart ? 'on' : 'off',
projects: JSON.stringify(projects),
notifyUser: JSON.stringify(targetKeysTemp),
useLanguage: this.temp.useLanguage
notifyUser: JSON.stringify(targetKeysTemp)
//useLanguage: this.temp.useLanguage
}
this.confirmLoading = true
editMonitor(params)

View File

@ -1,7 +1,12 @@
<template>
<div>
<!-- 数据表格 -->
<a-table
<CustomTable
is-show-tools
default-auto-refresh
:auto-refresh-time="30"
table-name="monitor-log-list"
:active-page="activePage"
:data-source="list"
size="middle"
:columns="columns"
@ -11,6 +16,7 @@
x: 'max-content'
}"
@change="change"
@refresh="loadData"
>
<template #title>
<a-space wrap class="search-box">
@ -46,7 +52,7 @@
</a-tooltip>
</a-space>
</template>
<template #bodyCell="{ column, text, record }">
<template #tableBodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'nodeId'">
<a-tooltip placement="topLeft" :title="text">
<span>{{ nodeMap[text] }}</span>
@ -70,7 +76,7 @@
<a-button size="small" type="primary" @click="handleDetail(record)">{{ $t('i18n_f26225bde6') }}</a-button>
</template>
</template>
</a-table>
</CustomTable>
<!-- 详情区 -->
<CustomModal
v-if="detailVisible"
@ -172,6 +178,9 @@ export default {
//
pagination() {
return COMPUTED_PAGINATION(this.listQuery)
},
activePage() {
return this.$attrs.routerUrl === this.$route.path
}
},
created() {