Merge branch 'master' into update-wall-233

This commit is contained in:
tomsun28
2025-04-21 23:12:02 +08:00
committed by GitHub
9 changed files with 172 additions and 24 deletions
+14 -18
View File
@@ -29,7 +29,7 @@ param:
monitorUDP: "{{randAlpha 6}}"
monitorWebsite: "{{randAlpha 6}}"
monitorFTP: "{{randAlpha 6}}"
tagName: "{{randAlpha 3}}"
labelName: "{{randAlpha 3}}"
items:
- name: login
request:
@@ -342,39 +342,35 @@ items:
header:
Authorization: Bearer {{.login.data.token}}
## Tag
- name: createTag
## Label
- name: createLabel
request:
api: /api/tag
api: /api/label
method: POST
header:
Authorization: Bearer {{.login.data.token}}
Content-type: application/json
body: |
[
{
"color": "#ff4081",
"name": "{{.param.tagName}}"
}
]
- name: listTags
{
"name": "{{.param.labelName}}"
}
- name: listLabels
request:
api: /api/tag?pageIndex=0&pageSize=8&type=1
api: /api/label?pageIndex=0&pageSize=8&type=1
header:
Authorization: Bearer {{.login.data.token}}
- name: updateTag
- name: updateLabel
request:
api: /api/tag
api: /api/label
method: PUT
header:
Authorization: Bearer {{.login.data.token}}
Content-type: application/json
body: |
{
"id": {{(index .listTags.data.content 0).id | int64}},
"id": {{(index .listLabels.data.content 0).id | int64}},
"name": "{{randAlpha 3}}",
"tagValue": "{{randAlpha 3}}",
"color": "#ff4081",
"type": 1,
"creator": "admin",
"modifier": "admin"
@@ -382,9 +378,9 @@ items:
expect:
bodyFieldsExpect:
code: "0"
- name: deleteTag
- name: deleteLabel
request:
api: /api/tag?ids={{(index .listTags.data.content 0).id | int64}}
api: /api/label?ids={{(index .listLabels.data.content 0).id | int64}}
method: DELETE
header:
Authorization: Bearer {{.login.data.token}}
@@ -44,6 +44,7 @@ import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
@@ -72,7 +73,7 @@ public class RealTimeAlertCalculator {
private static final Pattern APP_PATTERN = Pattern.compile("equals\\(__app__,\"([^\"]+)\"\\)");
private static final Pattern AVAILABLE_PATTERN = Pattern.compile("equals\\(__available__,\"([^\"]+)\"\\)");
private static final Pattern LABEL_PATTERN = Pattern.compile("contains\\(__labels__,\\s*\"([^\"]+)\"\\)");
private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\\s\"(\\d+)\"\\)");
private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\\s*\"(\\d+)\"\\)");
private static final Pattern METRICS_PATTERN = Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)");
private final AlerterWorkerPool workerPool;
@@ -81,18 +82,38 @@ public class RealTimeAlertCalculator {
private final AlarmCommonReduce alarmCommonReduce;
private final AlarmCacheManager alarmCacheManager;
@Autowired
public RealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue,
AlertDefineService alertDefineService, SingleAlertDao singleAlertDao,
AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager) {
this(workerPool, dataQueue, alertDefineService, singleAlertDao, alarmCommonReduce, alarmCacheManager, true);
}
/**
* Constructor for RealTimeAlertCalculator with a toggle to control whether to start alert calculation threads.
*
* @param workerPool The worker pool used for concurrent alert calculation.
* @param dataQueue The queue from which metric data is pulled and pushed.
* @param alertDefineService The service providing alert definition rules.
* @param singleAlertDao The DAO for fetching persisted alert states from storage.
* @param alarmCommonReduce The component responsible for reducing and sending alerts.
* @param start If true, the alert calculation threads will start automatically;
* set to false to disable thread start (useful for unit testing).
*/
public RealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue,
AlertDefineService alertDefineService, SingleAlertDao singleAlertDao,
AlarmCommonReduce alarmCommonReduce, AlarmCacheManager alarmCacheManager, boolean start) {
this.workerPool = workerPool;
this.dataQueue = dataQueue;
this.alarmCommonReduce = alarmCommonReduce;
this.alertDefineService = alertDefineService;
this.alarmCacheManager = alarmCacheManager;
startCalculate();
if (start) {
startCalculate();
}
}
private void startCalculate() {
public void startCalculate() {
Runnable runnable = () -> {
while (!Thread.currentThread().isInterrupted()) {
try {
@@ -252,7 +273,7 @@ public class RealTimeAlertCalculator {
* @param priority Current priority
* @return Filtered alert definitions
*/
private List<AlertDefine> filterThresholdsByAppAndMetrics(List<AlertDefine> thresholds, String app, String metrics, Map<String, String> labels, String instance, int priority) {
public List<AlertDefine> filterThresholdsByAppAndMetrics(List<AlertDefine> thresholds, String app, String metrics, Map<String, String> labels, String instance, int priority) {
return thresholds.stream()
.filter(define -> {
if (StringUtils.isBlank(define.getExpr())) {
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.hertzbeat.alert.calculate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.AlertDefineService;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
class RealTimeAlertCalculatorTest {
private RealTimeAlertCalculator calculator;
@BeforeEach
void setUp() {
AlerterWorkerPool mockPool = Mockito.mock(AlerterWorkerPool.class);
CommonDataQueue mockQueue = Mockito.mock(CommonDataQueue.class);
AlertDefineService mockAlertDefineService = Mockito.mock(AlertDefineService.class);
SingleAlertDao mockDao = Mockito.mock(SingleAlertDao.class);
AlarmCommonReduce mockReduce = Mockito.mock(AlarmCommonReduce.class);
AlarmCacheManager alarmCacheManager = Mockito.mock(AlarmCacheManager.class);
Mockito.when(mockDao.querySingleAlertsByStatus(Mockito.anyString()))
.thenReturn(Collections.emptyList());
calculator = new RealTimeAlertCalculator(mockPool, mockQueue, mockAlertDefineService, mockDao, mockReduce, alarmCacheManager, false);
}
@Test
void testFilterThresholdsByAppAndMetrics_withInstanceExpr_HasSpace() {
String app = "redis";
String instanceId = "501045327364864";
int priority = 0;
AlertDefine matchDefine = new AlertDefine();
matchDefine.setExpr("equals(__app__,\"redis\") && equals(__instance__, \"501045327364864\")");
AlertDefine unmatchDefine = new AlertDefine();
unmatchDefine.setExpr("equals(__app__,\"redis\") && equals(__instance__, \"999999999\")");
List<AlertDefine> allDefines = Arrays.asList(matchDefine, unmatchDefine);
List<AlertDefine> filtered = calculator.filterThresholdsByAppAndMetrics(allDefines, app, "", Map.of(), instanceId, priority);
// It should filter out 999999999.
assertEquals(1, filtered.size());
assertEquals("equals(__app__,\"redis\") && equals(__instance__, \"501045327364864\")",
filtered.get(0).getExpr());
}
@Test
void testFilterThresholdsByAppAndMetrics_withInstanceExpr_NoSpace() {
String app = "redis";
String instanceId = "501045327364864";
int priority = 0;
AlertDefine matchDefine = new AlertDefine();
matchDefine.setExpr("equals(__app__,\"redis\") && equals(__instance__,\"501045327364864\")");
AlertDefine unmatchDefine = new AlertDefine();
unmatchDefine.setExpr("equals(__app__,\"redis\") && equals(__instance__,\"999999999\")");
List<AlertDefine> allDefines = Arrays.asList(matchDefine, unmatchDefine);
List<AlertDefine> filtered = calculator.filterThresholdsByAppAndMetrics(allDefines, app, "", Map.of(), instanceId, priority);
// It should filter out 999999999.
assertEquals(1, filtered.size());
assertEquals("equals(__app__,\"redis\") && equals(__instance__,\"501045327364864\")",
filtered.get(0).getExpr());
}
}
@@ -116,6 +116,7 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
clearText: this.i18nSvc.fanyi('alert.center.clear')
}
];
notifiedAlert: any[] = [];
count = 0;
loading = false;
popoverVisible = false;
@@ -247,6 +248,16 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
event.stopPropagation();
const updatedMuteState = !this.mute.mute;
const updatedMuteConfig = { ...this.mute, mute: updatedMuteState };
// request notification permission
debugger;
if (!updatedMuteState) {
Notification.requestPermission().then(permission => {
if (permission !== 'granted') {
console.log('Notification permission denied.');
}
});
}
let saveConfig$ = this.configSvc
.saveGeneralConfig(updatedMuteConfig, 'mute')
.pipe(
@@ -290,11 +301,21 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
status: alert.status,
type: this.i18nSvc.fanyi('dashboard.alerts.title-no')
};
console.log('alert:', alert);
list.push(item);
this.data = this.updateNoticeData(list);
if (!this.mute.mute) {
if (!this.mute.mute && !this.notifiedAlert.includes(alert.id)) {
this.notifiedAlert.push(alert.id);
this.alertSound.playAlertSound(this.i18nSvc.currentLang);
const notification = new Notification(this.i18nSvc.fanyi('alert.notify.title'), {
body: this.i18nSvc.fanyi('alert.notify.body'),
icon: 'assets/logo.svg'
});
notification.onclick = () => {
window.focus();
this.router.navigateByUrl(`/alert/center`);
notification.close();
};
}
this.cdr.detectChanges();
});
+2
View File
@@ -220,6 +220,8 @@
"alert.notice.type.userId": "User ID",
"alert.notice.type.wechat": "Open WeChat",
"alert.notice.type.wechat-id": "WeChat OPENID",
"alert.notify.title": "New Alert",
"alert.notify.body": "You have a new alert, please handle it in time",
"alert.setting.bind.available": "Available Monitors",
"alert.setting.bind.manage": "Manage Associations",
"alert.setting.bind.monitors": "Associated Monitors",
+2
View File
@@ -220,6 +220,8 @@
"alert.notice.type.userId": "ユーザーID",
"alert.notice.type.wechat": "Open WeChat",
"alert.notice.type.wechat-id": "WeChat OPENID",
"alert.notify.title": "新しいアラーム通知",
"alert.notify.body": "新しいアラームがあります。時間内に処理してください。",
"alert.setting.bind.available": "利用可能なモニター",
"alert.setting.bind.manage": "関連付けを管理",
"alert.setting.bind.monitors": "関連付けられたモニター",
+2
View File
@@ -348,6 +348,8 @@
"alert.notice.sender.sms.tencent.signName": "Nome de Assinatura do SMS Tencent",
"alert.notice.sender.sms.tencent.appId": "AppId do SMS Tencent",
"alert.notice.sender.sms.tencent.templateId": "ID do Modelo do SMS Tencent",
"alert.notify.title": "Nova notificação de alarme",
"alert.notify.body": "Você tem um novo alarme, por favor, resolva-o a tempo!",
"alert.export.switch-type": "Selecione o formato do arquivo de exportação!",
"alert.export.use-type": "Exportar regras no formato de arquivo {{type}}",
"alert.group-converge.name": "Nome da política",
+2
View File
@@ -220,6 +220,8 @@
"alert.notice.type.userId": "用户ID",
"alert.notice.type.wechat": "微信公众号",
"alert.notice.type.wechat-id": "微信OPENID",
"alert.notify.title": "新告警通知",
"alert.notify.body": "您有一条新的告警,请及时处理!",
"alert.setting.bind.available": "可选监控",
"alert.setting.bind.manage": "关联监控",
"alert.setting.bind.monitors": "关联监控",
+2
View File
@@ -219,6 +219,8 @@
"alert.notice.type.userId": "用戶ID",
"alert.notice.type.wechat": "微信公衆號",
"alert.notice.type.wechat-id": "微信OPENID",
"alert.notify.title": "新告警通知",
"alert.notify.body": "您有一條新的告警,請及時處理!",
"alert.setting.bind.available": "可選監控",
"alert.setting.bind.manage": "關聯監控",
"alert.setting.bind.monitors": "關聯監控",