Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,12 @@ public interface AlertGroupConvergeDao extends JpaRepository<AlertGroupConverge,
* @return group alarm converge list
*/
List<AlertGroupConverge> findAlertGroupConvergesByEnableIsTrue();


/**
* Query group alarm converge list based on the name
* @param name alert converge name
* @return group alarm converge list
*/
List<AlertGroupConverge> findAlertGroupConvergesByName(String name);

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.alert.reduce.AlarmGroupReduce;
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Expand All @@ -43,41 +45,52 @@
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class AlertGroupConvergeServiceImpl implements AlertGroupConvergeService {

@Autowired
private AlertGroupConvergeDao alertGroupConvergeDao;

@Autowired
private AlarmGroupReduce alarmGroupReduce;


protected ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");

@Override
public void validate(AlertGroupConverge alertGroupConverge, boolean isModify) throws IllegalArgumentException {
// todo
if (alertGroupConverge == null || !StringUtils.hasText(alertGroupConverge.getName())) {
throw new IllegalArgumentException(bundle.getString("alerter.converge.empty.name"));
}
List<AlertGroupConverge> sameNameConverges = alertGroupConvergeDao.findAlertGroupConvergesByName(alertGroupConverge.getName());
if (sameNameConverges != null && !sameNameConverges.isEmpty()) {
boolean isDuplicate = sameNameConverges.stream().anyMatch(it -> !it.getId().equals(alertGroupConverge.getId()));
if (isDuplicate) {
throw new IllegalArgumentException(bundle.getString("alerter.converge.duplicate.name"));
}
}
}

@Override
public void addAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException {
alertGroupConvergeDao.save(alertGroupConverge);
refreshAlertGroupConvergesCache();
}

@Override
public void modifyAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException {
alertGroupConvergeDao.save(alertGroupConverge);
refreshAlertGroupConvergesCache();
}

@Override
public AlertGroupConverge getAlertGroupConverge(long convergeId) throws RuntimeException {
return alertGroupConvergeDao.findById(convergeId).orElse(null);
}

@Override
public void deleteAlertGroupConverges(Set<Long> convergeIds) throws RuntimeException {
alertGroupConvergeDao.deleteAlertGroupConvergesByIdIn(convergeIds);
refreshAlertGroupConvergesCache();
}

@Override
public Page<AlertGroupConverge> getAlertGroupConverges(List<Long> convergeIds, String search, String sort, String order, int pageIndex, int pageSize) {
Specification<AlertGroupConverge> specification = (root, query, criteriaBuilder) -> {
Expand Down Expand Up @@ -105,7 +118,7 @@ public Page<AlertGroupConverge> getAlertGroupConverges(List<Long> convergeIds, S
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
return alertGroupConvergeDao.findAll(specification, pageRequest);
}

private void refreshAlertGroupConvergesCache() {
List<AlertGroupConverge> alertGroupConverges = alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue();
alarmGroupReduce.refreshGroupDefines(alertGroupConverges);
Expand Down
2 changes: 2 additions & 0 deletions hertzbeat-alerter/src/main/resources/alerter_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ alerter.priority.1 = Critical Alert
alerter.priority.2 = Warning Alert
alerter.calculate.parse.error = Expression is not fully parsed, may have syntax errors or incomplete inputs
alerter.datasource.executor.not.found = No query executor found
alerter.converge.duplicate.name = Group convergence strategy name already exists
alerter.converge.empty.name = Group convergence strategy name cannot be empty
2 changes: 2 additions & 0 deletions hertzbeat-alerter/src/main/resources/alerter_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ alerter.priority.1 = 严重告警
alerter.priority.2 = 警告告警
alerter.calculate.parse.error = 表达式未完全解析,可能存在语法错误或输入不完整
alerter.datasource.executor.not.found = 未找到查询执行器
alerter.converge.duplicate.name = 分组收敛策略名称已存在
alerter.converge.empty.name = 分组收敛策略名称不能为空
2 changes: 2 additions & 0 deletions hertzbeat-alerter/src/main/resources/alerter_zh_TW.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ alerter.priority.1 = 嚴重警報
alerter.priority.2 = 警告警報
alerter.calculate.parse.error = 表達式未完全解析,可能存在語法錯誤或輸入不完整
alerter.datasource.executor.not.found = 未找到查詢執行器
alerter.converge.duplicate.name = 分組收斂策略名稱已存在
alerter.converge.empty.name = 分組收斂策略名稱不能為空
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.service.impl;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.alert.reduce.AlarmGroupReduce;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
* Test case for {@link AlertGroupConvergeServiceImpl}
*/
@ExtendWith(MockitoExtension.class)
public class AlertGroupConvergeServiceImplTest {

@Mock
private AlertGroupConvergeDao alertGroupConvergeDao;

@Mock
private AlarmGroupReduce alarmGroupReduce;

@InjectMocks
private AlertGroupConvergeServiceImpl alertGroupConvergeService;

private AlertGroupConverge converge;

@BeforeEach
void setUp() {
converge = AlertGroupConverge.builder()
.id(1L)
.name("test-converge")
.build();
}

@Test
void testValidateNewSuccess() {
converge.setId(null);
when(alertGroupConvergeDao.findAlertGroupConvergesByName("test-converge"))
.thenReturn(Collections.emptyList());
assertDoesNotThrow(() -> alertGroupConvergeService.validate(converge, false));
}

@Test
void testValidateNewDuplicateName() {
converge.setId(null);
AlertGroupConverge existing = AlertGroupConverge.builder()
.id(2L)
.name("test-converge")
.build();
when(alertGroupConvergeDao.findAlertGroupConvergesByName("test-converge"))
.thenReturn(List.of(existing));
assertThrows(IllegalArgumentException.class, () -> alertGroupConvergeService.validate(converge, false));
}

@Test
void testValidateNewEmptyName() {
converge.setName("");
assertThrows(IllegalArgumentException.class, () -> alertGroupConvergeService.validate(converge, false));

converge.setName(null);
assertThrows(IllegalArgumentException.class, () -> alertGroupConvergeService.validate(converge, false));
}

@Test
void testValidateModifySuccessNameUnchanged() {
when(alertGroupConvergeDao.findAlertGroupConvergesByName("test-converge"))
.thenReturn(List.of(converge));
assertDoesNotThrow(() -> alertGroupConvergeService.validate(converge, true));
}

@Test
void testValidateModifySuccessNameChanged() {
converge.setName("new-name");
when(alertGroupConvergeDao.findAlertGroupConvergesByName("new-name"))
.thenReturn(Collections.emptyList());
assertDoesNotThrow(() -> alertGroupConvergeService.validate(converge, true));
}

@Test
void testValidateModifyDuplicateName() {
converge.setName("existing-name");
AlertGroupConverge existing = AlertGroupConverge.builder()
.id(2L)
.name("existing-name")
.build();
when(alertGroupConvergeDao.findAlertGroupConvergesByName("existing-name"))
.thenReturn(List.of(existing));
assertThrows(IllegalArgumentException.class, () -> alertGroupConvergeService.validate(converge, true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
Expand All @@ -45,7 +46,9 @@
* Alert group converge strategy entity
*/
@Entity
@Table(name = "hzb_alert_group_converge")
@Table(name = "hzb_alert_group_converge", indexes = {
@Index(name = "idx_name", columnList = "name")
})
@Data
@Builder
@AllArgsConstructor
Expand All @@ -62,21 +65,22 @@ public class AlertGroupConverge {
@Schema(title = "Policy name", example = "group-converge-1")
@Size(max = 100)
@NotNull
@Column(name = "name")
private String name;

@Schema(title = "Labels to group by", example = "[\"instance\"]")
@Convert(converter = JsonStringListAttributeConverter.class)
@Column(name = "group_labels", length = 1024)
private List<String> groupLabels;

@Schema(title = "Initial wait time before sending first group alert (s)", example = "30")
@Column(name = "group_wait")
private Long groupWait;

@Schema(title = "Interval between group alert sends (s)", example = "300")
@Column(name = "group_interval")
private Long groupInterval;

@Schema(title = "Interval for repeating firing alerts (s), set to 0 to disable repeating", example = "9000")
@Column(name = "repeat_interval")
private Long repeatInterval;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@
<span>{{ data.name }}</span>
</td>
<td nzAlign="center">
<nz-tag *ngFor="let label of data.groupLabels">{{ label }}</nz-tag>
<nz-tag *ngFor="let label of data.groupLabels" nz-tooltip [nzTooltipTitle]="label.length > 15 ? label : null">
{{ label.length > 15 ? (label | slice : 0 : 15) + '...' : label }}
</nz-tag>
</td>
<td nzAlign="center"> {{ data.groupWait }}{{ 'alert.group-converge.seconds' | i18n }} </td>
<td nzAlign="center"> {{ data.groupInterval }}{{ 'alert.group-converge.seconds' | i18n }} </td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class AlertGroupConvergeComponent implements OnInit {

onNewGroupConverge() {
this.groupConverge = new AlertGroupConverge();
this.groupConverge.groupLabels = [''];
this.groupConverge.groupLabels = [];
this.isManageModalAdd = true;
this.isManageModalVisible = true;
this.isManageModalOkLoading = false;
Expand All @@ -234,7 +234,7 @@ export class AlertGroupConvergeComponent implements OnInit {
if (message.code === 0) {
this.groupConverge = message.data;
if (!Array.isArray(this.groupConverge.groupLabels) || this.groupConverge.groupLabels.length === 0) {
this.groupConverge.groupLabels = [''];
this.groupConverge.groupLabels = [];
}
this.isManageModalVisible = true;
} else {
Expand Down
2 changes: 1 addition & 1 deletion web-app/src/assets/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"alert.inhibit.equal_labels.common": "Common Labels",
"alert.inhibit.equal_labels.custom": "Custom Labels",
"alert.inhibit.equal_labels.more": "{{count}} more labels",
"alert.inhibit.equal_labels.placeholder": "Press Enter after entering label name, or select from dropdown",
"alert.inhibit.equal_labels.placeholder": "Input or select labels",
"alert.inhibit.equal_labels.tip": "Source and target alerts must have equal values for these label keys, common keys like alertname, instance, severity etc",
"alert.inhibit.name": "Inhibit Rule Name",
"alert.inhibit.name.tip": "Name to identify this inhibit rule, must be unique",
Expand Down
2 changes: 1 addition & 1 deletion web-app/src/assets/i18n/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"alert.inhibit.equal_labels.common": "共通ラベル",
"alert.inhibit.equal_labels.custom": "カスタムラベル",
"alert.inhibit.equal_labels.more": "{{count}} 個のラベルが追加",
"alert.inhibit.equal_labels.placeholder": "ラベル名を入力後、Enterキーを押すか、ドロップダウンから選択してください",
"alert.inhibit.equal_labels.placeholder": "ラベルを入力または選択",
"alert.inhibit.equal_labels.tip": "ソースおよびターゲットのアラートは、これらのラベルキーに対して等しい値を持つ必要があります。共通キーとしてalertname、instance、severityなどがあります",
"alert.inhibit.name": "抑制ルール名",
"alert.inhibit.name.tip": "この抑制ルールを識別するための名前。ユニークでなければなりません",
Expand Down
2 changes: 1 addition & 1 deletion web-app/src/assets/i18n/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
"alert.inhibit.equal_labels.common": "Tags comuns",
"alert.inhibit.equal_labels.custom": "Tags personalizadas",
"alert.inhibit.equal_labels.more": "Existem também {{count}} tags",
"alert.inhibit.equal_labels.placeholder": "Digite o nome da tag e pressione Enter ou selecione na lista suspensa",
"alert.inhibit.equal_labels.placeholder": "Insira ou selecione tags",
"alert.inhibit.equal_labels.tip": "As chaves de tag e os valores correspondentes dos alarmes de origem e dos alarmes de destino devem ser iguais. As chaves de tag comuns incluem alertname, instância, gravidade, etc.",
"alert.inhibit.name": "Suprimir nome da regra",
"alert.inhibit.name.tip": "Um nome que identifique esta regra de supressão precisa ser exclusivo",
Expand Down
2 changes: 1 addition & 1 deletion web-app/src/assets/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"alert.inhibit.equal_labels.common": "常用标签",
"alert.inhibit.equal_labels.custom": "自定义标签",
"alert.inhibit.equal_labels.more": "还有 {{count}} 个标签",
"alert.inhibit.equal_labels.placeholder": "输入标签名称后按回车,或从下拉列表选择",
"alert.inhibit.equal_labels.placeholder": "输入或选择标签",
"alert.inhibit.equal_labels.tip": "源告警和目标告警这些标签Key和对应值必须相等,常用标签Key如 alertname、instance、severity 等",
"alert.inhibit.name": "抑制规则名称",
"alert.inhibit.name.tip": "标识此抑制规则的名称,需要唯一",
Expand Down
2 changes: 1 addition & 1 deletion web-app/src/assets/i18n/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"alert.inhibit.equal_labels.common": "常用標籤",
"alert.inhibit.equal_labels.custom": "自定義標籤",
"alert.inhibit.equal_labels.more": "還有 {{count}} 個標籤",
"alert.inhibit.equal_labels.placeholder": "輸入標籤名稱後按回車,或從下拉列表選擇",
"alert.inhibit.equal_labels.placeholder": "輸入或選擇標籤",
"alert.inhibit.equal_labels.tip": "源告警和目標告警這些標籤Key和對應值必須相等,常用標籤Key如 alertname、instance、severity 等",
"alert.inhibit.name": "抑制規則名稱",
"alert.inhibit.name.tip": "標識此抑制規則的名稱,需要唯一",
Expand Down
Loading