[improve] fix some alarm relate bug, update alarm center ui (#2951)

Signed-off-by: tomsun28 <tomsun28@outlook.com>
This commit is contained in:
tomsun28
2025-01-06 11:25:52 +08:00
committed by GitHub
parent d6e5399121
commit 7bac0245a2
40 changed files with 575 additions and 421 deletions
@@ -302,6 +302,8 @@ public class RealTimeAlertCalculator {
String fingerprint = calculateFingerprint(fingerprints);
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
if (firingAlert != null) {
// todo consider multi times to tig for resolved alert
firingAlert.setTriggerTimes(1);
firingAlert.setEndAt(System.currentTimeMillis());
firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
@@ -55,12 +55,11 @@ public class AlertDefinesController {
public ResponseEntity<Message<Page<AlertDefine>>> getAlertDefines(
@Parameter(description = "Alarm Definition ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
@Parameter(description = "Search-Target Expr Template", example = "x") @RequestParam(required = false) String search,
@Parameter(description = "Alarm Definition Severity", example = "6565463543") @RequestParam(required = false) Byte priority,
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
Page<AlertDefine> alertDefinePage = alertDefineService.getAlertDefines(ids, search, priority, sort, order, pageIndex, pageSize);
Page<AlertDefine> alertDefinePage = alertDefineService.getAlertDefines(ids, search, sort, order, pageIndex, pageSize);
return ResponseEntity.ok(Message.success(alertDefinePage));
}
@@ -55,7 +55,7 @@ public class AlertsController {
public ResponseEntity<Message<Page<SingleAlert>>> getAlerts(
@Parameter(description = "Alarm Status", example = "resolved") @RequestParam(required = false) String status,
@Parameter(description = "Alarm content fuzzy query", example = "linux") @RequestParam(required = false) String search,
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "id") String sort,
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "gmtUpdate") String sort,
@Parameter(description = "Sort Type", example = "desc") @RequestParam(defaultValue = "desc") String order,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@Parameter(description = "Number of list pagination", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
@@ -68,7 +68,7 @@ public class AlertsController {
public ResponseEntity<Message<Page<GroupAlert>>> getGroupAlerts(
@Parameter(description = "Alarm Status", example = "resolved") @RequestParam(required = false) String status,
@Parameter(description = "Alarm content fuzzy query", example = "linux") @RequestParam(required = false) String search,
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "id") String sort,
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "gmtUpdate") String sort,
@Parameter(description = "Sort Type", example = "desc") @RequestParam(defaultValue = "desc") String order,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@Parameter(description = "Number of list pagination", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
@@ -88,7 +88,7 @@ public class AlertsController {
}
@PutMapping(path = "/group/status/{status}")
@Operation(summary = "Batch modify alarm status, set read and unread", description = "Batch modify alarm status, set read and unread")
@Operation(summary = "Batch modify alarm status, set firing or resolved", description = "Batch modify alarm status, set firing or resolved")
public ResponseEntity<Message<Void>> applyAlertDefinesStatus(
@Parameter(description = "Alarm status value", example = "resolved") @PathVariable String status,
@Parameter(description = "Alarm List IDS", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
@@ -53,4 +53,11 @@ public interface GroupAlertDao extends JpaRepository<GroupAlert, Long>, JpaSpeci
@Modifying
@Query("update GroupAlert set status = :status where id in :ids")
void updateGroupAlertsStatus(@Param(value = "status") String status, @Param(value = "ids") List<Long> ids);
/**
* find group alerts by id list
* @param ids ids
* @return group alerts
*/
List<GroupAlert> findGroupAlertsByIdIn(HashSet<Long> ids);
}
@@ -67,4 +67,11 @@ public interface SingleAlertDao extends JpaRepository<SingleAlert, Long>, JpaSpe
@Modifying
@Query("update SingleAlert set status = :status where id in :ids")
void updateSingleAlertsStatus(@Param(value = "status") String status, @Param(value = "ids") List<Long> ids);
/**
* delete alerts by fingerprint list
* @param firingAlerts fingerprint list
*/
@Modifying
void deleteSingleAlertsByFingerprintIn(List<String> firingAlerts);
}
@@ -17,13 +17,17 @@
package org.apache.hertzbeat.alert.notice.impl;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.GroupAlertDao;
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
import org.apache.hertzbeat.alert.notice.AlertStoreHandler;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.springframework.stereotype.Component;
@@ -46,53 +50,74 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
log.error("The Group Alerts is empty, ignore store");
return;
}
// 1. Find if there is an existing alert group
// 1. Find existing alert group
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
// 2. Process single alerts
List<String> alertFingerprints = new LinkedList<>();
// 2. Process individual alerts
Set<String> alertFingerprints = new HashSet<>(8);
groupAlert.getAlerts().forEach(singleAlert -> {
// Check if there is an existing alert with same fingerprint
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
if (existAlert != null) {
// Update existing alert
singleAlert.setId(existAlert.getId());
singleAlert.setStartAt(existAlert.getStartAt());
// If status changed from resolved to firing, update activeAt
if ("resolved".equals(existAlert.getStatus()) && "firing".equals(singleAlert.getStatus())) {
singleAlert.setActiveAt(System.currentTimeMillis());
} else {
singleAlert.setGmtCreate(existAlert.getGmtCreate());
// Status transition logic
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
singleAlert.setStartAt(existAlert.getStartAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes() + singleAlert.getTriggerTimes());
}
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
// Transition to resolved state
if (singleAlert.getEndAt() == null) {
singleAlert.setEndAt(System.currentTimeMillis());
}
singleAlert.setStartAt(existAlert.getStartAt());
singleAlert.setActiveAt(existAlert.getActiveAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
}
singleAlert.setTriggerTimes(existAlert.getTriggerTimes() + 1);
}
// Save new/updated alert
alertFingerprints.add(singleAlert.getFingerprint());
singleAlertDao.save(singleAlert);
});
// 3. If there is an existing alert group, handle resolved alerts
// 3. Process resolved alerts
if (existGroupAlert != null) {
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
if (existFingerprints != null) {
for (String fingerprint : existFingerprints) {
if (!alertFingerprints.contains(fingerprint)) {
// Old alert not in new alert list, mark as resolved
SingleAlert alert = singleAlertDao.findByFingerprint(fingerprint);
if (alert != null && !"resolved".equals(alert.getStatus())) {
alert.setStatus("resolved");
alert.setEndAt(System.currentTimeMillis());
singleAlertDao.save(alert);
}
}
alertFingerprints.addAll(existFingerprints);
}
// Merge group information
groupAlert.setId(existGroupAlert.getId());
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
// Merge other historical information to preserve
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
if (existCommonLabels != null) {
Map<String, String> commonLabels = groupAlert.getCommonLabels();
if (commonLabels != null) {
// filter common label in commonLabels and existCommonLabels
commonLabels = commonLabels.entrySet().stream()
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonLabels(commonLabels);
}
}
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
if (existCommonAnnotations != null) {
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
if (commonAnnotations != null) {
// filter common annotation in commonAnnotations and existCommonAnnotations
commonAnnotations = commonAnnotations.entrySet().stream()
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonAnnotations(commonAnnotations);
}
}
// Update alert group ID
groupAlert.setId(existGroupAlert.getId());
}
// 4. Save alert group
groupAlert.setAlertFingerprints(alertFingerprints);
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
groupAlertDao.save(groupAlert);
}
}
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.alert.reduce;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -32,6 +33,7 @@ import java.util.stream.Collectors;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
@@ -112,7 +114,6 @@ public class AlarmGroupReduce {
if (shouldSendGroup(cache, now)) {
sendGroupAlert(cache);
cache.setLastSendTime(now);
cache.getAlerts().clear();
cache.getAlertFingerprints().clear();
}
});
@@ -180,7 +181,7 @@ public class AlarmGroupReduce {
newCache.setGroupLabels(extractedLabels);
newCache.setGroupDefineName(defineName);
newCache.setCreateTime(System.currentTimeMillis());
newCache.setAlertFingerprints(new HashMap<>());
newCache.setAlertFingerprints(new ConcurrentHashMap<>(8));
return newCache;
});
String fingerprint = alert.getFingerprint();
@@ -188,32 +189,31 @@ public class AlarmGroupReduce {
SingleAlert existingAlert = cache.getAlertFingerprints().get(fingerprint);
if (existingAlert != null) {
// Update existing alert timestamp
existingAlert.setActiveAt(System.currentTimeMillis());
alert.setStartAt(existingAlert.getStartAt());
cache.getAlertFingerprints().put(fingerprint, alert);
return;
}
// Add new alert
cache.getAlertFingerprints().put(fingerprint, alert);
cache.getAlerts().add(alert);
if (shouldSendGroupImmediately(cache)) {
sendGroupAlert(cache);
cache.setLastSendTime(System.currentTimeMillis());
cache.getAlerts().clear();
cache.getAlertFingerprints().clear();
}
}
private void sendGroupAlert(GroupAlertCache cache) {
if (cache.getAlerts().isEmpty()) {
if (cache.getAlertFingerprints().isEmpty()) {
return;
}
long now = System.currentTimeMillis();
String status = determineGroupStatus(cache.getAlerts());
String status = determineGroupStatus(cache.getAlertFingerprints().values());
// For firing alerts, check repeat interval
if ("firing".equals(status)) {
if (CommonConstants.ALERT_STATUS_FIRING.equals(status)) {
AlertGroupConverge ruleConfig = groupDefines.get(cache.getGroupDefineName());
long repeatInterval = ruleConfig.getRepeatInterval() != null
? ruleConfig.getRepeatInterval() * MS_PER_SECOND : DEFAULT_REPEAT_INTERVAL;
@@ -229,9 +229,9 @@ public class AlarmGroupReduce {
GroupAlert groupAlert = GroupAlert.builder()
.groupKey(cache.getGroupKey())
.groupLabels(cache.getGroupLabels())
.commonLabels(extractCommonLabels(cache.getAlerts()))
.commonAnnotations(extractCommonAnnotations(cache.getAlerts()))
.alerts(new ArrayList<>(cache.getAlerts()))
.commonLabels(extractCommonLabels(cache.getAlertFingerprints().values()))
.commonAnnotations(extractCommonAnnotations(cache.getAlertFingerprints().values()))
.alerts(new ArrayList<>(cache.getAlertFingerprints().values()))
.status(status)
.build();
@@ -255,8 +255,8 @@ public class AlarmGroupReduce {
private boolean shouldSendGroupImmediately(GroupAlertCache cache) {
// Check if all alerts are resolved
return cache.getAlerts().stream()
.allMatch(alert -> "resolved".equals(alert.getStatus()));
return cache.getAlertFingerprints().values().stream()
.allMatch(alert -> CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus()));
}
private void sendSingleAlert(SingleAlert alert) {
@@ -281,9 +281,12 @@ public class AlarmGroupReduce {
.collect(Collectors.joining(","));
}
private Map<String, String> extractCommonLabels(List<SingleAlert> alerts) {
private Map<String, String> extractCommonLabels(Collection<SingleAlert> alerts) {
// Extract common labels from all alerts
Map<String, String> common = new HashMap<>(alerts.get(0).getLabels());
if (alerts.isEmpty()) {
return new HashMap<>(8);
}
Map<String, String> common = new HashMap<>(alerts.stream().findFirst().get().getLabels());
alerts.forEach(alert -> {
common.keySet().removeIf(key ->
!alert.getLabels().containsKey(key)
@@ -292,9 +295,12 @@ public class AlarmGroupReduce {
return common;
}
private Map<String, String> extractCommonAnnotations(List<SingleAlert> alerts) {
private Map<String, String> extractCommonAnnotations(Collection<SingleAlert> alerts) {
// Extract common annotations from all alerts
Map<String, String> common = new HashMap<>(alerts.get(0).getAnnotations());
if (alerts.isEmpty()) {
return new HashMap<>(8);
}
Map<String, String> common = new HashMap<>(alerts.stream().findFirst().get().getAnnotations());
alerts.forEach(alert -> {
common.keySet().removeIf(key ->
!alert.getAnnotations().containsKey(key)
@@ -303,11 +309,11 @@ public class AlarmGroupReduce {
return common;
}
private String determineGroupStatus(List<SingleAlert> alerts) {
private String determineGroupStatus(Collection<SingleAlert> alerts) {
// If any alert is firing, group is firing
return alerts.stream()
.anyMatch(alert -> "firing".equals(alert.getStatus()))
? "firing" : "resolved";
.anyMatch(alert -> CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus()))
? CommonConstants.ALERT_STATUS_FIRING : CommonConstants.ALERT_STATUS_RESOLVED;
}
@Data
@@ -315,8 +321,7 @@ public class AlarmGroupReduce {
private String groupDefineName;
private String groupKey;
private Map<String, String> groupLabels;
private List<SingleAlert> alerts = new ArrayList<>();
private Map<String, SingleAlert> alertFingerprints = new HashMap<>();
private Map<String, SingleAlert> alertFingerprints = new ConcurrentHashMap<>(8);
private long createTime;
private long lastSendTime;
private long lastRepeatTime;
@@ -17,11 +17,14 @@
package org.apache.hertzbeat.alert.reduce;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -47,6 +50,11 @@ public class AlarmInhibitReduce {
*/
private static final long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
/**
* Interval for checking and cleaning up expired source alerts
*/
private static final long CHECK_INTERVAL = 60_000L;
private final AlarmSilenceReduce alarmSilenceReduce;
private final Map<Long, AlertInhibit> inhibitRules;
@@ -64,6 +72,29 @@ public class AlarmInhibitReduce {
sourceAlertCache = new ConcurrentHashMap<>(8);
List<AlertInhibit> inhibits = alertInhibitDao.findAlertInhibitsByEnableIsTrue();
refreshInhibitRules(inhibits);
startScheduledCleanupCache();
}
private void startScheduledCleanupCache() {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setUncaughtExceptionHandler((thread, throwable) -> {
log.error("Scheduled clean up inhibit cache has uncaughtException.");
log.error(throwable.getMessage(), throwable);
})
.setDaemon(true)
.setNameFormat("inhibit-clean-up-%d")
.build();
ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory);
// Scheduled cleanup of all expired source alerts
scheduledExecutor.scheduleAtFixedRate(() -> {
try {
sourceAlertCache.values().forEach(this::cleanupExpiredEntries);
// Remove empty rule caches
sourceAlertCache.entrySet().removeIf(entry -> entry.getValue().isEmpty());
} catch (Exception e) {
log.error("Error during scheduled cleanup", e);
}
}, CHECK_INTERVAL, CHECK_INTERVAL, java.util.concurrent.TimeUnit.MILLISECONDS);
}
/**
@@ -75,12 +106,8 @@ public class AlarmInhibitReduce {
log.warn("Attempted to refresh inhibit rules with null list.");
return;
}
try {
this.inhibitRules.clear();
rules.forEach(rule -> this.inhibitRules.put(rule.getId(), rule));
} catch (Exception e) {
log.error("Error refreshing inhibit rules", e);
}
this.inhibitRules.clear();
rules.forEach(rule -> this.inhibitRules.put(rule.getId(), rule));
}
/**
@@ -126,15 +153,10 @@ public class AlarmInhibitReduce {
log.warn("Received null alert or rule in isSourceAlert");
return false;
}
try {
if (!"firing".equals(alert.getStatus())) {
return false;
}
return matchLabels(alert.getCommonLabels(), rule.getSourceLabels());
} catch (Exception e) {
log.error("Error checking if alert is source alert", e);
if (!"firing".equals(alert.getStatus())) {
return false;
}
return matchLabels(alert.getCommonLabels(), rule.getSourceLabels());
}
/**
@@ -146,32 +168,27 @@ public class AlarmInhibitReduce {
log.warn("Received null alert in shouldInhibit");
return false;
}
try {
if ("resolved".equals(alert.getStatus())) {
return false;
}
for (AlertInhibit rule : inhibitRules.values()) {
if (!matchLabels(alert.getCommonLabels(), rule.getTargetLabels())) {
continue;
}
List<GroupAlert> sourceAlerts = getActiveSourceAlerts(rule);
if (sourceAlerts.isEmpty()) {
continue;
}
for (GroupAlert source : sourceAlerts) {
if (matchEqualLabels(source, alert, rule.getEqualLabels())) {
return true;
}
}
}
return false;
} catch (Exception e) {
log.error("Error checking if alert should be inhibited", e);
if ("resolved".equals(alert.getStatus())) {
return false;
}
for (AlertInhibit rule : inhibitRules.values()) {
if (!matchLabels(alert.getCommonLabels(), rule.getTargetLabels())) {
continue;
}
List<GroupAlert> sourceAlerts = getActiveSourceAlerts(rule);
if (sourceAlerts.isEmpty()) {
continue;
}
for (GroupAlert source : sourceAlerts) {
if (matchEqualLabels(source, alert, rule.getEqualLabels())) {
return true;
}
}
}
return false;
}
/**
@@ -184,13 +201,8 @@ public class AlarmInhibitReduce {
log.warn("Received null alertLabels or requiredLabels in matchLabels");
return false;
}
try {
return requiredLabels.entrySet().stream()
.allMatch(entry -> entry.getValue().equals(alertLabels.get(entry.getKey())));
} catch (Exception e) {
log.error("Error matching labels", e);
return false;
}
return requiredLabels.entrySet().stream()
.allMatch(entry -> entry.getValue().equals(alertLabels.get(entry.getKey())));
}
/**
@@ -204,22 +216,17 @@ public class AlarmInhibitReduce {
log.warn("Received null source or target in matchEqualLabels");
return false;
}
try {
if (equalLabels == null || equalLabels.isEmpty()) {
return true;
}
Map<String, String> sourceLabels = source.getCommonLabels();
Map<String, String> targetLabels = target.getCommonLabels();
return equalLabels.stream().allMatch(label -> {
String sourceValue = sourceLabels.get(label);
String targetValue = targetLabels.get(label);
return sourceValue != null && sourceValue.equals(targetValue);
});
} catch (Exception e) {
log.error("Error matching equal labels", e);
return false;
if (equalLabels == null || equalLabels.isEmpty()) {
return true;
}
Map<String, String> sourceLabels = source.getCommonLabels();
Map<String, String> targetLabels = target.getCommonLabels();
return equalLabels.stream().allMatch(label -> {
String sourceValue = sourceLabels.get(label);
String targetValue = targetLabels.get(label);
return sourceValue != null && sourceValue.equals(targetValue);
});
}
/**
@@ -232,23 +239,19 @@ public class AlarmInhibitReduce {
log.warn("Received null alert or rule in cacheSourceAlert");
return;
}
try {
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.computeIfAbsent(
rule.getId(),
k -> new ConcurrentHashMap<>()
);
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.computeIfAbsent(
rule.getId(),
k -> new ConcurrentHashMap<>()
);
String fingerprint = generateAlertFingerprint(alert);
SourceAlertEntry entry = new SourceAlertEntry(
alert,
System.currentTimeMillis(),
System.currentTimeMillis() + SOURCE_ALERT_TTL
);
ruleCache.put(fingerprint, entry);
cleanupExpiredEntries(ruleCache);
} catch (Exception e) {
log.error("Error caching source alert", e);
}
String fingerprint = generateAlertFingerprint(alert);
SourceAlertEntry entry = new SourceAlertEntry(
alert,
System.currentTimeMillis(),
System.currentTimeMillis() + SOURCE_ALERT_TTL
);
ruleCache.put(fingerprint, entry);
cleanupExpiredEntries(ruleCache);
}
/**
@@ -260,21 +263,16 @@ public class AlarmInhibitReduce {
log.warn("Received null rule in getActiveSourceAlerts");
return Collections.emptyList();
}
try {
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.get(rule.getId());
if (ruleCache == null || ruleCache.isEmpty()) {
return Collections.emptyList();
}
long now = System.currentTimeMillis();
return ruleCache.values().stream()
.filter(entry -> entry.getExpiryTime() > now)
.map(SourceAlertEntry::getAlert)
.collect(Collectors.toList());
} catch (Exception e) {
log.error("Error getting active source alerts", e);
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.get(rule.getId());
if (ruleCache == null || ruleCache.isEmpty()) {
return Collections.emptyList();
}
long now = System.currentTimeMillis();
return ruleCache.values().stream()
.filter(entry -> entry.getExpiryTime() > now)
.map(SourceAlertEntry::getAlert)
.collect(Collectors.toList());
}
/**
@@ -286,18 +284,13 @@ public class AlarmInhibitReduce {
log.warn("Received null alert in generateAlertFingerprint");
return "";
}
try {
Map<String, String> labels = new HashMap<>(alert.getCommonLabels());
labels.remove("timestamp");
Map<String, String> labels = new HashMap<>(alert.getCommonLabels());
labels.remove("timestamp");
return labels.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey() + ":" + e.getValue())
.collect(Collectors.joining(","));
} catch (Exception e) {
log.error("Error generating alert fingerprint", e);
return "";
}
return labels.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey() + ":" + e.getValue())
.collect(Collectors.joining(","));
}
/**
@@ -309,26 +302,8 @@ public class AlarmInhibitReduce {
log.warn("Received null cache in cleanupExpiredEntries");
return;
}
try {
long now = System.currentTimeMillis();
cache.entrySet().removeIf(entry -> entry.getValue().getExpiryTime() <= now);
} catch (Exception e) {
log.error("Error cleaning up expired entries", e);
}
}
/**
* Scheduled cleanup of all expired source alerts
*/
@Scheduled(fixedRate = 60_000) // Run every minute
public void scheduledCleanup() {
try {
sourceAlertCache.values().forEach(this::cleanupExpiredEntries);
// Remove empty rule caches
sourceAlertCache.entrySet().removeIf(entry -> entry.getValue().isEmpty());
} catch (Exception e) {
log.error("Error during scheduled cleanup", e);
}
long now = System.currentTimeMillis();
cache.entrySet().removeIf(entry -> entry.getValue().getExpiryTime() <= now);
}
/**
@@ -78,14 +78,13 @@ public interface AlertDefineService {
* Dynamic conditional query
* @param defineIds Alarm Definition ID List
* @param search Search-Target Expr Template
* @param priority Alarm Definition Severity
* @param sort Sort field
* @param order Sort mode: asc: ascending, desc: descending
* @param pageIndex List current page
* @param pageSize Number of list pages
* @return The query results
*/
Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, Byte priority, String sort, String order, int pageIndex, int pageSize);
Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, String sort, String order, int pageIndex, int pageSize);
/**
* Export file configuration of specified type based on ID list and export file type
@@ -127,7 +127,7 @@ public class AlertDefineServiceImpl implements AlertDefineService {
}
@Override
public Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, Byte priority, String sort, String order, int pageIndex, int pageSize) {
public Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, String sort, String order, int pageIndex, int pageSize) {
// parse translation content list
ObjectMapper objectMapper = new ObjectMapper();
List<String> searchList = Collections.emptyList();
@@ -154,10 +154,10 @@ public class AlertDefineServiceImpl implements AlertDefineService {
for (String searchContent : finalSearchList) {
searchContent = searchContent.toLowerCase();
Predicate predicate = criteriaBuilder.or(
criteriaBuilder.like(criteriaBuilder.lower(root.get("app")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("metric")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("field")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("expr")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("labels")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("annotations")), "%" + searchContent + "%"),
criteriaBuilder.like(criteriaBuilder.lower(root.get("template")), "%" + searchContent + "%")
);
searchPredicates.add(predicate);
@@ -165,10 +165,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
// all search keywords are connected with or
andList.add(criteriaBuilder.or(searchPredicates.toArray(new Predicate[0])));
}
if (priority != null) {
Predicate predicate = criteriaBuilder.equal(root.get("priority"), priority);
andList.add(predicate);
}
Predicate[] predicates = new Predicate[andList.size()];
return criteriaBuilder.and(andList.toArray(predicates));
};
@@ -70,12 +70,28 @@ public class AlertServiceImpl implements AlertService {
Predicate predicate = criteriaBuilder.equal(root.get("status"), status);
andList.add(predicate);
}
Predicate[] andPredicates = new Predicate[andList.size()];
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
List<Predicate> orList = new ArrayList<>();
if (search != null && !search.isEmpty()) {
Predicate predicateContent = criteriaBuilder.like(root.get("content"), "%" + search + "%");
andList.add(predicateContent);
orList.add(predicateContent);
Predicate predicateLabels = criteriaBuilder.like(root.get("labels"), "%" + search + "%");
orList.add(predicateLabels);
Predicate predicateAnnotation = criteriaBuilder.like(root.get("annotations"), "%" + search + "%");
orList.add(predicateAnnotation);
}
Predicate[] orPredicates = new Predicate[orList.size()];
Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates));
if (andPredicates.length == 0 && orPredicates.length == 0) {
return query.where().getRestriction();
} else if (andPredicates.length == 0) {
return orPredicate;
} else if (orPredicates.length == 0) {
return andPredicate;
} else {
return query.where(andPredicate, orPredicate).getRestriction();
}
Predicate[] predicates = new Predicate[andList.size()];
return criteriaBuilder.and(andList.toArray(predicates));
};
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
@@ -90,12 +106,28 @@ public class AlertServiceImpl implements AlertService {
Predicate predicate = criteriaBuilder.equal(root.get("status"), status);
andList.add(predicate);
}
Predicate[] andPredicates = new Predicate[andList.size()];
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
List<Predicate> orList = new ArrayList<>();
if (search != null && !search.isEmpty()) {
Predicate predicateContent = criteriaBuilder.like(root.get("content"), "%" + search + "%");
andList.add(predicateContent);
Predicate predicateContent = criteriaBuilder.like(root.get("groupLabels"), "%" + search + "%");
orList.add(predicateContent);
Predicate predicateLabels = criteriaBuilder.like(root.get("commonLabels"), "%" + search + "%");
orList.add(predicateLabels);
Predicate predicateAnnotation = criteriaBuilder.like(root.get("commonAnnotations"), "%" + search + "%");
orList.add(predicateAnnotation);
}
Predicate[] orPredicates = new Predicate[orList.size()];
Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates));
if (andPredicates.length == 0 && orPredicates.length == 0) {
return query.where().getRestriction();
} else if (andPredicates.length == 0) {
return orPredicate;
} else if (orPredicates.length == 0) {
return andPredicate;
} else {
return query.where(andPredicate, orPredicate).getRestriction();
}
Predicate[] predicates = new Predicate[andList.size()];
return criteriaBuilder.and(andList.toArray(predicates));
};
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
@@ -110,6 +142,11 @@ public class AlertServiceImpl implements AlertService {
@Override
public void deleteGroupAlerts(HashSet<Long> ids) {
List<GroupAlert> groupAlerts = groupAlertDao.findGroupAlertsByIdIn(ids);
for (GroupAlert groupAlert : groupAlerts) {
List<String> firingAlerts = groupAlert.getAlertFingerprints();
singleAlertDao.deleteSingleAlertsByFingerprintIn(firingAlerts);
}
groupAlertDao.deleteGroupAlertsByIdIn(ids);
}
@@ -112,7 +112,7 @@ class AlertDefinesControllerTest {
// }
// }))).thenReturn(new PageImpl<AlertDefine>(new ArrayList<AlertDefine>()));
AlertDefine define = AlertDefine.builder().id(9L).expr("x").times(1).build();
Mockito.when(alertDefineService.getAlertDefines(null, null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
Mockito.when(alertDefineService.getAlertDefines(null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
mockMvc.perform(MockMvcRequestBuilders.get(
"/api/alert/defines")
@@ -123,7 +123,7 @@ class AlertDefineServiceTest {
@Test
void getAlertDefines() {
when(alertDefineDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(Page.empty());
assertNotNull(alertDefineService.getAlertDefines(null, null, null, "id", "desc", 1, 10));
assertNotNull(alertDefineService.getAlertDefines(null, null, "id", "desc", 1, 10));
verify(alertDefineDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
}
}
@@ -29,6 +29,7 @@ import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
@@ -36,6 +37,10 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.entity.manager.JsonStringListAttributeConverter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
@@ -82,6 +87,22 @@ public class GroupAlert {
@Column(length = 2048)
private List<String> alertFingerprints;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Transient
private List<SingleAlert> alerts;
}
@@ -28,12 +28,17 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
@@ -85,6 +90,22 @@ public class SingleAlert {
@Schema(title = "End At, when status is resolved has", example = "null")
private Long endAt;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@Schema(title = "This record was last modified by", example = "tom")
@LastModifiedBy
private String modifier;
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Override
public SingleAlert clone() {
// deep clone
@@ -56,15 +56,14 @@ public class MonitorsController {
public ResponseEntity<Message<Page<Monitor>>> getMonitors(
@Parameter(description = "Monitor ID", example = "6565463543") @RequestParam(required = false) final List<Long> ids,
@Parameter(description = "Monitor Type", example = "linux") @RequestParam(required = false) final String app,
@Parameter(description = "Monitor Name support fuzzy query", example = "linux-127.0.0.1") @RequestParam(required = false) final String name,
@Parameter(description = "Monitor Host support fuzzy query", example = "127.0.0.1") @RequestParam(required = false) final String host,
@Parameter(description = "Monitor Status 0:no monitor,1:usable,2:disabled,9:all status", example = "1") @RequestParam(required = false) final Byte status,
@Parameter(description = "Monitor Host support fuzzy query", example = "127.0.0.1") @RequestParam(required = false) final String search,
@Parameter(description = "Monitor labels ", example = "env:prod,instance:22") @RequestParam(required = false) final String labels,
@Parameter(description = "Sort Field ", example = "name") @RequestParam(defaultValue = "gmtCreate") final String sort,
@Parameter(description = "Sort mode eg:asc desc", example = "desc") @RequestParam(defaultValue = "desc") final String order,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@Parameter(description = "Number of list pagination ", example = "8") @RequestParam(defaultValue = "8") int pageSize,
@Parameter(description = "Monitor tag ", example = "env:prod") @RequestParam(required = false) final String tag) {
Page<Monitor> monitorPage = monitorService.getMonitors(ids, app, name, host, status, sort, order, pageIndex, pageSize, tag);
@Parameter(description = "Number of list pagination ", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
Page<Monitor> monitorPage = monitorService.getMonitors(ids, app, search, status, sort, order, pageIndex, pageSize, labels);
return ResponseEntity.ok(Message.success(monitorPage));
}
@@ -58,7 +58,7 @@ public interface MonitorDao extends JpaRepository<Monitor, Long>, JpaSpecificati
* @param status Monitor Status
* @return Monitor List
*/
List<Monitor> findMonitorsByStatusNotInAndAndJobIdNotNull(List<Byte> status);
List<Monitor> findMonitorsByStatusNotInAndJobIdNotNull(List<Byte> status);
/**
* Query monitoring by monitoring name
@@ -96,7 +96,7 @@ public class SchedulerInit implements CommandLineRunner {
.build();
collectorScheduling.collectorGoOnline(CommonConstants.MAIN_COLLECTOR_NODE, collectorInfo);
// init jobs
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndAndJobIdNotNull(List.of(CommonConstants.MONITOR_PAUSED_CODE));
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndJobIdNotNull(List.of(CommonConstants.MONITOR_PAUSED_CODE));
List<CollectorMonitorBind> monitorBinds = collectorMonitorBindDao.findAll();
final Set<Long> sdMonitorIds = monitorBindDao.findAllByType(CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR).stream()
.map(MonitorBind::getBizId)
@@ -102,17 +102,16 @@ public interface MonitorService {
* Dynamic conditional query
* @param monitorIds Monitor ID List
* @param app Monitor Type
* @param name Monitor Name support fuzzy query
* @param host Monitor Host support fuzzy query
* @param search Monitor Host support fuzzy query
* @param status Monitor Status 0:no monitor,1:usable,2:disabled,9:all status
* @param sort Sort Field
* @param order Sort mode eg:asc desc
* @param pageIndex List current page
* @param pageSize Number of list pagination
* @param tag Monitor tag
* @param labels Monitor labels
* @return Search Result
*/
Page<Monitor> getMonitors(List<Long> monitorIds, String app, String name, String host, Byte status, String sort, String order, int pageIndex, int pageSize, String tag);
Page<Monitor> getMonitors(List<Long> monitorIds, String app, String search, Byte status, String sort, String order, int pageIndex, int pageSize, String labels);
/**
* Unmanaged monitoring items in batches according to the monitoring ID list
@@ -20,8 +20,6 @@ package org.apache.hertzbeat.manager.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Sets;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
@@ -556,7 +554,7 @@ public class MonitorServiceImpl implements MonitorService {
}
@Override
public Page<Monitor> getMonitors(List<Long> monitorIds, String app, String name, String host, Byte status, String sort, String order, int pageIndex, int pageSize, String tag) {
public Page<Monitor> getMonitors(List<Long> monitorIds, String app, String search, Byte status, String sort, String order, int pageIndex, int pageSize, String labels) {
Specification<Monitor> specification = (root, query, criteriaBuilder) -> {
List<Predicate> andList = new ArrayList<>();
if (!CollectionUtils.isEmpty(monitorIds)) {
@@ -574,33 +572,31 @@ public class MonitorServiceImpl implements MonitorService {
Predicate predicateStatus = criteriaBuilder.equal(root.get("status"), status);
andList.add(predicateStatus);
}
if (StringUtils.isNotBlank(tag)) {
String[] tagArr = tag.split(":");
String tagName = tagArr[0];
ListJoin<Monitor, Tag> tagJoin = root
.join(root.getModel()
.getList("tags", org.apache.hertzbeat.common.entity.manager.Tag.class), JoinType.LEFT);
if (tagArr.length == TAG_LENGTH) {
String tagValue = tagArr[1];
andList.add(criteriaBuilder.equal(tagJoin.get("name"), tagName));
andList.add(criteriaBuilder.equal(tagJoin.get("tagValue"), tagValue));
} else {
andList.add(criteriaBuilder.equal(tagJoin.get("name"), tag));
}
}
Predicate[] andPredicates = new Predicate[andList.size()];
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
List<Predicate> orList = new ArrayList<>();
if (StringUtils.isNotBlank(host)) {
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + host + "%");
if (StringUtils.isNotBlank(search)) {
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + search + "%");
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
orList.add(predicateHost);
}
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
orList.add(predicateName);
}
if (StringUtils.isNotBlank(labels)) {
String[] labelAres = labels.split(",");
for (String label : labelAres) {
String[] labelArr = label.split(":");
String labelName = labelArr[0];
String labelValue = labelArr.length == 2 ? labelArr[1] : null;
// create every label condition
if (labelValue == null) {
orList.add(criteriaBuilder.like(root.get("labels"), "%" + labelName + "%"));
} else {
String pattern = String.format("%%\"%s\":\"%s\"%%", labelName, labelValue);
orList.add(criteriaBuilder.like(root.get("labels"), pattern));
}
}
}
Predicate[] orPredicates = new Predicate[orList.size()];
Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates));
@@ -90,9 +90,9 @@ class MonitorDaoTest extends AbstractSpringIntegrationTest {
}
@Test
void findMonitorsByStatusNotInAndAndJobIdNotNull() {
void findMonitorsByStatusNotInAndJobIdNotNull() {
List<Byte> bytes = Arrays.asList((byte) 2, (byte) 3);
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndAndJobIdNotNull(bytes);
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndJobIdNotNull(bytes);
assertNotNull(monitors);
assertEquals(1, monitors.size());
}
@@ -647,7 +647,7 @@ class MonitorServiceTest {
@Test
void getMonitors() {
doReturn(Page.empty()).when(monitorDao).findAll(any(Specification.class), any(PageRequest.class));
assertNotNull(monitorService.getMonitors(null, null, null, null, null, "gmtCreate", "desc", 1, 1, null));
assertNotNull(monitorService.getMonitors(null, null, null, null, "gmtCreate", "desc", 1, 1, null));
}
@Test
@@ -17,8 +17,12 @@
package org.apache.hertzbeat.warehouse.store;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.plugin.PostCollectPlugin;
@@ -26,6 +30,7 @@ import org.apache.hertzbeat.plugin.runner.PluginRunner;
import org.apache.hertzbeat.warehouse.WarehouseWorkerPool;
import org.apache.hertzbeat.warehouse.store.history.HistoryDataWriter;
import org.apache.hertzbeat.warehouse.store.realtime.RealTimeDataWriter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
/**
@@ -37,22 +42,26 @@ public class DataStorageDispatch {
private final CommonDataQueue commonDataQueue;
private final WarehouseWorkerPool workerPool;
private final JdbcTemplate jdbcTemplate;
private final RealTimeDataWriter realTimeDataWriter;
private final Optional<HistoryDataWriter> historyDataWriter;
private final PluginRunner pluginRunner;
private final Set<Long> monitorDownStatusCache;
public DataStorageDispatch(CommonDataQueue commonDataQueue,
WarehouseWorkerPool workerPool,
JdbcTemplate jdbcTemplate,
Optional<HistoryDataWriter> historyDataWriter,
RealTimeDataWriter realTimeDataWriter,
PluginRunner pluginRunner) {
this.commonDataQueue = commonDataQueue;
this.workerPool = workerPool;
this.jdbcTemplate = jdbcTemplate;
this.realTimeDataWriter = realTimeDataWriter;
this.historyDataWriter = historyDataWriter;
this.pluginRunner = pluginRunner;
this.monitorDownStatusCache = new ConcurrentSkipListSet<>();
initMonitorDownStatusCache();
startPersistentDataStorage();
}
@@ -65,6 +74,7 @@ public class DataStorageDispatch {
if (metricsData == null) {
continue;
}
calculateMonitorStatus(metricsData);
historyDataWriter.ifPresent(dataWriter -> dataWriter.saveData(metricsData));
pluginRunner.pluginExecute(PostCollectPlugin.class, ((postCollectPlugin, pluginContext) -> postCollectPlugin.execute(metricsData, pluginContext)));
realTimeDataWriter.saveData(metricsData);
@@ -78,5 +88,27 @@ public class DataStorageDispatch {
workerPool.executeJob(runnable);
}
private void initMonitorDownStatusCache() {
String sql = "SELECT id FROM hzb_monitor WHERE status = 2";
List<Long> ids = jdbcTemplate.query(sql, (rs, rowNum) -> rs.getLong("id"));
monitorDownStatusCache.addAll(ids);
}
protected void calculateMonitorStatus(CollectRep.MetricsData metricsData) {
if (metricsData.getPriority() == 0) {
long id = metricsData.getId();
CollectRep.Code code = metricsData.getCode();
if (code == CollectRep.Code.SUCCESS && monitorDownStatusCache.contains(id)) {
monitorDownStatusCache.remove(id);
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, id);
} else if (code != CollectRep.Code.SUCCESS && !monitorDownStatusCache.contains(id)) {
monitorDownStatusCache.add(id);
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
jdbcTemplate.update(sql, CommonConstants.MONITOR_DOWN_CODE, id);
}
}
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ export class SingleAlert {
startAt!: number;
endAt!: number;
activeAt!: number;
triggerTime!: number;
triggerTimes!: number;
creator!: string;
modifier!: string;
gmtCreate!: number;
@@ -27,43 +27,38 @@
<nz-divider></nz-divider>
<app-toolbar>
<ng-template #left>
<button nz-button nzType="primary" (click)="sync()" nz-tooltip [nzTooltipTitle]="'common.refresh' | i18n">
<i nz-icon nzType="sync" nzTheme="outline"></i>
</button>
</ng-template>
<ng-template #right>
<nz-select
class="mobile-hide"
nzAllowClear
[nzPlaceHolder]="'alert.center.filter-priority' | i18n"
[(ngModel)]="filterPriority"
(ngModelChange)="loadAlertsTable()"
>
<nz-option [nzLabel]="'alert.severity.all' | i18n" [nzValue]="9"></nz-option>
<nz-option [nzLabel]="'alert.severity.2' | i18n" [nzValue]="2"></nz-option>
<nz-option [nzLabel]="'alert.severity.1' | i18n" [nzValue]="1"></nz-option>
<nz-option [nzLabel]="'alert.severity.0' | i18n" [nzValue]="0"></nz-option>
</nz-select>
<nz-select
class="mobile-hide"
nzAllowClear
[nzPlaceHolder]="'alert.center.filter-status' | i18n"
[(ngModel)]="filterStatus"
(ngModelChange)="loadAlertsTable()"
>
<nz-option [nzLabel]="'alert.status.all' | i18n" [nzValue]="'all'"></nz-option>
<nz-option [nzLabel]="'alert.status.0' | i18n" [nzValue]="'emergency'"></nz-option>
<nz-option [nzLabel]="'alert.status.2' | i18n" [nzValue]="'critical'"></nz-option>
<nz-option [nzLabel]="'alert.status.3' | i18n" [nzValue]="'warning'"></nz-option>
</nz-select>
<app-multi-func-input
groupStyle="width: 250px;"
class="mobile-hide"
[placeholder]="'alert.center.search' | i18n"
[(value)]="filterContent"
(keydown.enter)="loadAlertsTable()"
/>
<ng-template #center>
<div class="center-content">
<button nz-button (click)="sync()" nz-tooltip [nzTooltipTitle]="'common.refresh' | i18n">
<i nz-icon nzType="sync" nzTheme="outline"></i>
</button>
<div class="search-wrapper">
<nz-input-group [nzPrefix]="prefixTemplate" class="search-input">
<input
type="text"
nz-input
[(ngModel)]="filterContent"
(keydown.enter)="loadAlertsTable()"
[placeholder]="'alert.center.search' | i18n"
/>
</nz-input-group>
<ng-template #prefixTemplate>
<i nz-icon nzType="search"></i>
</ng-template>
</div>
<nz-select
class="mobile-hide"
nzAllowClear
[nzPlaceHolder]="'alert.center.filter-status' | i18n"
[(ngModel)]="filterStatus"
(ngModelChange)="loadAlertsTable()"
>
<nz-option [nzLabel]="'alert.status.firing' | i18n" [nzValue]="'firing'"></nz-option>
<nz-option [nzLabel]="'alert.status.resolved' | i18n" [nzValue]="'resolved'"></nz-option>
</nz-select>
</div>
</ng-template>
</app-toolbar>
@@ -73,40 +68,34 @@
<div class="alert-header">
<div class="alert-info">
<div class="alert-labels">
<nz-tag *ngFor="let label of recordEntries(group.groupLabels)">
{{ label[0] + ':' + label[1] }}
</nz-tag>
<nz-tag *ngFor="let item of group.groupLabels | keyvalue">{{ item.key }}:{{ item.value }}</nz-tag>
</div>
<div class="alert-meta-info">
<span class="alert-time">
<i nz-icon nzType="clock-circle" nzTheme="outline"></i>
{{ group.gmtCreate | date : 'yyyy-MM-dd HH:mm:ss' }}
</span>
<span class="alert-count" *ngIf="group?.alerts">
<i nz-icon nzType="alert" nzTheme="outline"></i>
{{ 'alert.center.time.tip' | i18n : { times: group?.alerts?.length } }}
{{ group.gmtUpdate | date : 'yyyy-MM-dd HH:mm:ss' }}
</span>
</div>
</div>
<div class="alert-actions">
<button
*ngIf="group.status != 'firing'"
nz-button
(click)="onMarkReadOneAlert(group.id)"
nz-tooltip
[nzTooltipTitle]="'alert.center.deal' | i18n"
>
<i nz-icon nzType="down-circle" nzTheme="outline"></i>
</button>
<button
*ngIf="group.status == 'firing'"
nz-button
(click)="onMarkUnReadOneAlert(group.id)"
nz-tooltip
[nzTooltipTitle]="'alert.center.no-deal' | i18n"
>
<i nz-icon nzType="up-circle" nzTheme="outline"></i>
</button>
<!-- <button-->
<!-- *ngIf="group.status != 'firing'"-->
<!-- nz-button-->
<!-- (click)="onMarkReadOneAlert(group.id)"-->
<!-- nz-tooltip-->
<!-- [nzTooltipTitle]="'alert.center.deal' | i18n"-->
<!-- >-->
<!-- <i nz-icon nzType="down-circle" nzTheme="outline"></i>-->
<!-- </button>-->
<!-- <button-->
<!-- *ngIf="group.status == 'firing'"-->
<!-- nz-button-->
<!-- (click)="onMarkUnReadOneAlert(group.id)"-->
<!-- nz-tooltip-->
<!-- [nzTooltipTitle]="'alert.center.no-deal' | i18n"-->
<!-- >-->
<!-- <i nz-icon nzType="up-circle" nzTheme="outline"></i>-->
<!-- </button>-->
<button nz-button nzDanger (click)="onDeleteOneAlert(group.id)" nz-tooltip [nzTooltipTitle]="'alert.center.delete' | i18n">
<i nz-icon nzType="delete" nzTheme="outline"></i>
</button>
@@ -117,6 +106,7 @@
<div class="alert-details">
<nz-collapse nzGhost>
<nz-collapse-panel
#panel
*ngFor="let item of group.alerts"
[nzHeader]="alertHeader"
[nzExtra]="alertExtra"
@@ -124,22 +114,33 @@
[nzExpandedIcon]="expandedIcon"
>
<ng-template #alertHeader>
<div class="alert-content">{{ item.content }}</div>
<div class="alert-content">
<nz-tag *ngIf="item.labels.alertname" style="font-size: 14px; margin-right: 8px">{{ item.labels.alertname }}</nz-tag>
<span>{{ item.content }}</span>
</div>
</ng-template>
<ng-template #alertExtra>
<div class="alert-time">{{ item.activeAt | date : 'yyyy-MM-dd HH:mm:ss' }}</div>
<div class="alert-time">{{
item.endAt ? (item.endAt | date : 'yyyy-MM-dd HH:mm:ss') : (item.activeAt | date : 'yyyy-MM-dd HH:mm:ss')
}}</div>
</ng-template>
<ng-template #expandedIcon let-active>
<i nz-icon [nzType]="active ? 'caret-down' : 'caret-right'"></i>
<ng-template #expandedIcon>
<i nz-icon [nzType]="panel.nzActive ? 'caret-down' : 'caret-right'"></i>
</ng-template>
<!-- Trigger Times -->
<div class="detail-section" *ngIf="item.triggerTimes">
<span class="alert-count" *ngIf="group?.alerts">
<i style="margin-right: 4px" nz-icon nzType="alert" nzTheme="outline"></i>
{{ 'alert.center.time.tip' | i18n : { times: item.triggerTimes } }}
</span>
</div>
<!-- Labels -->
<div class="detail-section" *ngIf="item.labels">
<div class="section-title">{{ 'alert.center.labels' | i18n }}</div>
<div class="alert-labels">
<nz-tag *ngFor="let label of recordEntries(item.labels)">
{{ label[0] + ':' + label[1] }}
</nz-tag>
<nz-tag *ngFor="let label of item.labels | keyvalue">{{ label.key }}:{{ label.value }}</nz-tag>
</div>
</div>
@@ -147,9 +148,9 @@
<div class="detail-section" *ngIf="item.annotations">
<div class="section-title">{{ 'annotation' | i18n }}</div>
<div class="alert-annotations">
<div *ngFor="let anno of recordEntries(item.annotations)" class="annotation-item">
<span class="annotation-key">{{ anno[0] }}:</span>
<span class="annotation-value">{{ anno[1] }}</span>
<div *ngFor="let anno of item.annotations | keyvalue" class="annotation-item">
<span class="annotation-key">{{ anno.key }}:</span>
<span class="annotation-value">{{ anno.value }}</span>
</div>
</div>
</div>
@@ -162,10 +163,14 @@
<span class="time-label">{{ 'alert.center.first-time' | i18n }}:</span>
<span class="time-value">{{ item.startAt | date : 'yyyy-MM-dd HH:mm:ss' }}</span>
</div>
<div class="time-item">
<div *ngIf="item.activeAt" class="time-item">
<span class="time-label">{{ 'alert.center.last-time' | i18n }}:</span>
<span class="time-value">{{ item.activeAt | date : 'yyyy-MM-dd HH:mm:ss' }}</span>
</div>
<div *ngIf="item.endAt" class="time-item">
<span class="time-label">{{ 'alert.center.end-time' | i18n }}:</span>
<span class="time-value">{{ item.endAt | date : 'yyyy-MM-dd HH:mm:ss' }}</span>
</div>
</div>
</div>
</nz-collapse-panel>
@@ -1,3 +1,68 @@
/* 调整工具栏布局 */
:host ::ng-deep app-toolbar {
.center-content {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
padding: 0 16px;
button {
flex-shrink: 0;
}
nz-select {
min-width: 120px;
flex-shrink: 0;
}
}
}
.search-wrapper {
display: flex;
justify-content: center;
width: 100%;
max-width: 600px;
margin: 0 auto;
padding: 0;
}
.search-input {
width: 100%;
:global {
.ant-input {
height: 40px;
font-size: 14px;
border-radius: 6px;
padding: 4px 11px 4px 40px;
}
.ant-input-prefix {
color: rgba(0, 0, 0, 0.45);
font-size: 16px;
margin-right: 8px;
margin-left: 12px;
}
.ant-input-affix-wrapper {
border-radius: 6px;
&:hover, &:focus {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
}
}
@media screen and (max-width: 768px) {
.search-wrapper {
max-width: 100%;
padding: 12px 16px;
}
}
.alert-cards {
display: flex;
flex-direction: column;
@@ -29,7 +94,7 @@
.alert-info {
flex: 1;
.alert-labels {
display: flex;
flex-wrap: wrap;
@@ -40,7 +105,7 @@
border-radius: 2px;
}
}
.alert-meta-info {
display: flex;
gap: 16px;
@@ -52,7 +117,7 @@
margin-right: 4px;
}
.alert-time, .alert-count {
.alert-time {
display: flex;
align-items: center;
}
@@ -97,7 +162,7 @@
padding: 8px 12px;
align-items: center;
cursor: pointer;
&:hover {
background-color: #fafafa;
}
@@ -121,7 +186,7 @@
.ant-collapse-content {
border-top: 1px solid #f0f0f0;
.ant-collapse-content-box {
padding: 12px;
}
@@ -143,6 +208,13 @@
margin-bottom: 8px;
font-weight: 500;
}
.alert-count {
display: flex;
align-items: center;
color: #8c8c8c;
font-size: 12px;
}
}
.alert-annotations {
@@ -201,11 +273,11 @@
&.status-firing {
border-left-color: #ff4d4f;
}
&.status-resolved {
border-left-color: #52c41a;
}
&.status-pending {
border-left-color: #faad14;
}
@@ -22,7 +22,6 @@ import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN } from '@delon/theme';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { NzTableQueryParams } from 'ng-zorro-antd/table';
import { GroupAlert } from '../../../pojo/GroupAlert';
import { AlertService } from '../../../service/alert.service';
@@ -46,8 +45,7 @@ export class AlertCenterComponent implements OnInit {
groupAlerts!: GroupAlert[];
tableLoading: boolean = false;
checkedAlertIds = new Set<number>();
filterStatus: string = 'firing';
filterPriority: number = 9;
filterStatus!: string;
filterContent: string | undefined;
ngOnInit(): void {
@@ -62,7 +60,6 @@ export class AlertCenterComponent implements OnInit {
this.tableLoading = true;
let alertsInit$ = this.alertSvc.loadGroupAlerts(this.filterStatus, this.filterContent, this.pageIndex - 1, this.pageSize).subscribe(
message => {
this.tableLoading = false;
if (message.code === 0) {
let page = message.data;
this.groupAlerts = page.content;
@@ -76,6 +73,7 @@ export class AlertCenterComponent implements OnInit {
} else {
console.warn(message.msg);
}
this.tableLoading = false;
alertsInit$.unsubscribe();
},
error => {
@@ -206,26 +204,4 @@ export class AlertCenterComponent implements OnInit {
}
);
}
// begin: List multiple choice paging
checkedAll: boolean = false;
onAllChecked(checked: boolean) {
if (checked) {
this.groupAlerts.forEach(monitor => this.checkedAlertIds.add(monitor.id));
} else {
this.checkedAlertIds.clear();
}
}
onItemChecked(monitorId: number, checked: boolean) {
if (checked) {
this.checkedAlertIds.add(monitorId);
} else {
this.checkedAlertIds.delete(monitorId);
}
}
// end: List multiple choice paging
recordEntries(record: Record<any, any>) {
return Object.entries(record);
}
}
@@ -194,7 +194,7 @@
required
>
</nz-input-number>
<span class="ant-form-text">{{ 'alert.group-converge.seconds' | i18n }}</span>
<span class="ant-form-text">{{ 'common.time.unit.second' | i18n }}</span>
</nz-form-control>
</nz-form-item>
@@ -69,7 +69,7 @@
groupStyle="width: 250px;"
[placeholder]="'alert.setting.search' | i18n"
[(value)]="search"
(valueChange)="loadAlertDefineTable()"
(keydown.enter)="loadAlertDefineTable()"
/>
</ng-template>
</app-toolbar>
@@ -96,7 +96,7 @@
<th nzAlign="center" nzWidth="8%">{{ 'alert.setting.type' | i18n }}</th>
<th nzAlign="center" nzWidth="24%">{{ 'alert.setting.expr' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'alert.setting.template' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'alert.severity' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'label.bind' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'alert.setting.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'common.edit' | i18n }}</th>
</tr>
@@ -120,18 +120,7 @@
</td>
<td nzAlign="center">{{ data.template }}</td>
<td nzAlign="center">
<nz-tag *ngIf="data.labels?.severity == 'emergency'" nzColor="red">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>{{ 'alert.severity.0' | i18n }}</span>
</nz-tag>
<nz-tag *ngIf="data.labels?.severity == 'critical'" nzColor="orange">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>{{ 'alert.severity.1' | i18n }}</span>
</nz-tag>
<nz-tag *ngIf="data.labels?.severity == 'warning'" nzColor="yellow">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>{{ 'alert.severity.2' | i18n }}</span>
</nz-tag>
<nz-tag *ngFor="let item of data.labels | keyvalue">{{ item.key }}:{{ item.value }}</nz-tag>
</td>
<td nzAlign="center">
<nz-switch [(ngModel)]="data.enable" (ngModelChange)="updateAlertDefine(data)" name="enable"></nz-switch>
@@ -463,21 +452,17 @@
{{ 'alert.setting.period' | i18n }}
</nz-form-label>
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group nzCompact>
<nz-input-number
[(ngModel)]="define.period"
required
[nzMin]="60"
[nzStep]="60"
name="period"
id="period"
[nzPlaceHolder]="'alert.setting.period.placeholder' | i18n"
>
</nz-input-number>
<div class="ant-input-group-addon" style="width: 40px; text-align: center">
{{ 'common.time.unit.second' | i18n }}
</div>
</nz-input-group>
<nz-input-number
[(ngModel)]="define.period"
required
[nzMin]="60"
[nzStep]="60"
name="period"
id="period"
[nzPlaceHolder]="'alert.setting.period.placeholder' | i18n"
>
</nz-input-number>
<span class="ant-form-text">{{ 'common.time.unit.second' | i18n }}</span>
</nz-form-control>
</nz-form-item>
<nz-form-item>
@@ -294,7 +294,7 @@
<angular-tag-cloud
class="br-4"
*ngIf="!wordCloudDataLoading"
(clicked)="onTagCloudClick($event)"
(clicked)="onLabelCloudClick($event)"
[font]="'italic bold 6px monospace'"
[data]="wordCloudData"
[width]="1"
@@ -113,8 +113,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
);
}
onTagCloudClick(data: CloudData): void {
this.router.navigate(['/monitors'], { queryParams: { tag: data.text } });
onLabelCloudClick(data: CloudData): void {
this.router.navigate(['/monitors'], { queryParams: { labels: data.text } });
}
// start -- quantitative information summary
@@ -115,7 +115,7 @@
groupStyle="width: 120px;"
class="mobile-hide"
[placeholder]="'monitors.search.tag' | i18n"
[(value)]="tag"
[(value)]="labels"
(valueChange)="onTagChanged()"
/>
<app-multi-func-input
@@ -21,7 +21,6 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN, MenuService } from '@delon/theme';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
import { ModalButtonOptions } from 'ng-zorro-antd/modal/modal-types';
import { NzNotificationService } from 'ng-zorro-antd/notification';
@@ -33,7 +32,7 @@ import { Monitor } from '../../../pojo/Monitor';
import { AppDefineService } from '../../../service/app-define.service';
import { MemoryStorageService } from '../../../service/memory-storage.service';
import { MonitorService } from '../../../service/monitor.service';
import { formatTagName, findDeepestSelected } from '../../../shared/utils/common-util';
import { findDeepestSelected } from '../../../shared/utils/common-util';
@Component({
selector: 'app-monitor-list',
@@ -47,7 +46,6 @@ export class MonitorListComponent implements OnInit, OnDestroy {
private modal: NzModalService,
private notifySvc: NzNotificationService,
private monitorSvc: MonitorService,
private messageSvc: NzMessageService,
private storageSvc: MemoryStorageService,
private appDefineSvc: AppDefineService,
private menuService: MenuService,
@@ -56,7 +54,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
isDefaultListMenu!: boolean;
app!: string | undefined;
tag!: string | undefined;
labels!: string | undefined;
pageIndex: number = 1;
pageSize: number = 8;
total: number = 0;
@@ -86,11 +84,11 @@ export class MonitorListComponent implements OnInit, OnDestroy {
});
this.route.queryParamMap.subscribe(paramMap => {
let appStr = paramMap.get('app');
let tagStr = paramMap.get('tag');
if (tagStr != null) {
this.tag = tagStr;
let labelsStr = paramMap.get('labels');
if (labelsStr != null) {
this.labels = labelsStr;
} else {
this.tag = undefined;
this.labels = undefined;
}
if (appStr != null) {
this.app = appStr;
@@ -126,7 +124,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
onTagChanged(): void {
this.router.navigate([], {
relativeTo: this.route,
queryParams: { ...this.route.snapshot.queryParams, tag: this.tag },
queryParams: { ...this.route.snapshot.queryParams, tag: this.labels },
queryParamsHandling: 'merge'
});
}
@@ -134,7 +132,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
onFilterSearchMonitors() {
this.tableLoading = true;
let filter$ = this.monitorSvc
.searchMonitors(this.app, this.tag, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize)
.searchMonitors(this.app, this.labels, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize)
.subscribe(
message => {
filter$.unsubscribe();
@@ -180,7 +178,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
loadMonitorTable(sortField?: string | null, sortOrder?: string | null) {
this.tableLoading = true;
let monitorInit$ = this.monitorSvc
.searchMonitors(this.app, this.tag, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize, sortField, sortOrder)
.searchMonitors(this.app, this.labels, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize, sortField, sortOrder)
.subscribe(
message => {
this.tableLoading = false;
@@ -205,7 +203,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
changeMonitorTable(sortField?: string | null, sortOrder?: string | null) {
this.tableLoading = true;
let monitorInit$ = this.monitorSvc
.searchMonitors(this.app, this.tag, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize, sortField, sortOrder)
.searchMonitors(this.app, this.labels, this.filterContent, this.filterStatus, this.pageIndex - 1, this.pageSize, sortField, sortOrder)
.subscribe(
message => {
this.tableLoading = false;
@@ -495,7 +493,7 @@ export class MonitorListComponent implements OnInit, OnDestroy {
// end: List multiple choice paging
notifyCopySuccess() {
this.messageSvc.success(this.i18nSvc.fanyi('common.notify.copy-success'), { nzDuration: 800 });
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.copy-success'), '');
}
/**
@@ -53,8 +53,8 @@
<nz-card [nzActions]="[copyAction, editAction, deleteAction]" [nzBorderless]="true" class="tag-card" [nzSize]="'small'">
<div class="tag-content">
<div class="tag-header">
<a routerLink="/monitors" [queryParams]="{ tag: formatTagName(data) }">
<nz-tag [nzColor]="data.color" class="tag-name" style="font-size: 14px; padding: 4px 8px">
<a routerLink="/monitors" [queryParams]="{ labels: formatTagName(data) }">
<nz-tag [nzColor]="data.color" class="tag-name">
{{ formatTagName(data) }}
</nz-tag>
</a>
@@ -5,7 +5,7 @@
transition: all 0.3s;
border-radius: 8px;
background: #fff;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
@@ -26,13 +26,13 @@
background: #fafafa;
border-top: 1px solid #f0f0f0;
min-height: 32px;
> li {
margin: 0;
> span {
padding: 4px 0;
&:hover {
color: #1890ff;
}
@@ -53,8 +53,8 @@
margin-bottom: 8px;
.tag-name {
font-size: 16px;
padding: 4px 16px;
font-size: 18px;
padding: 4px 8px;
border-radius: 4px;
margin: 0;
font-weight: 500;
@@ -64,11 +64,9 @@
}
.tag-description {
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
line-height: 1.5;
color: rgba(35, 34, 34, 0.65);
font-size: 12px;
margin: 0;
min-height: 32px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@@ -106,4 +104,4 @@
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.5;
}
}
+2 -2
View File
@@ -57,7 +57,7 @@ export class AlertService {
httpParams = httpParams.append('status', status);
}
if (search != undefined && search != '' && search.trim() != '') {
httpParams = httpParams.append('content', search.trim());
httpParams = httpParams.append('search', search.trim());
}
const options = { params: httpParams };
return this.http.get<Message<Page<SingleAlert>>>(alerts_uri, options);
@@ -83,7 +83,7 @@ export class AlertService {
httpParams = httpParams.append('status', status);
}
if (search != undefined && search != '' && search.trim() != '') {
httpParams = httpParams.append('content', search.trim());
httpParams = httpParams.append('search', search.trim());
}
const options = { params: httpParams };
return this.http.get<Message<Page<GroupAlert>>>(alerts_group_uri, options);
+6 -7
View File
@@ -109,8 +109,8 @@ export class MonitorService {
public searchMonitors(
app: string | undefined,
tag: string | undefined,
searchValue: string,
labels: string | undefined,
search: string,
status: number,
pageIndex: number,
pageSize: number,
@@ -125,8 +125,8 @@ export class MonitorService {
pageIndex: pageIndex,
pageSize: pageSize
});
if (tag != undefined) {
httpParams = httpParams.append('tag', tag);
if (labels != undefined) {
httpParams = httpParams.append('labels', labels);
}
if (status != undefined && status != 9) {
httpParams = httpParams.append('status', status);
@@ -140,9 +140,8 @@ export class MonitorService {
order: sortOrder == 'ascend' ? 'asc' : 'desc'
});
}
if (searchValue != undefined && searchValue != '' && searchValue.trim() != '') {
httpParams = httpParams.append('name', searchValue);
httpParams = httpParams.append('host', searchValue);
if (search != undefined && search != '' && search.trim() != '') {
httpParams = httpParams.append('search', search);
}
const options = { params: httpParams };
return this.http.get<Message<Page<Monitor>>>(monitors_uri, options);
+4 -4
View File
@@ -127,9 +127,8 @@
"status": {
"": "Alert Status",
"all": "All Status",
"0": "Pending",
"2": "Restored",
"3": "Processed"
"firing": "Firing",
"resolved": "Resolved"
},
"severity": {
"": "Alarm Severity",
@@ -244,7 +243,7 @@
"alert.center.clear": "Clear All",
"alert.center.deal": "Mark Processed",
"alert.center.no-deal": "Mark Pending",
"alert.center.search": "Search Alert Content",
"alert.center.search": "Search Alert",
"alert.center.filter-status": "Alert Status",
"alert.center.filter-priority": "Alert Priority",
"alert.center.target": "Metric Target",
@@ -257,6 +256,7 @@
"alert.center.time.tip": "Alerts were triggered {{times}} times during this alert period",
"alert.center.first-time": "Start Time",
"alert.center.last-time": "Latest Time",
"alert.center.end-time": "End Time",
"alert.center.confirm.delete": "Please confirm whether to delete!",
"alert.center.confirm.clear-all": "Please confirm whether to clear all alerts!",
"alert.center.notify.no-mark": "No items selected for mark!",
+6 -5
View File
@@ -126,9 +126,8 @@
"status": {
"": "告警状态",
"all": "全部状态",
"0": "未处理",
"2": "已恢复",
"3": "已处理"
"firing": "告警中",
"resolved": "已恢复"
},
"severity": {
"": "告警级别",
@@ -245,7 +244,7 @@
"alert.center.clear": "一键清空",
"alert.center.deal": "标记已处理",
"alert.center.no-deal": "标记未处理",
"alert.center.search": "搜索告警内容",
"alert.center.search": "搜索告警",
"alert.center.filter-status": "告警状态",
"alert.center.filter-priority": "告警级别",
"alert.center.target": "告警对象",
@@ -258,6 +257,7 @@
"alert.center.time.tip": "此告警期间累计触发 {{times}} 次告警",
"alert.center.first-time": "开始",
"alert.center.last-time": "最新",
"alert.center.end-time": "结束",
"alert.center.confirm.delete": "请确认是否删除!",
"alert.center.confirm.clear-all": "请确认是否清空所有告警记录!",
"alert.center.notify.no-mark": "未选中任何待标记项!",
@@ -851,5 +851,6 @@
"alert.inhibit.edit": "编辑抑制规则",
"alert.inhibit.delete": "删除抑制规则",
"alert.help.inhibit": "告警抑制用于配置告警之间的抑制关系。当某个告警发生时,可以抑制其他告警的产生。例如,当服务器宕机时,可以抑制该服务器上的所有告警。",
"alert.help.inhibit.link": "https://hertzbeat.apache.org/zh-cn/docs/help/alert_inhibit"
"alert.help.inhibit.link": "https://hertzbeat.apache.org/zh-cn/docs/help/alert_inhibit",
"alert.center.tag.search.placeholder": "搜索标签..."
}
+4 -4
View File
@@ -141,9 +141,8 @@
"status": {
"": "告警狀態",
"all": "全部狀態",
"0": "未處理",
"2": "已恢",
"3": "已處理"
"firing": "告警中",
"resolved": "已恢"
},
"severity": {
"": "告警級別",
@@ -257,7 +256,7 @@
"alert.center.clear": "一鍵清空",
"alert.center.deal": "標記已處理",
"alert.center.no-deal": "標記未處理",
"alert.center.search": "搜索告警內容",
"alert.center.search": "搜索告警",
"alert.center.filter-status": "告警狀態",
"alert.center.filter-priority": "告警級別",
"alert.center.target": "告警對象",
@@ -270,6 +269,7 @@
"alert.center.time.tip": "此告警期間累計觸發 {{times}} 次告警",
"alert.center.first-time": "開始",
"alert.center.last-time": "最新",
"alert.center.end-time": "结束",
"alert.center.confirm.delete": "請確認是否刪除!",
"alert.center.confirm.clear-all": "請確認是否清空所有告警記錄!",
"alert.center.notify.no-mark": "未選中任何待標記項!",