mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-29 18:38:32 +08:00
🚀 feat(server): 项目监控新增沉默时间配置,避免报警后不再触发报警
This commit is contained in:
parent
e2cf0b275c
commit
e19603b661
@ -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) )
|
||||
|
@ -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)) {
|
||||
//添加监控
|
||||
|
@ -73,6 +73,14 @@ public class MonitorModel extends BaseWorkspaceModel {
|
||||
* 使用语言
|
||||
*/
|
||||
private String useLanguage;
|
||||
/**
|
||||
* 静默时间
|
||||
*/
|
||||
private Integer silenceTime;
|
||||
/**
|
||||
* 静默单位
|
||||
*/
|
||||
private String silenceUnit;
|
||||
|
||||
public String getExecCron() {
|
||||
if (execCron == null) {
|
||||
|
@ -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,10 +150,6 @@ public class MonitorItem implements Task {
|
||||
title = StrUtil.format(I18nMessageUtil.get("i18n.node_running_status_abnormal.3160"), nodeModel.getName());
|
||||
context = ExceptionUtil.stacktraceToString(e);
|
||||
}
|
||||
// 获取上次状态
|
||||
boolean pre = this.getPreStatus(monitorModel.getId(), nodeModel.getId(), id);
|
||||
if (pre) {
|
||||
// 上次正常
|
||||
MonitorNotifyLog monitorNotifyLog = new MonitorNotifyLog();
|
||||
monitorNotifyLog.setStatus(false);
|
||||
monitorNotifyLog.setTitle(title);
|
||||
@ -159,8 +158,22 @@ public class MonitorItem implements Task {
|
||||
monitorNotifyLog.setNodeId(nodeModel.getId());
|
||||
monitorNotifyLog.setProjectId(id);
|
||||
monitorNotifyLog.setMonitorId(monitorModel.getId());
|
||||
//
|
||||
// 获取上次状态
|
||||
MonitorNotifyLog preData = this.getPreData(monitorModel.getId(), nodeModel.getId(), id);
|
||||
boolean pre = preData == null || preData.status();
|
||||
if (pre) {
|
||||
// 上次正常
|
||||
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,26 +218,39 @@ 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) {
|
||||
// 上一次是异常,并且当前还是异常
|
||||
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);
|
||||
monitorNotifyLog.setTitle(title);
|
||||
@ -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));
|
||||
|
@ -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,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)
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user