This is an automated email from the ASF dual-hosted git repository. zhaoqingran pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push: new a8ce48b50d [bugfix] support no-whitespace alert expressions in instance filtering (#3276) a8ce48b50d is described below commit a8ce48b50dbadee71e9f52700ba6b49160d5b4fa Author: Yang Chen <1597081...@qq.com> AuthorDate: Mon Apr 21 15:46:16 2025 +0800 [bugfix] support no-whitespace alert expressions in instance filtering (#3276) Co-authored-by: Logic <zqr10...@dromara.org> --- .../alert/calculate/RealTimeAlertCalculator.java | 29 +++++- .../calculate/RealTimeAlertCalculatorTest.java | 100 +++++++++++++++++++++ 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java index 35b0334270..0db6bb15cc 100644 --- a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java +++ b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java @@ -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())) { diff --git a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java new file mode 100644 index 0000000000..bdcc34ff77 --- /dev/null +++ b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculatorTest.java @@ -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()); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@hertzbeat.apache.org For additional commands, e-mail: notifications-h...@hertzbeat.apache.org