Compare commits

..
Author SHA1 Message Date
Logic 821c62ba60 Merge branch 'master' into copy-token 2025-01-18 20:55:10 +08:00
Logic fbc1c0a29a Merge branch 'master' into copy-token 2025-01-17 23:25:58 +08:00
Logic 648064227f refactor(web-app): update alert integration modal button layout
- Swap OK and Cancel button positions in alert integration modal- Improve user experience by aligning with common UI practices
2025-01-17 23:05:53 +08:00
Logic a08fdd210c feat(alert): add copy button for alert integration token
- Add a copy button below the token display
- Implement copyToken() method to copy the token to clipboard
- Show a success notification after copying
2025-01-17 22:38:49 +08:00
125 changed files with 37059 additions and 40369 deletions
-73
View File
@@ -2225,79 +2225,6 @@
"contributions": [
"code"
]
},
{
"login": "helei1030",
"name": "helei1030",
"avatar_url": "https://avatars.githubusercontent.com/u/11839080?v=4",
"profile": "https://github.com/helei1030",
"contributions": [
"doc"
]
},
{
"login": "pjfanning",
"name": "PJ Fanning",
"avatar_url": "https://avatars.githubusercontent.com/u/11783444?v=4",
"profile": "https://medium.com/@pjfanning",
"contributions": [
"code"
]
},
{
"login": "MonsterChenzhuo",
"name": "monster",
"avatar_url": "https://avatars.githubusercontent.com/u/60029759?v=4",
"profile": "https://github.com/MonsterChenzhuo",
"contributions": [
"code"
]
},
{
"login": "MasamiYui",
"name": "Sherlock Yin",
"avatar_url": "https://avatars.githubusercontent.com/u/22274133?v=4",
"profile": "https://github.com/MasamiYui",
"contributions": [
"code"
]
},
{
"login": "wanhao23",
"name": "wanhao",
"avatar_url": "https://avatars.githubusercontent.com/u/29560961?v=4",
"profile": "https://github.com/wanhao23",
"contributions": [
"code",
"doc"
]
},
{
"login": "jonasHanhan",
"name": "jonasHanhan",
"avatar_url": "https://avatars.githubusercontent.com/u/130035609?v=4",
"profile": "https://github.com/jonasHanhan",
"contributions": [
"code"
]
},
{
"login": "NikhilMurugesan",
"name": "NikhilMurugesan",
"avatar_url": "https://avatars.githubusercontent.com/u/49281792?v=4",
"profile": "https://github.com/NikhilMurugesan",
"contributions": [
"code"
]
},
{
"login": "myangle1120",
"name": "myangle1120",
"avatar_url": "https://avatars.githubusercontent.com/u/19237013?v=4",
"profile": "https://github.com/myangle1120",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
+1 -1
View File
@@ -73,7 +73,7 @@ jobs:
# upload application logs
- name: Upload logs & API test reports
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
if: always()
with:
name: hz-logs-${{ github.run_id }}
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
run: npx docusaurus-prince-pdf -u https://hertzbeat.apache.org/docs --output docs-en.pdf
- name: Upload results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: docs-cn-pdf
path: docs-cn.pdf
@@ -47,7 +47,7 @@ jobs:
retention-days: 1
- name: Upload results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: docs-en-pdf
path: docs-en.pdf
-12
View File
@@ -496,18 +496,6 @@ Thanks to these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Suvrat1629"><img src="https://avatars.githubusercontent.com/u/140749446?v=4?s=100" width="100px;" alt="Suvrat1629"/><br /><sub><b>Suvrat1629</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Suvrat1629" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://ghyghoo8.github.io/"><img src="https://avatars.githubusercontent.com/u/363129?v=4?s=100" width="100px;" alt="ghy"/><br /><sub><b>ghy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ghyghoo8" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helei1030"><img src="https://avatars.githubusercontent.com/u/11839080?v=4?s=100" width="100px;" alt="helei1030"/><br /><sub><b>helei1030</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=helei1030" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@pjfanning"><img src="https://avatars.githubusercontent.com/u/11783444?v=4?s=100" width="100px;" alt="PJ Fanning"/><br /><sub><b>PJ Fanning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MonsterChenzhuo"><img src="https://avatars.githubusercontent.com/u/60029759?v=4?s=100" width="100px;" alt="monster"/><br /><sub><b>monster</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MonsterChenzhuo" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MasamiYui"><img src="https://avatars.githubusercontent.com/u/22274133?v=4?s=100" width="100px;" alt="Sherlock Yin"/><br /><sub><b>Sherlock Yin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wanhao23"><img src="https://avatars.githubusercontent.com/u/29560961?v=4?s=100" width="100px;" alt="wanhao"/><br /><sub><b>wanhao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jonasHanhan"><img src="https://avatars.githubusercontent.com/u/130035609?v=4?s=100" width="100px;" alt="jonasHanhan"/><br /><sub><b>jonasHanhan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jonasHanhan" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NikhilMurugesan"><img src="https://avatars.githubusercontent.com/u/49281792?v=4?s=100" width="100px;" alt="NikhilMurugesan"/><br /><sub><b>NikhilMurugesan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NikhilMurugesan" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/myangle1120"><img src="https://avatars.githubusercontent.com/u/19237013?v=4?s=100" width="100px;" alt="myangle1120"/><br /><sub><b>myangle1120</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=myangle1120" title="Code">💻</a></td>
</tr>
</tbody>
</table>
-12
View File
@@ -495,18 +495,6 @@ Thanks these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Suvrat1629"><img src="https://avatars.githubusercontent.com/u/140749446?v=4?s=100" width="100px;" alt="Suvrat1629"/><br /><sub><b>Suvrat1629</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Suvrat1629" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://ghyghoo8.github.io/"><img src="https://avatars.githubusercontent.com/u/363129?v=4?s=100" width="100px;" alt="ghy"/><br /><sub><b>ghy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ghyghoo8" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helei1030"><img src="https://avatars.githubusercontent.com/u/11839080?v=4?s=100" width="100px;" alt="helei1030"/><br /><sub><b>helei1030</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=helei1030" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@pjfanning"><img src="https://avatars.githubusercontent.com/u/11783444?v=4?s=100" width="100px;" alt="PJ Fanning"/><br /><sub><b>PJ Fanning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MonsterChenzhuo"><img src="https://avatars.githubusercontent.com/u/60029759?v=4?s=100" width="100px;" alt="monster"/><br /><sub><b>monster</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MonsterChenzhuo" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MasamiYui"><img src="https://avatars.githubusercontent.com/u/22274133?v=4?s=100" width="100px;" alt="Sherlock Yin"/><br /><sub><b>Sherlock Yin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wanhao23"><img src="https://avatars.githubusercontent.com/u/29560961?v=4?s=100" width="100px;" alt="wanhao"/><br /><sub><b>wanhao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jonasHanhan"><img src="https://avatars.githubusercontent.com/u/130035609?v=4?s=100" width="100px;" alt="jonasHanhan"/><br /><sub><b>jonasHanhan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jonasHanhan" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NikhilMurugesan"><img src="https://avatars.githubusercontent.com/u/49281792?v=4?s=100" width="100px;" alt="NikhilMurugesan"/><br /><sub><b>NikhilMurugesan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NikhilMurugesan" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/myangle1120"><img src="https://avatars.githubusercontent.com/u/19237013?v=4?s=100" width="100px;" alt="myangle1120"/><br /><sub><b>myangle1120</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=myangle1120" title="Code">💻</a></td>
</tr>
</tbody>
</table>
-5
View File
@@ -40,11 +40,6 @@
<groupId>org.apache.hertzbeat</groupId>
<artifactId>hertzbeat-plugin</artifactId>
</dependency>
<!-- warehouse -->
<dependency>
<groupId>org.apache.hertzbeat</groupId>
<artifactId>hertzbeat-warehouse</artifactId>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -17,172 +17,90 @@
package org.apache.hertzbeat.alert.calculate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.DataSourceService;
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
/**
* Periodic Alert Calculator
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class PeriodicAlertCalculator {
private static final String VALUE = "__value__";
private static final String TIMESTAMP = "__timestamp__";
private final DataSourceService dataSourceService;
private final AlarmCommonReduce alarmCommonReduce;
/**
* The alarm in the process is triggered
* key - labels fingerprint
*/
private final Map<String, SingleAlert> pendingAlertMap;
/**
* The not recover alert
* key - labels fingerprint
*/
private final Map<String, SingleAlert> firingAlertMap;
private final JexlExpressionRunner expressionRunner;
private final Map<String, SingleAlert> notRecoveredAlertMap = new ConcurrentHashMap<>(16);
public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce) {
this.dataSourceService = dataSourceService;
this.alarmCommonReduce = alarmCommonReduce;
this.pendingAlertMap = new ConcurrentHashMap<>(8);
this.firingAlertMap = new ConcurrentHashMap<>(8);
}
public void calculate(AlertDefine rule) {
public List<SingleAlert> calculate(AlertDefine rule) {
if (!rule.isEnable() || StringUtils.isEmpty(rule.getExpr())) {
log.error("Periodic rule {} is disabled or expression is empty", rule.getName());
return;
return Collections.emptyList();
}
long currentTimeMilli = System.currentTimeMillis();
// todo: implement the following logic
try {
// for prometheus is instant promql query, for db is sql query
// result: [{'value': 100, 'timestamp': 1343554, 'instance': 'node1'},{'value': 200, 'timestamp': 1343555, 'instance': 'node2'}]
// the return result should be matched with threshold
try {
List<Map<String, Object>> results = dataSourceService.calculate(
rule.getDatasource(),
rule.getExpr()
);
// if no match the expr threshold, the results item map {'value': null} should be null and others field keep
// if results has multi list, should trigger multi alert
if (CollectionUtils.isEmpty(results)) {
return;
}
for (Map<String, Object> result : results) {
Map<String, String> fingerPrints = new HashMap<>(8);
// here use the alert name as finger, not care the alert name may be changed
fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
fingerPrints.putAll(rule.getLabels());
for (Map.Entry<String, Object> entry : result.entrySet()) {
if (entry.getValue() != null && !VALUE.equals(entry.getKey())
&& !TIMESTAMP.equals(entry.getKey())) {
fingerPrints.put(entry.getKey(), entry.getValue().toString());
}
}
if (result.get(VALUE) == null) {
// recovery the alert
handleRecoveredAlert(fingerPrints);
continue;
}
Map<String, Object> fieldValueMap = new HashMap<>(8);
fieldValueMap.putAll(rule.getLabels());
fieldValueMap.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
for (Map.Entry<String, Object> entry : result.entrySet()) {
if (entry.getValue() != null) {
fieldValueMap.put(entry.getKey(), entry.getValue());
}
}
afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, rule);
}
} catch (Exception ignored) {
// ignore the query exception eg: no result, timeout, etc
return;
// Execute query
List<Map<String, Object>> queryResults = dataSourceService.query(
rule.getDatasource(),
rule.getExpr()
);
if (CollectionUtils.isEmpty(queryResults)) {
return Collections.emptyList();
}
// Execute expression calculation on query results
List<SingleAlert> newAlerts = queryResults.stream()
.filter(result -> execAlertExpression(result, rule.getExpr()))
.map(result -> buildAlert(rule, result))
.collect(Collectors.toList());
// Handle recovery notification
if (newAlerts.isEmpty()) {
return handleAlertRecover(rule);
}
return newAlerts;
} catch (Exception e) {
log.error("Calculate periodic rule {} failed: {}", rule.getName(), e.getMessage());
return Collections.emptyList();
}
}
private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, String> fingerPrints,
Map<String, Object> fieldValueMap, AlertDefine define) {
String fingerprint = calculateFingerprint(fingerPrints);
SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
Map<String, String> labels = new HashMap<>(8);
fieldValueMap.putAll(define.getLabels());
labels.putAll(fingerPrints);
int requiredTimes = define.getTimes() == null ? 1 : define.getTimes();
if (existingAlert == null) {
// First time triggering alert, create new alert and set to pending status
SingleAlert newAlert = SingleAlert.builder()
.labels(labels)
// todo render var content in annotations
.annotations(define.getAnnotations())
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
.status(CommonConstants.ALERT_STATUS_PENDING)
.triggerTimes(1)
.startAt(currentTimeMilli)
.activeAt(currentTimeMilli)
.build();
// If required trigger times is 1, set to firing status directly
if (requiredTimes <= 1) {
newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
firingAlertMap.put(fingerprint, newAlert);
alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
} else {
// Otherwise put into pending queue first
pendingAlertMap.put(fingerprint, newAlert);
}
} else {
// Update existing alert
existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1);
existingAlert.setActiveAt(currentTimeMilli);
// Check if required trigger times reached
if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) {
// Reached trigger times threshold, change to firing status
pendingAlertMap.remove(fingerprint);
existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
firingAlertMap.put(fingerprint, existingAlert);
alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
}
}
private boolean execAlertExpression(Map<String, Object> result, String expr) {
return false;
}
private void handleRecoveredAlert(Map<String, String> fingerprints) {
String fingerprint = calculateFingerprint(fingerprints);
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
private SingleAlert buildAlert(AlertDefine rule, Map<String, Object> metrics) {
return SingleAlert.builder()
.labels(rule.getLabels())
.annotations(rule.getAnnotations())
.triggerTimes(1)
.startAt(System.currentTimeMillis())
.activeAt(System.currentTimeMillis())
.build();
}
private List<SingleAlert> handleAlertRecover(AlertDefine rule) {
SingleAlert firingAlert = notRecoveredAlertMap.remove(rule.getId().toString());
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());
return Collections.singletonList(buildResolvedAlert(rule, firingAlert));
}
pendingAlertMap.remove(fingerprint);
return Collections.emptyList();
}
private String calculateFingerprint(Map<String, String> fingerPrints) {
List<String> keyList = fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
List<String> valueList = fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
+ Arrays.hashCode(valueList.toArray(new String[0]));
private SingleAlert buildResolvedAlert(AlertDefine rule, SingleAlert firingAlert) {
return null;
}
}
@@ -17,79 +17,13 @@
package org.apache.hertzbeat.alert.calculate;
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_PERIODIC;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
/**
* Periodic Alert Rule Scheduler
* period alert rule scheduler
*/
@Slf4j
@Component
public class PeriodicAlertRuleScheduler implements CommandLineRunner {
private final PeriodicAlertCalculator calculator;
private final AlertDefineDao alertDefineDao;
private final ScheduledExecutorService scheduledExecutor;
private final Map<Long, ScheduledFuture<?>> scheduledFutures;
public PeriodicAlertRuleScheduler(PeriodicAlertCalculator calculator, AlertDefineDao alertDefineDao) {
this.calculator = calculator;
this.alertDefineDao = alertDefineDao;
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setUncaughtExceptionHandler((thread, throwable) -> {
log.error("Scheduled periodic alert threshold has uncaughtException.");
log.error(throwable.getMessage(), throwable);
})
.setDaemon(true)
.setNameFormat("periodic-alert-threshold-worker-%d")
.build();
this.scheduledExecutor = Executors.newScheduledThreadPool(10, threadFactory);
this.scheduledFutures = new ConcurrentHashMap<>();
}
public void cancelSchedule(Long ruleId) {
if (ruleId == null) {
return;
}
ScheduledFuture<?> future = scheduledFutures.get(ruleId);
if (future != null) {
future.cancel(true);
scheduledFutures.remove(ruleId);
}
}
public void updateSchedule(AlertDefine rule) {
if (rule == null || rule.getId() == null) {
log.error("Alert rule is null or rule id is null.");
return;
}
cancelSchedule(rule.getId());
if (rule.getType().equals(ALERT_THRESHOLD_TYPE_PERIODIC)) {
ScheduledFuture<?> future = scheduledExecutor.scheduleAtFixedRate(() -> {
calculator.calculate(rule);
}, 0, rule.getPeriod(), java.util.concurrent.TimeUnit.SECONDS);
scheduledFutures.put(rule.getId(), future);
}
}
@Override
public void run(String... args) throws Exception {
log.info("Starting periodic alert rule scheduler...");
List<AlertDefine> periodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(ALERT_THRESHOLD_TYPE_PERIODIC);
for (AlertDefine rule : periodicRules) {
updateSchedule(rule);
}
}
public class PeriodicAlertRuleScheduler {
// todo implement the following logic
}
@@ -1,63 +0,0 @@
/*
* 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.config;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* SSE manager for alert
*/
@Component
public class AlertSseManager {
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
public SseEmitter createEmitter(Long clientId) {
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
emitter.onCompletion(() -> removeEmitter(clientId));
emitter.onTimeout(() -> removeEmitter(clientId));
emitters.put(clientId, emitter);
return emitter;
}
@Async
public void broadcast(String data) {
emitters.forEach((clientId, emitter) -> {
try {
emitter.send(SseEmitter.event()
.id(String.valueOf(System.currentTimeMillis()))
.name("ALERT_EVENT")
.data(data));
} catch (IOException e) {
emitter.complete();
removeEmitter(clientId);
}
});
}
private void removeEmitter(Long clientId) {
emitters.remove(clientId);
}
}
@@ -1,48 +0,0 @@
/*
* 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.controller;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;
import org.apache.hertzbeat.alert.config.AlertSseManager;
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* SSE controller for alert
*/
@RestController
@RequestMapping(path = "/api/alert/sse", produces = {TEXT_EVENT_STREAM_VALUE})
public class AlertSseController {
private final AlertSseManager emitterManager;
public AlertSseController(AlertSseManager emitterManager) {
this.emitterManager = emitterManager;
}
@GetMapping(path = "/subscribe")
public SseEmitter subscribe() {
Long clientId = SnowFlakeIdGenerator.generateId();
return emitterManager.createEmitter(clientId);
}
}
@@ -31,7 +31,6 @@ import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.alert.service.NoticeConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -83,18 +82,9 @@ public class NoticeConfigController {
@GetMapping(path = "/receivers")
@Operation(summary = "Get a list of message notification recipients based on query filter items",
description = "Get a list of message notification recipients based on query filter items")
public ResponseEntity<Message<Page<NoticeReceiver>>> getReceivers(
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name,
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name, pageIndex, pageSize)));
}
@GetMapping(path = "/receivers/all")
@Operation(summary = "Get a list of all message notification recipients",
description = "Get a list of all message notification recipients")
public ResponseEntity<Message<List<NoticeReceiver>>> getAllReceivers() {
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeReceivers()));
public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers(
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name) {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name)));
}
@GetMapping(path = "/receiver/{id}")
@@ -139,11 +129,9 @@ public class NoticeConfigController {
@GetMapping(path = "/rules")
@Operation(summary = "Get a list of message notification policies based on query filter items",
description = "Get a list of message notification policies based on query filter items")
public ResponseEntity<Message<Page<NoticeRule>>> getRules(
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name,
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name, pageIndex, pageSize)));
public ResponseEntity<Message<List<NoticeRule>>> getRules(
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name) {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name)));
}
@GetMapping(path = "/rule/{id}")
@@ -188,22 +176,12 @@ public class NoticeConfigController {
@GetMapping(path = "/templates")
@Operation(summary = "Get a list of message notification templates based on query filter items",
description = "Get a list of message notification templates based on query filter items")
public ResponseEntity<Message<Page<NoticeTemplate>>> getTemplates(
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name,
@Parameter(description = "Whether it is a preset template", example = "true") @RequestParam(defaultValue = "true") final boolean preset,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
Page<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name, preset, pageIndex, pageSize);
public ResponseEntity<Message<List<NoticeTemplate>>> getTemplates(
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name) {
List<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name);
return ResponseEntity.ok(Message.success(templatePage));
}
@GetMapping(path = "/templates/all")
@Operation(summary = "Get a list of all message notification templates",
description = "Get a list of all message notification templates")
public ResponseEntity<Message<List<NoticeTemplate>>> getAllTemplates() {
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeTemplates()));
}
@GetMapping(path = "/template/{id}")
@Operation(summary = "Get the notification template information based on the template ID",
description = "Get the notification template information based on the template ID")
@@ -23,13 +23,11 @@ import java.util.Map;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.config.AlertSseManager;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.alert.service.NoticeConfigService;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.plugin.PostAlertPlugin;
import org.apache.hertzbeat.plugin.Plugin;
import org.apache.hertzbeat.plugin.runner.PluginRunner;
@@ -47,18 +45,16 @@ public class AlertNoticeDispatch {
private final AlertStoreHandler alertStoreHandler;
private final Map<Byte, AlertNotifyHandler> alertNotifyHandlerMap;
private final PluginRunner pluginRunner;
private final AlertSseManager emitterManager;
public AlertNoticeDispatch(AlerterWorkerPool workerPool,
NoticeConfigService noticeConfigService,
AlertStoreHandler alertStoreHandler,
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner, AlertSseManager emitterManager) {
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner) {
this.workerPool = workerPool;
this.noticeConfigService = noticeConfigService;
this.alertStoreHandler = alertStoreHandler;
this.pluginRunner = pluginRunner;
alertNotifyHandlerMap = Maps.newHashMapWithExpectedSize(alertNotifyHandlerList.size());
this.emitterManager = emitterManager;
alertNotifyHandlerList.forEach(r -> alertNotifyHandlerMap.put(r.type(), r));
}
@@ -108,27 +104,27 @@ public class AlertNoticeDispatch {
public void dispatchAlarm(GroupAlert groupAlert) {
if (groupAlert != null) {
// Determining alarm type storage
GroupAlert storedGroupAlert = alertStoreHandler.store(groupAlert);
alertStoreHandler.store(groupAlert);
// Notice distribution
sendNotify(storedGroupAlert);
sendNotify(groupAlert);
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(storedGroupAlert));
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(groupAlert));
// Execute the plugin if enable with params
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(storedGroupAlert, pluginContext));
// Send alert to the sse client
emitterManager.broadcast(JsonUtil.toJson(storedGroupAlert));
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(groupAlert, pluginContext));
}
}
private void sendNotify(GroupAlert alert) {
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> workerPool.executeNotify(() -> rule.getReceiverId()
.forEach(receiverId -> {
try {
sendNoticeMsg(getOneReceiverById(receiverId),
getOneTemplateById(rule.getTemplateId()), alert);
} catch (AlertNoticeException e) {
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
}
}))));
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> {
workerPool.executeNotify(() -> rule.getReceiverId()
.forEach(receiverId -> {
try {
sendNoticeMsg(getOneReceiverById(receiverId),
getOneTemplateById(rule.getTemplateId()), alert);
} catch (AlertNoticeException e) {
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
}
}));
}));
}
}
@@ -28,9 +28,7 @@ public interface AlertStoreHandler {
* Persistent alarm records
* It is necessary to associate and assign values
* to the alert tag information tags while persisting.
*
* @param alert alarm information
* @return groupAlert
*/
GroupAlert store(GroupAlert alert);
void store(GroupAlert alert);
}
@@ -17,7 +17,6 @@
package org.apache.hertzbeat.alert.notice.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -47,53 +46,44 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
private final SingleAlertDao singleAlertDao;
@Override
public GroupAlert store(GroupAlert groupAlert) {
public void store(GroupAlert groupAlert) {
if (groupAlert == null || groupAlert.getAlerts() == null || groupAlert.getAlerts().isEmpty()) {
log.error("The Group Alerts is empty, ignore store");
return groupAlert;
return;
}
// 1. Find existing alert group
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
// 2. Process individual alerts
Set<String> alertFingerprints = new HashSet<>(8);
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
List<SingleAlert> newAlerts = new ArrayList<>();
for (SingleAlert singleAlert : originalAlerts) {
groupAlert.getAlerts().forEach(singleAlert -> {
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
if (existAlert != null) {
// Update the existing alert with the ID and creation time from the database
// Update existing alert
singleAlert.setId(existAlert.getId());
singleAlert.setGmtCreate(existAlert.getGmtCreate());
// Status transition logic
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
singleAlert.setStartAt(existAlert.getStartAt());
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1) + Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
singleAlert.setTriggerTimes(triggerTimes);
}
}
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
// Transition to resolved state
if (singleAlert.getEndAt() == null) {
singleAlert.setEndAt(System.currentTimeMillis());
singleAlert.setEndAt(System.currentTimeMillis());
}
singleAlert.setStartAt(existAlert.getStartAt());
singleAlert.setActiveAt(existAlert.getActiveAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
}
}
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
newAlerts.add(savedSingleAlert);
alertFingerprints.add(savedSingleAlert.getFingerprint());
}
groupAlert.setAlerts(newAlerts);
alertFingerprints.add(singleAlert.getFingerprint());
singleAlertDao.save(singleAlert);
});
// 3. Process resolved alerts
if (existGroupAlert != null) {
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
@@ -130,8 +120,6 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
// 4. Save alert group
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
savedGroupAlert.setAlerts(groupAlert.getAlerts());
return savedGroupAlert;
groupAlertDao.save(groupAlert);
}
}
@@ -26,10 +26,10 @@ import java.util.Map;
public interface DataSourceService {
/**
* execute query expr calculate
* execute query
* @param datasource datasource
* @param expr query expr
* @param query query
* @return result
*/
List<Map<String, Object>> calculate(String datasource, String expr);
List<Map<String, Object>> query(String datasource, String query);
}
@@ -23,7 +23,6 @@ import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.springframework.data.domain.Page;
/**
* Message notification configuration interface
@@ -33,32 +32,23 @@ public interface NoticeConfigService {
/**
* Dynamic conditional query
* @param name Recipient name,support fuzzy query
* @param pageIndex Page number
* @param pageSize Number of records per page
* @return Search result
*/
Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize);
List<NoticeReceiver> getNoticeReceivers(String name);
/**
* Dynamic conditional query
* @param name Template name,support fuzzy query
* @param preset Whether it is a system preset template
* true: System preset template
* false: Custom template
* @param pageIndex Page number
* @param pageSize Number of records per page
* @return Search result
*/
Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize);
List<NoticeTemplate> getNoticeTemplates(String name);
/**
* Dynamic conditional query
* @param name Recipient name ,support fuzzy query
* @param pageIndex Page number
* @param pageSize Number of records per page
* @param name Recipient name
* @return Search result
*/
Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize);
List<NoticeRule> getNoticeRules(String name);
/**
* Add a notification recipient
@@ -164,15 +154,4 @@ public interface NoticeConfigService {
*/
boolean sendTestMsg(NoticeReceiver noticeReceiver);
/**
* Query all notification recipients
* @return Recipient List
*/
List<NoticeReceiver> getAllNoticeReceivers();
/**
* Query all notification policies
* @return Notification Policy List
*/
List<NoticeTemplate> getAllNoticeTemplates();
}
@@ -25,7 +25,6 @@ import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler;
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
import org.apache.hertzbeat.alert.service.AlertDefineService;
@@ -68,9 +67,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
@Autowired
private AlertDefineDao alertDefineDao;
@Autowired
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
@@ -102,22 +98,19 @@ public class AlertDefineServiceImpl implements AlertDefineService {
@Override
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
alertDefine = alertDefineDao.save(alertDefine);
periodicAlertRuleScheduler.updateSchedule(alertDefine);
alertDefineDao.save(alertDefine);
CacheFactory.clearAlertDefineCache();
}
@Override
public void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException {
alertDefineDao.save(alertDefine);
periodicAlertRuleScheduler.updateSchedule(alertDefine);
CacheFactory.clearAlertDefineCache();
}
@Override
public void deleteAlertDefine(long alertId) throws RuntimeException {
alertDefineDao.deleteById(alertId);
periodicAlertRuleScheduler.cancelSchedule(alertId);
CacheFactory.clearAlertDefineCache();
}
@@ -130,9 +123,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
@Override
public void deleteAlertDefines(Set<Long> alertIds) throws RuntimeException {
alertDefineDao.deleteAlertDefinesByIdIn(alertIds);
for (Long alertId : alertIds) {
periodicAlertRuleScheduler.cancelSchedule(alertId);
}
CacheFactory.clearAlertDefineCache();
}
@@ -17,21 +17,13 @@
package org.apache.hertzbeat.alert.service.impl;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Stack;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.service.DataSourceService;
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.util.StringUtils;
/**
* datasource service
@@ -41,307 +33,21 @@ import org.springframework.util.StringUtils;
public class DataSourceServiceImpl implements DataSourceService {
@Autowired(required = false)
private List<QueryExecutor> executors;
private static final Pattern EXPR_TOKEN = Pattern.compile("\\(|\\)|[a-zA-Z_][a-zA-Z0-9_={}\\[\\]\".]*|\\d+(\\.\\d+)?|>=|<=|==|!=|>|<|and|or|unless");
private static final String THRESHOLD = "__threshold__";
private static final String VALUE = "__value__";
private Map<String, QueryExecutor> executors;
@Override
public List<Map<String, Object>> calculate(String datasource, String expr) {
if (!StringUtils.hasText(expr)) {
throw new IllegalArgumentException("Empty expression");
}
if (executors == null || executors.isEmpty()) {
throw new IllegalArgumentException("No query executor found");
}
QueryExecutor executor = executors.stream().filter(e -> e.support(datasource)).findFirst().orElse(null);
public List<Map<String, Object>> query(String datasource, String query) {
QueryExecutor executor = executors.get(datasource);
if (executor == null) {
throw new IllegalArgumentException("Unsupported datasource: " + datasource);
}
// replace all white space
expr = expr.replaceAll("\\s+", " ");
try {
return evaluate(expr, executor);
} catch (Exception e) {
log.error("Error executing query on datasource {}: {}", datasource, e.getMessage());
throw new RuntimeException("Query execution failed", e);
}
return executor.execute(query);
}
private List<Map<String, Object>> evaluate(String expr, QueryExecutor executor) {
Stack<List<Map<String, Object>>> values = new Stack<>();
Stack<String> operators = new Stack<>();
Matcher matcher = EXPR_TOKEN.matcher(expr);
List<String> tokens = new ArrayList<>();
while (matcher.find()) {
tokens.add(matcher.group());
}
for (String token : tokens) {
if (token.equals("(")) {
operators.push(token);
} else if (token.equals(")")) {
while (!operators.isEmpty() && !operators.peek().equals("(")) {
applyOperator(values, operators.pop());
}
// remove the left parenthesis
operators.pop();
} else if (token.matches(">=|<=|==|!=|>|<")) {
operators.push(token);
} else if (token.equals("and") || token.equals("or") || token.equals("unless")) {
while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(token)) {
applyOperator(values, operators.pop());
}
operators.push(token);
} else if (token.matches("\\d+(\\.\\d+)?")) {
double value = Double.parseDouble(token);
List<Map<String, Object>> numAsList = new ArrayList<>();
numAsList.add(Map.of(THRESHOLD, value));
values.push(numAsList);
} else if (token.matches("[a-zA-Z_][a-zA-Z0-9_={}\\[\\]\".]*")) {
List<Map<String, Object>> results = executor.execute(token);
values.push(results);
}
}
while (!operators.isEmpty()) {
applyOperator(values, operators.pop());
}
return values.isEmpty() ? new LinkedList<>() : values.pop();
}
private int precedence(String op) {
return switch (op) {
case "or" -> 1;
case "unless" -> 2;
case "and" -> 3;
case ">", "<", ">=", "<=", "==", "!=" -> 4;
default -> 0;
};
}
private void applyOperator(Stack<List<Map<String, Object>>> values, String op) {
if (values.size() < 2) {
return;
};
List<Map<String, Object>> rightOperand = values.pop();
List<Map<String, Object>> leftOperand = values.pop();
if (rightOperand.size() == 1 && rightOperand.get(0).containsKey(THRESHOLD)) {
double threshold = (double) rightOperand.get(0).get(THRESHOLD);
List<Map<String, Object>> result = new ArrayList<>();
for (Map<String, Object> item : leftOperand) {
Object queryValues = item.get(VALUE);
if (queryValues == null) {
// ignore the query result data is empty
continue;
}
// queryValues may be a list of values, or a single value
Object matchValue = evaluateCondition(queryValues, op, threshold);
item.put(VALUE, matchValue);
// if matchValue is null, mean not match the threshold
// if not null, mean match the threshold
result.add(new HashMap<>(item));
}
if (!result.isEmpty()) {
values.push(result);
}
return;
}
Map<String, Object> leftMap = null;
boolean leftMatch = false;
Map<String, Object> rightMap = null;
boolean rightMatch = false;
switch (op) {
case "and" -> {
for (Map<String, Object> item : leftOperand) {
if (leftMap == null) {
leftMap = item;
}
if (item.get(VALUE) != null) {
leftMap = item;
leftMatch = true;
break;
}
}
for (Map<String, Object> item : rightOperand) {
if (rightMap == null) {
rightMap = item;
}
if (item.get(VALUE) != null) {
rightMap = item;
rightMatch = true;
break;
}
}
if (leftMatch && rightMatch) {
rightMap.putAll(leftMap);
values.push(new LinkedList<>(List.of(rightMap)));
} else if (leftMap != null) {
leftMap.put(VALUE, null);
values.push(new LinkedList<>(List.of(leftMap)));
} else if (rightMap != null) {
rightMap.put(VALUE, null);
values.push(new LinkedList<>(List.of(rightMap)));
}
}
case "or" -> {
for (Map<String, Object> item : leftOperand) {
if (leftMap == null) {
leftMap = item;
}
if (item.get(VALUE) != null) {
leftMap = item;
leftMatch = true;
break;
}
}
for (Map<String, Object> item : rightOperand) {
if (rightMap == null) {
rightMap = item;
}
if (item.get(VALUE) != null) {
rightMap = item;
rightMatch = true;
break;
}
}
if (leftMatch && rightMatch) {
rightMap.putAll(leftMap);
values.push(new LinkedList<>(List.of(rightMap)));
} else if (leftMatch) {
values.push(new LinkedList<>(List.of(leftMap)));
} else if (rightMatch) {
values.push(new LinkedList<>(List.of(rightMap)));
} else {
if (leftMap != null && rightMap != null) {
rightMap.putAll(leftMap);
values.push(new LinkedList<>(List.of(rightMap)));
} else if (leftMap != null) {
values.push(new LinkedList<>(List.of(leftMap)));
} else if (rightMap != null){
values.push(new LinkedList<>(List.of(rightMap)));
}
}
}
case "unless" -> {
for (Map<String, Object> item : leftOperand) {
if (leftMap == null) {
leftMap = item;
}
if (item.get(VALUE) != null) {
leftMap = item;
leftMatch = true;
break;
}
}
for (Map<String, Object> item : rightOperand) {
if (rightMap == null) {
rightMap = item;
}
if (item.get(VALUE) != null) {
rightMap = item;
rightMatch = true;
break;
}
}
if (leftMatch && !rightMatch) {
values.push(new LinkedList<>(List.of(leftMap)));
} else {
if (leftMap != null) {
leftMap.put(VALUE, null);
values.push(new LinkedList<>(List.of(leftMap)));
} else {
if (rightMap != null) {
rightMap.put(VALUE, null);
values.push(new LinkedList<>(List.of(rightMap)));
}
}
}
}
default -> throw new IllegalArgumentException("Unsupported operator: " + op);
}
}
private Object evaluateCondition(Object value, String operator, Double threshold) {
// value may be a list of values, or a single value
switch (operator) {
case ">":
// if value is list, return the max value
if (value instanceof List<?> values) {
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
.max(Double::compareTo).orElse(null);
if (doubleValue != null) {
return doubleValue > threshold ? doubleValue : null;
} else {
return null;
}
} else {
return Double.parseDouble(value.toString()) > threshold ? value : null;
}
case ">=":
if (value instanceof List<?> values) {
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
.max(Double::compareTo).orElse(null);
if (doubleValue != null) {
return doubleValue >= threshold ? doubleValue : null;
} else {
return null;
}
} else {
return Double.parseDouble(value.toString()) >= threshold ? value : null;
}
case "<":
if (value instanceof List<?> values) {
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
.min(Double::compareTo).orElse(null);
if (doubleValue != null) {
return doubleValue < threshold ? doubleValue : null;
} else {
return null;
}
} else {
return Double.parseDouble(value.toString()) < threshold ? value : null;
}
case "<=":
if (value instanceof List<?> values) {
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
.min(Double::compareTo).orElse(null);
if (doubleValue != null) {
return doubleValue <= threshold ? doubleValue : null;
} else {
return null;
}
} else {
return Double.parseDouble(value.toString()) <= threshold ? value : null;
}
case "==":
if (value instanceof List<?> values) {
for (Object v : values) {
if (v.equals(threshold)) {
return v;
}
}
return null;
} else {
return value.equals(threshold) ? value : null;
}
case "!=":
if (value instanceof List<?> values) {
for (Object v : values) {
if (v.equals(threshold)) {
return null;
}
}
return value;
} else {
return value.equals(threshold) ? null : value;
}
default:
// unsupported operator todo add more operator
return null;
}
}
public void setExecutors(List<QueryExecutor> mockExecutor) {
this.executors = mockExecutor;
/**
*
*/
public interface QueryExecutor {
List<Map<String, Object>> execute(String query);
}
}
@@ -18,6 +18,18 @@
package org.apache.hertzbeat.alert.service.impl;
import jakarta.persistence.criteria.Predicate;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.cache.CacheFactory;
@@ -39,28 +51,10 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Message notification configuration implementation
*/
@@ -86,87 +80,44 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
private AlertNoticeDispatch dispatcherAlarm;
@Override
public Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize) {
public List<NoticeReceiver> getNoticeReceivers(String name) {
Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
);
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
predicate = criteriaBuilder.and(predicateName);
}
return predicate;
};
return noticeReceiverDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
return noticeReceiverDao.findAll(specification);
}
@Override
public List<NoticeReceiver> getAllNoticeReceivers() {
return noticeReceiverDao.findAll();
}
@Override
public Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize) {
if (preset) {
// Query preset templates
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
// Filter by name (case-insensitive)
List<NoticeTemplate> filteredDefaultTemplates = defaultTemplates.stream()
.filter(template -> StringUtils.isBlank(name)
|| template.getName().toLowerCase().contains(name.toLowerCase()))
.collect(Collectors.toList());
// Pagination logic
int totalItems = filteredDefaultTemplates.size();
int fromIndex = Math.min(pageIndex * pageSize, totalItems);
int toIndex = Math.min(fromIndex + pageSize, totalItems);
if (fromIndex >= totalItems) {
return new PageImpl<>(Collections.emptyList(), PageRequest.of(pageIndex, pageSize), totalItems);
public List<NoticeTemplate> getNoticeTemplates(String name) {
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
predicate = criteriaBuilder.and(predicateName);
}
List<NoticeTemplate> paginatedTemplates = filteredDefaultTemplates.subList(fromIndex, toIndex);
return new PageImpl<>(paginatedTemplates, PageRequest.of(pageIndex, pageSize), totalItems);
} else {
// Query custom templates
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
);
predicate = criteriaBuilder.and(predicateName);
}
return predicate;
};
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id"));
return noticeTemplateDao.findAll(specification, pageRequest);
}
}
@Override
public List<NoticeTemplate> getAllNoticeTemplates() {
return predicate;
};
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
defaultTemplates.addAll(noticeTemplateDao.findAll());
defaultTemplates.addAll(noticeTemplateDao.findAll(specification));
return defaultTemplates;
}
@Override
public Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize) {
public List<NoticeRule> getNoticeRules(String name) {
Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
);
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
predicate = criteriaBuilder.and(predicateName);
}
return predicate;
};
return noticeRuleDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
return noticeRuleDao.findAll(specification);
}
@Override
@@ -1,32 +0,0 @@
# 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.
alerter.availability.recover = 可用性警報已解決,監視狀態現在正常
alerter.alarm.recover = 警報解決通知
alerter.notify.title = HertzBeat 警報通知
alerter.notify.target = 監視目標
alerter.notify.monitorId = 監視 ID
alerter.notify.monitorName = 監視名稱
alerter.notify.monitorHost = 監視主機
alerter.notify.priority = 警報優先級
alerter.notify.triggerTime = 警報觸發時間
alerter.notify.restoreTime = 警報恢復時間
alerter.notify.times = 警報觸發次數
alerter.notify.tags = 警報標籤
alerter.notify.content = 警報內容
alerter.notify.console = 控制台登錄
alerter.priority.0 = 緊急警報
alerter.priority.1 = 嚴重警報
alerter.priority.2 = 警告警報
@@ -28,6 +28,7 @@ import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -42,6 +43,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
/**
* Test case for {@link AlertDefineController}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class AlertDefineControllerTest {
@@ -64,7 +66,6 @@ class AlertDefineControllerTest {
this.alertDefine = AlertDefine.builder()
.id(1L)
.name("alertDefine")
.expr("1 > 0")
.times(1)
.template("template")
@@ -137,4 +138,14 @@ class AlertDefineControllerTest {
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
@Test
void applyAlertDefineMonitorsBind() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/api/alert/define/" + this.alertDefine.getId() + "/monitors")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(this.alertDefineMonitorBinds)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
}
@@ -19,6 +19,7 @@ package org.apache.hertzbeat.alert.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -30,6 +31,7 @@ import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -48,6 +50,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
* Test case for {@link AlertDefinesController}
* Test whether the data mocked at the mock is correct, and test whether the format of the returned data is correct
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class AlertDefinesControllerTest {
@@ -90,7 +93,8 @@ class AlertDefinesControllerTest {
pageRequest = PageRequest.of((Integer) content.get("pageIndex"), (Integer) content.get("pageSize"), sortExp);
}
@Test
// @Test
// todo: fix this test
void getAlertDefines() throws Exception {
// Test the correctness of the mock
@@ -108,27 +112,31 @@ class AlertDefinesControllerTest {
// }
// }))).thenReturn(new PageImpl<AlertDefine>(new ArrayList<AlertDefine>()));
AlertDefine define = AlertDefine.builder().id(9L).expr("x").times(1).build();
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Order.asc(sort)));
Mockito.when(alertDefineService.getAlertDefines(
Mockito.eq(ids),
Mockito.isNull(),
Mockito.eq(sort),
Mockito.eq(order),
Mockito.eq(pageIndex),
Mockito.eq(pageSize)
)).thenReturn(new PageImpl<>(Collections.singletonList(define), pageRequest, 1));
Mockito.when(alertDefineService.getAlertDefines(null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
mockMvc.perform(MockMvcRequestBuilders.get("/api/alert/defines")
mockMvc.perform(MockMvcRequestBuilders.get(
"/api/alert/defines")
.param("ids", ids.toString().substring(1, ids.toString().length() - 1))
.param("priority", priority.toString())
.param("sort", sort)
.param("order", order)
.param("pageIndex", String.valueOf(pageIndex))
.param("pageSize", String.valueOf(pageSize)))
.param("pageIndex", pageIndex.toString())
.param("pageSize", pageSize.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.data.content[0].id").value(9))
.andExpect(jsonPath("$.data.content[0].expr").value("x"))
.andExpect(jsonPath("$.data.content[0].times").value(1))
.andExpect(jsonPath("$.data.content").value(new ArrayList<>()))
.andExpect(jsonPath("$.data.pageable").value("INSTANCE"))
.andExpect(jsonPath("$.data.totalPages").value(1))
.andExpect(jsonPath("$.data.totalElements").value(0))
.andExpect(jsonPath("$.data.last").value(true))
.andExpect(jsonPath("$.data.number").value(0))
.andExpect(jsonPath("$.data.size").value(0))
.andExpect(jsonPath("$.data.first").value(true))
.andExpect(jsonPath("$.data.numberOfElements").value(0))
.andExpect(jsonPath("$.data.empty").value(true))
.andExpect(jsonPath("$.data.sort.empty").value(true))
.andExpect(jsonPath("$.data.sort.sorted").value(false))
.andExpect(jsonPath("$.data.sort.unsorted").value(true))
.andReturn();
}
@@ -27,12 +27,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -44,6 +44,7 @@ import org.springframework.test.web.servlet.MockMvc;
/**
* test case for {@link AlertGroupConvergeController}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
public class AlertGroupConvergeControllerTest {
@@ -78,8 +79,8 @@ public class AlertGroupConvergeControllerTest {
mockMvc.perform(post("/api/alert/group")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(alertGroupConverge)))
.andExpect(status().isOk())
.content(JsonUtil.toJson(alertGroupConverge))
).andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Add success"));
}
@@ -92,8 +93,8 @@ public class AlertGroupConvergeControllerTest {
mockMvc.perform(put("/api/alert/group")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(alertGroupConverge)))
.andExpect(status().isOk())
.content(JsonUtil.toJson(alertGroupConverge))
).andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Modify success"));
}
@@ -118,6 +119,7 @@ public class AlertGroupConvergeControllerTest {
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
.andExpect(jsonPath("$.msg").value("Alert Group Converge not exist."));
.andExpect(jsonPath("$.msg").value("AlertGroupConverge not exist."));
}
}
@@ -33,6 +33,7 @@ import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -51,6 +52,7 @@ import org.springframework.test.web.servlet.MockMvc;
*/
@ExtendWith(MockitoExtension.class)
@Disabled
class AlertGroupConvergesControllerTest {
private MockMvc mockMvc;
@@ -99,7 +101,6 @@ class AlertGroupConvergesControllerTest {
.param("order", "desc")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.data.content[0].id").value(1))
.andExpect(jsonPath("$.data.content[0].name").value("Converge1"))
.andExpect(jsonPath("$.data.content[1].id").value(2))
@@ -117,4 +118,5 @@ class AlertGroupConvergesControllerTest {
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE));
}
}
}
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.alert.controller;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -43,10 +44,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@@ -171,38 +168,12 @@ class NoticeConfigControllerTest {
@Test
void getReceivers() throws Exception {
NoticeReceiver receiver1 = new NoticeReceiver();
receiver1.setId(1L);
receiver1.setName("Receiver1");
NoticeReceiver receiver2 = new NoticeReceiver();
receiver2.setId(2L);
receiver2.setName("Receiver2");
Page<NoticeReceiver> receiverPage = new PageImpl<>(
Arrays.asList(receiver1, receiver2),
PageRequest.of(0, 8, Sort.by("id").descending()),
2
);
when(noticeConfigService.getNoticeReceivers("Receiver", 0, 8)).thenReturn(receiverPage);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers")
.param("name", "Receiver")
.param("pageIndex", "0")
.param("pageSize", "8")
.param("sort", "id")
.param("order", "desc")
.accept(MediaType.APPLICATION_JSON))
//Mockito.when(noticeConfigService.getNoticeReceivers())
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers?name={name}", "tom"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.content[0].id").value(1))
.andExpect(jsonPath("$.data.content[0].name").value("Receiver1"))
.andExpect(jsonPath("$.data.content[1].id").value(2))
.andExpect(jsonPath("$.data.content[1].name").value("Receiver2"))
.andExpect(jsonPath("$.data.totalElements").value(2))
.andExpect(jsonPath("$.data.totalPages").value(1))
.andExpect(jsonPath("$.data.size").value(8))
.andExpect(jsonPath("$.data.number").value(0));
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
@Test
@@ -274,38 +245,15 @@ class NoticeConfigControllerTest {
@Test
void getRules() throws Exception {
NoticeRule rule1 = new NoticeRule();
rule1.setId(1L);
rule1.setName("Rule1");
NoticeRule rule2 = new NoticeRule();
rule2.setId(2L);
rule2.setName("Rule2");
Page<NoticeRule> rulePage = new PageImpl<>(
Arrays.asList(rule1, rule2),
PageRequest.of(0, 8, Sort.by("id").descending()),
2
);
when(noticeConfigService.getNoticeRules("Rule", 0, 8)).thenReturn(rulePage);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules")
.param("name", "Rule")
.param("pageIndex", "0")
.param("pageSize", "8")
.param("sort", "id")
.param("order", "desc")
.accept(MediaType.APPLICATION_JSON))
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.content[0].id").value(1))
.andExpect(jsonPath("$.data.content[0].name").value("Rule1"))
.andExpect(jsonPath("$.data.content[1].id").value(2))
.andExpect(jsonPath("$.data.content[1].name").value("Rule2"))
.andExpect(jsonPath("$.data.totalElements").value(2))
.andExpect(jsonPath("$.data.totalPages").value(1))
.andExpect(jsonPath("$.data.size").value(8))
.andExpect(jsonPath("$.data.number").value(0));
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules?name={name}", "tom"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
@Test
@@ -414,40 +362,22 @@ class NoticeConfigControllerTest {
}
@Test
void getTemplates() throws Exception {
void testGetTemplates() throws Exception {
// Mock the service response
NoticeTemplate template1 = new NoticeTemplate();
template1.setId(1L);
template1.setName("Template1");
NoticeTemplate template2 = new NoticeTemplate();
template2.setId(2L);
template2.setName("Template2");
List<NoticeTemplate> templates = Arrays.asList(template1, template2);
when(noticeConfigService.getNoticeTemplates(any())).thenReturn(templates);
Page<NoticeTemplate> templatePage = new PageImpl<>(
Arrays.asList(template1, template2),
PageRequest.of(0, 8, Sort.by("id").descending()),
2
);
when(noticeConfigService.getNoticeTemplates("Template", true, 0, 8)).thenReturn(templatePage);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates")
.param("name", "Template")
.param("preset", "true")
.param("pageIndex", "0")
.param("pageSize", "8")
.param("sort", "id")
.param("order", "desc")
.accept(MediaType.APPLICATION_JSON))
// Perform the GET request and verify the response
this.mockMvc.perform(get("/api/notice/templates")
.param("name", "Template"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.content[0].id").value(1))
.andExpect(jsonPath("$.data.content[0].name").value("Template1"))
.andExpect(jsonPath("$.data.content[1].id").value(2))
.andExpect(jsonPath("$.data.content[1].name").value("Template2"))
.andExpect(jsonPath("$.data.totalElements").value(2))
.andExpect(jsonPath("$.data.totalPages").value(1))
.andExpect(jsonPath("$.data.size").value(8))
.andExpect(jsonPath("$.data.number").value(0));
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.data[0].name").value("Template1"))
.andExpect(jsonPath("$.data[1].name").value("Template2"));
}
@Test
@@ -484,25 +414,5 @@ class NoticeConfigControllerTest {
verify(noticeConfigService, times(1)).sendTestMsg(noticeReceiver);
}
@Test
void getAllTemplates() throws Exception {
List<NoticeTemplate> templates = Arrays.asList(new NoticeTemplate(), new NoticeTemplate());
when(noticeConfigService.getAllNoticeTemplates()).thenReturn(templates);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates/all"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
@Test
void getAllReceivers() throws Exception {
List<NoticeReceiver> receivers = Arrays.asList(new NoticeReceiver(), new NoticeReceiver());
when(noticeConfigService.getAllNoticeReceivers()).thenReturn(receivers);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers/all"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
}
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.config.AlertSseManager;
import org.apache.hertzbeat.alert.service.NoticeConfigService;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
@@ -61,9 +60,6 @@ class AlertNoticeDispatchTest {
@Mock
private AlertNotifyHandler alertNotifyHandler;
@Mock
private AlertSseManager emitterManager;
private AlertNoticeDispatch alertNoticeDispatch;
private static final int DISPATCH_THREADS = 3;
@@ -81,8 +77,7 @@ class AlertNoticeDispatchTest {
noticeConfigService,
alertStoreHandler,
alertNotifyHandlerList,
pluginRunner,
emitterManager
pluginRunner
);
receiver = NoticeReceiver.builder()
@@ -27,6 +27,7 @@ import org.apache.hertzbeat.alert.dao.SingleAlertDao;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -38,6 +39,7 @@ import java.util.List;
/**
* Test case for {@link DbAlertStoreHandlerImpl}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class DbAlertStoreHandlerImplTest {
@@ -73,34 +75,27 @@ class DbAlertStoreHandlerImplTest {
public void testStoreNewAlert() {
String groupKey = "test-group";
groupAlert.setGroupKey(groupKey);
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(null);
SingleAlert savedSingleAlert = new SingleAlert();
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(savedSingleAlert);
GroupAlert savedGroupAlert = new GroupAlert();
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(savedGroupAlert);
dbAlertStoreHandler.store(groupAlert);
verify(singleAlertDao).save(any(SingleAlert.class));
verify(groupAlertDao).save(groupAlert);
}
@Test
public void testStoreExistingAlert() {
String groupKey = "test-group";
String fingerprint = "test-fingerprint";
groupAlert.setGroupKey(groupKey);
singleAlert.setFingerprint(fingerprint);
GroupAlert existingGroup = new GroupAlert();
existingGroup.setId(1L);
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(existingGroup);
SingleAlert existingAlert = new SingleAlert();
existingAlert.setId(1L);
existingAlert.setStatus("firing");
@@ -108,15 +103,11 @@ class DbAlertStoreHandlerImplTest {
existingAlert.setActiveAt(2000L);
existingAlert.setTriggerTimes(1);
when(singleAlertDao.findByFingerprint(fingerprint)).thenReturn(existingAlert);
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(existingAlert);
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(existingGroup);
dbAlertStoreHandler.store(groupAlert);
verify(singleAlertDao).save(any(SingleAlert.class));
verify(groupAlertDao).save(groupAlert);
assertEquals(1L, groupAlert.getId());
}
}
@@ -30,17 +30,18 @@ import java.util.Map;
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
import org.apache.hertzbeat.common.cache.CacheFactory;
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Test for {@link AlarmSilenceReduce}
* Test for AlarmSilenceReduce
*/
@Disabled
class AlarmSilenceReduceTest {
@Mock
@@ -54,23 +55,21 @@ class AlarmSilenceReduceTest {
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
CacheFactory.clearAlertSilenceCache();
when(alertSilenceDao.findAll()).thenReturn(Collections.emptyList());
alarmSilenceReduce = new AlarmSilenceReduce(alertSilenceDao, alertNoticeDispatch);
}
@Test
void whenNoSilenceRules_shouldForwardAlert() {
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.emptyList());
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
alarmSilenceReduce.silenceAlarm(alert);
verify(alertNoticeDispatch).dispatchAlarm(alert);
}
@Test
void whenMatchingSilenceRule_shouldNotForwardAlert() {
// Create silence rule
AlertSilence silenceRule = AlertSilence.builder()
.enable(true)
@@ -82,7 +81,7 @@ class AlarmSilenceReduceTest {
.times(0)
.build();
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
@@ -108,9 +107,9 @@ class AlarmSilenceReduceTest {
.times(0)
.build();
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
alarmSilenceReduce.silenceAlarm(alert);
@@ -131,7 +130,29 @@ class AlarmSilenceReduceTest {
.times(0)
.build();
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
alarmSilenceReduce.silenceAlarm(alert);
verify(alertNoticeDispatch).dispatchAlarm(alert);
verify(alertSilenceDao, never()).save(any());
}
@Test
void whenSilenceRuleDisabled_shouldForwardAlert() {
AlertSilence silenceRule = AlertSilence.builder()
.enable(false)
.matchAll(false)
.type((byte) 0)
.labels(createLabels("service", "web"))
.periodStart(LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault()))
.periodEnd(LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault()))
.times(0)
.build();
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
@@ -24,18 +24,16 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
import org.apache.hertzbeat.alert.service.impl.AlertDefineExcelImExportServiceImpl;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -44,6 +42,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
/**
* test case for {@link AlertDefineExcelImExportServiceImpl}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
public class AlertDefineExcelImExportServiceTest {
@@ -62,13 +61,15 @@ public class AlertDefineExcelImExportServiceTest {
Row row = initialSheet.createRow(1);
row.createCell(0).setCellValue("app1");
row.createCell(1).setCellValue("metric1");
row.createCell(2).setCellValue("expr1");
row.createCell(3).setCellValue(10);
row.createCell(4).setCellValue(1);
row.createCell(5).setCellValue(JsonUtil.toJson(Map.of("key", "value")));
row.createCell(6).setCellValue(JsonUtil.toJson(Map.of("key", "value")));
row.createCell(7).setCellValue("template1");
row.createCell(2).setCellValue("field1");
row.createCell(3).setCellValue(true);
row.createCell(4).setCellValue("expr1");
row.createCell(5).setCellValue(1);
row.createCell(6).setCellValue(10);
row.createCell(7).setCellValue("[{\"name\":\"tag1\",\"value\":\"value1\"}]");
row.createCell(8).setCellValue(true);
row.createCell(9).setCellValue(true);
row.createCell(10).setCellValue("template1");
ByteArrayInputStream inputStream = new ByteArrayInputStream(toByteArray(initialWorkbook));
@@ -87,12 +88,9 @@ public class AlertDefineExcelImExportServiceTest {
assertEquals("app1", alertDefineDTO.getName());
assertEquals("metric1", alertDefineDTO.getType());
assertEquals("expr1", alertDefineDTO.getExpr());
assertEquals(10, alertDefineDTO.getPeriod());
assertEquals(1, alertDefineDTO.getTimes());
assertEquals(Map.of("key", "value"), alertDefineDTO.getLabels());
assertEquals(Map.of("key", "value"), alertDefineDTO.getAnnotations());
assertEquals("template1", alertDefineDTO.getTemplate());
assertEquals(10, alertDefineDTO.getTimes());
assertTrue(alertDefineDTO.getEnable());
assertEquals("template1", alertDefineDTO.getTemplate());
}
}
@@ -105,12 +103,9 @@ public class AlertDefineExcelImExportServiceTest {
alertDefineDTO.setName("app1");
alertDefineDTO.setType("metric1");
alertDefineDTO.setExpr("expr1");
alertDefineDTO.setPeriod(10);
alertDefineDTO.setTimes(1);
alertDefineDTO.setLabels(Map.of("key", "value"));
alertDefineDTO.setAnnotations(Map.of("key", "value"));
alertDefineDTO.setTemplate("template1");
alertDefineDTO.setTimes(10);
alertDefineDTO.setEnable(true);
alertDefineDTO.setTemplate("template1");
exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
exportAlertDefineList.add(exportAlertDefineDTO);
@@ -120,26 +115,21 @@ public class AlertDefineExcelImExportServiceTest {
try (Workbook resultWorkbook = WorkbookFactory.create(new ByteArrayInputStream(outputStream.toByteArray()))) {
Sheet resultSheet = resultWorkbook.getSheetAt(0);
Row headerRow = resultSheet.getRow(0);
assertEquals("Name", headerRow.getCell(0).getStringCellValue());
assertEquals("Type", headerRow.getCell(1).getStringCellValue());
assertEquals("Expr", headerRow.getCell(2).getStringCellValue());
assertEquals("Period", headerRow.getCell(3).getStringCellValue());
assertEquals("Times", headerRow.getCell(4).getStringCellValue());
assertEquals("Labels", headerRow.getCell(5).getStringCellValue());
assertEquals("Annotations", headerRow.getCell(6).getStringCellValue());
assertEquals("Template", headerRow.getCell(7).getStringCellValue());
assertEquals("Enable", headerRow.getCell(8).getStringCellValue());
assertEquals("app", headerRow.getCell(0).getStringCellValue());
assertEquals("metric", headerRow.getCell(1).getStringCellValue());
Row dataRow = resultSheet.getRow(1);
assertEquals("app1", dataRow.getCell(0).getStringCellValue());
assertEquals("metric1", dataRow.getCell(1).getStringCellValue());
assertEquals("expr1", dataRow.getCell(2).getStringCellValue());
assertEquals(10, (int) dataRow.getCell(3).getNumericCellValue());
assertEquals(1, (int) dataRow.getCell(4).getNumericCellValue());
assertEquals(JsonUtil.toJson(Map.of("key", "value")), dataRow.getCell(5).getStringCellValue());
assertEquals(JsonUtil.toJson(Map.of("key", "value")), dataRow.getCell(6).getStringCellValue());
assertEquals("template1", dataRow.getCell(7).getStringCellValue());
assertEquals("field1", dataRow.getCell(2).getStringCellValue());
assertTrue(dataRow.getCell(3).getBooleanCellValue());
assertEquals("expr1", dataRow.getCell(4).getStringCellValue());
assertEquals(1, (int) dataRow.getCell(5).getNumericCellValue());
assertEquals(10, (int) dataRow.getCell(6).getNumericCellValue());
assertEquals("[{\"name\":\"tag1\",\"value\":\"value1\"}]", dataRow.getCell(7).getStringCellValue());
assertTrue(dataRow.getCell(8).getBooleanCellValue());
assertTrue(dataRow.getCell(9).getBooleanCellValue());
assertEquals("template1", dataRow.getCell(10).getStringCellValue());
}
}
}
@@ -152,4 +142,4 @@ public class AlertDefineExcelImExportServiceTest {
}
}
}
}
@@ -33,7 +33,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
@@ -69,7 +68,7 @@ class AlertDefineJsonImExportServiceTest {
@BeforeEach
public void setup() {
inputStream = new ByteArrayInputStream(JSON_DATA.getBytes(StandardCharsets.UTF_8));
inputStream = new ByteArrayInputStream(JSON_DATA.getBytes());
AlertDefineDTO alertDefine = new AlertDefineDTO();
alertDefine.setName("App1");
@@ -30,7 +30,6 @@ import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler;
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
@@ -55,9 +54,6 @@ class AlertDefineServiceTest {
@Mock
private AlertDefineDao alertDefineDao;
@Mock
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
@Mock
private List<AlertDefineImExportService> alertDefineImExportServiceList;
@@ -68,7 +64,6 @@ class AlertDefineServiceTest {
@BeforeEach
void setUp() {
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineDao", alertDefineDao);
ReflectionTestUtils.setField(this.alertDefineService, "periodicAlertRuleScheduler", periodicAlertRuleScheduler);
this.alertDefine = AlertDefine.builder()
.id(1L)
@@ -39,6 +39,7 @@ import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
import org.apache.hertzbeat.alert.service.impl.AlertDefineYamlImExportServiceImpl;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -47,7 +48,8 @@ import org.yaml.snakeyaml.Yaml;
/**
* test case for {@link AlertDefineYamlImExportServiceImpl}
*/
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class AlertDefineYamlImExportServiceTest {
@@ -17,27 +17,22 @@
package org.apache.hertzbeat.alert.service;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.alert.reduce.AlarmGroupReduce;
import org.apache.hertzbeat.alert.service.impl.AlertGroupConvergeServiceImpl;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anySet;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
import org.apache.hertzbeat.alert.service.impl.AlertGroupConvergeServiceImpl;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.junit.jupiter.api.Disabled;
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;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -47,9 +42,10 @@ import org.springframework.data.jpa.domain.Specification;
/**
* test case for {@link AlertGroupConvergeServiceImpl}
*/
*/
@ExtendWith(MockitoExtension.class)
@Disabled
class AlertGroupConvergeServiceTest {
@Mock
@@ -58,28 +54,22 @@ class AlertGroupConvergeServiceTest {
@InjectMocks
private AlertGroupConvergeServiceImpl alertGroupConvergeService;
@Mock
private AlarmGroupReduce alarmGroupReduce;
@Test
public void testAddAlertGroupConverge() {
AlertGroupConverge alertGroupConverge = new AlertGroupConverge();
doNothing().when(alarmGroupReduce).refreshGroupDefines(anyList());
when(alertGroupConvergeDao.save(alertGroupConverge)).thenReturn(alertGroupConverge);
alertGroupConvergeService.addAlertGroupConverge(alertGroupConverge);
verify(alertGroupConvergeDao, times(1)).save(alertGroupConverge);
verify(alarmGroupReduce, times(1)).refreshGroupDefines(anyList());
}
AlertGroupConverge alertGroupConverge = new AlertGroupConverge();
alertGroupConvergeService.addAlertGroupConverge(alertGroupConverge);
verify(alertGroupConvergeDao, times(1)).save(alertGroupConverge);
}
@Test
public void testModifyAlertGroupConverge() {
AlertGroupConverge alertGroupConverge = new AlertGroupConverge();
doNothing().when(alarmGroupReduce).refreshGroupDefines(anyList());
when(alertGroupConvergeDao.save(alertGroupConverge)).thenReturn(alertGroupConverge);
alertGroupConvergeService.modifyAlertGroupConverge(alertGroupConverge);
verify(alertGroupConvergeDao, times(1)).save(alertGroupConverge);
verify(alarmGroupReduce, times(1)).refreshGroupDefines(anyList());
}
@Test
@@ -89,6 +79,7 @@ class AlertGroupConvergeServiceTest {
AlertGroupConverge alertGroupConverge = new AlertGroupConverge();
when(alertGroupConvergeDao.findById(convergeId)).thenReturn(Optional.of(alertGroupConverge));
AlertGroupConverge result = alertGroupConvergeService.getAlertGroupConverge(convergeId);
verify(alertGroupConvergeDao, times(1)).findById(convergeId);
assertEquals(alertGroupConverge, result);
}
@@ -96,11 +87,10 @@ class AlertGroupConvergeServiceTest {
@Test
public void testDeleteAlertGroupConverges() {
doNothing().when(alertGroupConvergeDao).deleteAlertGroupConvergesByIdIn(anySet());
doNothing().when(alarmGroupReduce).refreshGroupDefines(anyList());
alertGroupConvergeService.deleteAlertGroupConverges(new HashSet<>(Arrays.asList(1L, 2L)));
verify(alertGroupConvergeDao, times(1)).deleteAlertGroupConvergesByIdIn(anySet());
verify(alarmGroupReduce, times(1)).refreshGroupDefines(anyList());
Set<Long> convergeIds = Set.of(1L, 2L, 3L);
alertGroupConvergeService.deleteAlertGroupConverges(convergeIds);
verify(alertGroupConvergeDao, times(1)).deleteAlertGroupConvergesByIdIn(convergeIds);
}
@Test
@@ -17,23 +17,25 @@
package org.apache.hertzbeat.alert.service;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.hertzbeat.alert.dao.GroupAlertDao;
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
import org.apache.hertzbeat.alert.dto.AlertSummary;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.impl.AlertServiceImpl;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -42,8 +44,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
/**
* Test case for {@link AlertService}
*/
*/
@ExtendWith(MockitoExtension.class)
@Disabled
class AlertServiceTest {
@Mock
private GroupAlertDao groupAlertDao;
@@ -80,23 +83,14 @@ class AlertServiceTest {
}
@Test
void testGetAlertsSummary() {
SingleAlert alert = new SingleAlert();
alert.setLabels(Collections.singletonMap(CommonConstants.LABEL_ALERT_SEVERITY, CommonConstants.ALERT_SEVERITY_CRITICAL));
void getAlertsSummary() {
List<SingleAlert> singleAlerts = new ArrayList<>();
singleAlerts.add(SingleAlert.builder().status("firing")
.labels(Map.of(CommonConstants.LABEL_ALERT_SEVERITY, "warning")).build());
when(singleAlertDao.querySingleAlertsByStatus(any())).thenReturn(singleAlerts);
when(singleAlertDao.count()).thenReturn(1L);
when(singleAlertDao.querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING)).thenReturn(Collections.singletonList(alert));
when(singleAlertDao.count()).thenReturn(10L);
AlertSummary summary = alertService.getAlertsSummary();
assertNotNull(summary);
assertEquals(1, summary.getPriorityCriticalNum());
assertEquals(0, summary.getPriorityEmergencyNum());
assertEquals(0, summary.getPriorityWarningNum());
assertEquals(10L, summary.getTotal());
assertEquals(90.0f, summary.getRate());
verify(singleAlertDao, times(1)).querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING);
verify(singleAlertDao, times(1)).count();
assertDoesNotThrow(() -> alertService.getAlertsSummary());
assertNotNull(alertService.getAlertsSummary());
}
}
@@ -1,547 +0,0 @@
/*
* 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.
*/
/*
* 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;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.HashMap;
import org.apache.hertzbeat.alert.service.impl.DataSourceServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import org.mockito.Mockito;
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
/**
* test case for {@link DataSourceService}
*/
class DataSourceServiceTest {
private DataSourceServiceImpl dataSourceService;
@BeforeEach
void setUp() {
dataSourceService = new DataSourceServiceImpl();
}
@Test
void calculate1() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(200.0, result.get(1).get("__value__"));
}
@Test
void calculate2() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total <= 100");
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate3() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total >= 200");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(200.0, result.get(1).get("__value__"));
}
@Test
void calculate4() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 250");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate5() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total < 100");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate6() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 200");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate7() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total <= 100)");
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate8() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"}[4m] <= 100");
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate9() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} == 100");
assertEquals(2, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
assertNull(result.get(1).get("__value__"));
}
@Test
void calculate10() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} != 100");
assertEquals(2, result.size());
assertNull(result.get(0).get("__value__"));
assertEquals(200.0, result.get(1).get("__value__"));
}
@Test
void calculate11() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 120");
assertEquals(1, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
}
@Test
void calculate12() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 50) and (node_cpu_seconds_total{mode=\"idle\"} < 120)");
assertEquals(1, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
}
@Test
void calculate13() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 and node_cpu_seconds_total{mode=\"idle\"} < 220");
assertEquals(1, result.size());
assertEquals(200.0, result.get(0).get("__value__"));
}
@Test
void calculate14() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 150) and (node_cpu_seconds_total{mode=\"idle\"} < 220)");
assertEquals(1, result.size());
assertEquals(200.0, result.get(0).get("__value__"));
}
@Test
void calculate15() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 and node_cpu_seconds_total{mode=\"idle\"} < 220");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
}
@Test
void calculate16() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
}
@Test
void calculate17() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 or node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertEquals(200.0, result.get(0).get("__value__"));
}
@Test
void calculate18() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 120");
assertEquals(1, result.size());
assertEquals(100.0, result.get(0).get("__value__"));
}
@Test
void calculate19() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
}
@Test
void calculate20() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 or node_cpu_seconds_total{mode=\"idle\"} < 320");
assertEquals(1, result.size());
assertNotNull(result.get(0).get("__value__"));
}
@Test
void calculate21() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 320");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
}
@Test
void calculate22() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNotNull(result.get(0).get("__value__"));
}
@Test
void calculate23() {
List<Map<String, Object>> prometheusData1 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
List<Map<String, Object>> prometheusData2 = List.of(
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(1, result.size());
assertNull(result.get(0).get("__value__"));
}
@Test
void calculate24() {
List<Map<String, Object>> prometheusData = List.of();
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
assertEquals(0, result.size());
}
@Test
void calculate25() {
List<Map<String, Object>> prometheusData = List.of();
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
assertEquals(0, result.size());
}
@Test
void calculate26() {
List<Map<String, Object>> prometheusData = List.of(
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
);
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
dataSourceService.setExecutors(List.of(mockExecutor));
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
assertEquals(1, result.size());
assertEquals(200.0, result.get(0).get("__value__"));
}
}
@@ -17,42 +17,33 @@
package org.apache.hertzbeat.alert.service;
import org.apache.hertzbeat.alert.dao.NoticeReceiverDao;
import org.apache.hertzbeat.alert.dao.NoticeRuleDao;
import org.apache.hertzbeat.alert.dao.NoticeTemplateDao;
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
import org.apache.hertzbeat.alert.service.impl.NoticeConfigServiceImpl;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
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;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.hertzbeat.alert.service.impl.NoticeConfigServiceImpl;
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
import org.apache.hertzbeat.alert.dao.NoticeReceiverDao;
import org.apache.hertzbeat.alert.dao.NoticeRuleDao;
import org.apache.hertzbeat.alert.dao.NoticeTemplateDao;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
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;
import org.springframework.data.jpa.domain.Specification;
/**
* Test case for {@link NoticeConfigService}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class NoticeConfigServiceTest {
@@ -67,130 +58,26 @@ class NoticeConfigServiceTest {
@InjectMocks
private NoticeConfigServiceImpl noticeConfigService;
private NoticeReceiver receiver1;
private NoticeReceiver receiver2;
private NoticeTemplate template1;
private NoticeTemplate template2;
private NoticeRule rule1;
private NoticeRule rule2;
@BeforeEach
void setUp() {
receiver1 = new NoticeReceiver();
receiver1.setId(1L);
receiver1.setName("Receiver1");
receiver2 = new NoticeReceiver();
receiver2.setId(2L);
receiver2.setName("Receiver2");
template1 = new NoticeTemplate();
template1.setId(1L);
template1.setName("Template1");
template2 = new NoticeTemplate();
template2.setId(2L);
template2.setName("Template2");
rule1 = new NoticeRule();
rule1.setId(1L);
rule1.setName("Rule1");
rule2 = new NoticeRule();
rule2.setId(2L);
rule2.setName("Rule2");
}
@Test
void getNoticeReceivers() {
Page<NoticeReceiver> receiverPage = new PageImpl<>(
Arrays.asList(receiver1, receiver2),
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
2
);
when(noticeReceiverDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(receiverPage);
Page<NoticeReceiver> result = noticeConfigService.getNoticeReceivers("Receiver", 0, 8);
assertEquals(2, result.getTotalElements());
assertEquals(1, result.getTotalPages());
assertEquals(8, result.getSize());
assertEquals(0, result.getNumber());
assertEquals(receiver1, result.getContent().get(0));
assertEquals(receiver2, result.getContent().get(1));
verify(noticeReceiverDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
}
@Test
void getAllNoticeReceivers() {
when(noticeReceiverDao.findAll()).thenReturn(Arrays.asList(receiver1, receiver2));
List<NoticeReceiver> result = noticeConfigService.getAllNoticeReceivers();
assertEquals(2, result.size());
assertEquals(receiver1, result.get(0));
assertEquals(receiver2, result.get(1));
verify(noticeReceiverDao, times(1)).findAll();
noticeConfigService.getNoticeReceivers(null);
verify(noticeReceiverDao, times(1)).findAll(any(Specification.class));
}
@Test
void getNoticeTemplates() {
Page<NoticeTemplate> templatePage = new PageImpl<>(
Arrays.asList(template1, template2),
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
2
);
when(noticeTemplateDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(templatePage);
Page<NoticeTemplate> result = noticeConfigService.getNoticeTemplates("Template", false, 0, 8);
assertEquals(2, result.getTotalElements());
assertEquals(1, result.getTotalPages());
assertEquals(8, result.getSize());
assertEquals(0, result.getNumber());
assertEquals(template1, result.getContent().get(0));
assertEquals(template2, result.getContent().get(1));
verify(noticeTemplateDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
}
@Test
void getAllNoticeTemplates() {
when(noticeTemplateDao.findAll()).thenReturn(Arrays.asList(template1, template2));
List<NoticeTemplate> result = noticeConfigService.getAllNoticeTemplates();
assert result.size() >= 2;
assertEquals(template1, result.get(result.size() - 2));
assertEquals(template2, result.get(result.size() - 1));
verify(noticeTemplateDao, times(1)).findAll();
noticeConfigService.getNoticeTemplates(null);
verify(noticeTemplateDao, times(1)).findAll(any(Specification.class));
}
@Test
void getNoticeRules() {
Page<NoticeRule> rulePage = new PageImpl<>(
Arrays.asList(rule1, rule2),
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
2
);
when(noticeRuleDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(rulePage);
Page<NoticeRule> result = noticeConfigService.getNoticeRules("Rule", 0, 8);
assertEquals(2, result.getTotalElements());
assertEquals(1, result.getTotalPages());
assertEquals(8, result.getSize());
assertEquals(0, result.getNumber());
assertEquals(rule1, result.getContent().get(0));
assertEquals(rule2, result.getContent().get(1));
verify(noticeRuleDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
noticeConfigService.getNoticeRules(null);
verify(noticeRuleDao, times(1)).findAll(any(Specification.class));
}
@Test
@@ -41,6 +41,7 @@ import javax.net.ssl.SSLException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.util.Base64;
import org.apache.hertzbeat.collector.collect.AbstractCollect;
import org.apache.hertzbeat.collector.collect.common.http.CommonHttpClient;
import org.apache.hertzbeat.collector.collect.http.promethus.AbstractPrometheusParse;
@@ -58,7 +59,6 @@ import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.apache.http.Header;
@@ -153,7 +153,7 @@ public class HttpCollectImpl extends AbstractCollect {
case DispatchConstants.PARSE_XML_PATH ->
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
case DispatchConstants.PARSE_WEBSITE ->
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime);
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime, response);
case DispatchConstants.PARSE_SITE_MAP ->
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
case DispatchConstants.PARSE_HEADER ->
@@ -222,7 +222,8 @@ public class HttpCollectImpl extends AbstractCollect {
}
private void parseResponseByWebsite(String resp, Metrics metrics, HttpProtocol http,
CollectRep.MetricsData.Builder builder, Long responseTime) {
CollectRep.MetricsData.Builder builder, Long responseTime,
CloseableHttpResponse response) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
for (String alias : metrics.getAliasFields()) {
@@ -289,10 +290,9 @@ public class HttpCollectImpl extends AbstractCollect {
long startTime = System.currentTimeMillis();
try {
HttpGet httpGet = new HttpGet(siteUrl);
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(httpGet)) {
statusCode = response.getStatusLine().getStatusCode();
EntityUtils.consume(response.getEntity());
}
CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(httpGet);
statusCode = response.getStatusLine().getStatusCode();
EntityUtils.consume(response.getEntity());
} catch (ClientProtocolException e1) {
if (e1.getCause() != null) {
errorMsg = e1.getCause().getMessage();
@@ -549,7 +549,7 @@ public class HttpCollectImpl extends AbstractCollect {
if (StringUtils.hasText(authorization.getBasicAuthUsername())
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
String authStr = authorization.getBasicAuthUsername() + SignConstants.DOUBLE_MARK + authorization.getBasicAuthPassword();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
}
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.collector.collect.http.promethus.exporter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -418,7 +419,7 @@ public class ExporterParser {
* @return true/false
*/
private boolean isValidLabelValue(String s) {
return s != null;
return s != null && s.equals(new String(s.getBytes(StandardCharsets.UTF_8)));
}
private boolean isSum(String s) {
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.net.ssl.SSLException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.util.Base64;
import org.apache.hertzbeat.collector.collect.common.http.CommonHttpClient;
import org.apache.hertzbeat.collector.collect.prometheus.parser.MetricFamily;
import org.apache.hertzbeat.collector.collect.prometheus.parser.TextParser;
@@ -43,7 +44,6 @@ import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.PrometheusProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.apache.http.HttpHeaders;
@@ -89,8 +89,9 @@ public class PrometheusAutoCollectImpl {
}
HttpContext httpContext = createHttpContext(metrics.getPrometheus());
HttpUriRequest request = createHttpRequest(metrics.getPrometheus());
try (CloseableHttpResponse response =
CommonHttpClient.getHttpClient().execute(request, httpContext)) {
try {
CloseableHttpResponse response = CommonHttpClient.getHttpClient()
.execute(request, httpContext);
int statusCode = response.getStatusLine().getStatusCode();
boolean isSuccessInvoke = defaultSuccessStatusCodes.contains(statusCode);
log.debug("http response status: {}", statusCode);
@@ -277,7 +278,7 @@ public class PrometheusAutoCollectImpl {
if (StringUtils.hasText(authorization.getBasicAuthUsername())
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
String authStr = authorization.getBasicAuthUsername() + ":" + authorization.getBasicAuthPassword();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + " " + encodedAuth);
}
}
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.collector.collect.prometheus.parser;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -299,6 +300,6 @@ public class TextParser {
* @return true/false
*/
private static boolean isValidLabelValue(String s) {
return s != null;
return s != null && s.equals(new String(s.getBytes(StandardCharsets.UTF_8)));
}
}
@@ -86,7 +86,8 @@ public class PushCollectImpl extends AbstractCollect {
HttpContext httpContext = createHttpContext(pushProtocol);
HttpUriRequest request = createHttpRequest(pushProtocol, monitorId, time);
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext)) {
try {
CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SUCCESS_CODE) {
builder.setCode(CollectRep.Code.FAIL);
@@ -20,7 +20,6 @@ package org.apache.hertzbeat.collector.collect.telnet;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -123,7 +122,7 @@ public class TelnetCollectImpl extends AbstractCollect {
return new HashMap<>(16);
}
OutputStream outputStream = telnetClient.getOutputStream();
outputStream.write(cmd.getBytes(StandardCharsets.UTF_8));
outputStream.write(cmd.getBytes());
outputStream.flush();
String result = new String(telnetClient.getInputStream().readAllBytes());
String[] lines = result.split("\n");
@@ -28,7 +28,6 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
@@ -129,16 +128,16 @@ public class WebsocketCollectImpl extends AbstractCollect {
byte[] key = generateRandomKey();
String base64Key = base64Encode(key);
String requestLine = "GET " + websocketProtocol.getPath() + " HTTP/1.1\r\n";
out.write(requestLine.getBytes(StandardCharsets.UTF_8));
out.write(requestLine.getBytes());
String hostName = InetAddress.getLocalHost().getHostAddress();
out.write(("Host:" + hostName + "\r\n").getBytes(StandardCharsets.UTF_8));
out.write("Upgrade: websocket\r\n".getBytes(StandardCharsets.UTF_8));
out.write("Connection: Upgrade\r\n".getBytes(StandardCharsets.UTF_8));
out.write("Sec-WebSocket-Version: 13\r\n".getBytes(StandardCharsets.UTF_8));
out.write("Sec-WebSocket-Extensions: chat, superchat\r\n".getBytes(StandardCharsets.UTF_8));
out.write(("Sec-WebSocket-Key: " + base64Key + "\r\n").getBytes(StandardCharsets.UTF_8));
out.write("Content-Length: 0\r\n".getBytes(StandardCharsets.UTF_8));
out.write("\r\n".getBytes(StandardCharsets.UTF_8));
out.write(("Host:" + hostName + "\r\n").getBytes());
out.write("Upgrade: websocket\r\n".getBytes());
out.write("Connection: Upgrade\r\n".getBytes());
out.write("Sec-WebSocket-Version: 13\r\n".getBytes());
out.write("Sec-WebSocket-Extensions: chat, superchat\r\n".getBytes());
out.write(("Sec-WebSocket-Key: " + base64Key + "\r\n").getBytes());
out.write("Content-Length: 0\r\n".getBytes());
out.write("\r\n".getBytes());
out.flush();
}
@@ -17,63 +17,68 @@
package org.apache.hertzbeat.collector.collect.prometheus.parser;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class OnlineParserTest {
@Disabled // Disabled due to the fact that the URL is not reachable unless you have the Prometheus server running
@Test
void parseMetrics() throws Exception {
URL url = new URL("http://localhost:9090/metrics");
InputStream inputStream = url.openStream();
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
assertNotNull(metricFamilyMap);
void parseMetrics() {
try {
URL url = new URL("http://localhost:9090/metrics");
InputStream inputStream = url.openStream();
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
System.out.println(1);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Test
void parseMetrics2() throws Exception {
String str = """
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds { quantile="0"} 2.0209e-05 321312
go_gc_duration_seconds{ quantile = "0.25" } 6.6917e-05
go_gc_duration_seconds{quantile="0.5"} -Inf
go_gc_duration_seconds{ quantile = "0.75"} +Inf
go_gc_duration_seconds{quantile="1"} NaN
go_gc_duration_seconds_sum 0.001134793 321314
go_gc_duration_seconds_count 5 43
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 32
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.21.6"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 1.5716224e+07
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 2.0707544e+07
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.457881e+06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 50438
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 4.614808e+06""";
InputStream inputStream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
assertNotNull(metricFamilyMap);
void parseMetrics2() {
try {
String str = """
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds { quantile="0"} 2.0209e-05 321312
go_gc_duration_seconds{ quantile = "0.25" } 6.6917e-05
go_gc_duration_seconds{quantile="0.5"} -Inf
go_gc_duration_seconds{ quantile = "0.75"} +Inf
go_gc_duration_seconds{quantile="1"} NaN
go_gc_duration_seconds_sum 0.001134793 321314
go_gc_duration_seconds_count 5 43
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 32
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.21.6"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 1.5716224e+07
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 2.0707544e+07
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.457881e+06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 50438
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 4.614808e+06""";
InputStream inputStream = new ByteArrayInputStream(str.getBytes());
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
System.out.println(1);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -144,25 +144,12 @@ public class CommonHttpClient {
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1, threadFactory);
scheduledExecutor.scheduleWithFixedDelay(() -> {
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(40, TimeUnit.SECONDS);
connectionManager.closeIdleConnections(100, TimeUnit.SECONDS);
}, 40L, 40L, TimeUnit.SECONDS);
// shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(CommonHttpClient::close));
} catch (Exception ignored) {}
}
public static CloseableHttpClient getHttpClient() {
return httpClient;
}
public static void close() {
try {
httpClient.close();
} catch (Exception e) {
log.error("close http client error", e);
}
}
}
@@ -94,11 +94,13 @@ public class NebulaGraphCollectImpl extends AbstractCollect {
String resp;
long responseTime;
HashMap<String, String> resultMap = new HashMap<>(64);
CloseableHttpResponse response;
HttpContext httpContext = createHttpContext(nebulaGraph.getHost(), nebulaGraph.getPort());
HttpUriRequest request = createHttpRequest(nebulaGraph.getHost(), nebulaGraph.getPort(),
nebulaGraph.getUrl(), nebulaGraph.getTimeout());
// Send an HTTP request to obtain response data
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext)) {
try {
// Send an HTTP request to obtain response data
response = CommonHttpClient.getHttpClient().execute(request, httpContext);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SUCCESS_CODE) {
builder.setCode(CollectRep.Code.FAIL);
@@ -25,12 +25,13 @@ import java.util.Arrays;
public enum AiTypeEnum {
/**
* ZhiPu
* 智普
*/
zhiPu,
/**
* sparkDesk
* 科大讯飞
*/
sparkDesk,
@@ -25,7 +25,7 @@ public interface NetworkConstants {
String KEEP_ALIVE = "Keep-Alive";
String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36";
String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36\")";
String IPV6 = "ipv6";
@@ -59,8 +59,14 @@ public interface NetworkConstants {
int READ_TIME_OUT = 6 * 1000;
int WRITE_TIME_OUT = 6 * 1000;
int CONNECT_TIME_OUT = 6 * 1000;
int CHUNK_SIZE = 8196;
int MAX_IDLE_CONNECTIONS = 20;
int KEEP_ALIVE_TIMEOUT = 30 * 1000;
int HTTP_CLIENT_CONNECTION_MANAGER_MAX_PER_ROUTE = 20;
int HTTP_CLIENT_CONNECTION_MANAGER_MAX_TOTAL = 20;
int HTTPCLIENT_KEEP_ALIVE_DURATION = 30 * 1000;
int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_WAIT_TIME_MS = 1000;
int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_IDLE_TIME_S = 30;
}
}
@@ -18,7 +18,6 @@
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
@@ -98,12 +97,10 @@ public class GroupAlert {
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtUpdate;
@Transient
@@ -18,7 +18,6 @@
package org.apache.hertzbeat.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
@@ -101,12 +100,10 @@ public class SingleAlert {
@Schema(title = "This record creation time (millisecond timestamp)")
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtCreate;
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtUpdate;
@Override
@@ -52,7 +52,7 @@ public class RocketmqProtocol implements CommonRequestProtocol, Protocol {
private String secretKey;
/**
* jsonpath parsing script
* jsonpath解析脚本
*/
private String parseScript;
@@ -22,7 +22,6 @@ package org.apache.hertzbeat.common.entity.message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -407,7 +406,7 @@ public final class CollectRep {
fieldIndex < row.getColumnsList().size()) {
String value = row.getColumns(fieldIndex);
if (value != null) {
vector.set(rowIndex, value.getBytes(StandardCharsets.UTF_8));
vector.set(rowIndex, value.getBytes());
}
}
}
@@ -90,7 +90,7 @@ public final class AesUtil {
// encode content to byte array
byte[] byteAes = cipher.doFinal(byteEncode);
// base64 encode content
return Base64.getEncoder().encodeToString(byteAes);
return new String(Base64.getEncoder().encode(byteAes), StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("aes encode content error: {}", e.getMessage(), e);
return content;
@@ -17,7 +17,6 @@
package org.apache.hertzbeat.common.util;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
@@ -40,8 +39,4 @@ public final class Base64Util {
return false;
}
}
public static String encode(String str) {
return Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
}
}
@@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.ipc.ArrowStreamWriter;
@@ -87,7 +86,7 @@ class KafkaMetricsDataDeserializerTest {
@Test
void testDeserializeWithInvalidBytes() {
byte[] invalidBytes = "invalid data".getBytes(StandardCharsets.UTF_8);
byte[] invalidBytes = "invalid data".getBytes();
assertThrows(RuntimeException.class, () -> deserializer.deserialize("", invalidBytes));
}
@@ -24,8 +24,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import java.nio.charset.StandardCharsets;
/**
* test case for {@link FileUtil}.
*/
@@ -43,12 +41,9 @@ class FileUtilTest {
@BeforeEach
void setUp() {
jsonFile = new MockMultipartFile("file", "test.json", MediaType.APPLICATION_JSON_VALUE,
"test content".getBytes(StandardCharsets.UTF_8));
excelFile = new MockMultipartFile("file", "test.xlsx", EXCEL_TYPE,
"test content".getBytes(StandardCharsets.UTF_8));
yamlFile = new MockMultipartFile("file", "test.yaml", YAML_TYPE,
"test content".getBytes(StandardCharsets.UTF_8));
jsonFile = new MockMultipartFile("file", "test.json", MediaType.APPLICATION_JSON_VALUE, "test content".getBytes());
excelFile = new MockMultipartFile("file", "test.xlsx", EXCEL_TYPE, "test content".getBytes());
yamlFile = new MockMultipartFile("file", "test.yaml", YAML_TYPE, "test content".getBytes());
emptyFile = new MockMultipartFile("file", "", null, (byte[]) null);
}
@@ -40,7 +40,6 @@ import org.springframework.util.ResourceUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.LinkedList;
@@ -104,10 +103,9 @@ public class DockerMonitorE2eTest extends AbstractCollectE2eTest {
private void sendJsonResponse(HttpExchange exchange, String response) throws IOException {
exchange.getResponseHeaders().set("Content-Type", "application/json");
final byte[] array = response.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, array.length);
exchange.sendResponseHeaders(200, response.getBytes().length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(array);
os.write(response.getBytes());
}
}
@@ -1,123 +0,0 @@
/*
* 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.collector.collect.basic.http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.collector.collect.AbstractCollectE2eTest;
import org.apache.hertzbeat.collector.collect.http.HttpCollectImpl;
import org.apache.hertzbeat.collector.util.CollectUtil;
import org.apache.hertzbeat.common.entity.job.Configmap;
import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* E2E test for HTTP monitor
*/
@Slf4j
@ExtendWith(MockitoExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HttpMonitorE2eTest extends AbstractCollectE2eTest {
private static final int MOCK_SERVER_PORT = 52376;
private static final String LOCALHOST = "127.0.0.1";
private static final String RELATIVE_PATH = "/";
private static HttpServer mockServer;
@AfterAll
public static void tearDown() {
if (mockServer != null) {
mockServer.stop(0);
}
}
@BeforeEach
public void setUp() throws Exception {
super.setUp();
// Setup collect instance
collect = new HttpCollectImpl();
// Setup mock server and endpoints
mockServer = HttpServer.create(new InetSocketAddress(MOCK_SERVER_PORT), 0);
mockServer.setExecutor(null);
mockServer.start();
mockServer.createContext(RELATIVE_PATH, exchange -> sendJsonResponse(exchange, ""));
}
private void sendJsonResponse(HttpExchange exchange, String response) throws IOException {
exchange.getResponseHeaders().set("Content-Type", "application/json");
final byte[] array = response.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, array.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(array);
}
}
@Test
public void testHttpMonitor() {
Job dockerJob = appService.getAppDefine("api");
List<Map<String, Configmap>> configmapFromPreCollectData = new LinkedList<>();
for (Metrics metricsDef : dockerJob.getMetrics()) {
metricsDef = CollectUtil.replaceCryPlaceholderToMetrics(metricsDef, configmapFromPreCollectData.size() > 0 ? configmapFromPreCollectData.get(0) : new HashMap<>());
CollectRep.MetricsData metricsData = validateMetricsCollection(metricsDef, metricsDef.getName());
configmapFromPreCollectData = CollectUtil.getConfigmapFromPreCollectData(metricsData);
}
}
@Override
protected Protocol buildProtocol(Metrics metricsDef) {
// Setup HTTP protocol
HttpProtocol protocol = new HttpProtocol();
protocol.setHost(LOCALHOST);
protocol.setUrl(RELATIVE_PATH);
protocol.setMethod("GET");
protocol.setPort(String.valueOf(MOCK_SERVER_PORT));
protocol.setParseType(metricsDef.getHttp().getParseType());
protocol.setParseScript(metricsDef.getHttp().getParseScript());
return protocol;
}
@Override
protected CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef) {
HttpProtocol protocol = (HttpProtocol) buildProtocol(metricsDef);
metrics.setHttp(protocol);
return collectMetricsData(metrics, metricsDef);
}
}
@@ -25,8 +25,6 @@ import static org.apache.hertzbeat.grafana.common.GrafanaConstants.GET_SERVICE_A
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.GRAFANA_CONFIG;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
@@ -194,9 +192,9 @@ public class ServiceAccountService {
private HttpHeaders createHeaders() {
String auth = username + SignConstants.DOUBLE_MARK + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
String authHeader = NetworkConstants.BASIC
+ SignConstants.BLANK + encodedAuth;
+ SignConstants.BLANK + new String(encodedAuth);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
@@ -27,7 +27,6 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
/**
* start up class.
@@ -40,7 +39,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
@ComponentScan(basePackages = {"org.apache.hertzbeat"})
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
@EnableAsync
public class Manager {
public static void main(String[] args) {
SpringApplication.run(Manager.class, args);
@@ -17,11 +17,10 @@
package org.apache.hertzbeat.manager.scheduler;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* List of assigned collection tasks
@@ -34,17 +33,17 @@ public class AssignJobs {
* current assign jobIds
*/
private Set<Long> jobs;
/**
* jobs to be adding
*/
private Set<Long> addingJobs;
/**
* jobs to be removed
*/
private Set<Long> removingJobs;
/**
* jobs has pinned in this collector
*/
@@ -60,15 +59,15 @@ public class AssignJobs {
public void addAssignJob(Long jobId) {
jobs.add(jobId);
}
public void addAddingJob(Long jobId) {
addingJobs.add(jobId);
}
public void addRemovingJob(Long jobId) {
removingJobs.add(jobId);
}
public void addPinnedJob(Long jobId) {
pinnedJobs.add(jobId);
}
@@ -78,19 +77,19 @@ public class AssignJobs {
jobs.addAll(jobSet);
}
}
public void addAddingJobs(Set<Long> jobSet) {
if (jobSet != null && !jobSet.isEmpty()) {
addingJobs.addAll(jobSet);
}
}
public void addRemovingJobs(Set<Long> jobSet) {
if (jobSet != null && !jobSet.isEmpty()) {
removingJobs.addAll(jobSet);
}
}
public void addPinnedJobs(Set<Long> jobSet) {
if (jobSet != null && !jobSet.isEmpty()) {
pinnedJobs.addAll(jobSet);
@@ -103,14 +102,14 @@ public class AssignJobs {
}
jobs.removeAll(jobIds);
}
public void removeAddingJobs(Set<Long> jobIds) {
if (addingJobs == null || jobIds == null || jobIds.isEmpty()) {
return;
}
addingJobs.removeAll(jobIds);
}
public void clearRemovingJobs() {
if (removingJobs == null) {
return;
@@ -119,10 +118,9 @@ public class AssignJobs {
}
/**
* Check if the corresponding jobId exists
*
* 判断是否存在对应的jobId
* @param jobId jobId
* @return true if exists and removes the jobId from assignJobs
* @return 若存在返回true,并把jobId从assignJobs remove掉
*/
public boolean containAndRemoveJob(Long jobId) {
if (jobs.isEmpty()) {
@@ -130,21 +128,21 @@ public class AssignJobs {
}
return jobs.remove(jobId);
}
public void removeAddingJob(Long jobId) {
if (addingJobs == null || jobId == null) {
return;
}
addingJobs.remove(jobId);
}
public void removeRemovingJob(Long jobId) {
if (removingJobs == null || jobId == null) {
return;
}
removingJobs.remove(jobId);
}
public void removePinnedJob(Long jobId) {
if (pinnedJobs == null || jobId == null) {
return;
@@ -153,7 +151,7 @@ public class AssignJobs {
}
/**
* Clean data
* 清理数据
*/
public void clear() {
if (!jobs.isEmpty()) {
@@ -71,7 +71,7 @@ public class CollectorServiceImpl implements CollectorService {
Specification<Collector> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isNotBlank(name)) {
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%");
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
predicate = criteriaBuilder.and(predicateName);
}
return predicate;
@@ -566,7 +566,7 @@ public class MonitorServiceImpl implements MonitorService {
List<Predicate> orList = new ArrayList<>();
if (StringUtils.isNotBlank(search)) {
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + search + "%");
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
Predicate predicateId = criteriaBuilder.like(root.get("id"), "%" + search + "%");
orList.add(predicateHost);
orList.add(predicateName);
@@ -92,9 +92,9 @@ public class TagServiceImpl implements TagService {
List<Predicate> orList = new ArrayList<>();
if (StringUtils.isNotBlank(search)) {
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
orList.add(predicateName);
Predicate predicateValue = criteriaBuilder.like(criteriaBuilder.lower(root.get("tagValue")), "%" + search.toLowerCase() + "%");
Predicate predicateValue = criteriaBuilder.like(root.get("tagValue"), "%" + search + "%");
orList.add(predicateValue);
}
Predicate[] orPredicates = new Predicate[orList.size()];
@@ -14,7 +14,7 @@
# limitations under the License.
# The monitoring type categoryservice-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
category: llm
category: server
# The monitoring type eg: linux windows tomcat mysql aws...
app: nvidia
# The monitoring i18n name
@@ -1,331 +0,0 @@
# 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.
category: bigdata
app: starrocks_be
name:
zh-CN: StarRocks BE
en-US: StarRocks BE
help:
zh-CN: Hertzbeat 对 StarRocks 数据库BE的核心监控指标(CPU使用率、磁盘状态、Compaction分数、请求数、磁盘IO利用率、磁盘容量、内存使用、网络流量等)进行测量监控。<br>您可以点击 "<i>新建 StarRocks BE</i>" 并进行配置,或者选择"<i>更多操作</i>",导入已有配置。
en-US: Hertzbeat monitoring StarRocks BE through core performance metrics such as CPU usage, disk state, compaction score, request count, disk IO utilization, disk capacity, memory usage and network traffic. You could click the "<i>New StarRocks BE Monitor</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat 對 StarRocks 資料庫BE的核心監控指標(CPU使用率、磁碟狀態、Compaction分數、請求數、磁碟IO利用率、磁碟容量、記憶體使用、網路流量等)進行量測監控。<br>您可以點擊"<i>新建 StarRocks BE</i>"並進行配寘,或者選擇"<i>更多操作</i>",導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/starrocks_be/
en-US: https://hertzbeat.apache.org/docs/help/starrocks_be/
params:
- field: host
name:
zh-CN: 目标Host
en-US: Target Host
type: host
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
type: number
range: '[0,65535]'
defaultValue: 8040
required: true
- field: timeout
name:
zh-CN: 查询超时时间
en-US: Query Timeout
type: number
required: false
hide: true
defaultValue: 6000
metrics:
- name: starrocks_be_cpu
i18n:
zh-CN: /proc/stat中的CPU使用信息
en-US: CPU Usage in /proc/stat
priority: 0
fields:
- field: mode
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_disks_state
i18n:
zh-CN: 磁盘状态
en-US: Disk State
priority: 1
fields:
- field: path
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_tablet_base_max_compaction_score
i18n:
zh-CN: Base Compaction分数
en-US: Base Compaction Score
priority: 2
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_tablet_cumulative_max_compaction_score
i18n:
zh-CN: Cumulative Compaction分数
en-US: Cumulative Compaction Score
priority: 3
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_engine_requests_total
i18n:
zh-CN: 总请求数
en-US: Total Requests
priority: 4
fields:
- field: status
type: 1
label: true
- field: type
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_max_disk_io_util_percent
i18n:
zh-CN: 最大磁盘IO利用率(%)
en-US: Max Disk IO Utilization Rate(%)
priority: 5
fields:
- field: value
type: 0
unit: '%'
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_disks_avail_capacity
i18n:
zh-CN: 磁盘可用容量
en-US: Disk Available Capacity
priority: 6
fields:
- field: path
type: 1
label: true
- field: value
type: 0
unit: MB
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_disks_data_used_capacity
i18n:
zh-CN: 磁盘已用容量
en-US: Disk Used Capacity
priority: 7
fields:
- field: path
type: 1
label: true
- field: value
type: 0
unit: MB
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_load_bytes
i18n:
zh-CN: 总导入字节数
en-US: Total Load Bytes
priority: 8
fields:
- field: value
type: 0
unit: bytes
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_load_rows
i18n:
zh-CN: 导入行数
en-US: Load Rows
priority: 9
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_process_mem_bytes
i18n:
zh-CN: 进程内存使用
en-US: Process Memory Usage
priority: 10
fields:
- field: value
type: 0
unit: MB
label: true
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_jemalloc_allocated_bytes
i18n:
zh-CN: 已分配的内存
en-US: Allocated Memory
priority: 11
fields:
- field: value
type: 0
unit: MB
label: true
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_network_receive_bytes
i18n:
zh-CN: 网络接收字节数
en-US: Network Received Bytes
priority: 12
fields:
- field: device
type: 1
label: true
- field: value
type: 0
unit: bytes
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_be_network_send_bytes
i18n:
zh-CN: 网络发送字节数
en-US: Network Sent Bytes
priority: 13
fields:
- field: device
type: 1
label: true
- field: value
type: 0
unit: bytes
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
@@ -1,407 +0,0 @@
# 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.
category: bigdata
# The monitoring type eg: linux windows tomcat mysql aws...
app: starrocks_fe
# The monitoring i18n name
name:
zh-CN: StarRocks FE
en-US: StarRocks FE
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 StarRocks 数据库FE的核心监控指标(JVM内存、线程、查询响应时间、编辑日志、加载任务、作业状态、事务状态、连接数等)进行测量监控。<br>您可以点击 "<i>新建 StarRocks FE</i>" 并进行配置,或者选择"<i>更多操作</i>",导入已有配置。
en-US: Hertzbeat monitoring StarRocks FE through core performance metrics such as JVM memory, threads, query response time, edit logs, load jobs, job status, transaction status and connections. You could click the "<i>New StarRocks FE Monitor</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat 對 StarRocks 資料庫FE的核心監控指標(JVM記憶體、執行緒、查詢響應時間、編輯日誌、載入任務、作業狀態、事務狀態、連接數等)進行量測監控。<br>您可以點擊"<i>新建 StarRocks FE</i>"並進行配寘,或者選擇"<i>更多操作</i>",導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/starrocks_fe/
en-US: https://hertzbeat.apache.org/docs/help/starrocks_fe/
# Input params define for monitoring(render web ui by the definition)
params:
# field-param field key
- field: host
# name-param field display i18n name
name:
zh-CN: 目标 Host
en-US: Target Host
# type-param field type(most mapping the html input type)
type: host
# required-true or false
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
range: '[0,65535]'
# default value
defaultValue: 8030
required: true
- field: timeout
name:
zh-CN: 查询超时时间
en-US: Query Timeout
type: number
required: false
# hide param-true or false
hide: true
defaultValue: 6000
metrics:
- name: jvm_heap_size_bytes
i18n:
zh-CN: JVM堆内存
en-US: JVM Heap Size
priority: 0
fields:
- field: type
type: 1
label: true
- field: value
type: 0
unit: MB
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: jvm_non_heap_size_bytes
i18n:
zh-CN: JVM非堆内存
en-US: JVM Non-Heap Size
priority: 1
fields:
- field: type
type: 1
label: true
- field: value
type: 0
unit: MB
units:
- value=B->MB
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: jvm_thread
i18n:
zh-CN: JVM线程
en-US: JVM Thread
priority: 2
fields:
- field: type
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_query_err
i18n:
zh-CN: 查询失败数
en-US: Failed Queries
priority: 3
fields:
- field: value
type: 0
label: true
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_query_latency_ms
i18n:
zh-CN: 查询响应时间(ms)
en-US: Query Response Time(ms)
priority: 4
fields:
- field: quantile
type: 1
label: true
- field: value
type: 0
unit: ms
label: true
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_edit_log_write
i18n:
zh-CN: 编辑日志写入速率
en-US: Edit Log Write Rate
priority: 5
fields:
- field: value
type: 0
label: true
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_load_add
i18n:
zh-CN: 新增加载任务数
en-US: New Load Jobs
priority: 6
fields:
- field: value
type: 0
label: true
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_load_finished
i18n:
zh-CN: 完成加载任务数
en-US: Finished Load Jobs
priority: 7
fields:
- field: value
type: 0
label: true
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_job
i18n:
zh-CN: FE作业状态
en-US: FE Job Status
priority: 8
fields:
- field: job
type: 1
label: true
- field: type
type: 1
label: true
- field: state
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_tablet_max_compaction_score
i18n:
zh-CN: 节点上最高的 Compaction Score
en-US: Max Compaction Score on Node
priority: 9
fields:
- field: backend
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_meta_log_count
i18n:
zh-CN: 未做 Checkpoint 的 Edit Log 数量
en-US: Edit Log Count without Checkpoint
priority: 10
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_query_total
i18n:
zh-CN: 查询总数
en-US: Total Queries
priority: 11
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_request_total
i18n:
zh-CN: 请求总数
en-US: Total Requests
priority: 12
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_txn_reject
i18n:
zh-CN: 事务拒绝数
en-US: Rejected Transactions
priority: 13
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_txn_begin
i18n:
zh-CN: 事务开始数
en-US: Beginning Transactions
priority: 14
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_txn_success
i18n:
zh-CN: 事务成功数
en-US: Successful Transactions
priority: 15
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_txn_failed
i18n:
zh-CN: 事务失败数
en-US: Failed Transactions
priority: 16
fields:
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
- name: starrocks_fe_connection_total
i18n:
zh-CN: 连接总数
en-US: Total Connections
priority: 17
fields:
- field: user
type: 1
label: true
- field: value
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: '/metrics'
timeout: ^_^timeout^_^
method: GET
parseType: prometheus
@@ -72,7 +72,6 @@ resourceRole:
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
excludedResource:
- /api/alerts/report/**===*
- /api/alert/sse/**===*
- /api/account/auth/**===*
- /api/i18n/**===get
- /api/apps/hierarchy===get
@@ -19,8 +19,6 @@ package org.apache.hertzbeat.manager.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.hertzbeat.common.constants.CommonConstants;
@@ -64,7 +62,7 @@ class PluginControllerTest {
"jarFile",
"plugin-test.jar",
"application/java-archive",
"This is the file content".getBytes(StandardCharsets.UTF_8)
"This is the file content".getBytes()
);
this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/plugin")
@@ -32,7 +32,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.hertzbeat.manager.service.impl.AbstractImExportServiceImpl;
import org.apache.hertzbeat.manager.service.impl.JsonImExportServiceImpl;
@@ -67,7 +66,7 @@ class JsonImExportServiceTest {
void testParseImport() throws IOException {
String json = "[{}]";
ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes());
AbstractImExportServiceImpl.MonitorDTO monitorDTO = new AbstractImExportServiceImpl.MonitorDTO();
@@ -28,8 +28,6 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -89,9 +87,7 @@ class PluginServiceTest {
PluginServiceImpl service = spy(pluginService);
doReturn(metadata).when(service).validateJarFile(any());
MockMultipartFile mockFile = new MockMultipartFile(
"file", "test-plugin.jar", "application/java-archive",
"plugin-content".getBytes(StandardCharsets.UTF_8));
MockMultipartFile mockFile = new MockMultipartFile("file", "test-plugin.jar", "application/java-archive", "plugin-content".getBytes());
PluginUpload pluginUpload = new PluginUpload(mockFile, "Test Plugin", true);
when(metadataDao.save(any(PluginMetadata.class))).thenReturn(new PluginMetadata());
@@ -1,118 +0,0 @@
/*
* 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.warehouse.db;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.NetworkConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* query executor for victor metrics
*/
@Component
@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true")
@Slf4j
public class GreptimePromqlQueryExecutor implements QueryExecutor {
private static final String QUERY_PATH = "/v1/prometheus/api/v1/query";
private final GreptimeProperties greptimeProperties;
private final RestTemplate restTemplate;
public GreptimePromqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) {
this.greptimeProperties = greptimeProperties;
this.restTemplate = restTemplate;
}
@Override
public List<Map<String, Object>> execute(String query) {
// http run the promql query
List<Map<String, Object>> results = new LinkedList<>();
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(greptimeProperties.username())
&& StringUtils.hasText(greptimeProperties.password())) {
String authStr = greptimeProperties.username() + ":" + greptimeProperties.password();
String encodedAuth = Base64Util.encode(authStr);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
URI uri = UriComponentsBuilder.fromHttpUrl(greptimeProperties.httpEndpoint() + QUERY_PATH)
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
.build(true).toUri();
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
&& responseEntity.getBody().getData().getResult() != null) {
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
for (PromQlQueryContent.ContentData.Content content : contents) {
Map<String, String> labels = content.getMetric();
Map<String, Object> queryResult = new HashMap<>(8);
queryResult.putAll(labels);
if (content.getValue() != null && content.getValue().length == 2) {
queryResult.put("__timestamp__", content.getValue()[0]);
queryResult.put("__value__", content.getValue()[1]);
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
List<Object> values = new LinkedList<>();
for (Object[] valueArr : content.getValues()) {
values.add(valueArr[1]);
}
queryResult.put("__value__", values);
}
results.add(queryResult);
}
}
} else {
log.error("query metrics data from greptime failed. {}", responseEntity);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return results;
}
@Override
public boolean support(String datasource) {
return "promql".equals(datasource);
}
}
@@ -1,31 +0,0 @@
/*
* 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.warehouse.db;
import java.util.List;
import java.util.Map;
/**
* query executor interface
*/
public interface QueryExecutor {
List<Map<String, Object>> execute(String query);
boolean support(String datasource);
}
@@ -1,118 +0,0 @@
/*
* 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.warehouse.db;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.NetworkConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
import org.apache.hertzbeat.warehouse.store.history.vm.VictoriaMetricsProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* query executor for victor metrics
*/
@Component
@ConditionalOnProperty(prefix = "warehouse.store.victoria-metrics", name = "enabled", havingValue = "true")
@Slf4j
public class VictoriaMetricsQueryExecutor implements QueryExecutor {
private static final String QUERY_PATH = "/api/v1/query";
private final VictoriaMetricsProperties victoriaMetricsProp;
private final RestTemplate restTemplate;
public VictoriaMetricsQueryExecutor(VictoriaMetricsProperties victoriaMetricsProp, RestTemplate restTemplate) {
this.victoriaMetricsProp = victoriaMetricsProp;
this.restTemplate = restTemplate;
}
@Override
public List<Map<String, Object>> execute(String query) {
// http run the promql query
List<Map<String, Object>> results = new LinkedList<>();
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
URI uri = UriComponentsBuilder.fromHttpUrl(victoriaMetricsProp.url() + QUERY_PATH)
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
.build(true).toUri();
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
&& responseEntity.getBody().getData().getResult() != null) {
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
for (PromQlQueryContent.ContentData.Content content : contents) {
Map<String, String> labels = content.getMetric();
Map<String, Object> queryResult = new HashMap<>(8);
queryResult.putAll(labels);
if (content.getValue() != null && content.getValue().length == 2) {
queryResult.put("__timestamp__", content.getValue()[0]);
queryResult.put("__value__", content.getValue()[1]);
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
List<Object> values = new LinkedList<>();
for (Object[] valueArr : content.getValues()) {
values.add(valueArr[1]);
}
queryResult.put("__value__", values);
}
results.add(queryResult);
}
}
} else {
log.error("query metrics data from victor-metrics failed. {}", responseEntity);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return results;
}
@Override
public boolean support(String datasource) {
return "promql".equals(datasource);
}
}
@@ -46,13 +46,13 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.constants.MetricDataConstants;
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
import org.apache.hertzbeat.common.entity.dto.Value;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.TimePeriodUtil;
import org.apache.hertzbeat.warehouse.store.history.AbstractHistoryDataStorage;
@@ -208,7 +208,7 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
if (StringUtils.hasText(greptimeProperties.username())
&& StringUtils.hasText(greptimeProperties.password())) {
String authStr = greptimeProperties.username() + ":" + greptimeProperties.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, BASIC + " " + encodedAuth);
}
Instant now = Instant.now();
@@ -40,6 +40,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.constants.MetricDataConstants;
@@ -48,7 +49,6 @@ import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
import org.apache.hertzbeat.common.entity.dto.Value;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.TimePeriodUtil;
@@ -217,7 +217,7 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
if (StringUtils.hasText(vmInsertProps.username())
&& StringUtils.hasText(vmInsertProps.password())) {
String authStr = vmInsertProps.username() + ":" + vmInsertProps.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
StringBuilder stringBuilder = new StringBuilder();
@@ -262,7 +262,8 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(vmSelectProps.username()) && StringUtils.hasText(vmSelectProps.password())) {
String authStr = vmSelectProps.username() + ":" + vmSelectProps.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)),
StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
+ SignConstants.BLANK + encodedAuth);
}
@@ -357,7 +358,8 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(vmSelectProps.username()) && StringUtils.hasText(vmSelectProps.password())) {
String authStr = vmSelectProps.username() + ":" + vmSelectProps.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)),
StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
+ SignConstants.BLANK + encodedAuth);
}
@@ -38,6 +38,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.constants.MetricDataConstants;
@@ -46,7 +47,6 @@ import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
import org.apache.hertzbeat.common.entity.dto.Value;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.TimePeriodUtil;
@@ -109,7 +109,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + SignConstants.DOUBLE_MARK + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + " " + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
@@ -222,7 +222,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
StringBuilder stringBuilder = new StringBuilder();
@@ -262,7 +262,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
@@ -356,7 +356,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
+ SignConstants.BLANK + encodedAuth);
}
-112
View File
@@ -1,112 +0,0 @@
---
id: starrocks_be
title: Monitoring StarRocks Database BE Monitoring
sidebar_label: StarRocks Database BE
keywords: [Open Source Monitoring System, Open Source Database Monitoring, StarRocks Database BE Monitoring]
---
> Collect and monitor general performance metrics for StarRocks database BE. Supports StarRocks 2.4.0 and later versions.
**Protocol: HTTP**
### Pre-monitoring Operations
Check the `be/conf/be.conf` file to obtain the value of the `http_port` configuration item, which is used for monitoring.
### Configuration Parameters
| Parameter Name | Parameter Description |
|---------------|----------------------|
| Monitor Host | The monitored target's IPV4, IPV6, or domain name. Note: Without the protocol header (e.g., https://, http://) |
| Task Name | A unique name identifying this monitoring task |
| Port | The port provided by the database to the outside, default is 8040, get the value of the `http_port` configuration item |
| Query Timeout | The timeout for the connection to not respond, in milliseconds, default is 6000 milliseconds |
| Description | Additional notes and descriptions for this monitoring task |
### Collection Metrics
> For more metrics, please refer to the StarRocks official documentation: [General Monitoring Metrics](https://docs.mirrorship.cn/docs/administration/management/monitoring/metrics/).
#### Metric Set: starrocks_be_cpu
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | CPU usage information returned by /proc/stat |
#### Metric Set: starrocks_be_disks_state
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | State of each disk. 1 indicates that the disk is in use, and 0 indicates that the disk is not in use |
#### Metric Set: starrocks_be_tablet_base_max_compaction_score
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Highest base compaction score of tablets in this BE |
#### Metric Set: starrocks_be_tablet_cumulative_max_compaction_score
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Highest cumulative compaction score of tablets in this BE |
#### Metric Set: starrocks_be_engine_requests_total
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Total count of all types of requests, including CREATE TABLE, Publish Version and Tablet Clone |
#### Metric Set: starrocks_be_max_disk_io_util_percent
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | % | Maximum disk I/O utilization percentage |
#### Metric Set: starrocks_be_disks_avail_capacity
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | MB | Available capacity of a specific disk |
#### Metric Set: starrocks_be_disks_data_used_capacity
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | MB | Used capacity of each disk (represented by a storage path) |
#### Metric Set: starrocks_be_load_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Bytes | Total loaded bytes |
#### Metric Set: starrocks_be_load_rows
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Total loaded rows |
#### Metric Set: starrocks_be_process_mem_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | MB | Memory used by this process |
#### Metric Set: starrocks_be_jemalloc_allocated_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | MB | Total number of bytes allocated by jemalloc |
#### Metric Set: starrocks_be_network_receive_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Bytes | Total bytes received via network |
#### Metric Set: starrocks_be_network_send_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Bytes | Number of bytes sent over the network |
-136
View File
@@ -1,136 +0,0 @@
---
id: starrocks_fe
title: Monitoring StarRocks Database FE Monitoring
sidebar_label: StarRocks Database FE
keywords: [Open Source Monitoring System, Open Source Database Monitoring, StarRocks Database FE Monitoring]
---
> Collect and monitor general performance metrics for StarRocks database FE. Supports StarRocks 2.4.0 and later versions.
**Protocol: HTTP**
### Pre-monitoring Operations
Check the `fe/conf/fe.conf` file to obtain the value of the `http_port` configuration item, which is used for monitoring.
### Configuration Parameters
| Parameter Name | Parameter Description |
|---------------|----------------------|
| Monitor Host | The monitored target's IPV4, IPV6, or domain name. Note: Without the protocol header (e.g., https://, http://) |
| Task Name | A unique name identifying this monitoring task |
| Port | The port provided by the database to the outside, default is 8030, get the value of the `http_port` configuration item |
| Query Timeout | The timeout for the connection to not respond, in milliseconds, default is 6000 milliseconds |
| Description | Additional notes and descriptions for this monitoring task |
### Collection Metrics
> For more metrics, please refer to the StarRocks official documentation: [General Monitoring Metrics](https://docs.mirrorship.cn/docs/administration/management/monitoring/metrics/).
#### Metric Set: jvm_heap_size_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Byte | JVM heap memory size |
#### Metric Set: jvm_non_heap_size_bytes
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Byte | JVM non-heap memory size |
#### Metric Set: jvm_thread
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of JVM threads |
#### Metric Set: starrocks_fe_query_err
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of failed queries |
#### Metric Set: starrocks_fe_query_latency_ms
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | ms | Query response time |
#### Metric Set: starrocks_fe_edit_log_write
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | Byte/s | Write speed of FE edit log |
#### Metric Set: starrocks_fe_load_add
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of new load jobs |
#### Metric Set: starrocks_fe_load_finished
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of finished load jobs |
#### Metric Set: starrocks_fe_job
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | FE job status |
#### Metric Set: starrocks_fe_tablet_max_compaction_score
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Indicates the highest Compaction Score on each BE node |
#### Metric Set: starrocks_fe_meta_log_count
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | The number of Edit Logs without a checkpoint. A value within 100000 is reasonable |
#### Metric Set: starrocks_fe_query_total
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Total number of queries |
#### Metric Set: starrocks_fe_request_total
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Total number of requests |
#### Metric Set: starrocks_fe_txn_reject
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of rejected transactions |
#### Metric Set: starrocks_fe_txn_begin
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of beginning transactions |
#### Metric Set: starrocks_fe_txn_success
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of successful transactions |
#### Metric Set: starrocks_fe_txn_failed
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Number of failed transactions |
#### Metric Set: starrocks_fe_connection_total
| Metric Name | Metric Unit | Metric Help Description |
|------------|-------------|------------------------|
| value | None | Total number of FE connections |
@@ -1,112 +0,0 @@
---
id: starrocks_be
title: 监控:StarRocks数据库BE监控
sidebar_label: StarRocks数据库BE
keywords: [开源监控系统, 开源数据库监控, StarRocks数据库BE监控]
---
> 对StarRocks数据库BE的通用性能指标进行采集监控。支持 StarRocks 2.4.0 及其之后版本。
**使用协议:HTTP**
### 监控前操作
查看 `be/conf/be.conf` 文件,获取 `http_port` 配置项的值,该值用作监控使用。
### 配置参数
| 参数名称 | 参数帮助描述 |
|--------|------------|
| 监控Host | 被监控的对端IPV4,IPV6或域名。注意⚠️不带协议头(eg: https://, http://) |
| 任务名称 | 标识此监控的名称,名称需要保证唯一性 |
| 端口 | 数据库对外提供的端口,默认为8040,取值自 `http_port` 配置项的值 |
| 查询超时时间 | 设置连接未响应的超时时间,单位ms毫秒,默认6000毫秒 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
### 采集指标
> 更多指标请参考StarRocks官网:[通用监控指标](https://docs.mirrorship.cn/zh/docs/administration/management/monitoring/metrics/)。
#### 指标集合:starrocks_be_cpu
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | /proc/stat 返回的 CPU 使用信息 |
#### 指标集合:starrocks_be_disks_state
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 磁盘的状态。1 表示磁盘正在使用,0 表示磁盘未被使用 |
#### 指标集合:starrocks_be_tablet_base_max_compaction_score
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 此 BE 中各 Tablet 的最高 Base Compaction Score |
#### 指标集合:starrocks_be_tablet_cumulative_max_compaction_score
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 此 BE 中各 Tablet 的最高 Cumulative Compaction Score |
#### 指标集合:starrocks_be_engine_requests_total
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | BE 和 FE 之间各种请求的总数,包括 CREATE TABLE、Publish Version 和 Tablet Clone |
#### 指标集合:starrocks_be_max_disk_io_util_percent
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | % | 最大磁盘 I/O 利用率百分比 |
#### 指标集合:starrocks_be_disks_avail_capacity
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | MB | 磁盘的可用容量 |
#### 指标集合:starrocks_be_disks_data_used_capacity
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | MB | 每个磁盘(由存储路径表示)的已用容量 |
#### 指标集合:starrocks_be_load_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节 | 总导入字节数 |
#### 指标集合:starrocks_be_load_rows
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 总导入行数 |
#### 指标集合:starrocks_be_process_mem_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | MB | 此进程使用的内存 |
#### 指标集合:starrocks_be_jemalloc_allocated_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | MB | jemalloc 已分配的总字节数 |
#### 指标集合:starrocks_be_network_receive_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节 | 通过网络接收的总字节数 |
#### 指标集合:starrocks_be_network_send_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节 | 通过网络发送的字节数 |
@@ -1,136 +0,0 @@
---
id: starrocks_fe
title: 监控:StarRocks数据库FE监控
sidebar_label: StarRocks数据库FE
keywords: [开源监控系统, 开源数据库监控, StarRocks数据库FE监控]
---
> 对StarRocks数据库FE的通用性能指标进行采集监控。支持 StarRocks 2.4.0 及其之后版本。
**使用协议:HTTP**
### 监控前操作
查看 `fe/conf/fe.conf` 文件,获取 `http_port` 配置项的值,该值用作监控使用。
### 配置参数
| 参数名称 | 参数帮助描述 |
|--------|------------|
| 监控Host | 被监控的对端IPV4,IPV6或域名。注意⚠️不带协议头(eg: https://, http://) |
| 任务名称 | 标识此监控的名称,名称需要保证唯一性 |
| 端口 | 数据库对外提供的端口,默认为8030,取值自 `http_port` 配置项的值 |
| 查询超时时间 | 设置连接未响应的超时时间,单位ms毫秒,默认6000毫秒 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
### 采集指标
> 更多指标请参考StarRocks官网:[通用监控指标](https://docs.mirrorship.cn/zh/docs/administration/management/monitoring/metrics/)。
#### 指标集合:jvm_heap_size_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节 | JVM堆内存大小 |
#### 指标集合:jvm_non_heap_size_bytes
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节 | JVM非堆内存大小 |
#### 指标集合:jvm_thread
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | JVM线程数量 |
#### 指标集合:starrocks_fe_query_err
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 查询失败数量 |
#### 指标集合:starrocks_fe_query_latency_ms
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 毫秒 | 查询响应时间 |
#### 指标集合:starrocks_fe_edit_log_write
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 字节/秒 | FE编辑日志写入速率 |
#### 指标集合:starrocks_fe_load_add
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 新增加载任务数量 |
#### 指标集合:starrocks_fe_load_finished
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 完成加载任务数量 |
#### 指标集合:starrocks_fe_job
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | FE作业状态 |
#### 指标集合:starrocks_fe_tablet_max_compaction_score
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 节点上最高的 Compaction Score |
#### 指标集合:starrocks_fe_meta_log_count
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 未做 Checkpoint 的 Edit Log 数量,该值在 100000 以内为合理 |
#### 指标集合:starrocks_fe_query_total
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 查询总数 |
#### 指标集合:starrocks_fe_request_total
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 请求总数 |
#### 指标集合:starrocks_fe_txn_reject
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 事务拒绝数 |
#### 指标集合:starrocks_fe_txn_begin
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 事务开始数 |
#### 指标集合:starrocks_fe_txn_success
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 事务成功数 |
#### 指标集合:starrocks_fe_txn_failed
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | 事务失败数 |
#### 指标集合:starrocks_fe_connection_total
| 指标名称 | 指标单位 | 指标帮助描述 |
|---------|---------|------------|
| value | 无 | FE连接总数 |
-2
View File
@@ -189,8 +189,6 @@
"help/clickhouse",
"help/doris_be",
"help/doris_fe",
"help/starrocks_be",
"help/starrocks_fe",
"help/elasticsearch",
"help/flink",
"help/flink_on_yarn",
File diff suppressed because it is too large Load Diff
+9 -166
View File
@@ -1635,7 +1635,7 @@
"@docusaurus/theme-search-algolia" "2.3.1"
"@docusaurus/types" "2.3.1"
"@docusaurus/react-loadable@5.5.2":
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
@@ -2248,11 +2248,6 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sindresorhus/merge-streams@^2.1.0":
version "2.3.0"
resolved "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958"
integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==
"@slorber/static-site-generator-webpack-plugin@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3"
@@ -3336,13 +3331,6 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.1.1"
browserslist@^4.0.0, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3, browserslist@^4.21.2:
version "4.21.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf"
@@ -4268,11 +4256,6 @@ dns-packet@^5.2.2:
dependencies:
"@leichtgewicht/ip-codec" "^2.0.1"
docusaurus-plugin-matomo@^0.0.8:
version "0.0.8"
resolved "https://registry.npmmirror.com/docusaurus-plugin-matomo/-/docusaurus-plugin-matomo-0.0.8.tgz#eb11cf9d0f145a27ed5b9dec3835db01fae71060"
integrity sha512-YSaDdjxI7uN1FuNLSGrQy4BmDEXwwyL6vac7GfmS7wFBLck/Ias5jHqENPBHXe36m2cYzyesGPQL1EsS9vanwA==
dom-converter@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@@ -4478,11 +4461,6 @@ entities@^4.2.0, entities@^4.3.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==
entities@^4.4.0:
version "4.5.0"
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -4716,17 +4694,6 @@ fast-glob@^3.2.11, fast-glob@^3.2.9:
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-glob@^3.3.2:
version "3.3.3"
resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.8"
fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -4812,13 +4779,6 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
@@ -5085,18 +5045,6 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globby@14.0.1:
version "14.0.1"
resolved "https://registry.npmmirror.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b"
integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==
dependencies:
"@sindresorhus/merge-streams" "^2.1.0"
fast-glob "^3.3.2"
ignore "^5.2.4"
path-type "^5.0.0"
slash "^5.1.0"
unicorn-magic "^0.1.0"
globby@^11.0.1, globby@^11.0.4, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
@@ -5459,11 +5407,6 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
ignore@^5.2.4:
version "5.3.2"
resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
image-size@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486"
@@ -5895,13 +5838,6 @@ joi@^17.6.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
js-yaml@^3.13.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
@@ -5910,6 +5846,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@@ -5957,11 +5900,6 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
jsonc-parser@3.2.1:
version "3.2.1"
resolved "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@@ -6020,13 +5958,6 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
linkify-it@^5.0.0:
version "5.0.0"
resolved "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421"
integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
dependencies:
uc.micro "^2.0.0"
loader-runner@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
@@ -6158,48 +6089,6 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
markdown-it@14.1.0:
version "14.1.0"
resolved "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45"
integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
dependencies:
argparse "^2.0.1"
entities "^4.4.0"
linkify-it "^5.0.0"
mdurl "^2.0.0"
punycode.js "^2.3.1"
uc.micro "^2.1.0"
markdownlint-cli2-formatter-default@0.0.4:
version "0.0.4"
resolved "https://registry.npmmirror.com/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz#81e26b0a50409c0357c6f0d38d8246946b236fab"
integrity sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg==
markdownlint-cli2@^0.13.0:
version "0.13.0"
resolved "https://registry.npmmirror.com/markdownlint-cli2/-/markdownlint-cli2-0.13.0.tgz#691cab01994295b4b8c87aa0485c0b1e0f792289"
integrity sha512-Pg4nF7HlopU97ZXtrcVISWp3bdsuc5M0zXyLp2/sJv2zEMlInrau0ZKK482fQURzVezJzWBpNmu4u6vGAhij+g==
dependencies:
globby "14.0.1"
js-yaml "4.1.0"
jsonc-parser "3.2.1"
markdownlint "0.34.0"
markdownlint-cli2-formatter-default "0.0.4"
micromatch "4.0.5"
markdownlint-micromark@0.1.9:
version "0.1.9"
resolved "https://registry.npmmirror.com/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz#4876996b60d4dceb3a02f4eee2d3a366eb9569fa"
integrity sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==
markdownlint@0.34.0:
version "0.34.0"
resolved "https://registry.npmmirror.com/markdownlint/-/markdownlint-0.34.0.tgz#bbc2047c952d1644269009a69ba227ed597b23fa"
integrity sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==
dependencies:
markdown-it "14.1.0"
markdownlint-micromark "0.1.9"
mdast-squeeze-paragraphs@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97"
@@ -6248,11 +6137,6 @@ mdurl@^1.0.0:
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
mdurl@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0"
integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -6290,7 +6174,7 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@@ -6298,14 +6182,6 @@ micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
micromatch@^4.0.8:
version "4.0.8"
resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
braces "^3.0.3"
picomatch "^2.3.1"
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@@ -6847,11 +6723,6 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
path-type@^5.0.0:
version "5.0.0"
resolved "https://registry.npmmirror.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8"
integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@@ -7269,11 +7140,6 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode.js@^2.3.1:
version "2.3.1"
resolved "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7"
integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@@ -7480,14 +7346,6 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"
"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
dependencies:
"@types/react" "*"
prop-types "^15.6.2"
react-resizable@^1.8.0:
version "1.11.1"
resolved "https://registry.npmmirror.com/react-resizable/-/react-resizable-1.11.1.tgz#02ca6850afa7a22c1b3e623e64aef71ee252af69"
@@ -8313,11 +8171,6 @@ slash@^4.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
slash@^5.1.0:
version "5.1.0"
resolved "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
slick-carousel@1.8.1:
version "1.8.1"
resolved "https://registry.npmmirror.com/slick-carousel/-/slick-carousel-1.8.1.tgz#a4bfb29014887bb66ce528b90bd0cda262cc8f8d"
@@ -8847,11 +8700,6 @@ ua-parser-js@^0.7.30:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
uc.micro@^2.0.0, uc.micro@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee"
integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@@ -8900,11 +8748,6 @@ unicode-property-aliases-ecmascript@^2.0.0:
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8"
integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==
unicorn-magic@^0.1.0:
version "0.1.0"
resolved "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4"
integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==
unified@9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8"
@@ -115,7 +115,7 @@ public class AccountController {
@PostMapping("/register")
@Transactional
public ResponseEntity<Message<String>> accountRegister(@RequestBody @Validated SignUpDto account) {
// TODO Let the front-end pass the plaintext password here first, and then change it to an encrypted password later
//此处先让前端传递明文密码后续改为加密密码
if (accountService.registerAccount(account)) {
Long authUser = roleService.getRoleIdByCode("role_user");
@@ -49,7 +49,7 @@ public class LocalFileStorageServiceImpl implements FileStorageService {
// Define the file path
File localFile = new File(directory,fileName);
try (FileOutputStream outputStream = new FileOutputStream(localFile)) {
outputStream.write(file.getBytes(StandardCharsets.UTF_8));
outputStream.write(file.getBytes());
}
// Return the file URL (local file path in this case)
@@ -70,38 +70,38 @@ public class SurenessFilterExample implements Filter {
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, authorization,x-requested-with, *");
try {
// Retrieve the filters attribute from the filter chain
//获取过滤器链中的filters属性
Field filtersField = filterChain.getClass().getDeclaredField("filters");
// Reflection setting attribute reachable
//反射设置属性可达
filtersField.setAccessible(true);
// Get the value of the filters attribute
//获取filters属性值
FilterConfig[] filters = (FilterConfig[]) filtersField.get(filterChain);
// Skip the record of the number of filters
//跳过过滤器个数记录
int k = 0;
// Traverse all filters
//遍历所有过滤器
for (int i = 0; i < filters.length; i++) {
if (filters[i] != null) {
// Get the filterDef attribute of the filter
//获取过滤器filterDef属性
Field filterDefField = filters[i].getClass().getDeclaredField("filterDef");
filterDefField.setAccessible(true);
// Get the class of the filter
//获取filter的class
Field filterClassField = filterDefField.get(filters[i]).getClass().getDeclaredField("filterClass");
filterClassField.setAccessible(true);
String filterClass = (String) filterClassField.get(filterDefField.get(filters[i]));
String FILTER_REFERENCE1 = "com.usthe.sureness.configuration.SurenessJakartaServletFilter";
String FILTER_REFERENCE2 = "org.apache.tomcat.websocket.server.WsFilter";
// Skip specified filter processing
//跳过指定过滤器处理
if (FILTER_REFERENCE1.equals(filterClass)||FILTER_REFERENCE2.equals(filterClass)) {
filters[i] = null;
k++;
break;
}
// Attribute can be disabled
//属性可达关闭
filterClassField.setAccessible(false);
filterDefField.setAccessible(false);
}
}
// Re assign the filter array and adjust it after removing the specified filter
//过滤器数组重新赋值调整移除指定过滤器后过滤器数组
int index = 0;
for (int i = 0; i < filters.length; i++) {
if (index == 0 && filters[i] == null) {
@@ -113,7 +113,7 @@ public class SurenessFilterExample implements Filter {
index = 0;
}
}
// Reassignment of n value
//n值重新赋值
filtersField.setAccessible(false);
Field n = filterChain.getClass().getDeclaredField("n");
n.setAccessible(true);
@@ -39,7 +39,7 @@ public class DatabasePathTreeProvider implements PathTreeProvider {
@Override
public Set<String> providePathData() {
// Read path information from the database and retrieve all path information with a status of 1, which is normal
// 从数据库中读取出path信息取出所有状态为1即正常的path信息
Set<String> pathSet = SurenessCommonUtil.attachContextPath(getContextPath(), resourceService.getAllEnableResourcePath());
return pathSet;
@@ -47,7 +47,7 @@ public class DatabasePathTreeProvider implements PathTreeProvider {
@Override
public Set<String> provideExcludedResource() {
// Read path information from the database and retrieve all path information with a status of 9, which is disabled
// 从数据库中读取出path信息取出所有状态为9即禁用的path信息
Set<String> exlResourceSet = SurenessCommonUtil.attachContextPath(getContextPath(), resourceService.getAllDisableResourcePath());
return exlResourceSet;
}
@@ -132,7 +132,7 @@ class VersionControllerTest {
when(versionService.upload(any(), any())).thenReturn(true);
mockMvc.perform(multipart("/version/upload")
.file("file", "dummy content".getBytes(StandardCharsets.UTF_8))
.file("file", "dummy content".getBytes())
.param("templateDto", templateDtoJson))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
@@ -132,7 +132,7 @@ class VersionDOControllerTest {
when(versionService.upload(any(), any())).thenReturn(true);
mockMvc.perform(multipart("/version/upload")
.file("file", "dummy content".getBytes(StandardCharsets.UTF_8))
.file("file", "dummy content".getBytes())
.param("templateDto", templateDtoJson))
.andExpect(status().isOk())
.andExpect(jsonPath("$.msg").value("upload success"));
@@ -7,7 +7,6 @@ import { NzNotificationService } from 'ng-zorro-antd/notification';
import { finalize } from 'rxjs/operators';
import { Mute } from '../../../pojo/Mute';
import { SingleAlert } from '../../../pojo/SingleAlert';
import { AlertSoundService } from '../../../service/alert-sound.service';
import { AlertService } from '../../../service/alert.service';
import { GeneralConfigService } from '../../../service/general-config.service';
@@ -123,7 +122,6 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
private previousCount = 0;
// default to mute status
mute: Mute = { mute: true };
private eventSource!: EventSource;
constructor(
private router: Router,
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService,
@@ -156,7 +154,9 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
}
);
this.loadData();
this.initSSEConnection();
this.refreshInterval = setInterval(() => {
this.loadData();
}, 10000); // every 10 seconds refresh the tabs
}
ngOnDestroy() {
@@ -207,6 +207,7 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
let item = {
id: alert.id,
avatar: '/assets/img/notification.svg',
// title: `${alert.tags?.monitorName}--${this.i18nSvc.fanyi(`alert.severity.${alert.severity}`)}`,
title: alert.content,
datetime: new Date(alert.activeAt).toLocaleString(),
color: 'blue',
@@ -216,6 +217,11 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
list.push(item);
});
this.data = this.updateNoticeData(list);
if (page.totalElements > this.previousCount && !this.mute.mute) {
this.alertSound.playAlertSound(this.i18nSvc.currentLang);
}
this.previousCount = page.totalElements;
this.count = page.totalElements;
} else {
console.warn(message.msg);
@@ -285,36 +291,4 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
}
});
}
private initSSEConnection(): void {
const sseUrl = '/api/alert/sse/subscribe';
this.eventSource = new EventSource(sseUrl);
this.eventSource.addEventListener('ALERT_EVENT', (evt: MessageEvent) => {
let list: any[] = [];
let alert: SingleAlert = JSON.parse(evt.data);
let item = {
id: alert.id,
avatar: '/assets/img/notification.svg',
// title: `${alert.tags?.monitorName}--${this.i18nSvc.fanyi(`alert.severity.${alert.severity}`)}`,
title: alert.content,
datetime: new Date(alert.activeAt).toLocaleString(),
color: 'blue',
status: alert.status,
type: this.i18nSvc.fanyi('dashboard.alerts.title-no')
};
list.push(item);
this.data = this.updateNoticeData(list);
if (!this.mute.mute) {
this.alertSound.playAlertSound(this.i18nSvc.currentLang);
}
this.cdr.detectChanges();
});
this.eventSource.onerror = error => {
console.error('SSE connection error:', error);
setTimeout(() => this.initSSEConnection(), 3000);
};
}
}
@@ -29,6 +29,10 @@
<app-toolbar>
<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
@@ -59,13 +63,7 @@
</app-toolbar>
<div class="alert-cards">
<nz-card
*ngFor="let group of groupAlerts"
class="alert-card"
[class.new-alert]="group.isNew"
[class]="'status-' + group.status"
[nzBordered]="false"
>
<nz-card *ngFor="let group of groupAlerts" class="alert-card" [class]="'status-' + group.status" [nzBordered]="false">
<!-- Alert Group Header -->
<div class="alert-header">
<div class="alert-info">
@@ -1,3 +1,4 @@
/* 调整工具栏布局 */
:host ::ng-deep app-toolbar {
.center-content {
display: flex;
@@ -281,91 +282,3 @@
border-left-color: #faad14;
}
}
@keyframes slideInFromRight {
0% {
transform: translate3d(120%, 0, 0) scale(0.95) rotate(3deg);
opacity: 0;
filter: blur(2px);
box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18);
}
50% {
opacity: 1;
filter: blur(0);
transform: translate3d(-5%, 0, 0) scale(1) rotate(-1deg);
}
75% {
transform: translate3d(2%, 0, 0) scale(1) rotate(0.5deg);
}
100% {
transform: translate3d(0, 0, 0) scale(1) rotate(0deg);
box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.1);
}
}
.alert-card {
position: relative;
transition:
transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94),
opacity 0.5s ease-out,
box-shadow 0.3s ease;
will-change: transform, opacity;
&:hover {
transform: translateY(-2px) scale(1.005);
box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15);
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%
);
opacity: 0;
animation: slideGlow 1s cubic-bezier(0.4, 0, 0.2, 1);
}
&.new-alert {
animation:
slideInFromRight 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards,
cardLanding 0.6s 0.3s ease-out forwards;
}
}
@keyframes slideGlow {
0% {
transform: translateX(-100%);
opacity: 0;
}
50% {
opacity: 0.6;
}
100% {
transform: translateX(100%);
opacity: 0;
}
}
@keyframes cardLanding {
0% { transform: translateY(0); }
50% { transform: translateY(-8px); }
80% { transform: translateY(2px); }
100% { transform: translateY(0); }
}
.alert-card:nth-child(1) { animation-delay: 0.1s; }
.alert-card:nth-child(2) { animation-delay: 0.15s; }
.alert-card:nth-child(3) { animation-delay: 0.2s; }
.alert-card:nth-child(n+4) { animation-delay: 0.25s; }
.alert-cards {
perspective: 1200px;
}
@@ -17,7 +17,7 @@
* under the License.
*/
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Component, Inject, OnInit } from '@angular/core';
import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN } from '@delon/theme';
import { NzModalService } from 'ng-zorro-antd/modal';
@@ -26,15 +26,12 @@ import { NzNotificationService } from 'ng-zorro-antd/notification';
import { GroupAlert } from '../../../pojo/GroupAlert';
import { AlertService } from '../../../service/alert.service';
interface ExtendedGroupAlert extends GroupAlert {
isNew?: boolean;
}
@Component({
selector: 'app-alert-center',
templateUrl: './alert-center.component.html',
styleUrl: './alert-center.component.less'
})
export class AlertCenterComponent implements OnInit, OnDestroy {
export class AlertCenterComponent implements OnInit {
constructor(
private notifySvc: NzNotificationService,
private modal: NzModalService,
@@ -45,109 +42,18 @@ export class AlertCenterComponent implements OnInit, OnDestroy {
pageIndex: number = 1;
pageSize: number = 8;
total: number = 0;
groupAlerts: ExtendedGroupAlert[] = [];
groupAlerts!: GroupAlert[];
tableLoading: boolean = false;
checkedAlertIds = new Set<number>();
filterStatus!: string;
filterContent: string | undefined;
private eventSource!: EventSource;
ngOnInit(): void {
this.loadAlertsTable();
this.initSSESubscription();
}
ngOnDestroy(): void {
if (this.eventSource) {
this.eventSource.close();
}
}
// Initialize SSE subscription for real-time alerts
private initSSESubscription(): void {
this.eventSource = new EventSource('/api/alert/sse/subscribe');
this.eventSource.addEventListener('ALERT_EVENT', (evt: MessageEvent) => {
try {
const newAlert: GroupAlert = JSON.parse(evt.data);
this.updateAlertList(newAlert);
} catch (error) {
console.error('Error parsing SSE data:', error);
}
});
// Handle SSE errors
this.eventSource.onerror = error => {
console.error('SSE connection error:', error);
this.eventSource.close();
};
}
private updateAlertList(newAlert: GroupAlert): void {
const extendedAlert: ExtendedGroupAlert = {
...newAlert,
isNew: true
};
if (!extendedAlert.alerts) {
extendedAlert.alerts = [];
}
const matchesFilter = this.checkAlertMatchesFilter(extendedAlert);
if (!matchesFilter) {
return;
}
const existingIndex = this.groupAlerts.findIndex(a => a.id === extendedAlert.id);
if (existingIndex === -1) {
this.groupAlerts = [extendedAlert, ...this.groupAlerts];
this.total += 1;
setTimeout(() => {
const index = this.groupAlerts.findIndex(a => a.id === extendedAlert.id);
if (index !== -1) {
this.groupAlerts[index].isNew = false;
// 触发变更检测
this.groupAlerts = [...this.groupAlerts];
}
}, 1000);
} else {
this.groupAlerts[existingIndex] = {
...extendedAlert,
isNew: true
};
setTimeout(() => {
if (this.groupAlerts[existingIndex]) {
this.groupAlerts[existingIndex].isNew = false;
this.groupAlerts = [...this.groupAlerts];
}
}, 1000);
this.groupAlerts = [...this.groupAlerts];
}
}
private checkAlertMatchesFilter(alert: ExtendedGroupAlert): boolean {
if (this.filterStatus && alert.status !== this.filterStatus) {
return false;
}
if (this.filterContent) {
const searchContent = this.filterContent.toLowerCase();
const hasMatchingContent = alert.alerts?.some(singleAlert => singleAlert.content?.toLowerCase().includes(searchContent));
const hasMatchingLabels = Object.entries(alert.groupLabels || {}).some(
([key, value]) => key.toLowerCase().includes(searchContent) || value.toLowerCase().includes(searchContent)
);
if (!hasMatchingContent && !hasMatchingLabels) {
return false;
}
}
return true;
sync() {
this.loadAlertsTable();
}
loadAlertsTable() {
@@ -82,8 +82,8 @@
<th nzAlign="center" nzWidth="10%">{{ 'alert.group-converge.group-wait' | i18n }}</th>
<th nzAlign="center" nzWidth="10%">{{ 'alert.group-converge.group-interval' | i18n }}</th>
<th nzAlign="center" nzWidth="10%">{{ 'alert.group-converge.repeat-interval' | i18n }}</th>
<th nzAlign="center" nzWidth="8%" nzRight>{{ 'alert.group-converge.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="12%" nzRight>{{ 'common.edit' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'alert.group-converge.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="12%">{{ 'common.edit' | i18n }}</th>
</tr>
</thead>
<tbody>
@@ -98,10 +98,10 @@
<td nzAlign="center"> {{ data.groupWait }}{{ 'alert.group-converge.seconds' | i18n }} </td>
<td nzAlign="center"> {{ data.groupInterval }}{{ 'alert.group-converge.seconds' | i18n }} </td>
<td nzAlign="center"> {{ data.repeatInterval }}{{ 'alert.group-converge.seconds' | i18n }} </td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<nz-switch [(ngModel)]="data.enable" (ngModelChange)="updateGroupConverge(data)" name="enable"></nz-switch>
</td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<div class="actions">
<button
nz-button
@@ -81,8 +81,8 @@
<th nzAlign="center" nzWidth="20%">{{ 'alert.inhibit.source_labels' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'alert.inhibit.target_labels' | i18n }}</th>
<th nzAlign="center" nzWidth="15%">{{ 'alert.inhibit.equal_labels' | i18n }}</th>
<th nzAlign="center" nzWidth="8%" nzRight>{{ 'common.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="12%" nzRight>{{ 'common.edit' | i18n }}</th>
<th nzAlign="center" nzWidth="8%">{{ 'common.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="12%">{{ 'common.edit' | i18n }}</th>
</tr>
</thead>
<tbody>
@@ -98,10 +98,10 @@
<td nzAlign="center">
<nz-tag *ngFor="let label of data.equalLabels">{{ label }}</nz-tag>
</td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<nz-switch [(ngModel)]="data.enable" (ngModelChange)="updateInhibit(data)" name="enable"></nz-switch>
</td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<div class="actions">
<button nz-button nzType="primary" (click)="editInhibit(data.id)" nz-tooltip [nzTooltipTitle]="'alert.inhibit.edit' | i18n">
<i nz-icon nzType="edit" nzTheme="outline"></i>
@@ -28,37 +28,15 @@
{{ 'alert.notice.receiver.new' | i18n }}
</button>
</ng-template>
<ng-template #right>
<app-multi-func-input
groupStyle="width: 250px;"
[placeholder]="'alert.notice.receiver.people.name' | i18n"
[(value)]="name"
(valueChange)="onSearch()"
/>
</ng-template>
</app-toolbar>
<nz-table
#fixedTable
[nzPageIndex]="pageIndex"
[nzPageSize]="pageSize"
[nzTotal]="total"
nzFrontPagination="false"
nzShowSizeChanger
[nzShowTotal]="rangeTemplate"
[nzPageSizeOptions]="[8, 15, 25]"
(nzQueryParams)="onTablePageChange($event)"
nzShowPagination="true"
[nzData]="receivers"
[nzLoading]="receiverTableLoading"
[nzScroll]="{ x: '1240px' }"
>
<nz-table #fixedTable [nzData]="receivers" [nzLoading]="receiverTableLoading" [nzScroll]="{ x: '1240px' }" nzFrontPagination="false">
<thead>
<tr>
<th nzAlign="center" nzWidth="15%">{{ 'alert.notice.receiver.people' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'alert.notice.receiver.type' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'alert.notice.receiver.setting' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'common.edit-time' | i18n }}</th>
<th nzAlign="center" nzWidth="15%" nzRight>{{ 'common.edit' | i18n }}</th>
<th nzAlign="center" nzWidth="25%">{{ 'common.edit' | i18n }}</th>
</tr>
</thead>
<tbody>
@@ -141,7 +119,7 @@
<span *ngIf="data.type == 13">{{ data.gotifyToken }}</span>
</td>
<td nzAlign="center">{{ (data.gmtUpdate ? data.gmtUpdate : data.gmtCreate) | date : 'YYYY-MM-dd HH:mm:ss' }}</td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<div class="actions">
<button
(click)="onEditOneNoticeReceiver(data)"
@@ -166,9 +144,6 @@
</tr>
</tbody>
</nz-table>
<ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} </ng-template>
<!-- new or update notice medium pop-up box -->
<nz-modal
(nzOnCancel)="onManageReceiverModalCancel()"
@@ -23,7 +23,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 { finalize } from 'rxjs/operators';
import { NoticeReceiver } from '../../../../pojo/NoticeReceiver';
@@ -42,10 +41,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
isManageReceiverModalOkLoading: boolean = false;
isSendTestButtonLoading: boolean = false;
receiver!: NoticeReceiver;
name!: string;
pageIndex: number = 1;
pageSize: number = 8;
total: number = 0;
@ViewChild('receiverForm', { static: false }) receiverForm: NgForm | undefined;
constructor(
@@ -65,14 +60,11 @@ export class AlertNoticeReceiverComponent implements OnInit {
loadReceiversTable() {
this.receiverTableLoading = true;
let receiverInit$ = this.noticeReceiverSvc.getReceivers(this.name, this.pageIndex - 1, this.pageSize).subscribe(
let receiverInit$ = this.noticeReceiverSvc.getReceivers().subscribe(
message => {
this.receiverTableLoading = false;
if (message.code === 0) {
let page = message.data;
this.receivers = page.content;
this.total = page.totalElements;
this.pageIndex = page.number + 1;
this.receivers = message.data;
} else {
console.warn(message.msg);
}
@@ -110,7 +102,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
message => {
if (message.code === 0) {
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
this.updatePageIndex(1);
this.loadReceiversTable();
} else {
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg);
@@ -122,11 +113,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
);
}
updatePageIndex(delSize: number) {
const lastPage = Math.max(1, Math.ceil((this.total - delSize) / this.pageSize));
this.pageIndex = this.pageIndex > lastPage ? lastPage : this.pageIndex;
}
onSplitTokenStr(type: number) {
let index = -1;
switch (this.receiver?.type) {
@@ -279,16 +265,4 @@ export class AlertNoticeReceiverComponent implements OnInit {
);
}
}
onTablePageChange(params: NzTableQueryParams) {
const { pageSize, pageIndex } = params;
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.loadReceiversTable();
}
onSearch() {
this.pageIndex = 1;
this.loadReceiversTable();
}
}
@@ -28,30 +28,8 @@
{{ 'alert.notice.rule.new' | i18n }}
</button>
</ng-template>
<ng-template #right>
<app-multi-func-input
groupStyle="width: 250px;"
[placeholder]="'alert.notice.rule.name' | i18n"
[(value)]="name"
(valueChange)="onSearch()"
/>
</ng-template>
</app-toolbar>
<nz-table
#ruleFixedTable
[nzPageIndex]="pageIndex"
[nzPageSize]="pageSize"
[nzTotal]="total"
nzFrontPagination="false"
nzShowSizeChanger
[nzShowTotal]="rangeTemplate"
[nzPageSizeOptions]="[8, 15, 25]"
(nzQueryParams)="onTablePageChange($event)"
nzShowPagination="true"
[nzData]="rules"
[nzLoading]="ruleTableLoading"
[nzScroll]="{ x: '1240px' }"
>
<nz-table #ruleFixedTable [nzData]="rules" [nzLoading]="ruleTableLoading" [nzScroll]="{ x: '1240px' }" nzFrontPagination="false">
<thead>
<tr>
<th nzAlign="center" nzWidth="15%">{{ 'alert.notice.rule.name' | i18n }}</th>
@@ -59,8 +37,8 @@
<th nzAlign="center" nzWidth="15%">{{ 'alert.notice.template.name' | i18n }}</th>
<th nzAlign="center" nzWidth="10%">{{ 'alert.notice.rule.all' | i18n }}</th>
<th nzAlign="center" nzWidth="10%">{{ 'common.enable' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'common.edit-time' | i18n }}</th>
<th nzAlign="center" nzWidth="15%" nzRight>{{ 'common.edit' | i18n }}</th>
<th nzAlign="center" nzWidth="15%">{{ 'common.edit-time' | i18n }}</th>
<th nzAlign="center" nzWidth="20%">{{ 'common.edit' | i18n }}</th>
</tr>
</thead>
<tbody>
@@ -84,7 +62,7 @@
<nz-switch [(ngModel)]="data.enable" (ngModelChange)="updateNoticeRule(data)"></nz-switch>
</td>
<td nzAlign="center">{{ (data.gmtUpdate ? data.gmtUpdate : data.gmtCreate) | date : 'YYYY-MM-dd HH:mm:ss' }}</td>
<td nzAlign="center" nzRight>
<td nzAlign="center">
<div class="actions">
<button
(click)="onEditOneNoticeRule(data)"
@@ -110,8 +88,6 @@
</tbody>
</nz-table>
<ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} </ng-template>
<!-- new or update notice strategy pop-up box -->
<nz-modal
(nzOnCancel)="onManageRuleModalCancel()"
@@ -23,7 +23,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 { finalize } from 'rxjs/operators';
import { NoticeReceiver } from '../../../../pojo/NoticeReceiver';
@@ -48,10 +47,6 @@ export class AlertNoticeRuleComponent implements OnInit {
switchReceiver!: NoticeReceiver;
receiversOption: any[] = [];
isLimit: boolean = false;
name!: string;
pageIndex: number = 1;
pageSize: number = 8;
total: number = 0;
@ViewChild('ruleForm', { static: false }) ruleForm: NgForm | undefined;
dayCheckOptions = [
@@ -83,14 +78,11 @@ export class AlertNoticeRuleComponent implements OnInit {
loadRulesTable() {
this.ruleTableLoading = true;
let rulesInit$ = this.noticeRuleSvc.getNoticeRules(this.name, this.pageIndex - 1, this.pageSize).subscribe(
let rulesInit$ = this.noticeRuleSvc.getNoticeRules().subscribe(
message => {
this.ruleTableLoading = false;
if (message.code === 0) {
let page = message.data;
this.rules = page.content;
this.total = page.totalElements;
this.pageIndex = page.number + 1;
this.rules = message.data;
} else {
console.warn(message.msg);
}
@@ -130,7 +122,6 @@ export class AlertNoticeRuleComponent implements OnInit {
message => {
if (message.code === 0) {
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
this.updatePageIndex(1);
this.loadRulesTable();
} else {
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg);
@@ -142,11 +133,6 @@ export class AlertNoticeRuleComponent implements OnInit {
);
}
updatePageIndex(delSize: number) {
const lastPage = Math.max(1, Math.ceil((this.total - delSize) / this.pageSize));
this.pageIndex = this.pageIndex > lastPage ? lastPage : this.pageIndex;
}
onNewNoticeRule() {
this.rule = new NoticeRule();
this.rule.templateId = -1;
@@ -227,7 +213,7 @@ export class AlertNoticeRuleComponent implements OnInit {
}
loadReceiversOption() {
let receiverOption$ = this.noticeReceiverSvc.getAllReceivers().subscribe(
let receiverOption$ = this.noticeReceiverSvc.getReceivers().subscribe(
message => {
if (message.code === 0) {
let data = message.data;
@@ -296,7 +282,7 @@ export class AlertNoticeRuleComponent implements OnInit {
}
loadTemplatesOption() {
let templateOption$ = this.noticeTemplateSvc.getAllNoticeTemplates().subscribe(
let templateOption$ = this.noticeTemplateSvc.getNoticeTemplates().subscribe(
message => {
if (message.code === 0) {
let data = message.data;
@@ -440,16 +426,4 @@ export class AlertNoticeRuleComponent implements OnInit {
);
}
}
onTablePageChange(params: NzTableQueryParams) {
const { pageSize, pageIndex } = params;
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.loadRulesTable();
}
onSearch() {
this.pageIndex = 1;
this.loadRulesTable();
}
}

Some files were not shown because too many files have changed in this diff Show More