mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 09:40:58 +00:00
Merge branch 'master' into update-wall-233
This commit is contained in:
+14
-18
@@ -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}}
|
||||
|
||||
+25
-4
@@ -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())) {
|
||||
|
||||
+100
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "関連付けられたモニター",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "关联监控",
|
||||
|
||||
@@ -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": "關聯監控",
|
||||
|
||||
Reference in New Issue
Block a user