Merge pull request #143 from dromara/dev

Dev
This commit is contained in:
李佳航
2025-10-21 17:04:29 +08:00
committed by GitHub
133 changed files with 1516 additions and 934 deletions

View File

@@ -1,6 +1,6 @@
version: '3.3'
# latest = 2.5.3
# latest = 2.5.4
# 支持以下源
# lijiahangmax/*

View File

@@ -7,7 +7,7 @@ set -e
source ./project-build.sh "$@"
# 版本号
version=2.5.3
version=2.5.4
# 是否推送镜像
push_image=false
# 是否构建 latest

View File

@@ -4,7 +4,7 @@ set -e
# DockerContext: orion-visor
# 版本号
version=2.5.3
version=2.5.4
# 是否构建 service
export build_service=false
# 是否构建 ui

View File

@@ -36,7 +36,7 @@ public interface AppConst extends OrionConst {
/**
* 同 ${orion.version} 迭代时候需要手动更改
*/
String VERSION = "2.5.3";
String VERSION = "2.5.4";
/**
* 同 ${spring.application.name}

View File

@@ -34,116 +34,116 @@ public interface ConfigKeys {
/**
* SFTP 文件预览大小
*/
String SFTP_PREVIEW_SIZE = "sftp_previewSize";
String SFTP_PREVIEW_SIZE = "sftp.preview-size";
/**
* SFTP 重复文件备份
*/
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp_uploadPresentBackup";
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp.upload-present-backup";
/**
* SFTP 备份文件名称
*/
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp_uploadBackupFileName";
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp.upload-backup-file-name";
/**
* 加密公钥
*/
String ENCRYPT_PUBLIC_KEY = "encrypt_publicKey";
String ENCRYPT_PUBLIC_KEY = "encrypt.public-key";
/**
* 加密私钥
*/
String ENCRYPT_PRIVATE_KEY = "encrypt_privateKey";
String ENCRYPT_PRIVATE_KEY = "encrypt.private-key";
/**
* 日志前端显示行数
*/
String LOG_WEB_SCROLL_LINES = "log_webScrollLines";
String LOG_WEB_SCROLL_LINES = "log.web-scroll-lines";
/**
* 日志加载偏移行
*/
String LOG_TRACKER_LOAD_LINES = "log_trackerLoadLines";
String LOG_TRACKER_LOAD_LINES = "log.tracker-load-lines";
/**
* 日志加载间隔毫秒
*/
String LOG_TRACKER_LOAD_INTERVAL = "log_trackerLoadInterval";
String LOG_TRACKER_LOAD_INTERVAL = "log.tracker-load-interval";
/**
* 是否生成详细的执行日志
*/
String LOG_EXEC_DETAIL_LOG = "log_execDetailLog";
String LOG_EXEC_DETAIL_ENABLED = "log.exec-detail.enabled";
/**
* 凭证有效期分
* 凭证有效期
*/
String LOGIN_LOGIN_SESSION_TIME = "login_loginSessionTime";
String LOGIN_LOGIN_SESSION_TIME = "login.login-session-time";
/**
* 允许多端登录
*/
String LOGIN_ALLOW_MULTI_DEVICE = "login_allowMultiDevice";
String LOGIN_ALLOW_MULTI_DEVICE = "login.allow-multi-device";
/**
* 允许凭证续签
*/
String LOGIN_ALLOW_REFRESH = "login_allowRefresh";
String LOGIN_ALLOW_REFRESH = "login.allow-refresh";
/**
* 凭证续签最大次数
*/
String LOGIN_MAX_REFRESH_COUNT = "login_maxRefreshCount";
String LOGIN_MAX_REFRESH_COUNT = "login.max-refresh-count";
/**
* 凭证续签间隔分
*/
String LOGIN_REFRESH_INTERVAL = "login_refreshInterval";
String LOGIN_REFRESH_INTERVAL = "login.refresh-interval";
/**
* 登录失败锁定
*/
String LOGIN_LOGIN_FAILED_LOCK = "login_loginFailedLock";
String LOGIN_LOGIN_FAILED_LOCK = "login.login-failed-lock";
/**
* 登录失败锁定阈值
* 登录失败锁定阈值
*/
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login_loginFailedLockThreshold";
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login.login-failed-lock-threshold";
/**
* 登录失败锁定时间分
* 登录失败锁定时间
*/
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login_loginFailedLockTime";
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login.login-failed-lock-time";
/**
* 登录失败发信
*/
String LOGIN_LOGIN_FAILED_SEND = "login_loginFailedSend";
String LOGIN_LOGIN_FAILED_SEND = "login.login-failed-send";
/**
* 登录失败发信阈值
*/
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login_loginFailedSendThreshold";
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login.login-failed-send-threshold";
/**
* 是否开启自动清理命令记录
*/
String AUTO_CLEAR_EXEC_LOG_ENABLED = "autoClear_execLogEnabled";
String AUTO_CLEAR_EXEC_LOG_ENABLED = "auto-clear.exec-log.enabled";
/**
* 自动清理命令记录保留天数
*/
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "autoClear_execLogKeepDays";
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "auto-clear.exec-log.keep-days";
/**
* 是否开启自动清理终端连接记录
*/
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "autoClear_terminalLogEnabled";
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "auto-clear.terminal-log.enabled";
/**
* 自动清理终端连接记录保留天数
*/
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "autoClear_terminalLogKeepDays";
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "auto-clear.terminal-log.keep-days";
}

View File

@@ -14,11 +14,11 @@
<url>https://github.com/dromara/orion-visor</url>
<properties>
<revision>2.5.3</revision>
<revision>2.5.4</revision>
<spring.boot.version>2.7.17</spring.boot.version>
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
<orion.kit.version>2.0.4</orion.kit.version>
<orion.kit.version>2.0.5</orion.kit.version>
<aspectj.version>1.9.7</aspectj.version>
<lombok.version>1.18.26</lombok.version>
<springdoc.version>1.6.15</springdoc.version>

View File

@@ -91,6 +91,7 @@
<a-table row-key="id"
#end
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
#if($vue.enableRowSelection)
@@ -99,6 +100,7 @@
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
#foreach($field in ${table.fields})

View File

@@ -14,7 +14,7 @@ spring:
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:Data@123456}
database: ${REDIS_DATABASE:1}
data-version: ${REDIS_DATA_VERSION:1}
data-version: ${REDIS_DATA_VERSION:2}
mock: false
redisson:
threads: 2

View File

@@ -25,7 +25,7 @@ spring:
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:Data@123456}
database: ${REDIS_DATABASE:0}
data-version: ${REDIS_DATA_VERSION:1}
data-version: ${REDIS_DATA_VERSION:2}
redisson:
threads: 4
netty-threads: 4

View File

@@ -39,9 +39,9 @@ import java.util.function.Function;
*/
public class ReplaceVersion {
private static final String TARGET_VERSION = "2.5.2";
private static final String TARGET_VERSION = "2.5.3";
private static final String REPLACE_VERSION = "2.5.3";
private static final String REPLACE_VERSION = "2.5.4";
private static final String PATH = new File("").getAbsolutePath();

View File

@@ -50,12 +50,12 @@ public class AppLogConfig {
/**
* 是否生成详细的执行日志
*/
private final ConfigRef<Boolean> execDetailLog;
private final ConfigRef<Boolean> execDetailEnabled;
public AppLogConfig(ConfigStore configStore) {
this.trackerLoadLines = configStore.int32(ConfigKeys.LOG_TRACKER_LOAD_LINES);
this.trackerLoadInterval = configStore.int32(ConfigKeys.LOG_TRACKER_LOAD_INTERVAL);
this.execDetailLog = configStore.bool(ConfigKeys.LOG_EXEC_DETAIL_LOG);
this.execDetailEnabled = configStore.bool(ConfigKeys.LOG_EXEC_DETAIL_ENABLED);
}
public Integer getTrackerLoadLines() {
@@ -66,8 +66,8 @@ public class AppLogConfig {
return trackerLoadInterval.value;
}
public Boolean getExecDetailLog() {
return execDetailLog.value;
public Boolean getExecDetailEnabled() {
return execDetailEnabled.value;
}
}

View File

@@ -193,7 +193,7 @@ public class ExecTaskHandler implements IExecTaskHandler {
* @return handler
*/
private IExecCommandHandler createCommandHandler(Long execHostId) {
if (Booleans.isTrue(appLogConfig.getExecDetailLog())) {
if (Booleans.isTrue(appLogConfig.getExecDetailEnabled())) {
// 详细日志
return new ExecCommandDetailHandler(execHostId, execLog, builtParams, timeoutChecker);
} else {

View File

@@ -45,20 +45,27 @@ public interface DataGroupDAO extends IMapper<DataGroupDO> {
*
* @param parentId parentId
* @param type type
* @param userId userId
* @return max(sort)
*/
Integer selectMaxSort(@Param("parentId") Long parentId, @Param("type") String type);
Integer selectMaxSort(@Param("parentId") Long parentId,
@Param("type") String type,
@Param("userId") Long userId);
/**
* 修改排序
*
* @param parentId parentId
* @param type type
* @param userId userId
* @param condition 条件
* @param referSort 对比值
* @param addition 自增步长
* @return effect
*/
Integer updateSort(@Param("parentId") Long parentId,
@Param("type") String type,
@Param("userId") Long userId,
@Param("condition") String condition,
@Param("referSort") Integer referSort,
@Param("addition") Integer addition);

View File

@@ -59,6 +59,7 @@ public interface UserCacheKeyDefine {
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
.key("user:login-failed:{}")
.desc("用户登录失败次数 ${username}")
.noPrefix()
.type(Integer.class)
.struct(RedisCacheStruct.STRING)
.build();

View File

@@ -82,7 +82,7 @@ public class DataGroupServiceImpl implements DataGroupService {
// 查询数据是否冲突
this.checkDataGroupPresent(record);
// 查询最大排序
Integer sort = dataGroupDAO.selectMaxSort(request.getParentId(), request.getType());
Integer sort = dataGroupDAO.selectMaxSort(request.getParentId(), request.getType(), request.getUserId());
record.setSort(sort + Const.DEFAULT_SORT);
// 插入
int effect = dataGroupDAO.insert(record);
@@ -130,14 +130,19 @@ public class DataGroupServiceImpl implements DataGroupService {
Assert.notNull(targetRecord, ErrorMessage.GROUP_ABSENT);
// 更新
String type = moveRecord.getType();
Long userId = moveRecord.getUserId();
Long targetParentId = targetRecord.getParentId();
int effect = 0;
// 修改排序
if (MovePosition.TOP.equals(position)) {
// 移动到元素上 将大于等于 targetRecord 的排序都加 10
dataGroupDAO.updateSort(targetParentId, ">=",
targetRecord.getSort(), Const.DEFAULT_SORT);
// 修改 parentId sort
dataGroupDAO.updateSort(targetParentId,
type,
userId,
">=",
targetRecord.getSort(),
Const.DEFAULT_SORT);
// 修改关联以及排序
DataGroupDO update = DataGroupDO.builder()
.id(id)
.parentId(targetParentId)
@@ -146,8 +151,8 @@ public class DataGroupServiceImpl implements DataGroupService {
effect = dataGroupDAO.updateById(update);
} else if (MovePosition.IN.equals(position)) {
// 移动到元素中 获取最大排序
Integer newSort = dataGroupDAO.selectMaxSort(targetId, type) + Const.DEFAULT_SORT;
// 修改 parentId sort
Integer newSort = dataGroupDAO.selectMaxSort(targetId, type, userId) + Const.DEFAULT_SORT;
// 修改关联以及排序
DataGroupDO update = DataGroupDO.builder()
.id(id)
.parentId(targetId)
@@ -156,9 +161,13 @@ public class DataGroupServiceImpl implements DataGroupService {
effect = dataGroupDAO.updateById(update);
} else if (MovePosition.BOTTOM.equals(position)) {
// 移动到元素下 将大于 targetRecord 的排序都加 10
dataGroupDAO.updateSort(targetParentId, ">",
targetRecord.getSort(), Const.DEFAULT_SORT);
// 修改 parentId sort
dataGroupDAO.updateSort(targetParentId,
type,
userId,
">",
targetRecord.getSort(),
Const.DEFAULT_SORT);
// 修改关联以及排序
DataGroupDO update = DataGroupDO.builder()
.id(id)
.parentId(targetParentId)
@@ -167,7 +176,7 @@ public class DataGroupServiceImpl implements DataGroupService {
effect = dataGroupDAO.updateById(update);
}
// 删除缓存
this.deleteCache(type, moveRecord.getUserId());
this.deleteCache(type, userId);
// 添加日志参数
OperatorLogs.add(OperatorLogs.SOURCE, moveRecord.getName());
OperatorLogs.add(OperatorLogs.TARGET, targetRecord.getName());

View File

@@ -22,19 +22,27 @@
id, parent_id, type, user_id, name, sort, create_time, update_time, creator, updater, deleted
</sql>
<update id="updateSort">
UPDATE data_group
SET sort = sort + #{addition}
WHERE parent_id = #{parentId}
AND sort ${condition} #{referSort}
</update>
<select id="selectMaxSort" resultType="java.lang.Integer">
SELECT IFNULL(MAX(sort), 0)
FROM data_group
WHERE deleted = 0
AND type = #{type}
AND parent_id = #{parentId}
AND type = #{type}
AND parent_id = #{parentId}
<if test="userId != null">
AND user_id = #{userId}
</if>
</select>
<update id="updateSort">
UPDATE data_group
SET sort = sort + #{addition}
WHERE deleted = 0
AND type = #{type}
AND parent_id = #{parentId}
AND sort ${condition} #{referSort}
<if test="userId != null">
AND user_id = #{userId}
</if>
</update>
</mapper>

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.context;
import cn.orionsec.kit.lang.define.cache.TimedCache;
import cn.orionsec.kit.lang.define.cache.TimedCacheBuilder;
@@ -29,15 +29,11 @@ import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.module.monitor.convert.MonitorHostConvert;
import org.dromara.visor.module.monitor.convert.MonitorMetricsConvert;
import org.dromara.visor.module.monitor.dao.MonitorHostDAO;
import org.dromara.visor.module.monitor.dao.MonitorMetricsDAO;
import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO;
import org.dromara.visor.module.monitor.entity.domain.MonitorMetricsDO;
import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO;
import org.dromara.visor.module.monitor.entity.dto.MonitorHostConfigDTO;
import org.dromara.visor.module.monitor.entity.dto.MonitorHostContextDTO;
import org.dromara.visor.module.monitor.entity.dto.MonitorMetricsContextDTO;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@@ -47,7 +43,7 @@ import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 监控上下文
* 监控探针上下文
*
* @author Jiahang Li
* @version 1.0.0
@@ -55,23 +51,13 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@Slf4j
@Component
public class MonitorContext {
public class MonitorAgentContext {
/**
* 监控主机缓存
*/
private static final ConcurrentHashMap<String, MonitorHostContextDTO> MONITOR_HOST_CACHE = new ConcurrentHashMap<>();
/**
* 监控指标缓存
*/
private static final ConcurrentHashMap<Long, MonitorMetricsContextDTO> MONITOR_METRICS_CACHE = new ConcurrentHashMap<>();
/**
* 监控指标引用缓存
*/
private static final ConcurrentHashMap<String, Long> MONITOR_METRICS_KEY_REL = new ConcurrentHashMap<>();
/**
* 最后心跳时间缓存 3min
*/
@@ -88,9 +74,6 @@ public class MonitorContext {
.checkInterval(Const.MS_S_60)
.build();
@Resource
private MonitorMetricsDAO monitorMetricsDAO;
@Resource
private MonitorHostDAO monitorHostDAO;
@@ -103,10 +86,6 @@ public class MonitorContext {
log.info("MonitorContext-init hosts start.");
this.loadMonitorHost();
log.info("MonitorContext-init hosts end.");
// 初始化监控指标
log.info("MonitorContext-init metrics start.");
this.loadMonitorMetrics();
log.info("MonitorContext-init metrics end.");
}
@PreDestroy
@@ -128,17 +107,6 @@ public class MonitorContext {
}
}
/**
* 加载监控指标
*/
public void loadMonitorMetrics() {
MONITOR_METRICS_CACHE.clear();
// 查询全部指标
List<MonitorMetricsDO> metrics = monitorMetricsDAO.selectList(null);
metrics.forEach(s -> MONITOR_METRICS_CACHE.put(s.getId(), MonitorMetricsConvert.MAPPER.toContext(s)));
metrics.forEach(s -> MONITOR_METRICS_KEY_REL.put(this.getMonitorMetricsKey(s.getMeasurement(), s.getValue()), s.getId()));
}
// ----------------------- 监控主机 ----------------------
/**
@@ -194,7 +162,7 @@ public class MonitorContext {
* @param metrics 指标
*/
public void setAgentMetrics(String agentKey, AgentMetricsDataDTO metrics) {
// 设置指标数据
// 设置指标数据
LATEST_METRICS_CACHE.put(agentKey, metrics);
// 更新心跳时间
AGENT_LAST_ACTIVE_TIME.put(agentKey, System.currentTimeMillis());
@@ -237,72 +205,4 @@ public class MonitorContext {
return AGENT_LAST_ACTIVE_TIME.get(agentKey) != null;
}
// ----------------------- 监控指标 ----------------------
/**
* 重新加载监控指标
*
* @param id id
*/
public void reloadMonitorMetrics(Long id) {
// 删除指标缓存
MONITOR_METRICS_CACHE.remove(id);
// 删除指标引用
MONITOR_METRICS_KEY_REL.entrySet().removeIf(entry -> entry.getValue().equals(id));
// 重新加载指标
MonitorMetricsDO metrics = monitorMetricsDAO.selectById(id);
if (metrics == null) {
return;
}
MONITOR_METRICS_CACHE.put(metrics.getId(), MonitorMetricsConvert.MAPPER.toContext(metrics));
MONITOR_METRICS_KEY_REL.put(this.getMonitorMetricsKey(metrics.getMeasurement(), metrics.getValue()), metrics.getId());
}
/**
* 获取监控指标
*
* @param id id
* @return cache
*/
public MonitorMetricsContextDTO getMonitorMetrics(Long id) {
return MONITOR_METRICS_CACHE.get(id);
}
/**
* 获取监控指标
*
* @param measurement measurement
* @param field field
* @return cache
*/
public MonitorMetricsContextDTO getMonitorMetrics(String measurement, String field) {
Long id = MONITOR_METRICS_KEY_REL.get(this.getMonitorMetricsKey(measurement, field));
if (id == null) {
return null;
}
return MONITOR_METRICS_CACHE.get(id);
}
/**
* 获取监控指标 id
*
* @param measurement measurement
* @param field field
* @return id
*/
public Long getMonitorMetricsId(String measurement, String field) {
return MONITOR_METRICS_KEY_REL.get(this.getMonitorMetricsKey(measurement, field));
}
/**
* 获取监控指标 key
*
* @param measurement measurement
* @param field field
* @return key
*/
private String getMonitorMetricsKey(String measurement, String field) {
return measurement + "_" + field;
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed 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.visor.module.monitor.context;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.module.monitor.convert.MonitorMetricsConvert;
import org.dromara.visor.module.monitor.dao.MonitorMetricsDAO;
import org.dromara.visor.module.monitor.entity.domain.MonitorMetricsDO;
import org.dromara.visor.module.monitor.entity.dto.MonitorMetricsContextDTO;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 监控指标上下文
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/10/12 19:39
*/
@Slf4j
@Component
public class MonitorMetricsContext {
/**
* 监控指标缓存
*/
private static final ConcurrentHashMap<Long, MonitorMetricsContextDTO> MONITOR_METRICS_CACHE = new ConcurrentHashMap<>();
/**
* 监控指标引用缓存
*/
private static final ConcurrentHashMap<String, Long> MONITOR_METRICS_KEY_REL = new ConcurrentHashMap<>();
@Resource
private MonitorMetricsDAO monitorMetricsDAO;
/**
* 初始化监控上下文
*/
@PostConstruct
public void initMonitorContext() {
// 初始化监控指标
log.info("MetricsContext-init start.");
this.loadMonitorMetrics();
log.info("MetricsContext-init end.");
}
/**
* 加载监控指标
*/
public void loadMonitorMetrics() {
MONITOR_METRICS_CACHE.clear();
// 查询全部指标
List<MonitorMetricsDO> metrics = monitorMetricsDAO.selectList(null);
metrics.forEach(s -> MONITOR_METRICS_CACHE.put(s.getId(), MonitorMetricsConvert.MAPPER.toContext(s)));
metrics.forEach(s -> MONITOR_METRICS_KEY_REL.put(this.getMonitorMetricsKey(s.getMeasurement(), s.getValue()), s.getId()));
}
// ----------------------- 监控指标 ----------------------
/**
* 重新加载监控指标
*
* @param id id
*/
public void reloadMonitorMetrics(Long id) {
// 删除指标缓存
MONITOR_METRICS_CACHE.remove(id);
// 删除指标引用
MONITOR_METRICS_KEY_REL.entrySet().removeIf(entry -> entry.getValue().equals(id));
// 重新加载指标
MonitorMetricsDO metrics = monitorMetricsDAO.selectById(id);
if (metrics == null) {
return;
}
MONITOR_METRICS_CACHE.put(metrics.getId(), MonitorMetricsConvert.MAPPER.toContext(metrics));
MONITOR_METRICS_KEY_REL.put(this.getMonitorMetricsKey(metrics.getMeasurement(), metrics.getValue()), metrics.getId());
}
/**
* 获取监控指标
*
* @param id id
* @return cache
*/
public MonitorMetricsContextDTO getMonitorMetrics(Long id) {
return MONITOR_METRICS_CACHE.get(id);
}
/**
* 获取监控指标
*
* @param measurement measurement
* @param field field
* @return cache
*/
public MonitorMetricsContextDTO getMonitorMetrics(String measurement, String field) {
Long id = MONITOR_METRICS_KEY_REL.get(this.getMonitorMetricsKey(measurement, field));
if (id == null) {
return null;
}
return MONITOR_METRICS_CACHE.get(id);
}
/**
* 获取监控指标 id
*
* @param measurement measurement
* @param field field
* @return id
*/
public Long getMonitorMetricsId(String measurement, String field) {
return MONITOR_METRICS_KEY_REL.get(this.getMonitorMetricsKey(measurement, field));
}
/**
* 获取监控指标 key
*
* @param measurement measurement
* @param field field
* @return key
*/
private String getMonitorMetricsKey(String measurement, String field) {
return measurement + "_" + field;
}
}

View File

@@ -104,9 +104,10 @@ public class AlarmPolicyController {
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/list")
@Operation(summary = "查询全部监控告警策略")
@Parameter(name = "type", description = "type", required = true)
@PreAuthorize("@ss.hasPermission('monitor:alarm-policy:query')")
public List<AlarmPolicyVO> getAlarmPolicyList() {
return alarmPolicyService.getAlarmPolicyListByCache();
public List<AlarmPolicyVO> getAlarmPolicyList(@RequestParam("type") String type) {
return alarmPolicyService.getAlarmPolicyListByCache(type);
}
@IgnoreLog(IgnoreLogMode.RET)

View File

@@ -35,8 +35,8 @@ import org.dromara.visor.framework.log.core.annotation.IgnoreLog;
import org.dromara.visor.framework.log.core.enums.IgnoreLogMode;
import org.dromara.visor.framework.web.core.annotation.DemoDisableApi;
import org.dromara.visor.framework.web.core.annotation.RestWrapper;
import org.dromara.visor.module.monitor.context.MonitorAgentContext;
import org.dromara.visor.module.monitor.define.operator.MonitorHostOperatorType;
import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO;
import org.dromara.visor.module.monitor.entity.request.host.*;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO;
@@ -68,7 +68,7 @@ public class MonitorHostController {
private MonitorHostService monitorHostService;
@Resource
private MonitorContext monitorContext;
private MonitorAgentContext monitorAgentContext;
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query")
@@ -84,7 +84,7 @@ public class MonitorHostController {
@Parameter(name = "agentKey", description = "agentKey", required = true)
@PreAuthorize("@ss.hasPermission('monitor:monitor-host:query')")
public AgentMetricsDataDTO getMonitorHostOverride(@RequestParam("agentKey") String agentKey) {
return monitorContext.getAgentMetrics(agentKey);
return monitorAgentContext.getAgentMetrics(agentKey);
}
@IgnoreLog(IgnoreLogMode.RET)

View File

@@ -22,6 +22,7 @@
*/
package org.dromara.visor.module.monitor.convert;
import org.dromara.visor.common.mapstruct.JsonConversion;
import org.dromara.visor.module.monitor.entity.domain.AlarmEventDO;
import org.dromara.visor.module.monitor.entity.dto.AlarmEventTriggerDTO;
import org.dromara.visor.module.monitor.entity.dto.AlarmPolicyAlarmCountDTO;
@@ -40,7 +41,7 @@ import java.util.List;
* @version 1.0.0
* @since 2025-9-17 21:31
*/
@Mapper
@Mapper(uses = JsonConversion.class)
public interface AlarmEventConvert {
AlarmEventConvert MAPPER = Mappers.getMapper(AlarmEventConvert.class);

View File

@@ -22,11 +22,11 @@
*/
package org.dromara.visor.module.monitor.convert;
import org.dromara.visor.module.monitor.engine.AlarmEngineRule;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyRuleDO;
import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyRuleCreateRequest;
import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyRuleUpdateRequest;
import org.dromara.visor.module.monitor.entity.vo.AlarmPolicyRuleVO;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEngineRule;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

View File

@@ -22,6 +22,7 @@
*/
package org.dromara.visor.module.monitor.convert;
import org.dromara.visor.common.mapstruct.StringConversion;
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
import org.dromara.visor.module.asset.entity.dto.host.HostQueryDTO;
import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO;
@@ -40,7 +41,7 @@ import org.mapstruct.factory.Mappers;
* @version 1.0.0
* @since 2025-8-14 16:27
*/
@Mapper
@Mapper(uses = StringConversion.class)
public interface MonitorHostConvert {
MonitorHostConvert MAPPER = Mappers.getMapper(MonitorHostConvert.class);

View File

@@ -59,4 +59,13 @@ public interface AlarmPolicyRuleDAO extends IMapper<AlarmPolicyRuleDO> {
return this.selectList(Conditions.eq(AlarmPolicyRuleDO::getPolicyId, policyId));
}
/**
* 通过 policyId 删除
*
* @param policyId policyId
*/
default void deleteByPolicyId(Long policyId) {
this.delete(Conditions.eq(AlarmPolicyRuleDO::getPolicyId, policyId));
}
}

View File

@@ -39,8 +39,8 @@ import java.util.concurrent.TimeUnit;
public interface AlarmPolicyCacheKeyDefine {
CacheKeyDefine ALARM_POLICY = new CacheKeyBuilder()
.key("alarm:policy:list")
.desc("告警策略")
.key("alarm:policy:list:{}")
.desc("告警策略 ${type}")
.type(AlarmPolicyCacheDTO.class)
.struct(RedisCacheStruct.HASH)
.timeout(8, TimeUnit.HOURS)
@@ -49,6 +49,7 @@ public interface AlarmPolicyCacheKeyDefine {
CacheKeyDefine ALARM_RULE_SILENCE = new CacheKeyBuilder()
.key("alarm:silence:{}:{}")
.desc("告警规则沉默标志 ${agentKey} ${ruleId}")
.noPrefix()
.type(Long.class)
.struct(RedisCacheStruct.STRING)
.build();

View File

@@ -57,17 +57,17 @@ public class AlarmEventDO extends BaseDO {
@TableField("agent_key")
private String agentKey;
@Schema(description = "主机id")
@TableField("host_id")
private Long hostId;
@Schema(description = "事件来源")
@TableField("source_type")
private String sourceType;
@Schema(description = "主机名称")
@TableField("host_name")
private String hostName;
@Schema(description = "事件来源id")
@TableField("source_id")
private Long sourceId;
@Schema(description = "主机地址")
@TableField("host_address")
private String hostAddress;
@Schema(description = "事件来源id")
@TableField("source_info")
private String sourceInfo;
@Schema(description = "策略id")
@TableField("policy_id")

View File

@@ -50,6 +50,10 @@ public class AlarmPolicyDO extends BaseDO {
private static final long serialVersionUID = 1L;
@Schema(description = "策略类型")
@TableField("type")
private String type;
@Schema(description = "策略名称")
@TableField("name")
private String name;

View File

@@ -56,14 +56,14 @@ public class AlarmEventTriggerDTO extends BaseDO {
@Schema(description = "agentKey")
private String agentKey;
@Schema(description = "主机id")
private Long hostId;
@Schema(description = "事件来源")
private String sourceType;
@Schema(description = "主机名称")
private String hostName;
@Schema(description = "事件来源id")
private Long sourceId;
@Schema(description = "主机地址")
private String hostAddress;
@Schema(description = "事件来源id")
private String sourceInfo;
@Schema(description = "策略id")
private Long policyId;

View File

@@ -48,13 +48,16 @@ public class AlarmEventQueryRequest extends BaseQueryRequest {
@Schema(description = "id")
private Long id;
@Schema(description = "主机名称")
private Long hostId;
@Size(max = 32)
@Schema(description = "agentKey")
private String agentKey;
@Schema(description = "事件来源")
private String sourceType;
@Schema(description = "事件来源id")
private Long sourceId;
@Schema(description = "策略id")
private Long policyId;

View File

@@ -47,6 +47,10 @@ public class AlarmPolicyCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotBlank
@Schema(description = "策略类型")
private String type;
@NotBlank
@Size(max = 64)
@Schema(description = "策略名称")

View File

@@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.dromara.visor.common.entity.BaseQueryRequest;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
@@ -46,6 +47,10 @@ public class AlarmPolicyQueryRequest extends BaseQueryRequest {
@Schema(description = "id")
private Long id;
@NotBlank
@Schema(description = "策略类型")
private String type;
@Size(max = 64)
@Schema(description = "策略名称")
private String name;

View File

@@ -22,6 +22,7 @@
*/
package org.dromara.visor.module.monitor.entity.vo;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -51,14 +52,14 @@ public class AlarmEventVO implements Serializable {
@Schema(description = "id")
private Long id;
@Schema(description = "主机名称")
private Long hostId;
@Schema(description = "事件来源")
private String sourceType;
@Schema(description = "主机名称")
private String hostName;
@Schema(description = "事件来源id")
private Long sourceId;
@Schema(description = "主机地址")
private String hostAddress;
@Schema(description = "事件来源id")
private JSONObject sourceInfo;
@Schema(description = "agentKey")
private String agentKey;

View File

@@ -51,6 +51,9 @@ public class AlarmPolicyVO implements Serializable {
@Schema(description = "id")
private Long id;
@Schema(description = "策略类型")
private String type;
@Schema(description = "策略名称")
private String name;

View File

@@ -76,6 +76,9 @@ public class MonitorHostVO implements Serializable {
@Schema(description = "主机地址")
private String address;
@Schema(description = "主机类型")
private List<String> types;
@Schema(description = "主机状态")
private String status;

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed 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.visor.module.monitor.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 告警事件来源
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/10/13 22:03
*/
@Getter
@AllArgsConstructor
public enum AlarmEventSourceTypeEnum {
/**
* 主机告警
*/
HOST,
/**
* 拨测告警
*/
UPTIME,
;
public static AlarmEventSourceTypeEnum of(String value) {
if (value == null) {
return null;
}
for (AlarmEventSourceTypeEnum item : values()) {
if (item.name().equals(value)) {
return item;
}
}
return null;
}
}

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.handler.alarm;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.collect.Lists;
@@ -37,6 +37,8 @@ import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyDO;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyNotifyDO;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyRuleDO;
import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEnginePolicy;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEngineRule;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.handler.alarm;
import cn.orionsec.kit.lang.define.cache.TimedCache;
import cn.orionsec.kit.lang.define.cache.TimedCacheBuilder;
@@ -32,23 +32,23 @@ import cn.orionsec.kit.lang.utils.collect.Lists;
import cn.orionsec.kit.lang.utils.collect.Maps;
import cn.orionsec.kit.lang.utils.io.Streams;
import cn.orionsec.kit.lang.utils.time.Dates;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.common.entity.PushUser;
import org.dromara.visor.common.enums.BooleanBit;
import org.dromara.visor.framework.biz.push.core.utils.PushUtils;
import org.dromara.visor.framework.redis.core.utils.RedisStrings;
import org.dromara.visor.module.asset.api.HostAgentApi;
import org.dromara.visor.module.asset.entity.dto.host.HostBaseDTO;
import org.dromara.visor.module.infra.api.SystemUserApi;
import org.dromara.visor.module.monitor.convert.AlarmEventConvert;
import org.dromara.visor.module.monitor.context.MonitorAgentContext;
import org.dromara.visor.module.monitor.context.MonitorMetricsContext;
import org.dromara.visor.module.monitor.define.cache.AlarmPolicyCacheKeyDefine;
import org.dromara.visor.module.monitor.entity.domain.AlarmEventDO;
import org.dromara.visor.module.monitor.entity.dto.*;
import org.dromara.visor.module.monitor.enums.*;
import org.dromara.visor.module.monitor.enums.AlarmLevelEnum;
import org.dromara.visor.module.monitor.enums.AlarmTriggerConditionEnum;
import org.dromara.visor.module.monitor.enums.MetricsUnitEnum;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEnginePolicy;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEngineRule;
import org.dromara.visor.module.monitor.service.AlarmEventService;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@@ -57,62 +57,50 @@ import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 告警引擎
* 告警引擎基类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/8/21 17:26
* @since 2025/10/13 10:12
*/
@Slf4j
@Component
public class AlarmEngine {
public abstract class BaseAlarmEngine implements IAlarmEngine {
/**
* 告警触发状态缓存 10min
*/
private static final TimedCache<AlarmTriggerStateDTO> TRIGGER_STATE_CACHE = TimedCacheBuilder.<AlarmTriggerStateDTO>create()
protected static final TimedCache<AlarmTriggerStateDTO> TRIGGER_STATE_CACHE = TimedCacheBuilder.<AlarmTriggerStateDTO>create()
.expireAfter(10 * Const.MS_S_60)
.checkInterval(Const.MS_S_60)
.build();
@Resource
private AlarmEngineContext alarmEngineContext;
protected AlarmEngineContext alarmEngineContext;
@Resource
private MonitorContext monitorContext;
protected MonitorAgentContext monitorAgentContext;
@Resource
private AlarmEventService alarmEventService;
protected MonitorMetricsContext monitorMetricsContext;
@Resource
private HostAgentApi hostAgentApi;
protected AlarmEventService alarmEventService;
@Resource
private SystemUserApi systemUserApi;
protected SystemUserApi systemUserApi;
@PreDestroy
public void destroyTimedCache() {
Streams.close(TRIGGER_STATE_CACHE);
}
/**
* 检查并且告警
*
* @param agentKey agentKey
* @param prevMetrics prevMetrics
* @param newMetrics newMetrics
*/
@Override
public void checkAndAlarm(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics) {
// 获取主机信息
MonitorHostContextDTO monitorHost = monitorContext.getMonitorHost(agentKey);
if (monitorHost == null) {
return;
}
// 检查策略是否开启
Long policyId = monitorHost.getPolicyId();
if (policyId == null || AlarmSwitchEnum.isOff(monitorHost.getAlarmSwitch())) {
// 获取告警策略
Long policyId = this.getAlarmPolicyId(agentKey);
if (policyId == null) {
return;
}
// 获取对应的策略
@@ -132,7 +120,7 @@ public class AlarmEngine {
// 检查指标
AlarmEventTriggerDTO event = this.checkAndAlarm(agentKey,
prevMetrics, newMetrics, agentMetrics, metricsField,
policy, monitorHost);
policy);
if (event != null) {
alarmEvents.add(event);
}
@@ -147,6 +135,14 @@ public class AlarmEngine {
}
}
/**
* 获取告警策略 id
*
* @param agentKey agentKey
* @return policyId
*/
protected abstract Long getAlarmPolicyId(String agentKey);
/**
* 检查并且告警
*
@@ -156,25 +152,22 @@ public class AlarmEngine {
* @param agentMetrics agentMetrics
* @param metricsField metricsField
* @param policy policy
* @param monitorHost monitorHost
* @return event
*/
private AlarmEventTriggerDTO checkAndAlarm(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics,
AgentMetricsDTO agentMetrics,
String metricsField,
AlarmEnginePolicy policy,
MonitorHostContextDTO monitorHost) {
Long timestamp = newMetrics.getTimestamp();
protected AlarmEventTriggerDTO checkAndAlarm(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics,
AgentMetricsDTO agentMetrics,
String metricsField,
AlarmEnginePolicy policy) {
Long alarmTimestamp = newMetrics.getTimestamp();
// 指标id
Long metricsId = monitorContext.getMonitorMetricsId(agentMetrics.getType(), metricsField);
Long metricsId = monitorMetricsContext.getMonitorMetricsId(agentMetrics.getType(), metricsField);
if (metricsId == null) {
return null;
}
// 指标值
BigDecimal metricsValue;
metricsValue = agentMetrics.getValues().getBigDecimal(metricsField);
BigDecimal metricsValue = agentMetrics.getValues().getBigDecimal(metricsField);
if (metricsValue == null) {
return null;
}
@@ -200,14 +193,30 @@ public class AlarmEngine {
return null;
}
// 检查是否在静默期
boolean inSilence = this.checkAndSetInSilencePeriod(agentKey, timestamp, matchedRule);
boolean inSilence = this.checkAndSetInSilencePeriod(agentKey, alarmTimestamp, matchedRule);
if (inSilence) {
return null;
}
// 创建告警事件
return this.createAlarmEvent(agentKey, monitorHost, timestamp, agentMetrics, metricsValue, matchedRule);
return this.createAlarmEvent(agentKey, alarmTimestamp, agentMetrics, metricsValue, matchedRule);
}
/**
* 创建告警事件
*
* @param agentKey agentKey
* @param alarmTimestamp alarmTimestamp
* @param agentMetrics agentMetrics
* @param metricsValue metricsValue
* @param rule rule
* @return event
*/
protected abstract AlarmEventTriggerDTO createAlarmEvent(String agentKey,
Long alarmTimestamp,
AgentMetricsDTO agentMetrics,
BigDecimal metricsValue,
AlarmEngineRule rule);
/**
* 获取到第一个匹配到达阈值的规则 包含 tag
*
@@ -216,9 +225,9 @@ public class AlarmEngine {
* @param metricsValue metricsValue
* @return rule
*/
private AlarmEngineRule matchTaggedAgentMetricsRule(List<AlarmEngineRule> rules,
Map<String, String> metricsTags,
BigDecimal metricsValue) {
protected AlarmEngineRule matchTaggedAgentMetricsRule(List<AlarmEngineRule> rules,
Map<String, String> metricsTags,
BigDecimal metricsValue) {
AlarmEngineRule matchedRule = null;
// context 根据 level 排序了
for (AlarmEngineRule rule : rules) {
@@ -265,7 +274,7 @@ public class AlarmEngine {
* @param metricsValue metricsValue
* @return rule
*/
private AlarmEngineRule matchAgentMetricsRule(List<AlarmEngineRule> rules, BigDecimal metricsValue) {
protected AlarmEngineRule matchAgentMetricsRule(List<AlarmEngineRule> rules, BigDecimal metricsValue) {
AlarmEngineRule matchedRule = null;
// context 根据 level 排序了
for (AlarmEngineRule rule : rules) {
@@ -289,13 +298,13 @@ public class AlarmEngine {
* @param rule rule
* @param metricValue metricValue
*/
private boolean checkAlarmCondition(AlarmEngineRule rule, BigDecimal metricValue) {
protected boolean checkAlarmCondition(AlarmEngineRule rule, BigDecimal metricValue) {
// 获取指标值
if (metricValue == null) {
return false;
}
// 获取指标单位
MonitorMetricsContextDTO metrics = monitorContext.getMonitorMetrics(rule.getMetricsId());
MonitorMetricsContextDTO metrics = monitorMetricsContext.getMonitorMetrics(rule.getMetricsId());
MetricsUnitEnum unit = Optional.ofNullable(metrics)
.map(MonitorMetricsContextDTO::getUnit)
.map(MetricsUnitEnum::of)
@@ -310,13 +319,8 @@ public class AlarmEngine {
}
// 将阈值转换为原始值
threshold = unit.getThresholdOriginalValue(threshold);
// 触发条件
AlarmTriggerConditionEnum condition = AlarmTriggerConditionEnum.of(rule.getTriggerCondition());
if (condition == null) {
return false;
}
// 判断是否达到触发条件
return this.evaluateCondition(condition, metricValue, threshold);
return this.evaluateCondition(rule.getTriggerCondition(), metricValue, threshold);
}
/**
@@ -327,10 +331,12 @@ public class AlarmEngine {
* @param threshold threshold
* @return eval
*/
private boolean evaluateCondition(AlarmTriggerConditionEnum condition,
BigDecimal metricValue,
BigDecimal threshold) {
switch (condition) {
protected boolean evaluateCondition(String condition,
BigDecimal metricValue,
BigDecimal threshold) {
// 触发条件
AlarmTriggerConditionEnum triggerCondition = AlarmTriggerConditionEnum.of(condition);
switch (triggerCondition) {
case GT:
return metricValue.compareTo(threshold) > 0;
case GE:
@@ -356,10 +362,10 @@ public class AlarmEngine {
* @param rule rule
* @return result
*/
private boolean checkConsecutiveCount(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics,
AlarmEngineRule rule) {
protected boolean checkConsecutiveCount(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics,
AlarmEngineRule rule) {
// 获取规则连续触发次数
Integer ruleConsecutiveCount = Objects1.def(rule.getConsecutiveCount(), 1);
// 获取指标连续触发次数
@@ -393,8 +399,8 @@ public class AlarmEngine {
* @param prevMetrics prevMetrics
* @return isConsecutiveTrigger
*/
private boolean isConsecutiveTrigger(AlarmTriggerStateDTO triggerState,
AgentMetricsDataDTO prevMetrics) {
protected boolean isConsecutiveTrigger(AlarmTriggerStateDTO triggerState,
AgentMetricsDataDTO prevMetrics) {
if (prevMetrics == null || triggerState == null) {
return false;
}
@@ -404,14 +410,14 @@ public class AlarmEngine {
/**
* 检查并且设置静默期
*
* @param agentKey agentKey
* @param timestamp timestamp
* @param rule rule
* @param agentKey agentKey
* @param alarmTimestamp alarmTimestamp
* @param rule rule
* @return inSilence
*/
private boolean checkAndSetInSilencePeriod(String agentKey,
Long timestamp,
AlarmEngineRule rule) {
protected boolean checkAndSetInSilencePeriod(String agentKey,
Long alarmTimestamp,
AlarmEngineRule rule) {
Integer silencePeriod = Objects1.def(rule.getSilencePeriod(), 0);
// 无静默期则触发
if (silencePeriod <= 0) {
@@ -427,75 +433,11 @@ public class AlarmEngine {
.noPrefix()
.timeout(silencePeriod, TimeUnit.MINUTES)
.build();
RedisStrings.set(key, timestamp);
RedisStrings.set(key, alarmTimestamp);
}
return inSilence;
}
/**
* 创建告警事件
*
* @param agentKey agentKey
* @param monitorHost monitorHost
* @param timestamp timestamp
* @param agentMetrics agentMetrics
* @param metricsValue metricsValue
* @param rule rule
* @return event
*/
private AlarmEventTriggerDTO createAlarmEvent(String agentKey,
MonitorHostContextDTO monitorHost,
Long timestamp,
AgentMetricsDTO agentMetrics,
BigDecimal metricsValue,
AlarmEngineRule rule) {
// 查询主机信息
HostBaseDTO host = hostAgentApi.getHostCacheByAgentKey(agentKey);
if (host == null) {
host = new HostBaseDTO();
}
// 获取指标
MonitorMetricsContextDTO metrics = monitorContext.getMonitorMetrics(rule.getMetricsId());
// 指标单位
MetricsUnitEnum unit = MetricsUnitEnum.of(metrics.getUnit());
// 获取连续触发次数
Integer consecutiveCount = Optional.ofNullable(TRIGGER_STATE_CACHE.get(this.getTriggerStateCacheKey(agentKey, rule)))
.map(AlarmTriggerStateDTO::getConsecutiveCount)
.orElse(1);
// 构建告警信息
String alarmInfo = this.buildAlarmInfo(metrics, rule, unit, metricsValue, consecutiveCount);
// 创建告警事件记录
Map<String, String> tags = agentMetrics.getTags();
AlarmEventDO alarmEvent = AlarmEventDO.builder()
.agentKey(agentKey)
.hostId(host.getId())
.hostName(host.getName())
.hostAddress(host.getAddress())
.policyId(rule.getPolicyId())
.policyRuleId(rule.getId())
.metricsId(rule.getMetricsId())
.metricsMeasurement(metrics.getMeasurement())
.alarmTags(tags == null ? Const.EMPTY_OBJECT : JSON.toJSONString(tags))
.alarmValue(metricsValue)
.alarmThreshold(unit.getThresholdOriginalValue(rule.getThreshold()))
.alarmInfo(alarmInfo)
.alarmLevel(rule.getLevel())
.triggerCondition(rule.getTriggerCondition())
.consecutiveCount(consecutiveCount)
.falseAlarm(BooleanBit.FALSE.getValue())
.handleStatus(AlarmHandleStatusEnum.NEW.name())
.handleUserId(monitorHost.getOwnerUserId())
.handleUsername(monitorHost.getOwnerUsername())
.createTime(new Date(timestamp))
.updateTime(new Date(timestamp))
.build();
// 保存告警事件
alarmEventService.createAlarmEvent(alarmEvent);
// 填充其他参数
return AlarmEventConvert.MAPPER.toTrigger(alarmEvent);
}
/**
* 构建告警信息
*
@@ -506,11 +448,11 @@ public class AlarmEngine {
* @param consecutiveCount consecutiveCount
* @return alarmInfo
*/
private String buildAlarmInfo(MonitorMetricsContextDTO metrics,
AlarmEngineRule rule,
MetricsUnitEnum unit,
BigDecimal metricsValue,
Integer consecutiveCount) {
protected String buildAlarmInfo(MonitorMetricsContextDTO metrics,
AlarmEngineRule rule,
MetricsUnitEnum unit,
BigDecimal metricsValue,
Integer consecutiveCount) {
return metrics.getName()
+ Const.SPACE + AlarmTriggerConditionEnum.of(rule.getTriggerCondition()).getCondition()
+ Const.SPACE + unit.format(rule.getThreshold(), new MetricsUnitEnum.FormatOptions(2, metrics.getSuffix()))
@@ -525,7 +467,7 @@ public class AlarmEngine {
* @param rule rule
* @return cacheKey
*/
private String getTriggerStateCacheKey(String agentKey, AlarmEngineRule rule) {
protected String getTriggerStateCacheKey(String agentKey, AlarmEngineRule rule) {
return agentKey + ":" + rule.getId();
}
@@ -535,7 +477,7 @@ public class AlarmEngine {
* @param policy policy
* @param alarmEvents alarmEvents
*/
private void notifyAlarmPolicyChannels(AlarmEnginePolicy policy, List<AlarmEventTriggerDTO> alarmEvents) {
protected void notifyAlarmPolicyChannels(AlarmEnginePolicy policy, List<AlarmEventTriggerDTO> alarmEvents) {
List<Long> notifyIdList = policy.getNotifyIdList();
if (Lists.isEmpty(notifyIdList)) {
return;
@@ -549,38 +491,41 @@ public class AlarmEngine {
// 构建参数
List<Map<String, Object>> paramsList = new ArrayList<>();
for (AlarmEventTriggerDTO event : alarmEvents) {
MonitorMetricsContextDTO metrics = monitorContext.getMonitorMetrics(event.getMetricsId());
MetricsUnitEnum unit = MetricsUnitEnum.of(metrics.getUnit());
AlarmLevelEnum level = AlarmLevelEnum.of(event.getAlarmLevel());
AlarmTriggerConditionEnum triggerCondition = AlarmTriggerConditionEnum.of(event.getTriggerCondition());
try {
MonitorMetricsContextDTO metrics = monitorMetricsContext.getMonitorMetrics(event.getMetricsId());
MetricsUnitEnum unit = MetricsUnitEnum.of(metrics.getUnit());
AlarmLevelEnum level = AlarmLevelEnum.of(event.getAlarmLevel());
AlarmTriggerConditionEnum triggerCondition = AlarmTriggerConditionEnum.of(event.getTriggerCondition());
// 告警事件参数
Map<String, Object> params = new HashMap<>();
params.put("id", event.getId());
params.put("relKey", event.getId());
params.put("policyId", policy.getId());
params.put("policyName", policy.getName());
params.put("ruleId", event.getPolicyRuleId());
params.put("hostId", event.getHostId());
params.put("hostName", event.getHostName());
params.put("hostAddress", event.getHostAddress());
params.put("metrics", metrics.getMeasurement() + "." + metrics.getValue());
params.put("metricsId", metrics.getId());
params.put("metricsName", metrics.getName());
params.put("metricsField", metrics.getValue());
params.put("metricsMeasurement", metrics.getMeasurement());
params.put("tags", event.getAlarmTags());
params.put("level", level.name());
params.put("levelLabel", level.getLabel());
params.put("levelSeverity", level.getSeverity());
params.put("levelColor", level.getColor());
params.put("consecutiveCount", event.getConsecutiveCount());
params.put("triggerCondition", triggerCondition.getCondition());
params.put("alarmInfo", event.getAlarmInfo());
params.put("alarmValue", unit.format(event.getAlarmValue(), new MetricsUnitEnum.FormatOptions(2, metrics.getSuffix())));
params.put("alarmThreshold", unit.format(event.getAlarmThreshold(), new MetricsUnitEnum.FormatOptions(4, metrics.getSuffix())));
params.put("alarmTime", Dates.format(event.getCreateTime()));
paramsList.add(params);
// 告警事件参数
Map<String, Object> params = new HashMap<>();
params.put("id", event.getId());
params.put("relKey", event.getId());
params.put("policyId", policy.getId());
params.put("policyName", policy.getName());
params.put("ruleId", event.getPolicyRuleId());
params.put("metrics", metrics.getMeasurement() + "." + metrics.getValue());
params.put("metricsId", metrics.getId());
params.put("metricsName", metrics.getName());
params.put("metricsField", metrics.getValue());
params.put("metricsMeasurement", metrics.getMeasurement());
params.put("tags", event.getAlarmTags());
params.put("level", level.name());
params.put("levelLabel", level.getLabel());
params.put("levelSeverity", level.getSeverity());
params.put("levelColor", level.getColor());
params.put("consecutiveCount", event.getConsecutiveCount());
params.put("triggerCondition", triggerCondition.getCondition());
params.put("alarmInfo", event.getAlarmInfo());
params.put("alarmValue", unit.format(event.getAlarmValue(), new MetricsUnitEnum.FormatOptions(2, metrics.getSuffix())));
params.put("alarmThreshold", unit.format(event.getAlarmThreshold(), new MetricsUnitEnum.FormatOptions(4, metrics.getSuffix())));
params.put("alarmTime", Dates.format(event.getCreateTime()));
// 设置额外告警推送参数
this.setExtraAlarmPushParams(params, event);
paramsList.add(params);
} catch (Exception e) {
log.info("AlarmEngine-setAlarmParams error", e);
}
}
// 推送消息
for (Map<String, Object> params : paramsList) {
@@ -590,4 +535,12 @@ public class AlarmEngine {
}
}
}
/**
* 设置告警推送参数
*
* @param params params
* @param event event
*/
protected abstract void setExtraAlarmPushParams(Map<String, Object> params, AlarmEventTriggerDTO event);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed 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.visor.module.monitor.handler.alarm;
import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO;
/**
* 告警引擎
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/10/13 10:22
*/
public interface IAlarmEngine {
/**
* 检查并且告警
*
* @param agentKey agentKey
* @param prevMetrics prevMetrics
* @param newMetrics newMetrics
*/
void checkAndAlarm(String agentKey,
AgentMetricsDataDTO prevMetrics,
AgentMetricsDataDTO newMetrics);
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed 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.visor.module.monitor.handler.alarm;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.common.enums.BooleanBit;
import org.dromara.visor.module.asset.api.HostAgentApi;
import org.dromara.visor.module.asset.entity.dto.host.HostBaseDTO;
import org.dromara.visor.module.monitor.convert.AlarmEventConvert;
import org.dromara.visor.module.monitor.entity.domain.AlarmEventDO;
import org.dromara.visor.module.monitor.entity.dto.*;
import org.dromara.visor.module.monitor.enums.AlarmEventSourceTypeEnum;
import org.dromara.visor.module.monitor.enums.AlarmHandleStatusEnum;
import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum;
import org.dromara.visor.module.monitor.enums.MetricsUnitEnum;
import org.dromara.visor.module.monitor.handler.alarm.model.AlarmEngineRule;
import org.dromara.visor.module.monitor.handler.alarm.model.HostAlarmSourceInfo;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
/**
* 告警引擎
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/8/21 17:26
*/
@Slf4j
@Component("metricsAlarmEngine")
public class MetricsAlarmEngine extends BaseAlarmEngine {
@Resource
protected HostAgentApi hostAgentApi;
@Override
protected Long getAlarmPolicyId(String agentKey) {
// 获取主机信息
MonitorHostContextDTO monitorHost = monitorAgentContext.getMonitorHost(agentKey);
if (monitorHost == null) {
return null;
}
// 检查策略是否开启
Long policyId = monitorHost.getPolicyId();
if (policyId == null || AlarmSwitchEnum.isOff(monitorHost.getAlarmSwitch())) {
return null;
}
return policyId;
}
@Override
protected AlarmEventTriggerDTO createAlarmEvent(String agentKey,
Long alarmTimestamp,
AgentMetricsDTO agentMetrics,
BigDecimal metricsValue,
AlarmEngineRule rule) {
MonitorHostContextDTO monitorHost = monitorAgentContext.getMonitorHost(agentKey);
// 查询主机信息
HostBaseDTO host = hostAgentApi.getHostCacheByAgentKey(agentKey);
if (host == null) {
host = new HostBaseDTO();
}
// 获取指标
MonitorMetricsContextDTO metrics = monitorMetricsContext.getMonitorMetrics(rule.getMetricsId());
// 指标单位
MetricsUnitEnum unit = MetricsUnitEnum.of(metrics.getUnit());
// 获取连续触发次数
Integer consecutiveCount = Optional.ofNullable(TRIGGER_STATE_CACHE.get(this.getTriggerStateCacheKey(agentKey, rule)))
.map(AlarmTriggerStateDTO::getConsecutiveCount)
.orElse(1);
// 构建告警信息
String alarmInfo = this.buildAlarmInfo(metrics, rule, unit, metricsValue, consecutiveCount);
// 创建告警事件记录
Map<String, String> tags = agentMetrics.getTags();
AlarmEventDO alarmEvent = AlarmEventDO.builder()
.agentKey(agentKey)
.sourceType(AlarmEventSourceTypeEnum.HOST.name())
.sourceId(host.getId())
.sourceInfo(HostAlarmSourceInfo.builder()
.name(host.getName())
.code(host.getCode())
.address(host.getAddress())
.build()
.toJsonString())
.policyId(rule.getPolicyId())
.policyRuleId(rule.getId())
.metricsId(rule.getMetricsId())
.metricsMeasurement(metrics.getMeasurement())
.alarmTags(tags == null ? Const.EMPTY_OBJECT : JSON.toJSONString(tags))
.alarmValue(metricsValue)
.alarmThreshold(unit.getThresholdOriginalValue(rule.getThreshold()))
.alarmInfo(alarmInfo)
.alarmLevel(rule.getLevel())
.triggerCondition(rule.getTriggerCondition())
.consecutiveCount(consecutiveCount)
.falseAlarm(BooleanBit.FALSE.getValue())
.handleStatus(AlarmHandleStatusEnum.NEW.name())
.handleUserId(monitorHost.getOwnerUserId())
.handleUsername(monitorHost.getOwnerUsername())
.createTime(new Date(alarmTimestamp))
.updateTime(new Date(alarmTimestamp))
.build();
// 保存告警事件
alarmEventService.createAlarmEvent(alarmEvent);
// 填充其他参数
return AlarmEventConvert.MAPPER.toTrigger(alarmEvent);
}
@Override
protected void setExtraAlarmPushParams(Map<String, Object> params, AlarmEventTriggerDTO event) {
HostAlarmSourceInfo sourceInfo = JSON.parseObject(event.getSourceInfo(), HostAlarmSourceInfo.class);
params.put("hostId", event.getSourceId());
params.put("hostName", sourceInfo.getName());
params.put("hostCode", sourceInfo.getCode());
params.put("hostAddress", sourceInfo.getAddress());
}
}

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.handler.alarm.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
@@ -48,6 +48,9 @@ public class AlarmEnginePolicy {
@Schema(description = "策略id")
private Long id;
@Schema(description = "策略类型")
private String type;
@Schema(description = "策略名称")
private String name;

View File

@@ -20,7 +20,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.handler.alarm.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;

View File

@@ -20,40 +20,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.module.monitor.engine;
package org.dromara.visor.module.monitor.handler.alarm.model;
import cn.orionsec.kit.lang.able.IJsonObject;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 告警触发状态 - 轻量级缓存对象
* 用于减少内存占用只保存必要的触发状态信息
* 主机告警源信息
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/3 18:00
* @since 2025/10/13 22:09
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AlarmTriggerState {
@Schema(name = "HostAlarmSourceInfo", description = "主机告警源信息")
public class HostAlarmSourceInfo implements IJsonObject {
/**
* 时间戳
*/
private Long timestamp;
@Schema(description = "主机名称")
private String name;
/**
* 是否触发告警
*/
private Boolean triggered;
@Schema(description = "主机编码")
private String code;
/**
* 规则键
*/
private String ruleKey;
@Schema(description = "主机地址")
private String address;
}
}

View File

@@ -23,7 +23,7 @@
package org.dromara.visor.module.monitor.listener;
import org.dromara.visor.module.asset.entity.event.AgentOfflineEvent;
import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.context.MonitorAgentContext;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@@ -41,13 +41,13 @@ import java.util.List;
public class AgentOfflineEventListener implements ApplicationListener<AgentOfflineEvent> {
@Resource
private MonitorContext monitorContext;
private MonitorAgentContext monitorAgentContext;
@SuppressWarnings("unchecked")
@Override
public void onApplicationEvent(AgentOfflineEvent event) {
List<String> agentKeys = (List<String>) event.getSource();
agentKeys.forEach(monitorContext::setAgentOffline);
agentKeys.forEach(monitorAgentContext::setAgentOffline);
}
}

View File

@@ -77,9 +77,10 @@ public interface AlarmPolicyService {
/**
* 通过缓存查询监控告警策略
*
* @param type type
* @return rows
*/
List<AlarmPolicyVO> getAlarmPolicyListByCache();
List<AlarmPolicyVO> getAlarmPolicyListByCache(String type);
/**
* 分页查询监控告警策略

View File

@@ -223,7 +223,8 @@ public class AlarmEventServiceImpl implements AlarmEventService {
public LambdaQueryWrapper<AlarmEventDO> buildQueryWrapper(AlarmEventQueryRequest request) {
return alarmEventDAO.wrapper()
.eq(AlarmEventDO::getId, request.getId())
.eq(AlarmEventDO::getHostId, request.getHostId())
.eq(AlarmEventDO::getSourceType, request.getSourceType())
.eq(AlarmEventDO::getSourceId, request.getSourceId())
.eq(AlarmEventDO::getAgentKey, request.getAgentKey())
.eq(AlarmEventDO::getPolicyId, request.getPolicyId())
.eq(AlarmEventDO::getMetricsId, request.getMetricsId())

View File

@@ -35,7 +35,6 @@ import org.dromara.visor.module.monitor.convert.AlarmPolicyRuleConvert;
import org.dromara.visor.module.monitor.dao.AlarmPolicyDAO;
import org.dromara.visor.module.monitor.dao.AlarmPolicyRuleDAO;
import org.dromara.visor.module.monitor.dao.MonitorMetricsDAO;
import org.dromara.visor.module.monitor.engine.AlarmEngineContext;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyDO;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyRuleDO;
import org.dromara.visor.module.monitor.entity.domain.MonitorMetricsDO;
@@ -44,6 +43,7 @@ import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyRuleUpda
import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyRuleUpdateSwitchRequest;
import org.dromara.visor.module.monitor.entity.vo.AlarmPolicyRuleVO;
import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum;
import org.dromara.visor.module.monitor.handler.alarm.AlarmEngineContext;
import org.dromara.visor.module.monitor.service.AlarmPolicyRuleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

View File

@@ -27,6 +27,7 @@ import cn.orionsec.kit.lang.utils.Booleans;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.common.constant.ErrorMessage;
import org.dromara.visor.common.utils.Assert;
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
@@ -38,7 +39,6 @@ import org.dromara.visor.module.monitor.dao.AlarmPolicyNotifyDAO;
import org.dromara.visor.module.monitor.dao.AlarmPolicyRuleDAO;
import org.dromara.visor.module.monitor.dao.MonitorHostDAO;
import org.dromara.visor.module.monitor.define.cache.AlarmPolicyCacheKeyDefine;
import org.dromara.visor.module.monitor.engine.AlarmEngineContext;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyDO;
import org.dromara.visor.module.monitor.entity.dto.AlarmPolicyAlarmCountDTO;
import org.dromara.visor.module.monitor.entity.dto.AlarmPolicyCacheDTO;
@@ -49,6 +49,7 @@ import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyCreateRe
import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyQueryRequest;
import org.dromara.visor.module.monitor.entity.request.alarm.AlarmPolicyUpdateRequest;
import org.dromara.visor.module.monitor.entity.vo.AlarmPolicyVO;
import org.dromara.visor.module.monitor.handler.alarm.AlarmEngineContext;
import org.dromara.visor.module.monitor.service.AlarmEventService;
import org.dromara.visor.module.monitor.service.AlarmPolicyNotifyService;
import org.dromara.visor.module.monitor.service.AlarmPolicyRuleService;
@@ -110,8 +111,11 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
Long id = record.getId();
// 设置告警通知
alarmPolicyNotifyService.setAlarmPolicyNotify(id, request.getNotifyIdList());
// 删除缓存
// 重新加载上下文
alarmEngineContext.reloadPolicy(id);
// 删除缓存
RedisMaps.delete(AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(record.getType()),
AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(Const.ALL));
// 设置日志参数
OperatorLogs.add(OperatorLogs.ID, id);
log.info("AlarmPolicyService-createAlarmPolicy id: {}, effect: {}", id, effect);
@@ -130,6 +134,11 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
Long newId = this.createAlarmPolicy(request);
// 复制策略规则
alarmPolicyRuleService.copyAlarmPolicyRule(id, newId);
// 重新加载上下文
alarmEngineContext.reloadPolicy(id);
// 删除缓存
RedisMaps.delete(AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(record.getType()),
AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(Const.ALL));
return newId;
}
@@ -143,6 +152,7 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
Assert.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
AlarmPolicyDO updateRecord = AlarmPolicyConvert.MAPPER.to(request);
updateRecord.setType(record.getType());
// 查询数据是否冲突
this.checkAlarmPolicyPresent(updateRecord);
// 更新
@@ -152,8 +162,11 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
alarmPolicyNotifyService.setAlarmPolicyNotify(id, request.getNotifyIdList());
}
log.info("AlarmPolicyService-updateAlarmPolicyById effect: {}", effect);
// 删除缓存
// 重新加载上下文
alarmEngineContext.reloadPolicy(id);
// 删除缓存
RedisMaps.delete(AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(record.getType()),
AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(Const.ALL));
return effect;
}
@@ -171,16 +184,21 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
}
@Override
public List<AlarmPolicyVO> getAlarmPolicyListByCache() {
public List<AlarmPolicyVO> getAlarmPolicyListByCache(String type) {
String cacheKey = AlarmPolicyCacheKeyDefine.ALARM_POLICY.format(type);
// 查询缓存
List<AlarmPolicyCacheDTO> list = RedisMaps.valuesJson(AlarmPolicyCacheKeyDefine.ALARM_POLICY);
List<AlarmPolicyCacheDTO> list = RedisMaps.valuesJson(cacheKey, AlarmPolicyCacheKeyDefine.ALARM_POLICY);
if (list.isEmpty()) {
// 查询数据库
list = alarmPolicyDAO.of().list(AlarmPolicyConvert.MAPPER::toCache);
list = alarmPolicyDAO.of()
.createWrapper()
.eq(!Const.ALL.equals(type), AlarmPolicyDO::getType, type)
.then()
.list(AlarmPolicyConvert.MAPPER::toCache);
// 设置屏障 防止穿透
CacheBarriers.checkBarrier(list, AlarmPolicyCacheDTO::new);
// 设置缓存
RedisMaps.putAllJson(AlarmPolicyCacheKeyDefine.ALARM_POLICY, s -> s.getId().toString(), list);
RedisMaps.putAllJson(cacheKey, AlarmPolicyCacheKeyDefine.ALARM_POLICY, s -> s.getId().toString(), list);
}
// 删除屏障
CacheBarriers.removeBarrier(list);
@@ -248,7 +266,7 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
// 删除策略通知
alarmPolicyNotifyDAO.deleteByPolicyId(id);
// 删除策略规则
alarmPolicyRuleService.deleteByPolicyId(id);
alarmPolicyRuleDAO.deleteByPolicyId(id);
// 删除缓存
alarmEngineContext.reloadPolicy(id);
log.info("AlarmPolicyService-deleteAlarmPolicyById effect: {}", effect);
@@ -259,6 +277,7 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
public LambdaQueryWrapper<AlarmPolicyDO> buildQueryWrapper(AlarmPolicyQueryRequest request) {
return alarmPolicyDAO.wrapper()
.eq(AlarmPolicyDO::getId, request.getId())
.eq(AlarmPolicyDO::getType, request.getType())
.like(AlarmPolicyDO::getName, request.getName())
.like(AlarmPolicyDO::getDescription, request.getDescription());
}
@@ -274,6 +293,7 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
// 更新时忽略当前记录
.ne(AlarmPolicyDO::getId, domain.getId())
// 用其他字段做重复校验
.eq(AlarmPolicyDO::getType, domain.getType())
.eq(AlarmPolicyDO::getName, domain.getName());
// 检查是否存在
boolean present = alarmPolicyDAO.of(wrapper).present();

View File

@@ -23,6 +23,7 @@
package org.dromara.visor.module.monitor.service.impl;
import cn.orionsec.kit.lang.able.Executable;
import cn.orionsec.kit.lang.annotation.Keep;
import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.lang.utils.collect.Lists;
import cn.orionsec.kit.lang.utils.collect.Maps;
@@ -38,14 +39,14 @@ import org.dromara.visor.framework.influxdb.core.utils.InfluxdbUtils;
import org.dromara.visor.module.asset.api.HostApi;
import org.dromara.visor.module.asset.entity.dto.host.HostDTO;
import org.dromara.visor.module.infra.api.SystemUserApi;
import org.dromara.visor.module.monitor.context.MonitorAgentContext;
import org.dromara.visor.module.monitor.dao.MonitorHostDAO;
import org.dromara.visor.module.monitor.engine.AlarmEngine;
import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO;
import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO;
import org.dromara.visor.module.monitor.entity.dto.HostMetaDTO;
import org.dromara.visor.module.monitor.entity.dto.MonitorHostConfigDTO;
import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum;
import org.dromara.visor.module.monitor.handler.alarm.IAlarmEngine;
import org.dromara.visor.module.monitor.service.MonitorAgentEndpointService;
import org.dromara.visor.module.monitor.utils.MetricsUtils;
import org.springframework.scheduling.annotation.Async;
@@ -79,18 +80,19 @@ public class MonitorAgentEndpointServiceImpl implements MonitorAgentEndpointServ
private SystemUserApi systemUserApi;
@Resource
private MonitorContext monitorContext;
private MonitorAgentContext monitorAgentContext;
@Keep
@Resource
private AlarmEngine alarmEngine;
private IAlarmEngine metricsAlarmEngine;
@Override
@Async("metricsExecutor")
public void addMetrics(String agentKey, AgentMetricsDataDTO newMetrics) {
log.info("MonitorAgentEndpointService.addMetrics start agentKey: {}", agentKey);
// 设置数据缓存
AgentMetricsDataDTO prevMetrics = monitorContext.getAgentMetrics(agentKey);
monitorContext.setAgentMetrics(agentKey, newMetrics);
AgentMetricsDataDTO prevMetrics = monitorAgentContext.getAgentMetrics(agentKey);
monitorAgentContext.setAgentMetrics(agentKey, newMetrics);
// 数据点
List<Point> points = newMetrics.getMetrics()
.stream()
@@ -102,7 +104,7 @@ public class MonitorAgentEndpointServiceImpl implements MonitorAgentEndpointServ
// 写入数据点
InfluxdbUtils.writePoints(points);
// 告警
alarmEngine.checkAndAlarm(agentKey, prevMetrics, newMetrics);
metricsAlarmEngine.checkAndAlarm(agentKey, prevMetrics, newMetrics);
}
@Override
@@ -149,7 +151,7 @@ public class MonitorAgentEndpointServiceImpl implements MonitorAgentEndpointServ
}
// 重新加载监控主机上下文
if (newConfig != null) {
monitorContext.reloadMonitorHost(agentKey);
monitorAgentContext.reloadMonitorHost(agentKey);
}
};
// 获取锁并执行同步逻辑

View File

@@ -48,10 +48,10 @@ import org.dromara.visor.module.asset.entity.dto.host.HostQueryDTO;
import org.dromara.visor.module.asset.enums.AgentOnlineStatusEnum;
import org.dromara.visor.module.infra.api.SystemUserApi;
import org.dromara.visor.module.monitor.constant.MetricsConst;
import org.dromara.visor.module.monitor.context.MonitorAgentContext;
import org.dromara.visor.module.monitor.convert.MonitorHostConvert;
import org.dromara.visor.module.monitor.dao.AlarmPolicyDAO;
import org.dromara.visor.module.monitor.dao.MonitorHostDAO;
import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.entity.domain.AlarmPolicyDO;
import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO;
import org.dromara.visor.module.monitor.entity.dto.*;
@@ -102,7 +102,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
private MonitorMetricsService monitorMetricsService;
@Resource
private MonitorContext monitorContext;
private MonitorAgentContext monitorAgentContext;
@Override
public DataGrid<MonitorHostVO> getMonitorHostPage(MonitorHostQueryRequest request) {
@@ -308,7 +308,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
monitorHostDAO.setPolicyIdWithNullById(id);
}
// 重新加载监控主机上下文
monitorContext.reloadMonitorHost(host.getAgentKey());
monitorAgentContext.reloadMonitorHost(host.getAgentKey());
log.info("MonitorHostService-updateMonitorHostById effect: {}", effect);
return effect;
}
@@ -334,7 +334,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
log.info("MonitorHostService-updateMonitorHostAlarmSwitch effect: {}", effect);
// 更新缓存
for (HostDTO host : hostList) {
monitorContext.reloadMonitorHost(host.getAgentKey());
monitorAgentContext.reloadMonitorHost(host.getAgentKey());
}
return effect;
}
@@ -350,7 +350,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
// 删除
int effect = monitorHostDAO.deleteByHostIdList(hostIdList);
// 删除缓存
hosts.forEach(s -> monitorContext.removeMonitorHost(s.getAgentKey()));
hosts.forEach(s -> monitorAgentContext.removeMonitorHost(s.getAgentKey()));
log.info("MonitorHostService.deleteByHostIdList finish effect: {}", effect);
return effect;
}
@@ -394,7 +394,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
List<String> fields = request.getFields();
// 获取配置信息
List<MonitorHostConfigDTO> configList = agentKeys.stream()
.map(monitorContext::getMonitorHost)
.map(monitorAgentContext::getMonitorHost)
.map(MonitorHostContextDTO::getConfig)
.filter(Objects::nonNull)
.collect(Collectors.toList());
@@ -446,7 +446,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
* @return data
*/
public MonitorHostMetricsDataVO getHostMetricsData(String agentKey, MonitorHostConfigDTO config) {
AgentMetricsDataDTO metrics = monitorContext.getAgentMetrics(agentKey);
AgentMetricsDataDTO metrics = monitorAgentContext.getAgentMetrics(agentKey);
// 无数据
if (metrics == null) {
return MonitorHostMetricsDataVO.noData(agentKey);
@@ -454,7 +454,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
// 从缓存中获取配置
if (config == null) {
config = Optional.of(agentKey)
.map(monitorContext::getMonitorHost)
.map(monitorAgentContext::getMonitorHost)
.map(MonitorHostContextDTO::getConfig)
.orElse(null);
}

View File

@@ -31,10 +31,10 @@ import org.dromara.visor.common.utils.Assert;
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import org.dromara.visor.framework.redis.core.utils.RedisMaps;
import org.dromara.visor.framework.redis.core.utils.barrier.CacheBarriers;
import org.dromara.visor.module.monitor.context.MonitorMetricsContext;
import org.dromara.visor.module.monitor.convert.MonitorMetricsConvert;
import org.dromara.visor.module.monitor.dao.MonitorMetricsDAO;
import org.dromara.visor.module.monitor.define.cache.MonitorMetricsCacheKeyDefine;
import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.entity.domain.MonitorMetricsDO;
import org.dromara.visor.module.monitor.entity.dto.MonitorMetricsCacheDTO;
import org.dromara.visor.module.monitor.entity.dto.MonitorMetricsContextDTO;
@@ -70,7 +70,7 @@ public class MonitorMetricsServiceImpl implements MonitorMetricsService {
private AlarmPolicyRuleService alarmPolicyRuleService;
@Resource
private MonitorContext monitorContext;
private MonitorMetricsContext monitorMetricsContext;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -88,7 +88,7 @@ public class MonitorMetricsServiceImpl implements MonitorMetricsService {
// 设置日志参数
OperatorLogs.add(OperatorLogs.ID, id);
// 重新加载本地缓存
monitorContext.reloadMonitorMetrics(id);
monitorMetricsContext.reloadMonitorMetrics(id);
log.info("MonitorMetricsService-createMonitorMetrics id: {}, effect: {}", id, effect);
return id;
}
@@ -112,7 +112,7 @@ public class MonitorMetricsServiceImpl implements MonitorMetricsService {
// 删除缓存
RedisMaps.delete(MonitorMetricsCacheKeyDefine.MONITOR_METRICS);
// 重新加载本地缓存
monitorContext.reloadMonitorMetrics(id);
monitorMetricsContext.reloadMonitorMetrics(id);
return effect;
}
@@ -151,7 +151,7 @@ public class MonitorMetricsServiceImpl implements MonitorMetricsService {
@Override
public String getMetricName(String measurement, String value) {
MonitorMetricsContextDTO metrics = monitorContext.getMonitorMetrics(measurement, value);
MonitorMetricsContextDTO metrics = monitorMetricsContext.getMonitorMetrics(measurement, value);
if (metrics == null) {
return value;
}
@@ -172,7 +172,7 @@ public class MonitorMetricsServiceImpl implements MonitorMetricsService {
// 删除缓存
RedisMaps.delete(MonitorMetricsCacheKeyDefine.MONITOR_METRICS, id);
// 重新加载本地缓存
monitorContext.reloadMonitorMetrics(id);
monitorMetricsContext.reloadMonitorMetrics(id);
// 设置日志参数
OperatorLogs.add(OperatorLogs.NAME, record.getName());
log.info("MonitorMetricsService-deleteMonitorMetricsById id: {}, effect: {}", id, effect);

View File

@@ -5,9 +5,9 @@
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="org.dromara.visor.module.monitor.entity.domain.AlarmEventDO">
<id column="id" property="id"/>
<result column="host_id" property="hostId"/>
<result column="host_name" property="hostName"/>
<result column="host_address" property="hostAddress"/>
<result column="source_type" property="sourceType"/>
<result column="source_id" property="sourceId"/>
<result column="source_info" property="sourceInfo"/>
<result column="agent_key" property="agentKey"/>
<result column="policy_id" property="policyId"/>
<result column="policy_rule_id" property="policyRuleId"/>
@@ -41,7 +41,7 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, host_id, host_name, host_address, agent_key, policy_id, policy_rule_id, metrics_id, metrics_measurement, alarm_tags, alarm_value, alarm_threshold, alarm_info, alarm_level, trigger_condition, consecutive_count, false_alarm, handle_status, handle_time, handle_remark, handle_user_id, handle_username, create_time, update_time, creator, updater, deleted
id, event_source, source_id, source_info, agent_key, policy_id, policy_rule_id, metrics_id, metrics_measurement, alarm_tags, alarm_value, alarm_threshold, alarm_info, alarm_level, trigger_condition, consecutive_count, false_alarm, handle_status, handle_time, handle_remark, handle_user_id, handle_username, create_time, update_time, creator, updater, deleted
</sql>
<select id="selectPolicyEventCount" resultMap="CountResultMap">

View File

@@ -5,6 +5,7 @@
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="org.dromara.visor.module.monitor.entity.domain.AlarmPolicyDO">
<id column="id" property="id"/>
<result column="type" property="type"/>
<result column="name" property="name"/>
<result column="description" property="description"/>
<result column="create_time" property="createTime"/>
@@ -16,7 +17,7 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, description, create_time, update_time, creator, updater, deleted
id, type, name, description, create_time, update_time, creator, updater, deleted
</sql>
</mapper>

View File

@@ -3,4 +3,4 @@ VITE_API_BASE_URL=http://127.0.0.1:9200/orion-visor/api
# websocket 路径
VITE_WS_BASE_URL=ws://127.0.0.1:9200/orion-visor/keep-alive
# 版本号
VITE_APP_VERSION=2.5.3
VITE_APP_VERSION=2.5.4

View File

@@ -3,4 +3,4 @@ VITE_API_BASE_URL=/orion-visor/api
# websocket 路径
VITE_WS_BASE_URL=/orion-visor/keep-alive
# 版本号
VITE_APP_VERSION=2.5.3
VITE_APP_VERSION=2.5.4

View File

@@ -1,7 +1,7 @@
{
"name": "orion-visor-ui",
"description": "Orion Visor UI",
"version": "2.5.3",
"version": "2.5.4",
"private": true,
"author": "Jiahang Li",
"license": "Apache 2.0",

View File

@@ -0,0 +1,12 @@
import type { AxiosResponse } from 'axios';
// api 错误
export class ApiError extends Error {
data: AxiosResponse;
constructor(message: string, data: AxiosResponse) {
super(message);
this.name = 'ApiError';
this.data = data;
}
}

View File

@@ -5,6 +5,7 @@ import { useUserStore } from '@/store';
import { getToken } from '@/utils/auth';
import { httpBaseUrl } from '@/utils/env';
import { reLoginTipsKey } from '@/types/symbol';
import { ApiError } from '@/api/error';
axios.defaults.timeout = 15000;
axios.defaults.setAuthorization = true;
@@ -72,7 +73,7 @@ axios.interceptors.response.use(
});
}
}
return Promise.reject(new Error(res.msg || 'Error'));
return Promise.reject(new ApiError(res.msg || 'Error', res));
},
(error) => {
// 判断是否弹出请求错误信息

View File

@@ -25,7 +25,8 @@ export interface AlarmEventFalseAlarmRequest {
*/
export interface AlarmEventQueryRequest extends Pagination, OrderDirection {
id?: number;
hostId?: number;
sourceType?: string;
sourceId?: number;
agentKey?: string;
policyId?: number;
metricsId?: number;
@@ -49,7 +50,9 @@ export interface AlarmEventClearRequest extends AlarmEventQueryRequest, ClearReq
*/
export interface AlarmEventQueryResponse extends TableData {
id: number;
hostId: number;
sourceType: string;
sourceId: number;
sourceInfo: Record<string, any>;
hostName: string;
hostAddress: string;
agentKey: string;

View File

@@ -7,6 +7,7 @@ import axios from 'axios';
*/
export interface AlarmPolicyCreateRequest {
name?: string;
type?: string;
description?: string;
notifyIdList?: Array<number>;
}
@@ -24,6 +25,7 @@ export interface AlarmPolicyUpdateRequest extends AlarmPolicyCreateRequest {
*/
export interface AlarmPolicyQueryRequest extends Pagination, OrderDirection {
id?: number;
type?: string;
name?: string;
description?: string;
}
@@ -33,6 +35,7 @@ export interface AlarmPolicyQueryRequest extends Pagination, OrderDirection {
*/
export interface AlarmPolicyQueryResponse extends TableData {
id: number;
type: string;
name: string;
description: string;
notifyIdList: Array<number>;
@@ -73,8 +76,8 @@ export function getAlarmPolicy(id: number) {
/**
* 查询全部监控告警策略
*/
export function getAlarmPolicyList() {
return axios.get<Array<AlarmPolicyQueryResponse>>('/monitor/alarm-policy/list');
export function getAlarmPolicyList(type: string) {
return axios.get<Array<AlarmPolicyQueryResponse>>('/monitor/alarm-policy/list', { params: { type } });
}
/**

View File

@@ -96,6 +96,7 @@ export interface MonitorHostQueryResponse extends TableData {
code: string;
address: string;
status: string;
types: Array<string>;
agentKey: string;
agentVersion: string;
latestVersion: string;

View File

@@ -45,53 +45,53 @@ export type SystemSetting = SftpSetting
* SFTP 设置
*/
export interface SftpSetting {
sftp_previewSize: number;
sftp_uploadPresentBackup: string;
sftp_uploadBackupFileName: string;
['sftp.preview-size']: number;
['sftp.upload-present-backup']: string;
['sftp.upload-backup-file-name']: string;
}
/**
* 登录设置
*/
export interface LoginSetting {
login_allowMultiDevice: string;
login_allowRefresh: string;
login_maxRefreshCount: number;
login_refreshInterval: number;
login_loginFailedLock: string;
login_loginFailedLockThreshold: number;
login_loginFailedLockTime: number;
login_loginFailedSend: string;
login_loginFailedSendThreshold: number;
login_loginSessionTime: number;
['login.allow-multi-device']: string;
['login.allow-refresh']: string;
['login.max-refresh-count']: number;
['login.refresh-interval']: number;
['login.login-failed-lock']: string;
['login.login-failed-lock-threshold']: number;
['login.login-failed-lock-time']: number;
['login.login-failed-send']: string;
['login.login-failed-send-threshold']: number;
['login.login-session-time']: number;
}
/**
* 加密设置
*/
export interface EncryptSetting {
encrypt_publicKey: string;
encrypt_privateKey: string;
['encrypt.public-key']: string;
['encrypt.private-key']: string;
}
/**
* 日志设置
*/
export interface LogSetting {
log_webScrollLines: number;
log_trackerLoadLines: number;
log_trackerLoadInterval: number;
log_execDetailLog: string;
['log.web-scroll-lines']: number;
['log.tracker-load-lines']: number;
['log.tracker-load-interval']: number;
['log.exec-detail.enabled']: string;
}
/**
* 自动清理设置
*/
export interface AutoClearSetting {
autoClear_execLogEnabled: string;
autoClear_execLogKeepDays: number;
autoClear_terminalLogEnabled: string;
autoClear_terminalLogKeepDays: number;
['auto-clear.exec-log.enabled']: string;
['auto-clear.exec-log.keep-days']: number;
['auto-clear.terminal-log.enabled']: string;
['auto-clear.terminal-log.keep-days']: number;
}
/**

View File

@@ -1,5 +1,5 @@
import type { MenuQueryResponse } from '@/api/system/menu';
import type { AxiosResponse } from 'axios';
import type { HttpResponse } from '@/types/global';
import axios from 'axios';
/**
@@ -36,7 +36,7 @@ export interface UserUpdatePasswordResponse {
* 获取用户聚合信息
*/
export function getUserAggregateInfo() {
return axios.get<AxiosResponse<UserAggregateResponse>>('/infra/user-aggregate/user', {
return axios.get<HttpResponse<UserAggregateResponse>>('/infra/user-aggregate/user', {
unwrap: true
});
}

View File

@@ -66,6 +66,17 @@
}
}
// 可调节列宽
.table-resize .arco-table-th:not(.arco-table-th-expand):not(.arco-table-operation)::after {
content: '';
position: absolute;
top: 25%;
right: 0;
width: 1px;
height: 50%;
background-color: var(--color-neutral-3);
}
// 垂直滚动
.arco-scrollbar-track-direction-vertical {
width: 10px;

View File

@@ -42,7 +42,7 @@
.table-title {
display: flex;
align-items: center;
font-size: 16px;
font-size: 1.17em;
font-weight: 600;
margin-right: 16px;
}

View File

@@ -1,14 +1,16 @@
<template>
<a-table row-key="id"
<a-table v-model:selected-keys="selectedKeysValue"
row-key="id"
ref="tableRef"
class="table-resize"
:columns="columns"
v-model:selected-keys="selectedKeysValue"
:row-selection="rowSelection"
row-class="pointer"
:data="hostList"
:scroll="{ y: '100%' }"
:pagination="false"
:bordered="true"
:column-resizable="true"
@row-click="clickRow">
<!-- -->
<template #empty>

View File

@@ -55,8 +55,8 @@
execLog.value = { ...record };
currentHostExecId.value = record.hosts[0].id;
// 获取最大显示行数
const { log_webScrollLines } = await useCacheStore().loadSystemSetting();
const scrollLines = toAnonymousNumber(log_webScrollLines) || 1000;
const { 'log.web-scroll-lines': webScrollLines } = await useCacheStore().loadSystemSetting();
const scrollLines = toAnonymousNumber(webScrollLines) || 1000;
// 创建 appender
appender.value = markRaw(new LogAppender({
id: record.id,

View File

@@ -45,11 +45,13 @@
<!-- table -->
<a-table row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="columns"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
:scroll="{ x: '100%', y: '60vh' }"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">

View File

@@ -19,6 +19,12 @@
import { useCacheStore } from '@/store';
import useLoading from '@/hooks/loading';
const props = withDefaults(defineProps<Partial<{
type?: string;
}>>(), {
type: 'all',
});
const modelValue = defineModel({ type: Number });
const { loading, setLoading } = useLoading();
@@ -30,7 +36,7 @@
const initOptions = async () => {
setLoading(true);
try {
const values = await cacheStore.loadMonitorAlarmPolicy();
const values = await cacheStore.loadMonitorAlarmPolicy(props.type);
optionData.value = values.map(s => {
return {
label: s.name,

View File

@@ -0,0 +1,65 @@
<template>
<a-select v-model:model-value="modelValue"
:options="optionData"
:loading="loading"
:multiple="multiple"
placeholder="请选择监控项"
allow-clear />
</template>
<script lang="ts">
export default {
name: 'monitorHostSelector'
};
</script>
<script lang="ts" setup>
import type { SelectOptionData } from '@arco-design/web-vue';
import type { HostType } from '@/api/asset/host';
import { onActivated, onMounted, ref } from 'vue';
import { useCacheStore } from '@/store';
import useLoading from '@/hooks/loading';
const props = withDefaults(defineProps<Partial<{
type: HostType;
status: string | undefined;
multiple: boolean;
}>>(), {
type: undefined,
status: undefined,
multiple: false,
});
const modelValue = defineModel<string | Array<string>>();
const { loading, setLoading } = useLoading();
const cacheStore = useCacheStore();
const optionData = ref<Array<SelectOptionData>>([]);
// 初始化选项
const initOptions = async () => {
setLoading(true);
try {
const hosts = await cacheStore.loadHosts(props.type);
optionData.value = hosts.filter(s => !props.status || s.status === props.status)
.map(s => {
return {
label: `${s.name} - ${s.address}`,
value: s.agentKey,
};
});
} catch (e) {
} finally {
setLoading(false);
}
};
// 初始化选项
onMounted(initOptions);
onActivated(initOptions);
</script>
<style lang="less" scoped>
</style>

View File

@@ -31,7 +31,10 @@ export default function setupUserLoginInfoGuard(router: Router) {
next();
}
} catch (error) {
Message.error('获取用户信息失败');
// 登录过期
if ((error as any)?.data?.data?.code !== 401) {
Message.error('获取用户信息失败');
}
// 获取失败退出登录
await userStore.logout();
next({

View File

@@ -177,8 +177,8 @@ export default defineStore('cache', {
},
// 查询监控告警策略列表
async loadMonitorAlarmPolicy(force = false) {
return await this.load('alarmPolicy', getAlarmPolicyList, ['monitor:alarm-policy:query'], force);
async loadMonitorAlarmPolicy(type: string = 'all', force = false) {
return await this.load(`alarmPolicy_${type}`, () => getAlarmPolicyList(type), ['monitor:alarm-policy:query'], force);
},
// 查询监控指标列表

View File

@@ -7,7 +7,7 @@ export type CacheType = 'users' | 'menus' | 'roles'
| 'authorizedHostKeys' | 'authorizedHostIdentities'
| 'commandSnippetGroups' | 'pathBookmarkGroups'
| 'commandSnippets' | 'pathBookmarks'
| 'alarmPolicy' | 'monitorMetrics'
| 'alarmPolicy_*' | 'monitorMetrics'
| 'systemSetting' | 'notifyTemplate*'
| '*_Tags' | 'preference_*'
| string

View File

@@ -7,6 +7,7 @@ import { clearToken, setToken } from '@/utils/auth';
import { removeRouteListener } from '@/utils/route-listener';
import { getUserAggregateInfo } from '@/api/user/user-aggregate';
import { useAppStore, useCacheStore, useMenuStore, useTabBarStore, useTipsStore } from '@/store';
import { ApiError } from '@/api/error';
const CHECK_APP_VERSION_KEY = 'check-app-version';
@@ -67,9 +68,13 @@ export default defineStore('user', {
// 获取用户信息
async getUserInfo() {
const { data: { data }, headers } = await getUserAggregateInfo();
const resp = await getUserAggregateInfo();
const { data: { code, msg, data }, headers } = resp;
// 检查版本更新
checkForVersionUpdate(headers?.['x-app-version']);
if (code !== 200) {
throw new ApiError(msg, resp);
}
// 设置用户信息
this.setUserInfo({
id: data.user.id,

View File

@@ -12,7 +12,7 @@ export const encrypt = async (data: string | undefined): Promise<string | undefi
return data;
}
// 获取公钥
const publicKey = (await useCacheStore().loadSystemSetting()).encrypt_publicKey;
const publicKey = (await useCacheStore().loadSystemSetting())['encrypt.public-key'];
const encryptor = new JSEncrypt();
encryptor.setPublicKey(publicKey);

View File

@@ -99,12 +99,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:row-selection="rowSelection"
:columns="tableColumns"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 连接用户 -->

View File

@@ -53,11 +53,13 @@
<!-- table -->
<a-table row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="columns"
:data="tableRenderData"
:pagination="false"
:bordered="false">
:bordered="false"
:column-resizable="true">
<!-- 连接用户 -->
<template #username="{ record }">
{{ record.username }}

View File

@@ -83,12 +83,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:row-selection="rowSelection"
:columns="tableColumns"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 操作用户 -->

View File

@@ -17,14 +17,15 @@
@on-selected="clickGroup" />
<a-divider direction="vertical" />
<!-- 主机表格 -->
<a-table class="group-main-hosts"
row-key="id"
<a-table row-key="id"
class="group-main-hosts table-resize"
:sticky-header="true"
:loading="loading"
:columns="hostColumns"
:data="selectedGroupHosts"
:pagination="false"
:bordered="false">
:bordered="false"
:column-resizable="true">
<!-- 空状态 -->
<template #empty>
<a-empty style="margin: 32px 0;" description="当前分组内无主机" />

View File

@@ -8,14 +8,15 @@
<!-- 主机身份表格 -->
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
class="host-identity-main-table"
class="table-resize host-identity-main-table"
:columns="hostIdentityColumns"
:row-selection="rowSelection"
row-class="pointer"
:sticky-header="true"
:data="hostIdentities"
:sticky-header="true"
:pagination="false"
:bordered="false"
:column-resizable="true"
@row-click="clickRow">
<!-- 类型 -->
<template #type="{ record }">

View File

@@ -8,7 +8,7 @@
<!-- 主机密钥表格 -->
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
class="host-key-main-table"
class="table-resize host-key-main-table"
:columns="hostKeyColumns"
:row-selection="rowSelection"
row-class="pointer"
@@ -16,6 +16,7 @@
:data="hostKeys"
:pagination="false"
:bordered="false"
:column-resizable="true"
@row-click="clickRow" />
</grant-layout>
</template>

View File

@@ -111,12 +111,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 类型 -->

View File

@@ -94,12 +94,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 操作 -->

View File

@@ -230,7 +230,7 @@
<a-button v-for="type in record.types"
:key="type"
size="mini"
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type} })">
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type }})">
{{ type }}
</a-button>
</a-space>

View File

@@ -146,12 +146,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 主机信息 -->
@@ -275,7 +277,7 @@
<a-button v-for="type in record.types"
:key="type"
size="mini"
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type} })">
@click="openNewRoute({ name: 'terminal', query: { connect: record.id, type }})">
{{ type }}
</a-button>
</a-space>

View File

@@ -13,11 +13,13 @@
<div class="card-body">
<!-- 表格 -->
<a-table row-key="id"
class="table-resize"
:loading="loading"
:columns="batchExecColumns"
:data="data.exec?.execLogList || []"
:pagination="false"
:bordered="false"
:column-resizable="true"
:scroll="{ y: 258 }">
<!-- 空状态 -->
<template #empty>

View File

@@ -13,11 +13,13 @@
<div class="card-body">
<!-- 表格 -->
<a-table row-key="id"
class="table-resize"
:loading="loading"
:columns="terminalLogColumns"
:data="data.terminal?.terminalConnectList || []"
:pagination="false"
:bordered="false"
:column-resizable="true"
:scroll="{ y: 258 }">
<!-- 空状态 -->
<template #empty>

View File

@@ -12,11 +12,13 @@
</div>
<div class="card-body">
<a-table row-key="id"
class="table-resize"
:loading="loading"
:columns="userLoginColumns"
:data="data.infra?.loginHistoryList || []"
:pagination="false"
:bordered="false"
:column-resizable="true"
:scroll="{ y: 388 }">
<!-- 登录设备 -->
<template #content="{ record }">

View File

@@ -2,13 +2,15 @@
<!-- table -->
<a-table row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="hostColumns"
:data="row.hosts"
:expandable="expandable"
:scroll="{ y: '100%' }"
:pagination="false"
:bordered="false">
:bordered="false"
:column-resizable="true">
<!-- 执行主机 -->
<template #hostName="{ record }">
<span class="table-cell-value span-blue">

View File

@@ -106,6 +106,7 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
@@ -113,14 +114,15 @@
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)"
@expand="loadExecHost">
<!-- 展开表格 -->
<template #expand-row="{ record }">
<exec-command-host-log-table :row="record"
@view-command="s => emits('viewCommand', s)"
@view-params="s => emits('viewParams', s)"
@view-command="(s: any) => emits('viewCommand', s)"
@view-params="(s: any) => emits('viewParams', s)"
@refresh-host="refreshExecHost" />
</template>
<!-- 执行命令 -->

View File

@@ -2,13 +2,15 @@
<!-- table -->
<a-table row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="hostColumns"
:data="row.hosts"
:expandable="expandable"
:scroll="{ y: '100%' }"
:pagination="false"
:bordered="false">
:bordered="false"
:column-resizable="true">
<!-- 执行主机 -->
<template #hostName="{ record }">
<span class="table-cell-value span-blue">

View File

@@ -97,6 +97,7 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
@@ -104,6 +105,7 @@
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)"
@expand="loadExecHost">

View File

@@ -89,12 +89,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- cron -->

View File

@@ -76,12 +76,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 模板命令 -->

View File

@@ -106,12 +106,14 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
@page-change="(page: number) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size: number) => fetchTableData(1, size)">
<!-- 上传路径 -->

View File

@@ -10,7 +10,7 @@
ok-text="清理"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
:on-before-ok="handleOk"
@close="handleClose">
<a-spin class="full" :loading="loading">
<a-form :model="formModel"
@@ -23,12 +23,13 @@
placeholder="请选择处理状态"
allow-clear />
</a-form-item>
<!-- 告警主机 -->
<a-form-item field="hostId" label="告警主机">
<host-selector v-model="formModel.hostId"
placeholder="请选择告警主机"
hide-button
allow-clear />
<!-- 告警来源 -->
<a-form-item field="agentKey" label="告警来源">
<!-- 选择告警来源 -->
<monitor-host-selector v-if="sourceType === AlarmSourceType.HOST"
v-model="formModel.agentKey"
placeholder="请选择告警来源"
allow-clear />
</a-form-item>
<!-- 告警级别 -->
<a-form-item field="alarmLevel" label="告警级别">
@@ -58,7 +59,7 @@
allow-clear />
</a-form-item>
<!-- 数据集 -->
<a-form-item field="metricsId" label="数据集">
<a-form-item field="metricsMeasurement" label="数据集">
<a-select v-model="formModel.metricsMeasurement"
:options="toOptions(MetricsMeasurementKey)"
placeholder="数据集"
@@ -85,12 +86,6 @@
hide-button
allow-clear />
</a-form-item>
<!-- agentKey -->
<a-form-item field="agentKey" label="agentKey">
<a-input v-model="formModel.agentKey"
placeholder="请输入agentKey"
allow-clear />
</a-form-item>
<!-- 告警时间 -->
<a-form-item field="createTimeRange" label="告警时间">
<a-range-picker v-model="formModel.createTimeRange"
@@ -118,21 +113,25 @@
import useVisible from '@/hooks/visible';
import { Message, Modal } from '@arco-design/web-vue';
import { useDictStore } from '@/store';
import { maxClearLimit, HandleStatusKey, AlarmLevelKey, MetricsMeasurementKey, FalseAlarmKey } from '../types/const';
import { maxClearLimit, HandleStatusKey, AlarmLevelKey, MetricsMeasurementKey, FalseAlarmKey, AlarmSourceType } from '../types/const';
import { assignOmitRecord } from '@/utils';
import UserSelector from '@/components/user/user/selector/index.vue';
import HostSelector from '@/components/asset/host/selector/index.vue';
import MonitorMetricsSelector from '@/components/monitor/metrics/selector/index.vue';
import AlarmPolicySelector from '@/components/monitor/alarm-policy/selector/index.vue';
import MonitorHostSelector from '@/components/monitor/host/selector/index.vue';
const { toOptions } = useDictStore();
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const props = defineProps<{
sourceType: string;
}>();
const defaultForm = (): AlarmEventQueryRequest => {
return {
id: undefined,
hostId: undefined,
sourceType: undefined,
agentKey: undefined,
policyId: undefined,
metricsId: undefined,
@@ -160,7 +159,7 @@
defineExpose({ open });
// 确定
const handlerOk = async () => {
const handleOk = async () => {
if (!formModel.value.limit) {
Message.error('请输入数量限制');
return false;
@@ -170,7 +169,7 @@
// 获取总数量
const { data } = await getAlarmEventCount(formModel.value);
if (data) {
// 清
// 清
doClear(data);
} else {
// 无数据
@@ -195,9 +194,7 @@
const { data } = await clearMonitorAlarmEvent(formModel.value);
Message.success(`已成功清理 ${data} 条数据`);
emits('clear');
// 清空
setVisible(false);
handlerClear();
handleClose();
} catch (e) {
} finally {
setLoading(false);
@@ -208,11 +205,12 @@
// 关闭
const handleClose = () => {
handlerClear();
handleClear();
setVisible(false);
};
// 清空
const handlerClear = () => {
const handleClear = () => {
setLoading(false);
};

View File

@@ -10,7 +10,7 @@
ok-text="处理"
:ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk"
:on-before-ok="handleOk"
@close="handleClose">
<a-spin class="full" :loading="loading">
<a-form ref="formRef"
@@ -90,7 +90,7 @@
defineExpose({ open });
// 确定
const handlerOk = async () => {
const handleOk = async () => {
setLoading(true);
try {
// 验证参数
@@ -102,8 +102,7 @@
await handleAlarmEvent(formModel.value);
Message.success('已处理');
emits('handled', { ...formModel.value });
// 清空
handlerClear();
handleClose();
return true;
} catch (e) {
return false;
@@ -114,13 +113,13 @@
// 关闭
const handleClose = () => {
handlerClear();
handleClear();
setVisible(false);
};
// 清空
const handlerClear = () => {
const handleClear = () => {
setLoading(false);
setVisible(false);
};
</script>

View File

@@ -16,7 +16,7 @@
<a-button v-if="showClearButton"
v-permission="['monitor:alarm-event:management:clear']"
status="danger"
@click="$emit('openClear', formModel)">
@click="openClear">
清理
<template #icon>
<icon-close />
@@ -26,7 +26,7 @@
<a-button v-permission="['monitor:alarm-event:handle']"
type="primary"
:disabled="selectedKeys.length === 0"
@click="$emit('openHandle', selectedKeys)">
@click="openHandle(selectedKeys)">
处理告警
<template #icon>
<icon-play-arrow-fill />
@@ -65,32 +65,36 @@
<a-table v-model:selected-keys="selectedKeys"
row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="tableColumns"
:row-selection="rowSelection"
:data="tableData"
:pagination="pagination"
:bordered="false"
:column-resizable="true"
:scroll="{ x: 'auto' }"
@page-change="(page: number) => $emit('query', page, pagination.pageSize)"
@page-size-change="(size: number) => $emit('query', 1, size)">
<!-- 主机信息 -->
<template #hostInfo="{ record }">
<!-- 来源信息 -->
<template #sourceInfo="{ record }">
<div class="info-wrapper">
<div class="info-item">
<!-- 主机名称 -->
<div v-if="record.sourceType === AlarmSourceType.HOST && record.sourceInfo?.name" class="info-item">
<span class="info-label">主机名称</span>
<span class="info-value text-copy text-ellipsis"
:title="record.hostName"
@click="copy(record.hostName, true)">
{{ record.hostName }}
:title="record.sourceInfo.name"
@click="copy(record.sourceInfo.name, true)">
{{ record.sourceInfo.name }}
</span>
</div>
<div class="info-item">
<!-- 主机地址 -->
<div v-if="record.sourceType === AlarmSourceType.HOST && record.sourceInfo?.address" class="info-item">
<span class="info-label">主机地址</span>
<span class="info-value span-blue text-copy text-ellipsis"
:title="record.hostAddress"
@click="copy(record.hostAddress, true)">
{{ record.hostAddress }}
:title="record.sourceInfo.address"
@click="copy(record.sourceInfo.address, true)">
{{ record.sourceInfo.address }}
</span>
</div>
</div>
@@ -147,7 +151,7 @@
<a-button v-permission="['monitor:alarm-event:handle']"
type="text"
size="mini"
@click="$emit('openHandle', [record.id])">
@click="openHandle([record.id])">
处理
</a-button>
<!-- 更多 -->
@@ -171,6 +175,13 @@
</div>
</template>
</a-table>
<!-- 处理模态框-->
<alarm-event-handle-modal ref="handleModal"
@handled="alarmHandled" />
<!-- 清理模态框-->
<alarm-event-clear-modal ref="clearModal"
:source-type="sourceType"
@clear="emits('reload')" />
</a-card>
</template>
@@ -181,28 +192,33 @@
</script>
<script lang="ts" setup>
import type { PaginationProps } from '@arco-design/web-vue';
import type { MetricsQueryResponse } from '@/api/monitor/metrics';
import type { AlarmEventQueryRequest, AlarmEventQueryResponse, AlarmEventHandleRequest } from '@/api/monitor/alarm-event';
import { h, ref } from 'vue';
import { batchDeleteAlarmEvent, setAlarmEventFalse } from '@/api/monitor/alarm-event';
import { Message, Modal, Space, Tag, type PaginationProps } from '@arco-design/web-vue';
import { Message, Modal, Space, Tag } from '@arco-design/web-vue';
import {
FalseAlarm,
HandleStatusKey,
FalseAlarmKey,
MetricsMeasurementKey,
AlarmLevelKey,
TriggerConditionKey
} from '@/views/monitor/alarm-event/types/const';
TriggerConditionKey,
AlarmSourceType
} from '../types/const';
import { useRowSelection, useTableColumns } from '@/hooks/table';
import { copy } from '@/hooks/copy';
import { useQueryOrder, DESC } from '@/hooks/query-order';
import { useDictStore, useCacheStore, useUserStore } from '@/store';
import { MetricsUnit, MetricUnitFormatter } from '@/utils/metrics';
import TableAdjust from '@/components/app/table-adjust/index.vue';
import AlarmEventClearModal from './alarm-event-clear-modal.vue';
import AlarmEventHandleModal from './alarm-event-handle-modal.vue';
const props = defineProps<{
tableName: string;
sourceType: string;
columns: any[];
tableData: AlarmEventQueryResponse[];
loading: boolean;
@@ -212,25 +228,38 @@
}>();
const emits = defineEmits<{
openHandle: [ids: number[]];
openClear: [formData: AlarmEventQueryRequest];
reload: [];
setLoading: [loading: boolean];
query: [page?: number, pageSize?: number];
}>();
const rowSelection = useRowSelection();
const userStore = useUserStore();
const cacheStore = useCacheStore();
const queryOrder = useQueryOrder(props.tableName, DESC);
const { tableColumns, columnsHook } = useTableColumns(props.tableName, props.columns);
const { monitorMetrics } = useCacheStore();
const { getDictValue } = useDictStore();
const handleModal = ref();
const clearModal = ref();
const selectedKeys = ref<Array<number>>([]);
// 告警处理回调
const alarmHandled = (request: Required<AlarmEventHandleRequest>) => {
props.tableData.filter(s => (request.idList || []).includes(s.id)).forEach(s => {
s.handleTime = request.handleTime;
s.handleStatus = request.handleStatus;
s.handleRemark = request.handleRemark;
s.handleUserId = userStore.id as number;
s.handleUsername = userStore.username as string;
});
selectedKeys.value = [];
};
// 获取指标名称
const getMetricsField = (metricsId: number, field: string) => {
return (monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.[field];
};
return (cacheStore.monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.[field];
};
// 提取标签
const extraTags = (record: AlarmEventQueryResponse) => {
@@ -303,19 +332,15 @@
});
};
// 告警处理回调
const alarmHandled = (request: Required<AlarmEventHandleRequest>) => {
props.tableData.filter(s => (request.idList || []).includes(s.id)).forEach(s => {
s.handleTime = request.handleTime;
s.handleStatus = request.handleStatus;
s.handleRemark = request.handleRemark;
s.handleUserId = userStore.id as number;
s.handleUsername = userStore.username as string;
});
selectedKeys.value = [];
// 打开处理
const openHandle = (idList: Array<number>) => {
handleModal.value.open(idList);
};
defineExpose({ alarmHandled });
// 打开清理
const openClear = () => {
clearModal.value.open(props.formModel);
};
</script>

View File

@@ -13,12 +13,13 @@
placeholder="请选择处理状态"
allow-clear />
</a-form-item>
<!-- 告警主机 -->
<a-form-item field="hostId" label="告警主机">
<host-selector v-model="formModel.hostId"
placeholder="请选择告警主机"
hide-button
allow-clear />
<!-- 告警来源 -->
<a-form-item field="agentKey" label="告警来源">
<!-- 选择告警来源 -->
<monitor-host-selector v-if="sourceType === AlarmSourceType.HOST"
v-model="formModel.agentKey"
placeholder="请选择告警来源"
allow-clear />
</a-form-item>
<!-- 告警级别 -->
<a-form-item field="alarmLevel" label="告警级别">
@@ -75,12 +76,6 @@
hide-button
allow-clear />
</a-form-item>
<!-- agentKey -->
<a-form-item field="agentKey" label="agentKey">
<a-input v-model="formModel.agentKey"
placeholder="请输入agentKey"
allow-clear />
</a-form-item>
<!-- 告警时间 -->
<a-form-item field="createTimeRange" label="告警时间">
<a-range-picker v-model="formModel.createTimeRange"
@@ -92,7 +87,7 @@
</query-header>
</a-card>
<!-- 表格 -->
<alarm-event-table-base ref="eventTable"
<alarm-event-table-base :source-type="sourceType"
:table-name="TableName"
:columns="columns"
:table-data="tableRenderData"
@@ -100,9 +95,8 @@
:form-model="formModel"
:pagination="pagination"
:show-clear-button="true"
@open-handle="emits('openHandle', $event)"
@open-clear="emits('openClear', formModel)"
@set-loading="setLoading"
@reload="reload"
@query="fetchTableData" />
</template>
@@ -113,25 +107,26 @@
</script>
<script lang="ts" setup>
import type { AlarmEventQueryRequest, AlarmEventQueryResponse, AlarmEventHandleRequest } from '@/api/monitor/alarm-event';
import type { AlarmEventQueryRequest, AlarmEventQueryResponse } from '@/api/monitor/alarm-event';
import { reactive, ref, onMounted } from 'vue';
import { getAlarmEventPage } from '@/api/monitor/alarm-event';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
import { TableName, FalseAlarm, HandleStatusKey, FalseAlarmKey, MetricsMeasurementKey, AlarmLevelKey } from '../types/const';
import { TableName, FalseAlarm, HandleStatusKey, FalseAlarmKey, MetricsMeasurementKey, AlarmLevelKey, AlarmSourceType } from '../types/const';
import { useTablePagination } from '@/hooks/table';
import { useRoute } from 'vue-router';
import { useDictStore } from '@/store';
import { useDictStore, useUserStore } from '@/store';
import { useQueryOrder, DESC } from '@/hooks/query-order';
import UserSelector from '@/components/user/user/selector/index.vue';
import HostSelector from '@/components/asset/host/selector/index.vue';
import MonitorMetricsSelector from '@/components/monitor/metrics/selector/index.vue';
import AlarmPolicySelector from '@/components/monitor/alarm-policy/selector/index.vue';
import AlarmEventTableBase from './alarm-event-table-base.vue';
import MonitorHostSelector from '@/components/monitor/host/selector/index.vue';
const emits = defineEmits(['openHandle', 'openClear']);
const props = defineProps<{
sourceType: string;
}>();
const eventTable = ref();
const pagination = useTablePagination();
const { toOptions } = useDictStore();
const { loading, setLoading } = useLoading();
@@ -139,8 +134,8 @@
const tableRenderData = ref<Array<AlarmEventQueryResponse>>([]);
const formModel = reactive<AlarmEventQueryRequest>({
id: undefined,
sourceType: props.sourceType,
agentKey: undefined,
hostId: undefined,
policyId: undefined,
metricsId: undefined,
metricsMeasurement: undefined,
@@ -158,12 +153,7 @@
fetchTableData();
};
// 告警处理回调
const alarmHandled = (request: Required<AlarmEventHandleRequest>) => {
eventTable.value.alarmHandled(request);
};
defineExpose({ reload, alarmHandled });
defineExpose({ reload });
// 加载数据
const doFetchTableData = async (request: AlarmEventQueryRequest) => {
@@ -186,10 +176,17 @@
};
onMounted(() => {
const key = useRoute().query.key as string;
const route = useRoute();
const key = route.query.key as string;
if (key) {
formModel.id = Number.parseInt(key);
}
// 当前用户
const action = route.query.action as string;
if (action === 'self') {
formModel.handleUserId = useUserStore().id;
}
// 查询数据
fetchTableData();
});

View File

@@ -1,15 +1,7 @@
<template>
<div class="layout-container" v-if="render">
<!-- 列表-表格 -->
<alarm-event-table ref="table"
@open-handle="(e: any) => handleModal.open(e)"
@open-clear="(e: any) => clearModal.open(e)" />
<!-- 处理模态框-->
<alarm-event-handle-modal ref="handleModal"
@handled="(e: any) => table.alarmHandled(e)" />
<!-- 清理模态框-->
<alarm-event-clear-modal ref="clearModal"
@clear="() => table.reload()" />
<alarm-event-table :source-type="AlarmSourceType.HOST" />
</div>
</template>
@@ -22,20 +14,18 @@
<script lang="ts" setup>
import { ref, onBeforeMount } from 'vue';
import { useDictStore, useCacheStore } from '@/store';
import { dictKeys } from './types/const';
import { dictKeys, AlarmSourceType } from './types/const';
import AlarmEventTable from './components/alarm-event-table.vue';
import AlarmEventClearModal from './components/alarm-event-clear-modal.vue';
import AlarmEventHandleModal from './components/alarm-event-handle-modal.vue';
const render = ref(false);
const table = ref();
const handleModal = ref();
const clearModal = ref();
onBeforeMount(async () => {
const cacheStore = useCacheStore();
// 加载告警策略
await cacheStore.loadMonitorAlarmPolicy();
// 加载指标列表
await cacheStore.loadMonitorMetricsList();
// 加载字典值
const dictStore = useDictStore();
await dictStore.loadKeys(dictKeys);
render.value = true;

View File

@@ -11,6 +11,12 @@ export const FalseAlarm = {
FALSE: 0,
};
// 告警来源类型
export const AlarmSourceType = {
HOST: 'HOST',
UPTIME: 'UPTIME',
};
// 告警条件 字典项
export const TriggerConditionKey = 'alarmTriggerCondition';

Some files were not shown because too many files have changed in this diff Show More