Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	sql/init-4-data.sql
This commit is contained in:
lijiahangmax
2025-10-07 22:25:16 +08:00
59 changed files with 436 additions and 393 deletions

View File

@@ -312,6 +312,19 @@ public class RedisMaps extends RedisUtils {
return getJson(key.getKey(), hashKey, (Class<V>) key.getType()); return getJson(key.getKey(), hashKey, (Class<V>) key.getType());
} }
/**
* 获取值 json
*
* @param define define
* @param key key
* @param hashKey hashKey
* @param <V> V
* @return value
*/
public static <V> V getJson(String key, CacheKeyDefine define, Object hashKey) {
return getJson(key, hashKey, (Class<V>) define.getType());
}
/** /**
* 获取值 json * 获取值 json
* *
@@ -355,9 +368,9 @@ public class RedisMaps extends RedisUtils {
/** /**
* 获取值 json * 获取值 json
* *
* @param key key * @param key key
* @param hashKeys hashKeys * @param hashKeys hashKeys
* @param <V> V * @param <V> V
* @return values * @return values
*/ */
public static <V> List<V> multiGetJson(CacheKeyDefine key, List<?> hashKeys) { public static <V> List<V> multiGetJson(CacheKeyDefine key, List<?> hashKeys) {
@@ -367,10 +380,10 @@ public class RedisMaps extends RedisUtils {
/** /**
* 获取值 json * 获取值 json
* *
* @param key key * @param key key
* @param hashKeys hashKeys * @param hashKeys hashKeys
* @param clazz clazz * @param clazz clazz
* @param <V> V * @param <V> V
* @return values * @return values
*/ */
public static <V> List<V> multiGetJson(String key, List<?> hashKeys, Class<V> clazz) { public static <V> List<V> multiGetJson(String key, List<?> hashKeys, Class<V> clazz) {

View File

@@ -48,10 +48,10 @@ public interface HostAgentApi {
/** /**
* 获取缓存名称 * 获取缓存名称
* *
* @param agentKeyList agentKeyList * @param agentKeys agentKeys
* @return nameMap * @return nameMap
*/ */
Map<String, String> getNameCacheByAgentKey(List<String> agentKeyList); Map<String, String> getNameCacheByAgentKey(List<String> agentKeys);
/** /**
* 获取缓存名称 * 获取缓存名称

View File

@@ -103,11 +103,11 @@ public class HostAgentApiImpl implements HostAgentApi {
} }
@Override @Override
public Map<String, String> getNameCacheByAgentKey(List<String> agentKeyList) { public Map<String, String> getNameCacheByAgentKey(List<String> agentKeys) {
Map<String, String> result = new HashMap<>(); Map<String, String> result = new HashMap<>();
List<String> queryList = new ArrayList<>(); List<String> queryList = new ArrayList<>();
// 查询缓存 // 查询缓存
for (String agentKey : agentKeyList) { for (String agentKey : agentKeys) {
HostBaseDTO host = AGENT_HOST_CACHE.get(agentKey); HostBaseDTO host = AGENT_HOST_CACHE.get(agentKey);
if (host != null) { if (host != null) {
result.put(agentKey, host.getName()); result.put(agentKey, host.getName());

View File

@@ -92,14 +92,14 @@ public interface HostDAO extends IMapper<HostDO> {
/** /**
* 更新探针信息 * 更新探针信息
* *
* @param keys agentKeyList * @param agentKeys agentKeys
* @param update update * @param update update
* @return effect * @return effect
*/ */
default int updateByAgentKeys(List<String> keys, HostDO update) { default int updateByAgentKeys(List<String> agentKeys, HostDO update) {
update.setUpdateTime(new Date()); update.setUpdateTime(new Date());
// 更新 // 更新
return this.update(update, Conditions.in(HostDO::getAgentKey, keys)); return this.update(update, Conditions.in(HostDO::getAgentKey, agentKeys));
} }
} }

View File

@@ -212,25 +212,25 @@ public class HostAgentEndpointServiceImpl implements HostAgentEndpointService {
/** /**
* 标记在线状态 * 标记在线状态
* *
* @param agentKeyList agentKeyList * @param agentKeys agentKeys
* @param status status * @param status status
*/ */
private void markOnlineStatus(List<String> agentKeyList, AgentOnlineStatusEnum status) { private void markOnlineStatus(List<String> agentKeys, AgentOnlineStatusEnum status) {
if (Lists.isEmpty(agentKeyList)) { if (Lists.isEmpty(agentKeys)) {
return; return;
} }
log.info("HostAgentEndpointService mark {}. count: {}, keys: {}", status, agentKeyList.size(), agentKeyList); log.info("HostAgentEndpointService mark {}. count: {}, keys: {}", status, agentKeys.size(), agentKeys);
// 更新数据 // 更新数据
HostDO update = HostDO.builder() HostDO update = HostDO.builder()
.agentOnlineStatus(status.getValue()) .agentOnlineStatus(status.getValue())
.agentOnlineChangeTime(new Date()) .agentOnlineChangeTime(new Date())
.build(); .build();
int effect = hostDAO.updateByAgentKeys(agentKeyList, update); int effect = hostDAO.updateByAgentKeys(agentKeys, update);
// 更新缓存 // 更新缓存
agentKeyList.forEach(s -> ONLINE_STATUS_CACHE.put(s, status.getValue())); agentKeys.forEach(s -> ONLINE_STATUS_CACHE.put(s, status.getValue()));
log.info("HostAgentEndpointService mark {}. effect: {}", status, effect); log.info("HostAgentEndpointService mark {}. effect: {}", status, effect);
// 插入日志 // 插入日志
List<HostAgentLogDO> logList = hostDAO.selectIdByAgentKeys(agentKeyList) List<HostAgentLogDO> logList = hostDAO.selectIdByAgentKeys(agentKeys)
.stream() .stream()
.map(s -> { .map(s -> {
HostAgentLogDO agentLog = HostAgentLogDO.builder() HostAgentLogDO agentLog = HostAgentLogDO.builder()
@@ -250,7 +250,7 @@ public class HostAgentEndpointServiceImpl implements HostAgentEndpointService {
} }
// 发送已下线事件 // 发送已下线事件
if (AgentOnlineStatusEnum.OFFLINE.equals(status)) { if (AgentOnlineStatusEnum.OFFLINE.equals(status)) {
SpringHolder.publishEvent(new AgentOfflineEvent(agentKeyList)); SpringHolder.publishEvent(new AgentOfflineEvent(agentKeys));
} }
} }

View File

@@ -48,13 +48,13 @@ import javax.annotation.Resource;
import java.util.List; import java.util.List;
/** /**
* 告警记录 api * 告警事件 api
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
* @since 2025-9-17 21:31 * @since 2025-9-17 21:31
*/ */
@Tag(name = "monitor - 告警记录服务") @Tag(name = "monitor - 告警事件服务")
@Slf4j @Slf4j
@Validated @Validated
@RestWrapper @RestWrapper
@@ -67,7 +67,7 @@ public class AlarmEventController {
@IgnoreLog(IgnoreLogMode.RET) @IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query") @PostMapping("/query")
@Operation(summary = "分页查询告警记录") @Operation(summary = "分页查询告警事件")
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:query')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:query')")
public DataGrid<AlarmEventVO> getAlarmEventPage(@Validated(Page.class) @RequestBody AlarmEventQueryRequest request) { public DataGrid<AlarmEventVO> getAlarmEventPage(@Validated(Page.class) @RequestBody AlarmEventQueryRequest request) {
return alarmEventService.getAlarmEventPage(request); return alarmEventService.getAlarmEventPage(request);
@@ -76,7 +76,7 @@ public class AlarmEventController {
@IgnoreLog(IgnoreLogMode.RET) @IgnoreLog(IgnoreLogMode.RET)
@OperatorLog(AlarmEventOperatorType.HANDLE) @OperatorLog(AlarmEventOperatorType.HANDLE)
@PostMapping("/handle") @PostMapping("/handle")
@Operation(summary = "处理告警记录") @Operation(summary = "处理告警事件")
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:handle')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:handle')")
public Integer handleAlarmEvent(@Validated @RequestBody AlarmEventHandleRequest request) { public Integer handleAlarmEvent(@Validated @RequestBody AlarmEventHandleRequest request) {
return alarmEventService.handleAlarmEvent(request); return alarmEventService.handleAlarmEvent(request);
@@ -92,7 +92,7 @@ public class AlarmEventController {
} }
@PostMapping("/count") @PostMapping("/count")
@Operation(summary = "查询告警记录数量") @Operation(summary = "查询告警事件数量")
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:query')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:query')")
public Long getAlarmEventCount(@Validated @RequestBody AlarmEventQueryRequest request) { public Long getAlarmEventCount(@Validated @RequestBody AlarmEventQueryRequest request) {
return alarmEventService.getAlarmEventCount(request); return alarmEventService.getAlarmEventCount(request);
@@ -101,7 +101,7 @@ public class AlarmEventController {
@DemoDisableApi @DemoDisableApi
@OperatorLog(AlarmEventOperatorType.DELETE) @OperatorLog(AlarmEventOperatorType.DELETE)
@DeleteMapping("/delete") @DeleteMapping("/delete")
@Operation(summary = "删除告警记录") @Operation(summary = "删除告警事件")
@Parameter(name = "id", description = "id", required = true) @Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:delete')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:delete')")
public Integer deleteAlarmEvent(@RequestParam("id") Long id) { public Integer deleteAlarmEvent(@RequestParam("id") Long id) {
@@ -111,7 +111,7 @@ public class AlarmEventController {
@DemoDisableApi @DemoDisableApi
@OperatorLog(AlarmEventOperatorType.DELETE) @OperatorLog(AlarmEventOperatorType.DELETE)
@DeleteMapping("/batch-delete") @DeleteMapping("/batch-delete")
@Operation(summary = "批量删除告警记录") @Operation(summary = "批量删除告警事件")
@Parameter(name = "idList", description = "idList", required = true) @Parameter(name = "idList", description = "idList", required = true)
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:delete')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:delete')")
public Integer batchDeleteAlarmEvent(@RequestParam("idList") List<Long> idList) { public Integer batchDeleteAlarmEvent(@RequestParam("idList") List<Long> idList) {
@@ -121,7 +121,7 @@ public class AlarmEventController {
@DemoDisableApi @DemoDisableApi
@OperatorLog(AlarmEventOperatorType.CLEAR) @OperatorLog(AlarmEventOperatorType.CLEAR)
@PostMapping("/clear") @PostMapping("/clear")
@Operation(summary = "清理告警记录") @Operation(summary = "清理告警事件")
@PreAuthorize("@ss.hasPermission('monitor:alarm-event:management:clear')") @PreAuthorize("@ss.hasPermission('monitor:alarm-event:management:clear')")
public Integer clearAlarmEvent(@Validated @RequestBody AlarmEventClearRequest request) { public Integer clearAlarmEvent(@Validated @RequestBody AlarmEventClearRequest request) {
return alarmEventService.clearAlarmEvent(request); return alarmEventService.clearAlarmEvent(request);

View File

@@ -93,11 +93,11 @@ public class AlarmPolicyRuleController {
@GetMapping("/list") @GetMapping("/list")
@Operation(summary = "查询全部监控告警规则") @Operation(summary = "查询全部监控告警规则")
@Parameter(name = "policyId", description = "policyId", required = true) @Parameter(name = "policyId", description = "policyId", required = true)
@Parameter(name = "metricsMeasurement", description = "metricsMeasurement") @Parameter(name = "measurement", description = "measurement")
@PreAuthorize("@ss.hasPermission('monitor:alarm-policy:query')") @PreAuthorize("@ss.hasPermission('monitor:alarm-policy:query')")
public List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(@RequestParam("policyId") Long policyId, public List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(@RequestParam("policyId") Long policyId,
@RequestParam(value = "metricsMeasurement", required = false) String metricsMeasurement) { @RequestParam(value = "measurement", required = false) String measurement) {
return alarmPolicyRuleService.getAlarmPolicyRuleList(policyId, metricsMeasurement); return alarmPolicyRuleService.getAlarmPolicyRuleList(policyId, measurement);
} }
@DemoDisableApi @DemoDisableApi

View File

@@ -38,10 +38,7 @@ import org.dromara.visor.framework.web.core.annotation.RestWrapper;
import org.dromara.visor.module.monitor.define.operator.MonitorHostOperatorType; import org.dromara.visor.module.monitor.define.operator.MonitorHostOperatorType;
import org.dromara.visor.module.monitor.engine.MonitorContext; import org.dromara.visor.module.monitor.engine.MonitorContext;
import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO; import org.dromara.visor.module.monitor.entity.dto.AgentMetricsDataDTO;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostChartRequest; import org.dromara.visor.module.monitor.entity.request.host.*;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostQueryRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostSwitchUpdateRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostUpdateRequest;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO;
import org.dromara.visor.module.monitor.service.MonitorHostService; import org.dromara.visor.module.monitor.service.MonitorHostService;
@@ -95,7 +92,7 @@ public class MonitorHostController {
@Operation(summary = "查询监控指标") @Operation(summary = "查询监控指标")
@PreAuthorize("@ss.hasPermission('monitor:monitor-host:query')") @PreAuthorize("@ss.hasPermission('monitor:monitor-host:query')")
public List<MonitorHostMetricsDataVO> getMonitorHostMetrics(@Validated(Key.class) @RequestBody MonitorHostQueryRequest request) { public List<MonitorHostMetricsDataVO> getMonitorHostMetrics(@Validated(Key.class) @RequestBody MonitorHostQueryRequest request) {
return monitorHostService.getMonitorHostMetrics(request.getAgentKeyList()); return monitorHostService.getMonitorHostMetrics(request.getAgentKeys());
} }
@IgnoreLog(IgnoreLogMode.RET) @IgnoreLog(IgnoreLogMode.RET)
@@ -106,6 +103,14 @@ public class MonitorHostController {
return monitorHostService.getMonitorHostChart(request); return monitorHostService.getMonitorHostChart(request);
} }
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/host-tags")
@Operation(summary = "查询监控告警标签")
@PreAuthorize("@ss.hasPermission('monitor:monitor-host:query')")
public List<String> getMonitorHostTags(@RequestBody MonitorHostQueryTagRequest request) {
return monitorHostService.getMonitorHostTags(request);
}
@DemoDisableApi @DemoDisableApi
@OperatorLog(MonitorHostOperatorType.UPDATE) @OperatorLog(MonitorHostOperatorType.UPDATE)
@PutMapping("/update") @PutMapping("/update")

View File

@@ -34,7 +34,7 @@ import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
/** /**
* 监控告警记录 内部对象转换器 * 监控告警事件 内部对象转换器
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0

View File

@@ -32,7 +32,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* 监控告警记录 Mapper 接口 * 监控告警事件 Mapper 接口
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0

View File

@@ -95,6 +95,20 @@ public interface MonitorHostDAO extends IMapper<MonitorHostDO> {
return this.delete(Conditions.in(MonitorHostDO::getHostId, hostIdList)); return this.delete(Conditions.in(MonitorHostDO::getHostId, hostIdList));
} }
/**
* 通过 policyId 查询
*
* @param policyId policyId
* @return row
*/
default List<MonitorHostDO> selectByPolicyId(Long policyId) {
return this.of()
.createWrapper()
.eq(MonitorHostDO::getPolicyId, policyId)
.then()
.list();
}
/** /**
* 设置 policyId 为 null * 设置 policyId 为 null
* *

View File

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

View File

@@ -30,7 +30,7 @@ import static org.dromara.visor.framework.biz.operator.log.core.enums.OperatorRi
import static org.dromara.visor.framework.biz.operator.log.core.enums.OperatorRiskLevel.L; import static org.dromara.visor.framework.biz.operator.log.core.enums.OperatorRiskLevel.L;
/** /**
* 监控告警记录 操作日志类型 * 监控告警事件 操作日志类型
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -51,9 +51,9 @@ public class AlarmEventOperatorType extends InitializingOperatorTypes {
public OperatorType[] types() { public OperatorType[] types() {
return new OperatorType[]{ return new OperatorType[]{
new OperatorType(L, HANDLE, "设置告警状态为 ${status} <sb>${count}</sb> 条"), new OperatorType(L, HANDLE, "设置告警状态为 ${status} <sb>${count}</sb> 条"),
new OperatorType(L, SET_FALSE, "设置告警记录为误报 <sb>${count}</sb> 条"), new OperatorType(L, SET_FALSE, "设置告警事件为误报 <sb>${count}</sb> 条"),
new OperatorType(H, DELETE, "删除告警记录 <sb>${count}</sb> 条"), new OperatorType(H, DELETE, "删除告警事件 <sb>${count}</sb> 条"),
new OperatorType(H, CLEAR, "清理告警记录 <sb>${count}</sb> 条"), new OperatorType(H, CLEAR, "清理告警事件 <sb>${count}</sb> 条"),
}; };
} }

View File

@@ -36,7 +36,7 @@ import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
/** /**
* 监控告警记录 实体对象 * 监控告警事件 实体对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -48,7 +48,7 @@ import java.util.Date;
@AllArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@TableName(value = "monitor_alarm_event", autoResultMap = true) @TableName(value = "monitor_alarm_event", autoResultMap = true)
@Schema(name = "AlarmEventDO", description = "监控告警记录 实体对象") @Schema(name = "AlarmEventDO", description = "监控告警事件 实体对象")
public class AlarmEventDO extends BaseDO { public class AlarmEventDO extends BaseDO {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -32,7 +32,7 @@ import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
/** /**
* 监控告警记录 清理请求对象 * 监控告警事件 清理请求对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -40,7 +40,7 @@ import javax.validation.constraints.NotNull;
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Schema(name = "AlarmEventClearRequest", description = "监控告警记录 清理请求对象") @Schema(name = "AlarmEventClearRequest", description = "监控告警事件 清理请求对象")
public class AlarmEventClearRequest extends AlarmEventQueryRequest implements DataClearRequest { public class AlarmEventClearRequest extends AlarmEventQueryRequest implements DataClearRequest {
@NotNull @NotNull

View File

@@ -38,7 +38,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* 监控告警记录 更新请求对象 * 监控告警事件 更新请求对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -48,7 +48,7 @@ import java.util.List;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(name = "AlarmEventUpdateRequest", description = "监控告警记录 更新请求对象") @Schema(name = "AlarmEventUpdateRequest", description = "监控告警事件 更新请求对象")
public class AlarmEventHandleRequest implements Serializable { public class AlarmEventHandleRequest implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -31,7 +31,7 @@ import javax.validation.constraints.Size;
import java.util.Date; import java.util.Date;
/** /**
* 监控告警记录 查询请求对象 * 监控告警事件 查询请求对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -42,7 +42,7 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Schema(name = "AlarmEventQueryRequest", description = "监控告警记录 查询请求对象") @Schema(name = "AlarmEventQueryRequest", description = "监控告警事件 查询请求对象")
public class AlarmEventQueryRequest extends BaseQueryRequest { public class AlarmEventQueryRequest extends BaseQueryRequest {
@Schema(description = "id") @Schema(description = "id")

View File

@@ -33,7 +33,7 @@ import java.io.Serializable;
import java.util.List; import java.util.List;
/** /**
* 监控告警记录 设置误报请求对象 * 监控告警事件 设置误报请求对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -43,7 +43,7 @@ import java.util.List;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(name = "AlarmEventSetFalseRequest", description = "监控告警记录 设置误报请求对象") @Schema(name = "AlarmEventSetFalseRequest", description = "监控告警事件 设置误报请求对象")
public class AlarmEventSetFalseRequest implements Serializable { public class AlarmEventSetFalseRequest implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -46,7 +46,7 @@ public class MonitorHostQueryRequest extends BaseQueryRequest {
@NotEmpty(groups = Key.class) @NotEmpty(groups = Key.class)
@Schema(description = "agentKey") @Schema(description = "agentKey")
private List<String> agentKeyList; private List<String> agentKeys;
@Schema(description = "搜索") @Schema(description = "搜索")
private String searchValue; private String searchValue;

View File

@@ -0,0 +1,53 @@
/*
* 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.entity.request.host;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.visor.common.entity.BaseQueryRequest;
import java.util.List;
/**
* 监控主机标签 查询请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025-8-14 16:27
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(name = "MonitorHostQueryTagRequest", description = "监控主机标签 查询请求对象")
public class MonitorHostQueryTagRequest extends BaseQueryRequest {
@Schema(description = "数据集")
private String measurement;
@Schema(description = "策略id")
private Long policyId;
@Schema(description = "agentKey")
private List<String> agentKeys;
}

View File

@@ -33,7 +33,7 @@ import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
/** /**
* 监控告警记录 视图响应对象 * 监控告警事件 视图响应对象
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -43,7 +43,7 @@ import java.util.Date;
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(name = "AlarmEventVO", description = "监控告警记录 视图响应对象") @Schema(name = "AlarmEventVO", description = "监控告警事件 视图响应对象")
public class AlarmEventVO implements Serializable { public class AlarmEventVO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -23,7 +23,7 @@
package org.dromara.visor.module.monitor.enums; package org.dromara.visor.module.monitor.enums;
/** /**
* 告警记录处理状态 * 告警事件处理状态
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0

View File

@@ -43,7 +43,7 @@ public enum MeasurementEnum {
/** /**
* cpu * cpu
*/ */
CPU("cpu", (s) -> { CPU("cpu", true, (s) -> {
s.accept(MetricsConst.CPU_USER_SECONDS_TOTAL, double.class); s.accept(MetricsConst.CPU_USER_SECONDS_TOTAL, double.class);
s.accept(MetricsConst.CPU_SYSTEM_SECONDS_TOTAL, double.class); s.accept(MetricsConst.CPU_SYSTEM_SECONDS_TOTAL, double.class);
s.accept(MetricsConst.CPU_TOTAL_SECONDS_TOTAL, double.class); s.accept(MetricsConst.CPU_TOTAL_SECONDS_TOTAL, double.class);
@@ -52,7 +52,7 @@ public enum MeasurementEnum {
/** /**
* 内存 * 内存
*/ */
MEMORY("memory", s -> { MEMORY("memory", false, s -> {
s.accept(MetricsConst.MEM_USED_BYTES_TOTAL, long.class); s.accept(MetricsConst.MEM_USED_BYTES_TOTAL, long.class);
s.accept(MetricsConst.MEM_USED_PERCENT, double.class); s.accept(MetricsConst.MEM_USED_PERCENT, double.class);
s.accept(MetricsConst.MEM_SWAP_USED_BYTES_TOTAL, long.class); s.accept(MetricsConst.MEM_SWAP_USED_BYTES_TOTAL, long.class);
@@ -62,7 +62,7 @@ public enum MeasurementEnum {
/** /**
* 负载 * 负载
*/ */
LOAD("load", s -> { LOAD("load", false, s -> {
s.accept(MetricsConst.LOAD1, double.class); s.accept(MetricsConst.LOAD1, double.class);
s.accept(MetricsConst.LOAD5, double.class); s.accept(MetricsConst.LOAD5, double.class);
s.accept(MetricsConst.LOAD15, double.class); s.accept(MetricsConst.LOAD15, double.class);
@@ -74,7 +74,7 @@ public enum MeasurementEnum {
/** /**
* 磁盘 * 磁盘
*/ */
DISK("disk", s -> { DISK("disk", true, s -> {
s.accept(MetricsConst.DISK_FS_USED_BYTES_TOTAL, long.class); s.accept(MetricsConst.DISK_FS_USED_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_FS_USED_PERCENT, double.class); s.accept(MetricsConst.DISK_FS_USED_PERCENT, double.class);
s.accept(MetricsConst.DISK_FS_INODES_USED_PERCENT, double.class); s.accept(MetricsConst.DISK_FS_INODES_USED_PERCENT, double.class);
@@ -83,7 +83,7 @@ public enum MeasurementEnum {
/** /**
* io * io
*/ */
IO("io", s -> { IO("io", false, s -> {
s.accept(MetricsConst.DISK_IO_READ_BYTES_TOTAL, long.class); s.accept(MetricsConst.DISK_IO_READ_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_WRITE_BYTES_TOTAL, long.class); s.accept(MetricsConst.DISK_IO_WRITE_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_READS_TOTAL, long.class); s.accept(MetricsConst.DISK_IO_READS_TOTAL, long.class);
@@ -97,7 +97,7 @@ public enum MeasurementEnum {
/** /**
* 网络 * 网络
*/ */
NETWORK("network", s -> { NETWORK("network", true, s -> {
s.accept(MetricsConst.NET_SENT_BYTES_TOTAL, long.class); s.accept(MetricsConst.NET_SENT_BYTES_TOTAL, long.class);
s.accept(MetricsConst.NET_RECV_BYTES_TOTAL, long.class); s.accept(MetricsConst.NET_RECV_BYTES_TOTAL, long.class);
s.accept(MetricsConst.NET_SENT_PACKETS_TOTAL, long.class); s.accept(MetricsConst.NET_SENT_PACKETS_TOTAL, long.class);
@@ -111,7 +111,7 @@ public enum MeasurementEnum {
/** /**
* 连接数 * 连接数
*/ */
CONNECTIONS("connections", s -> { CONNECTIONS("connections", false, s -> {
s.accept(MetricsConst.NET_TCP_CONNECTIONS, int.class); s.accept(MetricsConst.NET_TCP_CONNECTIONS, int.class);
s.accept(MetricsConst.NET_UDP_CONNECTIONS, int.class); s.accept(MetricsConst.NET_UDP_CONNECTIONS, int.class);
s.accept(MetricsConst.NET_INET_CONNECTIONS, int.class); s.accept(MetricsConst.NET_INET_CONNECTIONS, int.class);
@@ -121,10 +121,14 @@ public enum MeasurementEnum {
; ;
private final String measurement; private final String measurement;
private final boolean hasTags;
private final Map<String, Class<?>> fields; private final Map<String, Class<?>> fields;
MeasurementEnum(String measurement, Consumer<BiConsumer<String, Class<?>>> register) { MeasurementEnum(String measurement, boolean hasTags, Consumer<BiConsumer<String, Class<?>>> register) {
this.measurement = measurement; this.measurement = measurement;
this.hasTags = hasTags;
this.fields = new HashedMap<>(); this.fields = new HashedMap<>();
register.accept(this.fields::put); register.accept(this.fields::put);
} }

View File

@@ -1,159 +0,0 @@
/*
* 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.Getter;
import org.apache.commons.collections4.map.HashedMap;
import org.dromara.visor.module.monitor.constant.MetricsConst;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* 指标度量类型
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/8/14 10:27
*/
@Getter
public enum MeasurementFieldEnum {
/**
* cpu
*/
CPU("cpu", (s) -> {
s.accept(MetricsConst.CPU_USER_SECONDS_TOTAL, double.class);
s.accept(MetricsConst.CPU_SYSTEM_SECONDS_TOTAL, double.class);
s.accept(MetricsConst.CPU_TOTAL_SECONDS_TOTAL, double.class);
}),
/**
* 内存
*/
MEMORY("memory", s -> {
s.accept(MetricsConst.MEM_USED_BYTES_TOTAL, long.class);
s.accept(MetricsConst.MEM_USED_PERCENT, double.class);
s.accept(MetricsConst.MEM_SWAP_USED_BYTES_TOTAL, long.class);
s.accept(MetricsConst.MEM_SWAP_USED_PERCENT, double.class);
}),
/**
* 负载
*/
LOAD("load", s -> {
s.accept(MetricsConst.LOAD1, double.class);
s.accept(MetricsConst.LOAD5, double.class);
s.accept(MetricsConst.LOAD15, double.class);
s.accept(MetricsConst.LOAD1_CORE_RATIO, double.class);
s.accept(MetricsConst.LOAD5_CORE_RATIO, double.class);
s.accept(MetricsConst.LOAD15_CORE_RATIO, double.class);
}),
/**
* 磁盘
*/
DISK("disk", s -> {
s.accept(MetricsConst.DISK_FS_USED_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_FS_USED_PERCENT, double.class);
s.accept(MetricsConst.DISK_FS_INODES_USED_PERCENT, double.class);
}),
/**
* io
*/
IO("io", s -> {
s.accept(MetricsConst.DISK_IO_READ_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_WRITE_BYTES_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_READS_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_WRITES_TOTAL, long.class);
s.accept(MetricsConst.DISK_IO_READ_BYTES_PER_SECOND, double.class);
s.accept(MetricsConst.DISK_IO_WRITE_BYTES_PER_SECOND, double.class);
s.accept(MetricsConst.DISK_IO_READS_PER_SECOND, double.class);
s.accept(MetricsConst.DISK_IO_WRITES_PER_SECOND, double.class);
}),
/**
* 网络
*/
NETWORK("network", s -> {
s.accept(MetricsConst.NET_SENT_BYTES_TOTAL, long.class);
s.accept(MetricsConst.NET_RECV_BYTES_TOTAL, long.class);
s.accept(MetricsConst.NET_SENT_PACKETS_TOTAL, long.class);
s.accept(MetricsConst.NET_RECV_PACKETS_TOTAL, long.class);
s.accept(MetricsConst.NET_SENT_BYTES_PER_SECOND, double.class);
s.accept(MetricsConst.NET_RECV_BYTES_PER_SECOND, double.class);
s.accept(MetricsConst.NET_SENT_PACKETS_PER_SECOND, double.class);
s.accept(MetricsConst.NET_RECV_PACKETS_PER_SECOND, double.class);
}),
/**
* 连接数
*/
CONNECTIONS("connections", s -> {
s.accept(MetricsConst.NET_TCP_CONNECTIONS, int.class);
s.accept(MetricsConst.NET_UDP_CONNECTIONS, int.class);
s.accept(MetricsConst.NET_INET_CONNECTIONS, int.class);
s.accept(MetricsConst.NET_ALL_CONNECTIONS, int.class);
}),
;
private final String measurement;
private final Map<String, Class<?>> fields;
MeasurementFieldEnum(String measurement, Consumer<BiConsumer<String, Class<?>>> register) {
this.measurement = measurement;
this.fields = new HashedMap<>();
register.accept(this.fields::put);
}
public static MeasurementFieldEnum of(String measurement) {
if (measurement == null) {
return null;
}
for (MeasurementFieldEnum e : values()) {
if (e.measurement.equals(measurement)) {
return e;
}
}
return null;
}
/**
* 获取度量值类型
*
* @param measurement measurement
* @param field field
* @return type
*/
public static Class<?> getMetricsValueType(String measurement, String field) {
MeasurementFieldEnum m = of(measurement);
if (m == null) {
return null;
}
return m.getFields().get(field);
}
}

View File

@@ -35,7 +35,7 @@ import org.dromara.visor.module.monitor.entity.vo.AlarmEventVO;
import java.util.List; import java.util.List;
/** /**
* 监控告警记录 服务类 * 监控告警事件 服务类
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -44,14 +44,14 @@ import java.util.List;
public interface AlarmEventService { public interface AlarmEventService {
/** /**
* 创建监控告警记录 * 创建监控告警事件
* *
* @param record record * @param record record
*/ */
void createAlarmEvent(AlarmEventDO record); void createAlarmEvent(AlarmEventDO record);
/** /**
* 处理告警记录 * 处理告警事件
* *
* @param request request * @param request request
* @return effect * @return effect
@@ -67,7 +67,7 @@ public interface AlarmEventService {
Integer setAlarmEventFalse(AlarmEventSetFalseRequest request); Integer setAlarmEventFalse(AlarmEventSetFalseRequest request);
/** /**
* 分页查询监控告警记录 * 分页查询监控告警事件
* *
* @param request request * @param request request
* @return rows * @return rows
@@ -75,7 +75,7 @@ public interface AlarmEventService {
DataGrid<AlarmEventVO> getAlarmEventPage(AlarmEventQueryRequest request); DataGrid<AlarmEventVO> getAlarmEventPage(AlarmEventQueryRequest request);
/** /**
* 查询监控告警记录数量 * 查询监控告警事件数量
* *
* @param request request * @param request request
* @return count * @return count
@@ -83,7 +83,7 @@ public interface AlarmEventService {
Long getAlarmEventCount(AlarmEventQueryRequest request); Long getAlarmEventCount(AlarmEventQueryRequest request);
/** /**
* 删除监控告警记录 * 删除监控告警事件
* *
* @param id id * @param id id
* @return effect * @return effect
@@ -91,7 +91,7 @@ public interface AlarmEventService {
Integer deleteAlarmEventById(Long id); Integer deleteAlarmEventById(Long id);
/** /**
* 批量删除监控告警记录 * 批量删除监控告警事件
* *
* @param idList idList * @param idList idList
* @return effect * @return effect
@@ -99,7 +99,7 @@ public interface AlarmEventService {
Integer deleteAlarmEventByIdList(List<Long> idList); Integer deleteAlarmEventByIdList(List<Long> idList);
/** /**
* 清理监控告警记录 * 清理监控告警事件
* *
* @param request request * @param request request
* @return effect * @return effect
@@ -107,7 +107,7 @@ public interface AlarmEventService {
Integer clearAlarmEvent(AlarmEventClearRequest request); Integer clearAlarmEvent(AlarmEventClearRequest request);
/** /**
* 获取策略告警记录数量 * 获取策略告警事件数量
* *
* @param policyIdList policyIdList * @param policyIdList policyIdList
* @param startDay startDay * @param startDay startDay

View File

@@ -73,11 +73,11 @@ public interface AlarmPolicyRuleService {
/** /**
* 查询全部监控告警规则 * 查询全部监控告警规则
* *
* @param policyId policyId * @param policyId policyId
* @param metricsMeasurement metricsMeasurement * @param measurement measurement
* @return rows * @return rows
*/ */
List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(Long policyId, String metricsMeasurement); List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(Long policyId, String measurement);
/** /**
* 通过 metricsId 删除监控告警规则 * 通过 metricsId 删除监控告警规则

View File

@@ -24,10 +24,7 @@ package org.dromara.visor.module.monitor.service;
import cn.orionsec.kit.lang.define.wrapper.DataGrid; import cn.orionsec.kit.lang.define.wrapper.DataGrid;
import org.dromara.visor.common.entity.chart.TimeChartSeries; import org.dromara.visor.common.entity.chart.TimeChartSeries;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostChartRequest; import org.dromara.visor.module.monitor.entity.request.host.*;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostQueryRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostSwitchUpdateRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostUpdateRequest;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO;
@@ -53,10 +50,10 @@ public interface MonitorHostService {
/** /**
* 获取监控主机指标数据 * 获取监控主机指标数据
* *
* @param agentKeyList agentKeyList * @param agentKeys agentKeys
* @return metrics * @return metrics
*/ */
List<MonitorHostMetricsDataVO> getMonitorHostMetrics(List<String> agentKeyList); List<MonitorHostMetricsDataVO> getMonitorHostMetrics(List<String> agentKeys);
/** /**
* 获取监控主机图表数据 * 获取监控主机图表数据
@@ -66,6 +63,14 @@ public interface MonitorHostService {
*/ */
List<TimeChartSeries> getMonitorHostChart(MonitorHostChartRequest request); List<TimeChartSeries> getMonitorHostChart(MonitorHostChartRequest request);
/**
* 查询监控告警标签
*
* @param request request
* @return tags
*/
List<String> getMonitorHostTags(MonitorHostQueryTagRequest request);
/** /**
* 更新监控主机 * 更新监控主机
* *

View File

@@ -62,7 +62,7 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 监控告警记录 服务实现类 * 监控告警事件 服务实现类
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0

View File

@@ -160,13 +160,13 @@ public class AlarmPolicyRuleServiceImpl implements AlarmPolicyRuleService {
} }
@Override @Override
public List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(Long policyId, String metricsMeasurement) { public List<AlarmPolicyRuleVO> getAlarmPolicyRuleList(Long policyId, String measurement) {
// 查询 // 查询
return alarmPolicyRuleDAO.of() return alarmPolicyRuleDAO.of()
.createWrapper() .createWrapper()
.eq(AlarmPolicyRuleDO::getPolicyId, policyId) .eq(AlarmPolicyRuleDO::getPolicyId, policyId)
.eq(Strings.isNotBlank(metricsMeasurement), AlarmPolicyRuleDO::getMetricsMeasurement, metricsMeasurement) .eq(Strings.isNotBlank(measurement), AlarmPolicyRuleDO::getMetricsMeasurement, measurement)
// 同的指标在一起 // 同的指标在一起
.orderByAsc(AlarmPolicyRuleDO::getMetricsId) .orderByAsc(AlarmPolicyRuleDO::getMetricsId)
// 通过 p0 > p1 排序 // 通过 p0 > p1 排序
.orderByAsc(AlarmPolicyRuleDO::getLevel) .orderByAsc(AlarmPolicyRuleDO::getLevel)

View File

@@ -173,14 +173,14 @@ public class AlarmPolicyServiceImpl implements AlarmPolicyService {
@Override @Override
public List<AlarmPolicyVO> getAlarmPolicyListByCache() { public List<AlarmPolicyVO> getAlarmPolicyListByCache() {
// 查询缓存 // 查询缓存
List<AlarmPolicyCacheDTO> list = RedisMaps.valuesJson(AlarmPolicyCacheKeyDefine.MONITOR_ALARM_POLICY); List<AlarmPolicyCacheDTO> list = RedisMaps.valuesJson(AlarmPolicyCacheKeyDefine.ALARM_POLICY);
if (list.isEmpty()) { if (list.isEmpty()) {
// 查询数据库 // 查询数据库
list = alarmPolicyDAO.of().list(AlarmPolicyConvert.MAPPER::toCache); list = alarmPolicyDAO.of().list(AlarmPolicyConvert.MAPPER::toCache);
// 设置屏障 防止穿透 // 设置屏障 防止穿透
CacheBarriers.checkBarrier(list, AlarmPolicyCacheDTO::new); CacheBarriers.checkBarrier(list, AlarmPolicyCacheDTO::new);
// 设置缓存 // 设置缓存
RedisMaps.putAllJson(AlarmPolicyCacheKeyDefine.MONITOR_ALARM_POLICY, s -> s.getId().toString(), list); RedisMaps.putAllJson(AlarmPolicyCacheKeyDefine.ALARM_POLICY, s -> s.getId().toString(), list);
} }
// 删除屏障 // 删除屏障
CacheBarriers.removeBarrier(list); CacheBarriers.removeBarrier(list);

View File

@@ -147,7 +147,7 @@ public class MonitorAgentEndpointServiceImpl implements MonitorAgentEndpointServ
} }
monitorHostDAO.updateById(update); monitorHostDAO.updateById(update);
} }
// 设置配置缓存 // 重新加载监控主机上下文
if (newConfig != null) { if (newConfig != null) {
monitorContext.reloadMonitorHost(agentKey); monitorContext.reloadMonitorHost(agentKey);
} }

View File

@@ -55,10 +55,7 @@ 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.AlarmPolicyDO;
import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO; import org.dromara.visor.module.monitor.entity.domain.MonitorHostDO;
import org.dromara.visor.module.monitor.entity.dto.*; import org.dromara.visor.module.monitor.entity.dto.*;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostChartRequest; import org.dromara.visor.module.monitor.entity.request.host.*;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostQueryRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostSwitchUpdateRequest;
import org.dromara.visor.module.monitor.entity.request.host.MonitorHostUpdateRequest;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostMetricsDataVO;
import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO; import org.dromara.visor.module.monitor.entity.vo.MonitorHostVO;
import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum; import org.dromara.visor.module.monitor.enums.AlarmSwitchEnum;
@@ -188,8 +185,8 @@ public class MonitorHostServiceImpl implements MonitorHostService {
} }
@Override @Override
public List<MonitorHostMetricsDataVO> getMonitorHostMetrics(List<String> agentKeyList) { public List<MonitorHostMetricsDataVO> getMonitorHostMetrics(List<String> agentKeys) {
return agentKeyList.stream() return agentKeys.stream()
.map(s -> this.getHostMetricsData(s, null)) .map(s -> this.getHostMetricsData(s, null))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@@ -235,6 +232,44 @@ public class MonitorHostServiceImpl implements MonitorHostService {
return seriesList; return seriesList;
} }
@Override
public List<String> getMonitorHostTags(MonitorHostQueryTagRequest request) {
MeasurementEnum measurementEnum = MeasurementEnum.of(request.getMeasurement());
if (measurementEnum == null) {
return Collections.emptyList();
}
// 映射数据
Function<MonitorHostMetaDTO, List<String>> tagsGetter;
if (MeasurementEnum.CPU.equals(measurementEnum)) {
tagsGetter = MonitorHostMetaDTO::getCpus;
} else if (MeasurementEnum.DISK.equals(measurementEnum)) {
tagsGetter = MonitorHostMetaDTO::getDisks;
} else if (MeasurementEnum.NETWORK.equals(measurementEnum)) {
tagsGetter = MonitorHostMetaDTO::getNets;
} else {
return Collections.emptyList();
}
// 查询监控主机元数据
List<MonitorHostMetaDTO> metas = monitorHostDAO.of()
.createValidateWrapper()
.eq(MonitorHostDO::getPolicyId, request.getPolicyId())
.in(MonitorHostDO::getAgentKey, request.getAgentKeys())
.then()
.stream()
.map(MonitorHostDO::getMonitorMeta)
.filter(Objects::nonNull)
.map(s -> JSON.parseObject(s, MonitorHostMetaDTO.class))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 获取 tag
return metas.stream()
.map(tagsGetter)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Integer updateMonitorHostById(MonitorHostUpdateRequest request) { public Integer updateMonitorHostById(MonitorHostUpdateRequest request) {
@@ -272,7 +307,7 @@ public class MonitorHostServiceImpl implements MonitorHostService {
if (policyId == null) { if (policyId == null) {
monitorHostDAO.setPolicyIdWithNullById(id); monitorHostDAO.setPolicyIdWithNullById(id);
} }
// 更新缓存 // 重新加载监控主机上下文
monitorContext.reloadMonitorHost(host.getAgentKey()); monitorContext.reloadMonitorHost(host.getAgentKey());
log.info("MonitorHostService-updateMonitorHostById effect: {}", effect); log.info("MonitorHostService-updateMonitorHostById effect: {}", effect);
return effect; return effect;

View File

@@ -4,7 +4,7 @@ import axios from 'axios';
import qs from 'query-string'; import qs from 'query-string';
/** /**
* 告警记录处理请求 * 告警事件处理请求
*/ */
export interface AlarmEventHandleRequest { export interface AlarmEventHandleRequest {
idList?: Array<number>; idList?: Array<number>;
@@ -14,14 +14,14 @@ export interface AlarmEventHandleRequest {
} }
/** /**
* 告警记录误报请求 * 告警事件误报请求
*/ */
export interface AlarmEventFalseAlarmRequest { export interface AlarmEventFalseAlarmRequest {
idList?: Array<number>; idList?: Array<number>;
} }
/** /**
* 告警记录查询请求 * 告警事件查询请求
*/ */
export interface AlarmEventQueryRequest extends Pagination, OrderDirection { export interface AlarmEventQueryRequest extends Pagination, OrderDirection {
id?: number; id?: number;
@@ -39,13 +39,13 @@ export interface AlarmEventQueryRequest extends Pagination, OrderDirection {
} }
/** /**
* 告警记录清理请求 * 告警事件清理请求
*/ */
export interface AlarmEventClearRequest extends AlarmEventQueryRequest, ClearRequest { export interface AlarmEventClearRequest extends AlarmEventQueryRequest, ClearRequest {
} }
/** /**
* 告警记录查询响应 * 告警事件查询响应
*/ */
export interface AlarmEventQueryResponse extends TableData { export interface AlarmEventQueryResponse extends TableData {
id: number; id: number;
@@ -75,7 +75,7 @@ export interface AlarmEventQueryResponse extends TableData {
} }
/** /**
* 处理告警记录 * 处理告警事件
*/ */
export function handleAlarmEvent(request: AlarmEventHandleRequest) { export function handleAlarmEvent(request: AlarmEventHandleRequest) {
return axios.post<number>('/monitor/alarm-event/handle', request); return axios.post<number>('/monitor/alarm-event/handle', request);
@@ -89,28 +89,28 @@ export function setAlarmEventFalse(request: AlarmEventFalseAlarmRequest) {
} }
/** /**
* 分页查询告警记录 * 分页查询告警事件
*/ */
export function getAlarmEventPage(request: AlarmEventQueryRequest) { export function getAlarmEventPage(request: AlarmEventQueryRequest) {
return axios.post<DataGrid<AlarmEventQueryResponse>>('/monitor/alarm-event/query', request); return axios.post<DataGrid<AlarmEventQueryResponse>>('/monitor/alarm-event/query', request);
} }
/** /**
* 查询告警记录数量 * 查询告警事件数量
*/ */
export function getAlarmEventCount(request: AlarmEventQueryRequest) { export function getAlarmEventCount(request: AlarmEventQueryRequest) {
return axios.post<number>('/monitor/alarm-event/count', request); return axios.post<number>('/monitor/alarm-event/count', request);
} }
/** /**
* 删除告警记录 * 删除告警事件
*/ */
export function deleteAlarmEvent(id: number) { export function deleteAlarmEvent(id: number) {
return axios.delete<number>('/monitor/alarm-event/delete', { params: { id } }); return axios.delete<number>('/monitor/alarm-event/delete', { params: { id } });
} }
/** /**
* 批量删除告警记录 * 批量删除告警事件
*/ */
export function batchDeleteAlarmEvent(idList: Array<number>) { export function batchDeleteAlarmEvent(idList: Array<number>) {
return axios.delete<number>('/monitor/alarm-event/batch-delete', { return axios.delete<number>('/monitor/alarm-event/batch-delete', {
@@ -122,7 +122,7 @@ export function batchDeleteAlarmEvent(idList: Array<number>) {
} }
/** /**
* 清理告警记录 * 清理告警事件
*/ */
export function clearMonitorAlarmEvent(request: AlarmEventClearRequest) { export function clearMonitorAlarmEvent(request: AlarmEventClearRequest) {
return axios.post<number>('/monitor/alarm-event/clear', request); return axios.post<number>('/monitor/alarm-event/clear', request);

View File

@@ -72,8 +72,8 @@ export function updateAlarmRuleSwitch(request: AlarmRuleUpdateRequest) {
/** /**
* 查询全部监控告警规则 * 查询全部监控告警规则
*/ */
export function getAlarmRuleList(policyId: number, metricsMeasurement: string = '') { export function getAlarmRuleList(policyId: number, measurement: string = '') {
return axios.get<Array<AlarmRuleQueryResponse>>('/monitor/alarm-policy-rule/list', { params: { policyId, metricsMeasurement } }); return axios.get<Array<AlarmRuleQueryResponse>>('/monitor/alarm-policy-rule/list', { params: { policyId, measurement } });
} }
/** /**

View File

@@ -28,7 +28,7 @@ export interface MonitorHostSwitchUpdateRequest {
* 监控主机查询请求 * 监控主机查询请求
*/ */
export interface MonitorHostQueryRequest extends Pagination { export interface MonitorHostQueryRequest extends Pagination {
agentKeyList?: Array<string>; agentKeys?: Array<string>;
searchValue?: string; searchValue?: string;
alarmSwitch?: number; alarmSwitch?: number;
policyId?: number; policyId?: number;
@@ -43,6 +43,15 @@ export interface MonitorHostQueryRequest extends Pagination {
tags?: Array<number>; tags?: Array<number>;
} }
/**
* 监控主机标签查询请求
*/
export interface MonitorHostQueryTagRequest {
measurement?: string;
policyId?: number;
agentKeys?: Array<string>;
}
/** /**
* 监控主机图表查询请求 * 监控主机图表查询请求
*/ */
@@ -147,12 +156,12 @@ export interface MonitorHostMetricsData {
/** /**
* 查询监控主机指标 * 查询监控主机指标
*/ */
export function getMonitorHostMetrics(agentKeyList: Array<string>) { export function getMonitorHostMetrics(agentKeys: Array<string>) {
return axios.post<Array<MonitorHostMetricsData>>('/monitor/monitor-host/metrics', { return axios.post<Array<MonitorHostMetricsData>>('/monitor/monitor-host/metrics', {
agentKeyList agentKeys
}, { }, {
promptBizErrorMessage: false, promptBizErrorMessage: false,
promptRequestErrorMessage: false, promptRequestErrorMessage: false
}); });
} }
@@ -179,6 +188,13 @@ export function getMonitorHostPage(request: MonitorHostQueryRequest) {
return axios.post<DataGrid<MonitorHostQueryResponse>>('/monitor/monitor-host/query', request); return axios.post<DataGrid<MonitorHostQueryResponse>>('/monitor/monitor-host/query', request);
} }
/**
* 查询监控主机标签
*/
export function getMonitorHostTags(request: MonitorHostQueryTagRequest) {
return axios.post<Array<string>>('/monitor/monitor-host/host-tags', request);
}
/** /**
* 更新监控主机 * 更新监控主机
*/ */

View File

@@ -95,7 +95,7 @@
// 标准卡片 // 标准卡片
.general-card { .general-card {
border-radius: 4px; border-radius: 8px;
border: none; border: none;
& > .arco-card-header { & > .arco-card-header {

View File

@@ -14,58 +14,6 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
// -- echarts
.echarts-tooltip-diy {
background: linear-gradient(304.17deg,
rgba(253, 254, 255, 0.6) -6.04%,
rgba(244, 247, 252, 0.6) 85.2%) !important;
border: none !important;
backdrop-filter: blur(10px) !important;
/* Note: backdrop-filter has minimal browser support */
border-radius: 6px !important;
.content-panel {
display: flex;
justify-content: space-between;
padding: 0 9px;
background: rgba(255, 255, 255, 0.8);
width: 164px;
height: 32px;
line-height: 32px;
box-shadow: 6px 0 20px rgba(34, 87, 188, 0.1);
border-radius: 4px;
margin-bottom: 4px;
}
.tooltip-title {
margin: 0 0 10px 0;
}
p {
margin: 0;
}
.tooltip-title,
.tooltip-value {
font-size: 13px;
line-height: 15px;
display: flex;
align-items: center;
text-align: right;
color: #1D2129;
font-weight: bold;
}
.tooltip-item-icon {
display: inline-block;
margin-right: 8px;
width: 10px;
height: 10px;
border-radius: 50%;
}
}
// flex // flex
.flex-center { .flex-center {
display: flex; display: flex;

View File

@@ -162,8 +162,8 @@
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 4px; border-radius: 8px;
border: 1px solid var(--color-neutral-3); //border: 1px solid var(--color-neutral-3);
transition-property: all; transition-property: all;
& > .arco-card-header { & > .arco-card-header {
@@ -230,7 +230,7 @@
// -- card // -- card
.simple-card { .simple-card {
background: var(--color-bg-2); background: var(--color-bg-2);
border-radius: 4px; border-radius: 8px;
} }
// -- doption // -- doption

View File

@@ -27,7 +27,19 @@
</div> </div>
<!-- 状态 --> <!-- 状态 -->
<div class="exec-host-item-status"> <div class="exec-host-item-status">
<a-tag :color="getDictValue(execHostStatusKey, item.status, 'execColor')"> <!-- 执行结果 -->
<a-tag v-if="item.exitCode || item.exitCode === 0"
class="exit-code-tag"
title="exitCode"
:color="item.exitCode === 0 ? 'rgb(var(--arcoblue-4))' : 'rgb(var(--orangered-4))'">
<template #icon>
<icon-check v-if="item.exitCode === 0" />
<icon-exclamation v-else />
</template>
<span class="exit-code-value">{{ item.exitCode }}</span>
</a-tag>
<!-- 执行状态 -->
<a-tag v-else :color="getDictValue(execHostStatusKey, item.status, 'execColor')">
{{ getDictValue(execHostStatusKey, item.status) }} {{ getDictValue(execHostStatusKey, item.status) }}
</a-tag> </a-tag>
</div> </div>
@@ -141,8 +153,16 @@
&-status { &-status {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} gap: 8px;
:deep(.exit-code-tag .arco-tag-icon) {
color: #FFFFFF;
}
.exit-code-value {
font-weight: 600;
}
}
} }
</style> </style>

View File

@@ -15,7 +15,7 @@
<!-- exitCode --> <!-- exitCode -->
<a-tag v-if="host.exitCode || host.exitCode === 0" <a-tag v-if="host.exitCode || host.exitCode === 0"
:color="host.exitCode === 0 ? 'arcoblue' : 'orangered'" :color="host.exitCode === 0 ? 'arcoblue' : 'orangered'"
title="exit code"> title="exitCode">
<template #icon> <template #icon>
<icon-check v-if="host.exitCode === 0" /> <icon-check v-if="host.exitCode === 0" />
<icon-exclamation v-else /> <icon-exclamation v-else />

View File

@@ -179,7 +179,7 @@
&-wrapper { &-wrapper {
background: var(--color-bg-2); background: var(--color-bg-2);
padding: 0 12px; padding: 0 12px;
border-radius: 4px; border-radius: 8px;
.card-list-info { .card-list-info {
height: var(--header-info-height); height: var(--header-info-height);

View File

@@ -17,6 +17,7 @@ export interface TimeSeriesColor {
export interface TimeSeriesOption { export interface TimeSeriesOption {
name: string; name: string;
type: TimeSeriesType; type: TimeSeriesType;
smooth: boolean;
area: boolean; area: boolean;
lineColor: string; lineColor: string;
itemBorderColor: string; itemBorderColor: string;
@@ -126,7 +127,7 @@ export const createTimeSeries = (option: Partial<TimeSeriesOption>): LineSeriesO
name: option.name, name: option.name,
data: option.data || [], data: option.data || [],
type: option.type || 'line', type: option.type || 'line',
smooth: true, smooth: option.smooth ?? true,
symbol: 'circle', symbol: 'circle',
symbolSize: 10, symbolSize: 10,
itemStyle: { itemStyle: {

View File

@@ -12,6 +12,10 @@ export function isString(obj: any): obj is string {
return opt.call(obj) === '[object String]'; return opt.call(obj) === '[object String]';
} }
export function isBoolean(obj: any): obj is boolean {
return opt.call(obj) === '[object Boolean]';
}
export function isNumber(obj: any): obj is number { export function isNumber(obj: any): obj is number {
return opt.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line return opt.call(obj) === '[object Number]' && obj === obj; // eslint-disable-line
} }

View File

@@ -98,7 +98,7 @@
:deep(.card) { :deep(.card) {
padding: 16px 20px; padding: 16px 20px;
border-radius: 4px; border-radius: 8px;
background-color: var(--color-bg-2); background-color: var(--color-bg-2);
&-title { &-title {

View File

@@ -301,7 +301,7 @@
.panel-item { .panel-item {
height: 100%; height: 100%;
padding: 16px; padding: 16px;
border-radius: 4px; border-radius: 8px;
margin-right: 16px; margin-right: 16px;
position: relative; position: relative;
background: var(--color-bg-2); background: var(--color-bg-2);

View File

@@ -295,7 +295,7 @@
.exec-form-container, .exec-command-container, .exec-history-container { .exec-form-container, .exec-command-container, .exec-history-container {
background: var(--color-bg-2); background: var(--color-bg-2);
border-radius: 4px; border-radius: 8px;
height: 100%; height: 100%;
padding: 16px; padding: 16px;
position: relative; position: relative;

View File

@@ -6,7 +6,7 @@
<div class="table-left-bar-handle"> <div class="table-left-bar-handle">
<!-- 标题 --> <!-- 标题 -->
<div class="table-title"> <div class="table-title">
告警记录列表 告警事件列表
</div> </div>
</div> </div>
<!-- 右侧操作 --> <!-- 右侧操作 -->
@@ -229,8 +229,8 @@
// 获取指标名称 // 获取指标名称
const getMetricsField = (metricsId: number, field: string) => { const getMetricsField = (metricsId: number, field: string) => {
return (monitorMetrics as Array<MetricsQueryResponse>).find(m => m.id === metricsId)?.[field]; return (monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.[field];
}; };
// 提取标签 // 提取标签
const extraTags = (record: AlarmEventQueryResponse) => { const extraTags = (record: AlarmEventQueryResponse) => {

View File

@@ -48,7 +48,7 @@
allow-clear /> allow-clear />
</a-form-item> </a-form-item>
<!-- 数据集 --> <!-- 数据集 -->
<a-form-item field="metricsId" label="数据集"> <a-form-item field="metricsMeasurement" label="数据集">
<a-select v-model="formModel.metricsMeasurement" <a-select v-model="formModel.metricsMeasurement"
:options="toOptions(MetricsMeasurementKey)" :options="toOptions(MetricsMeasurementKey)"
placeholder="数据集" placeholder="数据集"

View File

@@ -14,7 +14,7 @@ export const FalseAlarm = {
// 告警条件 字典项 // 告警条件 字典项
export const TriggerConditionKey = 'alarmTriggerCondition'; export const TriggerConditionKey = 'alarmTriggerCondition';
// 告警记录处理状态 字典项 // 告警事件处理状态 字典项
export const HandleStatusKey = 'alarmEventHandleStatus'; export const HandleStatusKey = 'alarmEventHandleStatus';
// 是否为误报 字典项 // 是否为误报 字典项

View File

@@ -30,8 +30,8 @@
placeholder="请输入策略描述" placeholder="请输入策略描述"
allow-clear /> allow-clear />
</a-form-item> </a-form-item>
<!-- 通知模板 --> <!-- 通知渠道 -->
<a-form-item field="notifyIdList" label="通知模板"> <a-form-item field="notifyIdList" label="通知渠道">
<notify-template-selector v-model="formModel.notifyIdList" <notify-template-selector v-model="formModel.notifyIdList"
biz-type="ALARM" biz-type="ALARM"
multiple multiple

View File

@@ -6,7 +6,7 @@
:unmount-on-close="true" :unmount-on-close="true"
:ok-button-props="{ disabled: loading }" :ok-button-props="{ disabled: loading }"
:cancel-button-props="{ disabled: loading }" :cancel-button-props="{ disabled: loading }"
:on-before-ok="handlerOk" :on-before-ok="handleOk"
@cancel="handleClose"> @cancel="handleClose">
<a-spin class="full drawer-form-large" :loading="loading"> <a-spin class="full drawer-form-large" :loading="loading">
<a-form :model="formModel" <a-form :model="formModel"
@@ -17,11 +17,12 @@
<!-- 监控指标 --> <!-- 监控指标 -->
<a-form-item field="metricsId" label="监控指标"> <a-form-item field="metricsId" label="监控指标">
<monitor-metrics-selector v-model="formModel.metricsId" <monitor-metrics-selector v-model="formModel.metricsId"
class="metrics-selector" :class="[ hasTags ? 'metrics-selector-has-tag' : 'metrics-selector-no-tag']"
placeholder="请选择监控指标" placeholder="请选择监控指标"
allow-clear /> allow-clear />
<!-- 添加标签 --> <!-- 添加标签 -->
<a-button title="添加标签" <a-button v-if="hasTags"
title="添加标签"
:disabled="formModel.allEffect === 1" :disabled="formModel.allEffect === 1"
@click="addTag"> @click="addTag">
<template #icon> <template #icon>
@@ -34,20 +35,26 @@
<a-form-item v-if="formModel.allEffect === 0" <a-form-item v-if="formModel.allEffect === 0"
:field="'tag-' + (index + 1)" :field="'tag-' + (index + 1)"
:label="'指标标签-' + (index + 1)"> :label="'指标标签-' + (index + 1)">
<a-space :size="12"> <a-space :size="12" class="tag-wrapper">
<!-- 标签名称 --> <!-- 标签名称 -->
<a-input v-model="tag.key" <a-input v-model="tag.key"
style="width: 128px;" style="width: 108px;"
placeholder="指标标签名称" /> placeholder="标签名称" />
<!-- 标签值 --> <!-- 标签值 -->
<a-select v-model="tag.value" <a-select v-model="tag.value"
class="tag-values" class="tag-values"
style="width: 260px" style="width: 260px"
:max-tag-count="2" :max-tag-count="2"
placeholder="标签值" :options="measurementTags[measurement] || []"
tag-nowrap placeholder="输入或选择标签值"
multiple multiple
allow-create /> allow-create>
<template #empty>
<a-empty>
请输入标签值
</a-empty>
</template>
</a-select>
<!-- 移除 --> <!-- 移除 -->
<a-button title="移除" <a-button title="移除"
style="width: 32px" style="width: 32px"
@@ -72,7 +79,7 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- 全部生效 --> <!-- 全部生效 -->
<a-col :span="12"> <a-col v-if="hasTags" :span="12">
<a-form-item field="allEffect" <a-form-item field="allEffect"
label="全部生效" label="全部生效"
tooltip="开启后则忽略标签, 并生效与已配置标签的规则 (通常用于默认策略)" tooltip="开启后则忽略标签, 并生效与已配置标签的规则 (通常用于默认策略)"
@@ -190,16 +197,17 @@
import type { MetricsQueryResponse } from '@/api/monitor/metrics'; import type { MetricsQueryResponse } from '@/api/monitor/metrics';
import type { RuleTag } from '../types/const'; import type { RuleTag } from '../types/const';
import type { FormHandle } from '@/types/form'; import type { FormHandle } from '@/types/form';
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import formRules from '../types/form.rules'; import formRules from '../types/form.rules';
import { MetricsUnitKey } from '../types/const'; import { MetricsUnitKey, MeasurementKey } from '../types/const';
import { assignOmitRecord } from '@/utils'; import { assignOmitRecord } from '@/utils';
import { TriggerConditionKey, LevelKey, DefaultCondition, DefaultLevel, } from '../types/const'; import { TriggerConditionKey, LevelKey, DefaultCondition, DefaultLevel, } from '../types/const';
import { createAlarmRule, updateAlarmRule } from '@/api/monitor/alarm-rule'; import { createAlarmRule, updateAlarmRule } from '@/api/monitor/alarm-rule';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { useDictStore, useCacheStore } from '@/store'; import { useDictStore, useCacheStore } from '@/store';
import { getMonitorHostTags } from '@/api/monitor/monitor-host';
import MonitorMetricsSelector from '@/components/monitor/metrics/selector/index.vue'; import MonitorMetricsSelector from '@/components/monitor/metrics/selector/index.vue';
const emits = defineEmits(['added', 'updated']); const emits = defineEmits(['added', 'updated']);
@@ -214,6 +222,9 @@
const formRef = ref<any>(); const formRef = ref<any>();
const formModel = ref<AlarmRuleUpdateRequest>({}); const formModel = ref<AlarmRuleUpdateRequest>({});
const tags = ref<Array<RuleTag>>([]); const tags = ref<Array<RuleTag>>([]);
const hasTags = ref(false);
const measurement = ref('');
const measurementTags = ref<Record<string, string[]>>({});
const defaultForm = (): AlarmRuleUpdateRequest => { const defaultForm = (): AlarmRuleUpdateRequest => {
return { return {
@@ -223,7 +234,7 @@
tags: undefined, tags: undefined,
level: DefaultLevel, level: DefaultLevel,
ruleSwitch: 1, ruleSwitch: 1,
allEffect: 0, allEffect: 1,
triggerCondition: DefaultCondition, triggerCondition: DefaultCondition,
threshold: undefined, threshold: undefined,
consecutiveCount: 1, consecutiveCount: 1,
@@ -232,18 +243,28 @@
}; };
}; };
// 指标单位 // 检查是否有 tags
const metricsUnit = computed(() => { watch(() => formModel.value.metricsId, (metricsId) => {
const metricsId = formModel.value.metricsId;
if (!metricsId) { if (!metricsId) {
return ''; hasTags.value = false;
return;
} }
// 读取指标单位 // 获取数据集
const unit = (monitorMetrics as Array<MetricsQueryResponse>).find(m => m.id === metricsId)?.unit; const measurementValue = (monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.measurement;
if (!unit) { if (!measurementValue) {
return ''; hasTags.value = false;
return;
}
measurement.value = measurementValue;
// 获取标签
const value = getDictValue(MeasurementKey, measurementValue, 'hasTags');
if (value === true) {
hasTags.value = true;
// 加载全部标签
loadTags();
} else {
hasTags.value = false;
} }
return getDictValue(MetricsUnitKey, unit, 'alarmUnit');
}); });
// 打开新增 // 打开新增
@@ -284,7 +305,8 @@
// 添加标签 // 添加标签
const addTag = () => { const addTag = () => {
tags.value.push({ key: '', value: [] }); const hasNameTag = tags.value.some(s => s.key === 'name');
tags.value.push({ key: hasNameTag ? '' : 'name', value: [] });
}; };
// 移除标签 // 移除标签
@@ -292,8 +314,37 @@
tags.value.splice(index, 1); tags.value.splice(index, 1);
}; };
// 指标单位
const metricsUnit = computed(() => {
const metricsId = formModel.value.metricsId;
if (!metricsId) {
return '';
}
// 读取指标单位
const unit = (monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.unit;
if (!unit) {
return '';
}
return getDictValue(MetricsUnitKey, unit, 'alarmUnit');
});
// 加载全部标签
const loadTags = () => {
const tags = measurementTags.value[measurement.value];
if (tags) {
return;
}
// 加载标签
getMonitorHostTags({
measurement: measurement.value,
policyId: formModel.value.policyId,
}).then(({ data }) => {
measurementTags.value[measurement.value as any] = data;
});
};
// 确定 // 确定
const handlerOk = async () => { const handleOk = async () => {
setLoading(true); setLoading(true);
try { try {
// 验证参数 // 验证参数
@@ -301,21 +352,37 @@
if (error) { if (error) {
return false; return false;
} }
for (let tag of tags.value) { if (!hasTags.value) {
if (!tag.key) { // 无 tag
Message.error('请输入标签名称'); formModel.value.allEffect = 1;
return false; } else {
} // 有 tag
if (!tag.value) { if (formModel.value.allEffect === 1) {
Message.error('请输入标签值'); // 全部生效
return false; tags.value = [];
} else {
// 检查 tag
if (!tags.value.length) {
Message.error('请选择全部生效或添加对应的标签');
return false;
}
for (let tag of tags.value) {
if (!tag.key) {
Message.error('请输入标签名称');
return false;
}
if (!tag.value) {
Message.error('请输入标签值');
return false;
}
}
} }
} }
if (formHandle.value == 'add') { if (formHandle.value == 'add') {
// 新增 // 新增
await createAlarmRule({ await createAlarmRule({
...formModel.value, ...formModel.value,
tags: formModel.value.allEffect === 1 ? '[]' : JSON.stringify(tags.value) tags: JSON.stringify(tags.value)
}); });
Message.success('创建成功'); Message.success('创建成功');
emits('added'); emits('added');
@@ -323,13 +390,13 @@
// 修改 // 修改
await updateAlarmRule({ await updateAlarmRule({
...formModel.value, ...formModel.value,
tags: formModel.value.allEffect === 1 ? '[]' : JSON.stringify(tags.value) tags: JSON.stringify(tags.value)
}); });
Message.success('修改成功'); Message.success('修改成功');
emits('updated'); emits('updated');
} }
// 清空 handleClose();
handlerClear(); return true;
} catch (e) { } catch (e) {
return false; return false;
} finally { } finally {
@@ -339,22 +406,32 @@
// 关闭 // 关闭
const handleClose = () => { const handleClose = () => {
handlerClear(); handleClear();
setVisible(false);
}; };
// 清空 // 清空
const handlerClear = () => { const handleClear = () => {
setLoading(false); setLoading(false);
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
:deep(.metrics-selector) { :deep(.metrics-selector-no-tag) {
width: 100%;
}
:deep(.metrics-selector-has-tag) {
width: calc(100% - 42px); width: calc(100% - 42px);
margin-right: 12px; margin-right: 12px;
} }
.tag-wrapper {
width: 100%;
justify-content: space-between;
}
.alarm-level-select, .condition-select { .alarm-level-select, .condition-select {
:deep(.arco-select-view-suffix) { :deep(.arco-select-view-suffix) {
@@ -365,4 +442,5 @@
:deep(.tag-values .arco-select-view-inner) { :deep(.tag-values .arco-select-view-inner) {
flex-wrap: nowrap !important; flex-wrap: nowrap !important;
} }
</style> </style>

View File

@@ -203,7 +203,7 @@
// 获取指标名称 // 获取指标名称
const getMetricsField = (metricsId: number, field: string) => { const getMetricsField = (metricsId: number, field: string) => {
return (monitorMetrics as Array<MetricsQueryResponse>).find(m => m.id === metricsId)?.[field]; return (monitorMetrics as Array<MetricsQueryResponse> || []).find(m => m.id === metricsId)?.[field];
}; };
// 切换规则开关 // 切换规则开关

View File

@@ -41,7 +41,7 @@
allow-clear /> allow-clear />
</a-form-item> </a-form-item>
<!-- 数据集 --> <!-- 数据集 -->
<a-form-item field="metricsId" label="数据集"> <a-form-item field="metricsMeasurement" label="数据集">
<a-select v-model="formModel.metricsMeasurement" <a-select v-model="formModel.metricsMeasurement"
:options="toOptions(MetricsMeasurementKey)" :options="toOptions(MetricsMeasurementKey)"
placeholder="数据集" placeholder="数据集"

View File

@@ -9,7 +9,7 @@
:hide-content="true"> :hide-content="true">
<a-tab-pane :key="TabKeys.OVERVIEW" title="主机概览" /> <a-tab-pane :key="TabKeys.OVERVIEW" title="主机概览" />
<a-tab-pane :key="TabKeys.CHART" title="监控图表" /> <a-tab-pane :key="TabKeys.CHART" title="监控图表" />
<a-tab-pane :key="TabKeys.ALARM" title="告警记录" /> <a-tab-pane :key="TabKeys.ALARM" title="告警事件" />
</a-tabs> </a-tabs>
<a-divider direction="vertical" <a-divider direction="vertical"
style="height: 22px; margin: 0 16px 0 8px;" style="height: 22px; margin: 0 16px 0 8px;"
@@ -42,7 +42,7 @@
</div> </div>
<!-- 右侧 --> <!-- 右侧 -->
<div class="header-right"> <div class="header-right">
<!-- 告警记录标签 --> <!-- 告警事件标签 -->
<div v-if="activeKey === TabKeys.OVERVIEW" class="handle-wrapper"> <div v-if="activeKey === TabKeys.OVERVIEW" class="handle-wrapper">
<a-tag v-if="overrideTimestamp">更新时间: {{ dateFormat(new Date(overrideTimestamp)) }}</a-tag> <a-tag v-if="overrideTimestamp">更新时间: {{ dateFormat(new Date(overrideTimestamp)) }}</a-tag>
</div> </div>

View File

@@ -189,6 +189,7 @@
return createTimeSeries({ return createTimeSeries({
name: s.name, name: s.name,
type: props.option.type, type: props.option.type,
smooth: props.option.smooth,
area: props.option.background, area: props.option.background,
lineColor: colors?.[0], lineColor: colors?.[0],
itemBorderColor: colors?.[1], itemBorderColor: colors?.[1],

View File

@@ -19,6 +19,7 @@ export interface MetricsChartOption {
span?: number; span?: number;
legend?: boolean; legend?: boolean;
background?: boolean; background?: boolean;
smooth?: boolean;
colors: Array<[string, string]>; colors: Array<[string, string]>;
aggregate: string; aggregate: string;
unit: MetricUnitType; unit: MetricUnitType;

View File

@@ -177,6 +177,9 @@
if (type === ValueType.BOOLEAN) { if (type === ValueType.BOOLEAN) {
extraValue.value[nameKey] = false; extraValue.value[nameKey] = false;
continue; continue;
} else if (type === ValueType.STRING) {
extraValue.value[nameKey] = '';
continue;
} }
formRef.value.setFields({ formRef.value.setFields({
[nameKey]: { [nameKey]: {

View File

@@ -90,7 +90,7 @@
padding: 16px; padding: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 4px; border-radius: 8px;
} }
:deep(.arco-tabs-nav-tab-list) { :deep(.arco-tabs-nav-tab-list) {

View File

@@ -117,7 +117,7 @@
padding: 16px; padding: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 4px; border-radius: 8px;
} }
:deep(.arco-tabs-nav-tab-list) { :deep(.arco-tabs-nav-tab-list) {