mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
42
Commits
copy-token
...
ui-update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad532ce75 | ||
|
|
bf9d1f2b1d | ||
|
|
6c88c1ba03 | ||
|
|
8c58a35476 | ||
|
|
2ab05a5669 | ||
|
|
f3bc7f55c5 | ||
|
|
7effbc1f0d | ||
|
|
05e42182d9 | ||
|
|
5e69f4dfd0 | ||
|
|
aeb0083394 | ||
|
|
02f635bdd3 | ||
|
|
7dbcaaeda8 | ||
|
|
4ddf0a9f3f | ||
|
|
f1b1e3f7d6 | ||
|
|
921b8e5713 | ||
|
|
4009525f81 | ||
|
|
e0c7b4e111 | ||
|
|
5e01482caa | ||
|
|
028e2fbcbe | ||
|
|
d00cc6dcf4 | ||
|
|
4b7fdd6985 | ||
|
|
e45ec431aa | ||
|
|
8fa52fbca1 | ||
|
|
132fd00ab1 | ||
|
|
12ba2faa19 | ||
|
|
2f3abfbad4 | ||
|
|
95739d9114 | ||
|
|
428ae1f7d5 | ||
|
|
6509b8bfc6 | ||
|
|
783bd8de89 | ||
|
|
d1115b44d1 | ||
|
|
68e37d613a | ||
|
|
7db0df11ba | ||
|
|
e244bdc5a7 | ||
|
|
0b7aa487f0 | ||
|
|
59d8764dfb | ||
|
|
8a38a34965 | ||
|
|
fd7e1b0e05 | ||
|
|
5441838888 | ||
|
|
56b1dcb502 | ||
|
|
efbaf7feac | ||
|
|
7003b8a150 |
@@ -2225,6 +2225,79 @@
|
||||
"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,
|
||||
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
# upload application logs
|
||||
- name: Upload logs & API test reports
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: hz-logs-${{ github.run_id }}
|
||||
|
||||
@@ -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@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs-cn-pdf
|
||||
path: docs-cn.pdf
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs-en-pdf
|
||||
path: docs-en.pdf
|
||||
|
||||
@@ -496,6 +496,18 @@ 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>
|
||||
|
||||
|
||||
@@ -495,6 +495,18 @@ 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>
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
"type": 0,
|
||||
"paramValue": 1000
|
||||
},
|
||||
{
|
||||
"field": "ssl",
|
||||
"type": 1,
|
||||
"paramValue": false
|
||||
},
|
||||
{
|
||||
"field": "username",
|
||||
"type": 1
|
||||
|
||||
@@ -40,6 +40,11 @@
|
||||
<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>
|
||||
|
||||
+132
-50
@@ -17,90 +17,172 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.calculate;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
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
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class PeriodicAlertCalculator {
|
||||
|
||||
private static final String VALUE = "__value__";
|
||||
private static final String TIMESTAMP = "__timestamp__";
|
||||
|
||||
private final DataSourceService dataSourceService;
|
||||
private final JexlExpressionRunner expressionRunner;
|
||||
private final Map<String, SingleAlert> notRecoveredAlertMap = new ConcurrentHashMap<>(16);
|
||||
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;
|
||||
|
||||
public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce) {
|
||||
this.dataSourceService = dataSourceService;
|
||||
this.alarmCommonReduce = alarmCommonReduce;
|
||||
this.pendingAlertMap = new ConcurrentHashMap<>(8);
|
||||
this.firingAlertMap = new ConcurrentHashMap<>(8);
|
||||
}
|
||||
|
||||
public List<SingleAlert> calculate(AlertDefine rule) {
|
||||
public void calculate(AlertDefine rule) {
|
||||
if (!rule.isEnable() || StringUtils.isEmpty(rule.getExpr())) {
|
||||
return Collections.emptyList();
|
||||
log.error("Periodic rule {} is disabled or expression is empty", rule.getName());
|
||||
return;
|
||||
}
|
||||
// todo: implement the following logic
|
||||
long currentTimeMilli = System.currentTimeMillis();
|
||||
try {
|
||||
// Execute query
|
||||
List<Map<String, Object>> queryResults = dataSourceService.query(
|
||||
rule.getDatasource(),
|
||||
rule.getExpr()
|
||||
);
|
||||
|
||||
if (CollectionUtils.isEmpty(queryResults)) {
|
||||
return Collections.emptyList();
|
||||
// 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 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 boolean execAlertExpression(Map<String, Object> result, String expr) {
|
||||
return false;
|
||||
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 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());
|
||||
private void handleRecoveredAlert(Map<String, String> fingerprints) {
|
||||
String fingerprint = calculateFingerprint(fingerprints);
|
||||
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
|
||||
if (firingAlert != null) {
|
||||
return Collections.singletonList(buildResolvedAlert(rule, firingAlert));
|
||||
// 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.emptyList();
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
}
|
||||
|
||||
private SingleAlert buildResolvedAlert(AlertDefine rule, SingleAlert firingAlert) {
|
||||
return null;
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
+70
-4
@@ -17,13 +17,79 @@
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* period alert rule scheduler
|
||||
* Periodic Alert Rule Scheduler
|
||||
*/
|
||||
@Slf4j
|
||||
public class PeriodicAlertRuleScheduler {
|
||||
|
||||
// todo implement the following logic
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -38,9 +38,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Alarm Silence management API
|
||||
* Alarm Inhibit management API
|
||||
*/
|
||||
@Tag(name = "Alert Silence API")
|
||||
@Tag(name = "Alert Inhibit API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibit", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitController {
|
||||
@@ -49,7 +49,7 @@ public class AlertInhibitController {
|
||||
private AlertInhibitService alertInhibitService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "New Alarm Silence", description = "Added an alarm Silence")
|
||||
@Operation(summary = "New Alarm Inhibit", description = "Added an alarm Inhibit")
|
||||
public ResponseEntity<Message<Void>> addNewAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, false);
|
||||
alertInhibitService.addAlertInhibit(alertInhibit);
|
||||
@@ -57,7 +57,7 @@ public class AlertInhibitController {
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "Modifying an Alarm Silence", description = "Modify an existing alarm Silence")
|
||||
@Operation(summary = "Modifying an Alarm Inhibit", description = "Modify an existing alarm Inhibit")
|
||||
public ResponseEntity<Message<Void>> modifyAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, true);
|
||||
alertInhibitService.modifyAlertInhibit(alertInhibit);
|
||||
@@ -65,10 +65,10 @@ public class AlertInhibitController {
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}")
|
||||
@Operation(summary = "Querying Alarm Silence",
|
||||
description = "You can obtain alarm Silence information based on the alarm Silence ID")
|
||||
@Operation(summary = "Querying Alarm Inhibit",
|
||||
description = "You can obtain alarm Inhibit information based on the alarm Inhibit ID")
|
||||
public ResponseEntity<Message<AlertInhibit>> getAlertInhibit(
|
||||
@Parameter(description = "Alarm Silence ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
@Parameter(description = "Alarm Inhibit ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
AlertInhibit alertInhibit = alertInhibitService.getAlertInhibit(id);
|
||||
|
||||
return Objects.isNull(alertInhibit)
|
||||
|
||||
+4
-4
@@ -36,9 +36,9 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Silence the batch API for alarms
|
||||
* Inhibit the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Silence Batch API")
|
||||
@Tag(name = "Alert Inhibit Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibits", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitsController {
|
||||
@@ -50,7 +50,7 @@ public class AlertInhibitsController {
|
||||
@Operation(summary = "Query the alarm inhibit list",
|
||||
description = "You can obtain the list of alarm inhibit by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertInhibit>>> getAlertInhibits(
|
||||
@Parameter(description = "Alarm Silence ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Alarm Inhibit ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Search Name", example = "x") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@@ -64,7 +64,7 @@ public class AlertInhibitsController {
|
||||
@Operation(summary = "Delete alarm inhibit in batches",
|
||||
description = "Delete alarm inhibit in batches based on the alarm inhibit ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Silence IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
@Parameter(description = "Alarm Inhibit IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertInhibitService.deleteAlertInhibits(new HashSet<>(ids));
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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
-9
@@ -31,6 +31,7 @@ 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;
|
||||
@@ -82,9 +83,18 @@ 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<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)));
|
||||
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()));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/receiver/{id}")
|
||||
@@ -129,9 +139,11 @@ 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<List<NoticeRule>>> getRules(
|
||||
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name)));
|
||||
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)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/rule/{id}")
|
||||
@@ -176,12 +188,22 @@ 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<List<NoticeTemplate>>> getTemplates(
|
||||
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
List<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name);
|
||||
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);
|
||||
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")
|
||||
|
||||
+20
-16
@@ -23,11 +23,13 @@ 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;
|
||||
@@ -45,16 +47,18 @@ 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) {
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner, AlertSseManager emitterManager) {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -104,27 +108,27 @@ public class AlertNoticeDispatch {
|
||||
public void dispatchAlarm(GroupAlert groupAlert) {
|
||||
if (groupAlert != null) {
|
||||
// Determining alarm type storage
|
||||
alertStoreHandler.store(groupAlert);
|
||||
GroupAlert storedGroupAlert = alertStoreHandler.store(groupAlert);
|
||||
// Notice distribution
|
||||
sendNotify(groupAlert);
|
||||
sendNotify(storedGroupAlert);
|
||||
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(groupAlert));
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(storedGroupAlert));
|
||||
// Execute the plugin if enable with params
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(groupAlert, pluginContext));
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(storedGroupAlert, pluginContext));
|
||||
// Send alert to the sse client
|
||||
emitterManager.broadcast(JsonUtil.toJson(storedGroupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}))));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -28,7 +28,9 @@ 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
|
||||
*/
|
||||
void store(GroupAlert alert);
|
||||
GroupAlert store(GroupAlert alert);
|
||||
}
|
||||
|
||||
+26
-14
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -46,44 +47,53 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
private final SingleAlertDao singleAlertDao;
|
||||
|
||||
@Override
|
||||
public void store(GroupAlert groupAlert) {
|
||||
public GroupAlert store(GroupAlert groupAlert) {
|
||||
if (groupAlert == null || groupAlert.getAlerts() == null || groupAlert.getAlerts().isEmpty()) {
|
||||
log.error("The Group Alerts is empty, ignore store");
|
||||
return;
|
||||
return groupAlert;
|
||||
}
|
||||
// 1. Find existing alert group
|
||||
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
|
||||
|
||||
// 2. Process individual alerts
|
||||
Set<String> alertFingerprints = new HashSet<>(8);
|
||||
groupAlert.getAlerts().forEach(singleAlert -> {
|
||||
|
||||
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
|
||||
List<SingleAlert> newAlerts = new ArrayList<>();
|
||||
|
||||
|
||||
for (SingleAlert singleAlert : originalAlerts) {
|
||||
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
|
||||
|
||||
if (existAlert != null) {
|
||||
// Update existing alert
|
||||
// Update the existing alert with the ID and creation time from the database
|
||||
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())) {
|
||||
// Transition to resolved state
|
||||
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
|
||||
if (singleAlert.getEndAt() == null) {
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
}
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
singleAlert.setActiveAt(existAlert.getActiveAt());
|
||||
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
|
||||
}
|
||||
}
|
||||
alertFingerprints.add(singleAlert.getFingerprint());
|
||||
singleAlertDao.save(singleAlert);
|
||||
});
|
||||
|
||||
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
|
||||
newAlerts.add(savedSingleAlert);
|
||||
alertFingerprints.add(savedSingleAlert.getFingerprint());
|
||||
}
|
||||
groupAlert.setAlerts(newAlerts);
|
||||
// 3. Process resolved alerts
|
||||
if (existGroupAlert != null) {
|
||||
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
|
||||
@@ -120,6 +130,8 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
|
||||
// 4. Save alert group
|
||||
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
|
||||
groupAlertDao.save(groupAlert);
|
||||
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
|
||||
savedGroupAlert.setAlerts(groupAlert.getAlerts());
|
||||
return savedGroupAlert;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ package org.apache.hertzbeat.alert.reduce;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -267,7 +267,7 @@ public class AlarmGroupReduce {
|
||||
.groupLabels(alert.getLabels())
|
||||
.commonLabels(alert.getLabels())
|
||||
.commonAnnotations(alert.getAnnotations())
|
||||
.alerts(Collections.singletonList(alert))
|
||||
.alerts(new LinkedList<>(List.of(alert)))
|
||||
.status(alert.getStatus())
|
||||
.build();
|
||||
|
||||
|
||||
+3
-3
@@ -26,10 +26,10 @@ import java.util.Map;
|
||||
public interface DataSourceService {
|
||||
|
||||
/**
|
||||
* execute query
|
||||
* execute query expr calculate
|
||||
* @param datasource datasource
|
||||
* @param query query
|
||||
* @param expr query expr
|
||||
* @return result
|
||||
*/
|
||||
List<Map<String, Object>> query(String datasource, String query);
|
||||
List<Map<String, Object>> calculate(String datasource, String expr);
|
||||
}
|
||||
|
||||
+25
-4
@@ -23,6 +23,7 @@ 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
|
||||
@@ -32,23 +33,32 @@ 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
|
||||
*/
|
||||
List<NoticeReceiver> getNoticeReceivers(String name);
|
||||
Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
List<NoticeTemplate> getNoticeTemplates(String name);
|
||||
Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Recipient name
|
||||
* @param name Recipient name ,support fuzzy query
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
List<NoticeRule> getNoticeRules(String name);
|
||||
Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Add a notification recipient
|
||||
@@ -154,4 +164,15 @@ 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();
|
||||
}
|
||||
|
||||
+11
-1
@@ -25,6 +25,7 @@ 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;
|
||||
@@ -67,6 +68,9 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Autowired
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
|
||||
|
||||
@@ -98,19 +102,22 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Override
|
||||
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
alertDefine = alertDefineDao.save(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(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();
|
||||
}
|
||||
|
||||
@@ -123,6 +130,9 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
+303
-9
@@ -17,13 +17,21 @@
|
||||
|
||||
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
|
||||
@@ -33,21 +41,307 @@ import java.util.Map;
|
||||
public class DataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Map<String, QueryExecutor> executors;
|
||||
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__";
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> query(String datasource, String query) {
|
||||
QueryExecutor executor = executors.get(datasource);
|
||||
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);
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Unsupported datasource: " + datasource);
|
||||
}
|
||||
return executor.execute(query);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface QueryExecutor {
|
||||
List<Map<String, Object>> execute(String 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;
|
||||
}
|
||||
}
|
||||
|
||||
+76
-27
@@ -18,18 +18,6 @@
|
||||
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;
|
||||
@@ -51,10 +39,28 @@ 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
|
||||
*/
|
||||
@@ -80,44 +86,87 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
|
||||
private AlertNoticeDispatch dispatcherAlarm;
|
||||
|
||||
@Override
|
||||
public List<NoticeReceiver> getNoticeReceivers(String name) {
|
||||
public Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize) {
|
||||
Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeReceiverDao.findAll(specification);
|
||||
return noticeReceiverDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
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);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
|
||||
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() {
|
||||
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll(specification));
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll());
|
||||
return defaultTemplates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NoticeRule> getNoticeRules(String name) {
|
||||
public Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize) {
|
||||
Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeRuleDao.findAll(specification);
|
||||
return noticeRuleDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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 = 警告警報
|
||||
+1
-12
@@ -28,7 +28,6 @@ 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;
|
||||
@@ -43,7 +42,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
/**
|
||||
* Test case for {@link AlertDefineController}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefineControllerTest {
|
||||
|
||||
@@ -66,6 +64,7 @@ class AlertDefineControllerTest {
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.name("alertDefine")
|
||||
.expr("1 > 0")
|
||||
.times(1)
|
||||
.template("template")
|
||||
@@ -138,14 +137,4 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-24
@@ -19,7 +19,6 @@ 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;
|
||||
@@ -31,7 +30,6 @@ 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;
|
||||
@@ -50,7 +48,6 @@ 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 {
|
||||
|
||||
@@ -93,8 +90,7 @@ class AlertDefinesControllerTest {
|
||||
pageRequest = PageRequest.of((Integer) content.get("pageIndex"), (Integer) content.get("pageSize"), sortExp);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// todo: fix this test
|
||||
@Test
|
||||
void getAlertDefines() throws Exception {
|
||||
|
||||
// Test the correctness of the mock
|
||||
@@ -112,31 +108,27 @@ class AlertDefinesControllerTest {
|
||||
// }
|
||||
// }))).thenReturn(new PageImpl<AlertDefine>(new ArrayList<AlertDefine>()));
|
||||
AlertDefine define = AlertDefine.builder().id(9L).expr("x").times(1).build();
|
||||
Mockito.when(alertDefineService.getAlertDefines(null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
|
||||
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));
|
||||
|
||||
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", pageIndex.toString())
|
||||
.param("pageSize", pageSize.toString()))
|
||||
.param("pageIndex", String.valueOf(pageIndex))
|
||||
.param("pageSize", String.valueOf(pageSize)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.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))
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(9))
|
||||
.andExpect(jsonPath("$.data.content[0].expr").value("x"))
|
||||
.andExpect(jsonPath("$.data.content[0].times").value(1))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
|
||||
+6
-8
@@ -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,7 +44,6 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
/**
|
||||
* test case for {@link AlertGroupConvergeController}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertGroupConvergeControllerTest {
|
||||
|
||||
@@ -79,8 +78,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"));
|
||||
}
|
||||
@@ -93,8 +92,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"));
|
||||
}
|
||||
@@ -119,7 +118,6 @@ public class AlertGroupConvergeControllerTest {
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("AlertGroupConverge not exist."));
|
||||
.andExpect(jsonPath("$.msg").value("Alert Group Converge not exist."));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-4
@@ -33,7 +33,6 @@ 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;
|
||||
@@ -52,7 +51,6 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@Disabled
|
||||
class AlertGroupConvergesControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
@@ -101,6 +99,7 @@ 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))
|
||||
@@ -118,5 +117,4 @@ class AlertGroupConvergesControllerTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+114
-24
@@ -17,7 +17,6 @@
|
||||
|
||||
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;
|
||||
@@ -44,6 +43,10 @@ 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;
|
||||
@@ -168,12 +171,38 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getReceivers() throws Exception {
|
||||
NoticeReceiver receiver1 = new NoticeReceiver();
|
||||
receiver1.setId(1L);
|
||||
receiver1.setName("Receiver1");
|
||||
|
||||
//Mockito.when(noticeConfigService.getNoticeReceivers())
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers?name={name}", "tom"))
|
||||
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))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
.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));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,15 +274,38 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getRules() throws Exception {
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
NoticeRule rule1 = new NoticeRule();
|
||||
rule1.setId(1L);
|
||||
rule1.setName("Rule1");
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules?name={name}", "tom"))
|
||||
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))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
.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));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -362,22 +414,40 @@ class NoticeConfigControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTemplates() throws Exception {
|
||||
// Mock the service response
|
||||
void getTemplates() throws Exception {
|
||||
NoticeTemplate template1 = new NoticeTemplate();
|
||||
template1.setId(1L);
|
||||
template1.setName("Template1");
|
||||
NoticeTemplate template2 = new NoticeTemplate();
|
||||
template2.setName("Template2");
|
||||
List<NoticeTemplate> templates = Arrays.asList(template1, template2);
|
||||
when(noticeConfigService.getNoticeTemplates(any())).thenReturn(templates);
|
||||
|
||||
// Perform the GET request and verify the response
|
||||
this.mockMvc.perform(get("/api/notice/templates")
|
||||
.param("name", "Template"))
|
||||
NoticeTemplate template2 = new NoticeTemplate();
|
||||
template2.setId(2L);
|
||||
template2.setName("Template2");
|
||||
|
||||
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))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data[0].name").value("Template1"))
|
||||
.andExpect(jsonPath("$.data[1].name").value("Template2"));
|
||||
.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));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -414,5 +484,25 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -60,6 +61,9 @@ class AlertNoticeDispatchTest {
|
||||
@Mock
|
||||
private AlertNotifyHandler alertNotifyHandler;
|
||||
|
||||
@Mock
|
||||
private AlertSseManager emitterManager;
|
||||
|
||||
private AlertNoticeDispatch alertNoticeDispatch;
|
||||
|
||||
private static final int DISPATCH_THREADS = 3;
|
||||
@@ -77,7 +81,8 @@ class AlertNoticeDispatchTest {
|
||||
noticeConfigService,
|
||||
alertStoreHandler,
|
||||
alertNotifyHandlerList,
|
||||
pluginRunner
|
||||
pluginRunner,
|
||||
emitterManager
|
||||
);
|
||||
|
||||
receiver = NoticeReceiver.builder()
|
||||
|
||||
+19
-10
@@ -27,7 +27,6 @@ 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;
|
||||
@@ -39,7 +38,6 @@ import java.util.List;
|
||||
/**
|
||||
* Test case for {@link DbAlertStoreHandlerImpl}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DbAlertStoreHandlerImplTest {
|
||||
|
||||
@@ -75,27 +73,34 @@ 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");
|
||||
@@ -103,11 +108,15 @@ 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-32
@@ -30,18 +30,17 @@ 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 AlarmSilenceReduce
|
||||
* Test for {@link AlarmSilenceReduce}
|
||||
*/
|
||||
@Disabled
|
||||
class AlarmSilenceReduceTest {
|
||||
|
||||
@Mock
|
||||
@@ -55,21 +54,23 @@ class AlarmSilenceReduceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.emptyList());
|
||||
CacheFactory.clearAlertSilenceCache();
|
||||
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)
|
||||
@@ -81,7 +82,7 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
@@ -107,9 +108,9 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
|
||||
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
alarmSilenceReduce.silenceAlarm(alert);
|
||||
@@ -130,29 +131,7 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
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));
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
|
||||
+35
-25
@@ -24,16 +24,18 @@ 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;
|
||||
@@ -42,7 +44,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
/**
|
||||
* test case for {@link AlertDefineExcelImExportServiceImpl}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertDefineExcelImExportServiceTest {
|
||||
|
||||
@@ -61,15 +62,13 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
Row row = initialSheet.createRow(1);
|
||||
row.createCell(0).setCellValue("app1");
|
||||
row.createCell(1).setCellValue("metric1");
|
||||
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(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(8).setCellValue(true);
|
||||
row.createCell(9).setCellValue(true);
|
||||
row.createCell(10).setCellValue("template1");
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(toByteArray(initialWorkbook));
|
||||
|
||||
@@ -88,9 +87,12 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
assertEquals("app1", alertDefineDTO.getName());
|
||||
assertEquals("metric1", alertDefineDTO.getType());
|
||||
assertEquals("expr1", alertDefineDTO.getExpr());
|
||||
assertEquals(10, alertDefineDTO.getTimes());
|
||||
assertTrue(alertDefineDTO.getEnable());
|
||||
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());
|
||||
assertTrue(alertDefineDTO.getEnable());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +105,12 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
alertDefineDTO.setName("app1");
|
||||
alertDefineDTO.setType("metric1");
|
||||
alertDefineDTO.setExpr("expr1");
|
||||
alertDefineDTO.setTimes(10);
|
||||
alertDefineDTO.setEnable(true);
|
||||
alertDefineDTO.setPeriod(10);
|
||||
alertDefineDTO.setTimes(1);
|
||||
alertDefineDTO.setLabels(Map.of("key", "value"));
|
||||
alertDefineDTO.setAnnotations(Map.of("key", "value"));
|
||||
alertDefineDTO.setTemplate("template1");
|
||||
alertDefineDTO.setEnable(true);
|
||||
exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
|
||||
exportAlertDefineList.add(exportAlertDefineDTO);
|
||||
|
||||
@@ -115,21 +120,26 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
try (Workbook resultWorkbook = WorkbookFactory.create(new ByteArrayInputStream(outputStream.toByteArray()))) {
|
||||
Sheet resultSheet = resultWorkbook.getSheetAt(0);
|
||||
Row headerRow = resultSheet.getRow(0);
|
||||
assertEquals("app", headerRow.getCell(0).getStringCellValue());
|
||||
assertEquals("metric", headerRow.getCell(1).getStringCellValue());
|
||||
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());
|
||||
|
||||
Row dataRow = resultSheet.getRow(1);
|
||||
assertEquals("app1", dataRow.getCell(0).getStringCellValue());
|
||||
assertEquals("metric1", dataRow.getCell(1).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());
|
||||
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());
|
||||
assertTrue(dataRow.getCell(8).getBooleanCellValue());
|
||||
assertTrue(dataRow.getCell(9).getBooleanCellValue());
|
||||
assertEquals("template1", dataRow.getCell(10).getStringCellValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,4 +152,4 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -33,6 +33,7 @@ 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;
|
||||
@@ -68,7 +69,7 @@ class AlertDefineJsonImExportServiceTest {
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
|
||||
inputStream = new ByteArrayInputStream(JSON_DATA.getBytes());
|
||||
inputStream = new ByteArrayInputStream(JSON_DATA.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
AlertDefineDTO alertDefine = new AlertDefineDTO();
|
||||
alertDefine.setName("App1");
|
||||
|
||||
+5
@@ -30,6 +30,7 @@ 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;
|
||||
@@ -54,6 +55,9 @@ class AlertDefineServiceTest {
|
||||
|
||||
@Mock
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Mock
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
@Mock
|
||||
private List<AlertDefineImExportService> alertDefineImExportServiceList;
|
||||
@@ -64,6 +68,7 @@ class AlertDefineServiceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineDao", alertDefineDao);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "periodicAlertRuleScheduler", periodicAlertRuleScheduler);
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
|
||||
+1
-3
@@ -39,7 +39,6 @@ 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;
|
||||
@@ -48,8 +47,7 @@ import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* test case for {@link AlertDefineYamlImExportServiceImpl}
|
||||
*/
|
||||
@Disabled
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefineYamlImExportServiceTest {
|
||||
|
||||
|
||||
+32
-22
@@ -17,22 +17,27 @@
|
||||
|
||||
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;
|
||||
@@ -42,10 +47,9 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* test case for {@link AlertGroupConvergeServiceImpl}
|
||||
*/
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@Disabled
|
||||
class AlertGroupConvergeServiceTest {
|
||||
|
||||
@Mock
|
||||
@@ -54,22 +58,28 @@ 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());
|
||||
}
|
||||
|
||||
|
||||
@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
|
||||
@@ -79,7 +89,6 @@ 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);
|
||||
}
|
||||
@@ -87,10 +96,11 @@ class AlertGroupConvergeServiceTest {
|
||||
@Test
|
||||
public void testDeleteAlertGroupConverges() {
|
||||
|
||||
Set<Long> convergeIds = Set.of(1L, 2L, 3L);
|
||||
alertGroupConvergeService.deleteAlertGroupConverges(convergeIds);
|
||||
|
||||
verify(alertGroupConvergeDao, times(1)).deleteAlertGroupConvergesByIdIn(convergeIds);
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+21
-15
@@ -17,25 +17,23 @@
|
||||
|
||||
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.mockito.Mockito.any;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
@@ -44,9 +42,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertService}
|
||||
*/
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@Disabled
|
||||
class AlertServiceTest {
|
||||
@Mock
|
||||
private GroupAlertDao groupAlertDao;
|
||||
@@ -83,14 +80,23 @@ class AlertServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
void testGetAlertsSummary() {
|
||||
SingleAlert alert = new SingleAlert();
|
||||
alert.setLabels(Collections.singletonMap(CommonConstants.LABEL_ALERT_SEVERITY, CommonConstants.ALERT_SEVERITY_CRITICAL));
|
||||
|
||||
assertDoesNotThrow(() -> alertService.getAlertsSummary());
|
||||
assertNotNull(alertService.getAlertsSummary());
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* 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__"));
|
||||
}
|
||||
}
|
||||
+130
-17
@@ -17,33 +17,42 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service;
|
||||
|
||||
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 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.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.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;
|
||||
|
||||
|
||||
/**
|
||||
* Test case for {@link NoticeConfigService}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NoticeConfigServiceTest {
|
||||
|
||||
@@ -58,26 +67,130 @@ 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() {
|
||||
noticeConfigService.getNoticeReceivers(null);
|
||||
verify(noticeReceiverDao, times(1)).findAll(any(Specification.class));
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoticeTemplates() {
|
||||
noticeConfigService.getNoticeTemplates(null);
|
||||
verify(noticeTemplateDao, times(1)).findAll(any(Specification.class));
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoticeRules() {
|
||||
noticeConfigService.getNoticeRules(null);
|
||||
verify(noticeRuleDao, times(1)).findAll(any(Specification.class));
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -155,5 +155,9 @@
|
||||
<artifactId>plc4j-driver-modbus</artifactId>
|
||||
<version>0.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+104
-26
@@ -17,9 +17,11 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.ftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
@@ -29,6 +31,10 @@ import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.FtpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.sshd.client.SshClient;
|
||||
import org.apache.sshd.client.session.ClientSession;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.client.SftpClientFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -56,33 +62,14 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
Assert.hasText(ftpProtocol.getTimeout(), "Ftp Protocol timeout is required.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
// Set timeout
|
||||
ftpClient.setControlKeepAliveReplyTimeout(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
|
||||
// Collect data to load in CollectRep.ValueRow.Builder's object
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
Map<String, String> valueMap;
|
||||
try {
|
||||
valueMap = collectValue(ftpClient, ftpProtocol);
|
||||
metrics.getAliasFields().forEach(it -> {
|
||||
if (valueMap.containsKey(it)) {
|
||||
String fieldValue = valueMap.get(it);
|
||||
valueRowBuilder.addColumn(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE));
|
||||
} else {
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
return;
|
||||
boolean ssl = Boolean.parseBoolean(metrics.getFtp().getSsl());
|
||||
if (ssl){
|
||||
handleSftpCollect(builder, metrics);
|
||||
} else {
|
||||
handleFtpCollect(builder, metrics);
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,8 +100,23 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
};
|
||||
}
|
||||
|
||||
private Map<String, String> collectValue(SftpClient sftpClient, FtpProtocol ftpProtocol) {
|
||||
boolean isActive;
|
||||
String responseTime;
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
sftpClient.stat(ftpProtocol.getDirection());
|
||||
isActive = true;
|
||||
long endTime = System.currentTimeMillis();
|
||||
responseTime = String.valueOf(endTime - startTime);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("[SFTPClient] error: {}" + CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
return Map.of("isActive", Boolean.toString(isActive), "responseTime", responseTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* login
|
||||
* ftp login
|
||||
*/
|
||||
private void login(FTPClient ftpClient, FtpProtocol ftpProtocol) {
|
||||
try {
|
||||
@@ -147,8 +149,84 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
}
|
||||
|
||||
private ClientSession connect(SshClient client, FtpProtocol ftpProtocol) {
|
||||
client.start();
|
||||
try {
|
||||
ClientSession session = client.connect(ftpProtocol.getUsername(), ftpProtocol.getHost(), Integer.parseInt(ftpProtocol.getPort()))
|
||||
.verify(Integer.parseInt(ftpProtocol.getTimeout()))
|
||||
.getSession();
|
||||
session.addPasswordIdentity(ftpProtocol.getPassword());
|
||||
session.auth().verify(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
return session;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("[sftp connection] error: {}" + CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_FTP;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFtpCollect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
// Set timeout
|
||||
ftpClient.setControlKeepAliveReplyTimeout(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
|
||||
// Collect data to load in CollectRep.ValueRow.Builder's object
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
Map<String, String> valueMap;
|
||||
try {
|
||||
valueMap = collectValue(ftpClient, ftpProtocol);
|
||||
metrics.getAliasFields().forEach(it -> {
|
||||
if (valueMap.containsKey(it)) {
|
||||
String fieldValue = valueMap.get(it);
|
||||
valueRowBuilder.addColumn(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE));
|
||||
} else {
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
return;
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
private void handleSftpCollect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
ClientSession session = null;
|
||||
SftpClient sftpClient = null;
|
||||
SshClient client = null;
|
||||
try {
|
||||
client = SshClient.setUpDefaultClient();
|
||||
session = connect(client, ftpProtocol);
|
||||
sftpClient = SftpClientFactory.instance().createSftpClient(session);
|
||||
Map<String, String> valueMap = collectValue(sftpClient, ftpProtocol);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
metrics.getAliasFields().forEach(it ->
|
||||
valueRowBuilder.addColumn(valueMap.getOrDefault(it, CommonConstants.NULL_VALUE))
|
||||
);
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (sftpClient != null && sftpClient.isOpen()){
|
||||
sftpClient.close();
|
||||
}
|
||||
if (session != null && session.isOpen()){
|
||||
session.close();
|
||||
}
|
||||
if (client != null && client.isOpen()){
|
||||
client.close();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("[SFTPClient] error while closing: {}", CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -41,7 +41,6 @@ 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;
|
||||
@@ -59,6 +58,7 @@ 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, response);
|
||||
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime);
|
||||
case DispatchConstants.PARSE_SITE_MAP ->
|
||||
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
|
||||
case DispatchConstants.PARSE_HEADER ->
|
||||
@@ -222,8 +222,7 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
|
||||
private void parseResponseByWebsite(String resp, Metrics metrics, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime,
|
||||
CloseableHttpResponse response) {
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
for (String alias : metrics.getAliasFields()) {
|
||||
@@ -290,9 +289,10 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(siteUrl);
|
||||
CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(httpGet);
|
||||
statusCode = response.getStatusLine().getStatusCode();
|
||||
EntityUtils.consume(response.getEntity());
|
||||
try (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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
|
||||
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;
|
||||
@@ -419,7 +418,7 @@ public class ExporterParser {
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelValue(String s) {
|
||||
return s != null && s.equals(new String(s.getBytes(StandardCharsets.UTF_8)));
|
||||
return s != null;
|
||||
}
|
||||
|
||||
private boolean isSum(String s) {
|
||||
|
||||
+4
-5
@@ -32,7 +32,6 @@ 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;
|
||||
@@ -44,6 +43,7 @@ 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,9 +89,8 @@ 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);
|
||||
@@ -278,7 +277,7 @@ public class PrometheusAutoCollectImpl {
|
||||
if (StringUtils.hasText(authorization.getBasicAuthUsername())
|
||||
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
|
||||
String authStr = authorization.getBasicAuthUsername() + ":" + authorization.getBasicAuthPassword();
|
||||
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + " " + encodedAuth);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
|
||||
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;
|
||||
@@ -300,6 +299,6 @@ public class TextParser {
|
||||
* @return true/false
|
||||
*/
|
||||
private static boolean isValidLabelValue(String s) {
|
||||
return s != null && s.equals(new String(s.getBytes(StandardCharsets.UTF_8)));
|
||||
return s != null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -86,8 +86,7 @@ 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);
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ 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;
|
||||
@@ -122,7 +123,7 @@ public class TelnetCollectImpl extends AbstractCollect {
|
||||
return new HashMap<>(16);
|
||||
}
|
||||
OutputStream outputStream = telnetClient.getOutputStream();
|
||||
outputStream.write(cmd.getBytes());
|
||||
outputStream.write(cmd.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
String result = new String(telnetClient.getInputStream().readAllBytes());
|
||||
String[] lines = result.split("\n");
|
||||
|
||||
+10
-9
@@ -28,6 +28,7 @@ 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;
|
||||
@@ -128,16 +129,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());
|
||||
out.write(requestLine.getBytes(StandardCharsets.UTF_8));
|
||||
String hostName = InetAddress.getLocalHost().getHostAddress();
|
||||
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.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.flush();
|
||||
}
|
||||
|
||||
|
||||
+45
-50
@@ -17,68 +17,63 @@
|
||||
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -144,12 +144,25 @@ public class CommonHttpClient {
|
||||
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1, threadFactory);
|
||||
scheduledExecutor.scheduleWithFixedDelay(() -> {
|
||||
connectionManager.closeExpiredConnections();
|
||||
connectionManager.closeIdleConnections(100, TimeUnit.SECONDS);
|
||||
connectionManager.closeIdleConnections(40, 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-4
@@ -94,13 +94,11 @@ 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());
|
||||
try {
|
||||
// Send an HTTP request to obtain response data
|
||||
response = CommonHttpClient.getHttpClient().execute(request, httpContext);
|
||||
// Send an HTTP request to obtain response data
|
||||
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode != SUCCESS_CODE) {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
|
||||
+1
-2
@@ -25,13 +25,12 @@ import java.util.Arrays;
|
||||
public enum AiTypeEnum {
|
||||
|
||||
/**
|
||||
* 智普
|
||||
* ZhiPu
|
||||
*/
|
||||
zhiPu,
|
||||
|
||||
/**
|
||||
* sparkDesk
|
||||
* 科大讯飞
|
||||
*/
|
||||
sparkDesk,
|
||||
|
||||
|
||||
+1
-7
@@ -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,14 +59,8 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -18,6 +18,7 @@
|
||||
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;
|
||||
@@ -97,10 +98,12 @@ 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
|
||||
|
||||
+3
@@ -18,6 +18,7 @@
|
||||
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;
|
||||
@@ -100,10 +101,12 @@ 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
|
||||
|
||||
+6
@@ -59,4 +59,10 @@ public class FtpProtocol implements CommonRequestProtocol, Protocol {
|
||||
* Timeout
|
||||
*/
|
||||
private String timeout;
|
||||
|
||||
/**
|
||||
* Whether ftp uses link encryption ssl/tls, i.e. ftp or sftp
|
||||
*
|
||||
*/
|
||||
private String ssl = "false";
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class RocketmqProtocol implements CommonRequestProtocol, Protocol {
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* jsonpath解析脚本
|
||||
* jsonpath parsing script
|
||||
*/
|
||||
private String parseScript;
|
||||
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ 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;
|
||||
@@ -406,7 +407,7 @@ public final class CollectRep {
|
||||
fieldIndex < row.getColumnsList().size()) {
|
||||
String value = row.getColumns(fieldIndex);
|
||||
if (value != null) {
|
||||
vector.set(rowIndex, value.getBytes());
|
||||
vector.set(rowIndex, value.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public final class AesUtil {
|
||||
// encode content to byte array
|
||||
byte[] byteAes = cipher.doFinal(byteEncode);
|
||||
// base64 encode content
|
||||
return new String(Base64.getEncoder().encode(byteAes), StandardCharsets.UTF_8);
|
||||
return Base64.getEncoder().encodeToString(byteAes);
|
||||
} catch (Exception e) {
|
||||
log.error("aes encode content error: {}", e.getMessage(), e);
|
||||
return content;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.common.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
@@ -39,4 +40,8 @@ public final class Base64Util {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String encode(String str) {
|
||||
return Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ 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;
|
||||
@@ -86,7 +87,7 @@ class KafkaMetricsDataDeserializerTest {
|
||||
@Test
|
||||
void testDeserializeWithInvalidBytes() {
|
||||
|
||||
byte[] invalidBytes = "invalid data".getBytes();
|
||||
byte[] invalidBytes = "invalid data".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
assertThrows(RuntimeException.class, () -> deserializer.deserialize("", invalidBytes));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ 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}.
|
||||
*/
|
||||
@@ -41,9 +43,12 @@ class FileUtilTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
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());
|
||||
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));
|
||||
emptyFile = new MockMultipartFile("file", "", null, (byte[]) null);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,5 +53,10 @@
|
||||
<version>${hertzbeat.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+4
-2
@@ -40,6 +40,7 @@ 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;
|
||||
@@ -103,9 +104,10 @@ public class DockerMonitorE2eTest extends AbstractCollectE2eTest {
|
||||
|
||||
private void sendJsonResponse(HttpExchange exchange, String response) throws IOException {
|
||||
exchange.getResponseHeaders().set("Content-Type", "application/json");
|
||||
exchange.sendResponseHeaders(200, response.getBytes().length);
|
||||
final byte[] array = response.getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, array.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(response.getBytes());
|
||||
os.write(array);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 final List<String> ALLOW_EMPTY_WHITE_LIST = List.of("header");
|
||||
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;
|
||||
if (ALLOW_EMPTY_WHITE_LIST.contains(metricsDef.getName())) {
|
||||
metricsData = validateMetricsCollection(metricsDef, metricsDef.getName(), true);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -38,6 +38,8 @@ import org.testcontainers.lifecycle.Startables;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -54,6 +56,7 @@ public class SshCollectE2eTest extends AbstractCollectE2eTest {
|
||||
private static final String ROOT_USER = "root";
|
||||
private static final int SSH_PORT = 22;
|
||||
private static final int PASSWORD_LENGTH = 12;
|
||||
private static final List<String> ALLOW_EMPTY_WHITE_LIST = Arrays.asList("top_mem_process", "top_cpu_process");
|
||||
|
||||
private static GenericContainer<?> linuxContainer;
|
||||
|
||||
@@ -88,8 +91,13 @@ public class SshCollectE2eTest extends AbstractCollectE2eTest {
|
||||
Assertions.assertTrue(linuxContainer.isRunning(), "Ubuntu container should be running");
|
||||
|
||||
Job ubuntuJob = appService.getAppDefine("ubuntu");
|
||||
ubuntuJob.getMetrics().forEach(metricsDef ->
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName()));
|
||||
ubuntuJob.getMetrics().forEach(metricsDef -> {
|
||||
if (ALLOW_EMPTY_WHITE_LIST.contains(metricsDef.getName())) {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName(), true);
|
||||
} else {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.telnet;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollectE2eTest;
|
||||
import org.apache.hertzbeat.collector.collect.telnet.TelnetCollectImpl;
|
||||
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.Protocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.TelnetProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
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.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Integration test for Zookeeper monitoring functionality
|
||||
*/
|
||||
@Slf4j
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ZookeeperMonitorE2eTest extends AbstractCollectE2eTest {
|
||||
|
||||
private static final String ZOOKEEPER_IMAGE_NAME = "zookeeper:3.8.4";
|
||||
private static final String ZOOKEEPER_NAME = "zookeeper";
|
||||
private static final Integer ZOOKEEPER_PORT = 2181;
|
||||
private static GenericContainer<?> zookeeperContainer;
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
if (zookeeperContainer != null) {
|
||||
zookeeperContainer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
collect = new TelnetCollectImpl();
|
||||
metrics = new Metrics();
|
||||
|
||||
try {
|
||||
// Start Zookeeper container with custom configuration
|
||||
zookeeperContainer = new GenericContainer<>(DockerImageName.parse(ZOOKEEPER_IMAGE_NAME))
|
||||
.withExposedPorts(ZOOKEEPER_PORT)
|
||||
.withEnv("ZOO_4LW_COMMANDS_WHITELIST", "*")
|
||||
.withNetworkAliases(ZOOKEEPER_NAME)
|
||||
.waitingFor(
|
||||
Wait.forLogMessage(".*Started AdminServer on address.*\\n", 1)
|
||||
.withStartupTimeout(Duration.ofSeconds(60))
|
||||
)
|
||||
.withLogConsumer(outputFrame -> {
|
||||
log.info(outputFrame.getUtf8String());
|
||||
});
|
||||
|
||||
zookeeperContainer.start();
|
||||
log.info("Zookeeper container started at {}:{}",
|
||||
zookeeperContainer.getHost(),
|
||||
zookeeperContainer.getMappedPort(ZOOKEEPER_PORT));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("Failed to start Zookeeper container", e);
|
||||
throw e;
|
||||
}
|
||||
Thread.sleep(30000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef) {
|
||||
TelnetProtocol telnetProtocol = (TelnetProtocol) buildProtocol(metricsDef);
|
||||
metrics.setTelnet(telnetProtocol);
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
metricsData.setApp(ZOOKEEPER_NAME);
|
||||
metrics.setAliasFields(metricsDef.getAliasFields());
|
||||
return collectMetricsData(metrics, metricsDef, metricsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Protocol buildProtocol(Metrics metricsDef) {
|
||||
TelnetProtocol protocol = new TelnetProtocol();
|
||||
protocol.setHost(zookeeperContainer.getHost());
|
||||
protocol.setPort(String.valueOf(zookeeperContainer.getMappedPort(ZOOKEEPER_PORT)));
|
||||
protocol.setCmd(metricsDef.getTelnet().getCmd());
|
||||
return protocol;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZookeeperMonitor() {
|
||||
Assertions.assertTrue(zookeeperContainer.isRunning(), "Zookeeper container should be running");
|
||||
|
||||
Job dockerJob = appService.getAppDefine("zookeeper");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-8
@@ -22,6 +22,7 @@ import org.apache.hertzbeat.collector.dispatch.CollectDataDispatch;
|
||||
import org.apache.hertzbeat.collector.dispatch.MetricsCollect;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
@@ -77,20 +78,39 @@ public abstract class AbstractCollectE2eTest {
|
||||
|
||||
/**
|
||||
* Validate metrics collection, check if the metrics values are not empty <br/>
|
||||
* We believe that all monitoring metrics should have data
|
||||
* @param metricsDef metrics definition
|
||||
* @param metricName metric name
|
||||
* @return metrics data
|
||||
*/
|
||||
protected CollectRep.MetricsData validateMetricsCollection(Metrics metricsDef, String metricName) {
|
||||
// By default, we do not allow empty values
|
||||
return validateMetricsCollection(metricsDef, metricName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate metrics collection, check if the metrics values are not empty <br/>
|
||||
* We believe that all monitoring metrics should have data
|
||||
*
|
||||
* @param metricsDef metrics definition
|
||||
* @param metricName metric name
|
||||
* @param allowEmpty In some special scenarios, it is not necessary to check if the value is ` `
|
||||
*/
|
||||
protected CollectRep.MetricsData validateMetricsCollection(Metrics metricsDef, String metricName, boolean allowEmpty) {
|
||||
CollectRep.MetricsData.Builder metricsData = collectMetrics(metricsDef);
|
||||
|
||||
metricsCollect.calculateFields(metricsDef, metricsData);
|
||||
|
||||
Assertions.assertTrue(metricsData.getValuesList().size() > 0,
|
||||
String.format("%s metrics values should not be empty", metricName));
|
||||
String.format("%s metrics values should not be empty, detail: %s", metricName, metricsData.getMsg()));
|
||||
|
||||
for (CollectRep.ValueRow valueRow : metricsData.getValuesList()) {
|
||||
for (int i = 0; i < valueRow.getColumnsCount(); i++) {
|
||||
Assertions.assertFalse(valueRow.getColumns(i).isEmpty(),
|
||||
String.format("%s metric column %d should not be empty", metricName, i));
|
||||
if (!allowEmpty) {
|
||||
// Check if the value is not null
|
||||
Assertions.assertNotEquals(CommonConstants.NULL_VALUE, valueRow.getColumns(i), String.format("%s metric column %d should not be null", metricName, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,21 +118,31 @@ public abstract class AbstractCollectE2eTest {
|
||||
return metricsData.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alias fields for metrics
|
||||
*
|
||||
* @param metrics metrics
|
||||
* @param metricsDef metrics definition
|
||||
*/
|
||||
protected void setMetricsAliasFields(Metrics metrics, Metrics metricsDef) {
|
||||
metrics.setAliasFields(metricsDef.getAliasFields() == null
|
||||
? metricsDef.getFields().stream()
|
||||
.map(Metrics.Field::getField)
|
||||
.collect(Collectors.toList()) :
|
||||
metricsDef.getAliasFields());
|
||||
List<String> aliasFields = metricsDef.getAliasFields() == null
|
||||
? metricsDef.getFields().stream().map(Metrics.Field::getField).collect(Collectors.toList())
|
||||
: metricsDef.getAliasFields();
|
||||
metrics.setAliasFields(aliasFields);
|
||||
metricsDef.setAliasFields(aliasFields);
|
||||
}
|
||||
|
||||
protected abstract CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef);
|
||||
|
||||
protected CollectRep.MetricsData.Builder collectMetricsData(Metrics metrics, Metrics metricsDef) {
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
return this.collectMetricsData(metrics, metricsDef, metricsData);
|
||||
}
|
||||
|
||||
protected CollectRep.MetricsData.Builder collectMetricsData(Metrics metrics, Metrics metricsDef, CollectRep.MetricsData.Builder metricsData) {
|
||||
setMetricsAliasFields(metrics, metricsDef);
|
||||
|
||||
// Collect metrics
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
collect.collect(metricsData, metrics);
|
||||
return metricsData;
|
||||
}
|
||||
|
||||
+4
-2
@@ -25,6 +25,8 @@ 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;
|
||||
@@ -192,9 +194,9 @@ public class ServiceAccountService {
|
||||
|
||||
private HttpHeaders createHeaders() {
|
||||
String auth = username + SignConstants.DOUBLE_MARK + password;
|
||||
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
String authHeader = NetworkConstants.BASIC
|
||||
+ SignConstants.BLANK + new String(encodedAuth);
|
||||
+ SignConstants.BLANK + encodedAuth;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
@@ -27,6 +27,7 @@ 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.
|
||||
@@ -39,6 +40,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
@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);
|
||||
|
||||
+21
-19
@@ -17,11 +17,12 @@
|
||||
|
||||
package org.apache.hertzbeat.manager.scheduler;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* List of assigned collection tasks
|
||||
*/
|
||||
@@ -33,17 +34,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
|
||||
*/
|
||||
@@ -59,15 +60,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);
|
||||
}
|
||||
@@ -77,19 +78,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);
|
||||
@@ -102,14 +103,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;
|
||||
@@ -118,9 +119,10 @@ public class AssignJobs {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在对应的jobId
|
||||
* Check if the corresponding jobId exists
|
||||
*
|
||||
* @param jobId jobId
|
||||
* @return 若存在返回true,并把jobId从assignJobs remove掉
|
||||
* @return true if exists and removes the jobId from assignJobs
|
||||
*/
|
||||
public boolean containAndRemoveJob(Long jobId) {
|
||||
if (jobs.isEmpty()) {
|
||||
@@ -128,21 +130,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;
|
||||
@@ -151,7 +153,7 @@ public class AssignJobs {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理数据
|
||||
* Clean data
|
||||
*/
|
||||
public void clear() {
|
||||
if (!jobs.isEmpty()) {
|
||||
|
||||
+1
-1
@@ -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(root.get("name"), "%" + name + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
|
||||
+5
-3
@@ -566,11 +566,13 @@ 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(root.get("name"), "%" + search + "%");
|
||||
Predicate predicateId = criteriaBuilder.like(root.get("id"), "%" + search + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
|
||||
if (StringUtils.isNumeric(search)){
|
||||
Predicate predicateId = criteriaBuilder.equal(root.get("id"), Long.parseLong(search));
|
||||
orList.add(predicateId);
|
||||
}
|
||||
orList.add(predicateHost);
|
||||
orList.add(predicateName);
|
||||
orList.add(predicateId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(labels)) {
|
||||
String[] labelAres = labels.split(",");
|
||||
|
||||
+2
-2
@@ -92,9 +92,9 @@ public class TagServiceImpl implements TagService {
|
||||
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(search)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
|
||||
orList.add(predicateName);
|
||||
Predicate predicateValue = criteriaBuilder.like(root.get("tagValue"), "%" + search + "%");
|
||||
Predicate predicateValue = criteriaBuilder.like(criteriaBuilder.lower(root.get("tagValue")), "%" + search.toLowerCase() + "%");
|
||||
orList.add(predicateValue);
|
||||
}
|
||||
Predicate[] orPredicates = new Predicate[orList.size()];
|
||||
|
||||
@@ -88,6 +88,15 @@ params:
|
||||
range: '[0,100000]'
|
||||
required: true
|
||||
defaultValue: 1000
|
||||
- field: ssl
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 启用SFTP
|
||||
en-US: SFTP
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: boolean
|
||||
# required-true or false
|
||||
required: true
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
# metrics - basic
|
||||
@@ -122,3 +131,4 @@ metrics:
|
||||
password: ^_^password^_^
|
||||
direction: ^_^direction^_^
|
||||
timeout: ^_^timeout^_^
|
||||
ssl: ^_^ssl^_^
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
category: llm
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: nvidia
|
||||
# The monitoring i18n name
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
# 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
|
||||
@@ -0,0 +1,407 @@
|
||||
# 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,6 +72,7 @@ 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
|
||||
@@ -79,6 +80,7 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
+3
-1
@@ -19,6 +19,8 @@ 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;
|
||||
@@ -62,7 +64,7 @@ class PluginControllerTest {
|
||||
"jarFile",
|
||||
"plugin-test.jar",
|
||||
"application/java-archive",
|
||||
"This is the file content".getBytes()
|
||||
"This is the file content".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.multipart("/api/plugin")
|
||||
|
||||
+2
-1
@@ -32,6 +32,7 @@ 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;
|
||||
@@ -66,7 +67,7 @@ class JsonImExportServiceTest {
|
||||
void testParseImport() throws IOException {
|
||||
|
||||
String json = "[{}]";
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes());
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
AbstractImExportServiceImpl.MonitorDTO monitorDTO = new AbstractImExportServiceImpl.MonitorDTO();
|
||||
|
||||
|
||||
+5
-1
@@ -28,6 +28,8 @@ 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;
|
||||
@@ -87,7 +89,9 @@ 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());
|
||||
MockMultipartFile mockFile = new MockMultipartFile(
|
||||
"file", "test-plugin.jar", "application/java-archive",
|
||||
"plugin-content".getBytes(StandardCharsets.UTF_8));
|
||||
PluginUpload pluginUpload = new PluginUpload(mockFile, "Test Plugin", true);
|
||||
|
||||
when(metadataDao.save(any(PluginMetadata.class))).thenReturn(new PluginMetadata());
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
+12
-16
@@ -18,8 +18,11 @@
|
||||
package org.apache.hertzbeat.warehouse.store;
|
||||
|
||||
import java.util.Optional;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.plugin.PostCollectPlugin;
|
||||
@@ -27,7 +30,6 @@ import org.apache.hertzbeat.plugin.runner.PluginRunner;
|
||||
import org.apache.hertzbeat.warehouse.WarehouseWorkerPool;
|
||||
import org.apache.hertzbeat.warehouse.store.history.HistoryDataWriter;
|
||||
import org.apache.hertzbeat.warehouse.store.realtime.RealTimeDataWriter;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -44,6 +46,8 @@ public class DataStorageDispatch {
|
||||
private final RealTimeDataWriter realTimeDataWriter;
|
||||
private final Optional<HistoryDataWriter> historyDataWriter;
|
||||
private final PluginRunner pluginRunner;
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
public DataStorageDispatch(CommonDataQueue commonDataQueue,
|
||||
WarehouseWorkerPool workerPool,
|
||||
@@ -87,24 +91,16 @@ public class DataStorageDispatch {
|
||||
if (metricsData.getPriority() == 0) {
|
||||
long id = metricsData.getId();
|
||||
CollectRep.Code code = metricsData.getCode();
|
||||
// query current status
|
||||
String queryStatusSql = "SELECT status FROM hzb_monitor WHERE id = ?";
|
||||
try {
|
||||
int currentStatus = jdbcTemplate.queryForObject(queryStatusSql, Integer.class, id);
|
||||
if (code == CollectRep.Code.SUCCESS && currentStatus == CommonConstants.MONITOR_DOWN_CODE) {
|
||||
// if collect success and current status is DOWN, update to UP
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, id);
|
||||
} else if (code != CollectRep.Code.SUCCESS && currentStatus == CommonConstants.MONITOR_UP_CODE) {
|
||||
// if collect failed and current status is UP, update to DOWN
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql, CommonConstants.MONITOR_DOWN_CODE, id);
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ? AND status != ?";
|
||||
int status = code == CollectRep.Code.SUCCESS ? CommonConstants.MONITOR_UP_CODE : CommonConstants.MONITOR_DOWN_CODE;
|
||||
int matchedRows = jdbcTemplate.update(sql, status, id, status);
|
||||
if (matchedRows > 0) {
|
||||
entityManager.getEntityManagerFactory().getCache().evict(Monitor.class, id);
|
||||
}
|
||||
} catch (EmptyResultDataAccessException ignored) {
|
||||
// when query currentStatus result is null
|
||||
} catch (Exception e) {
|
||||
log.error("Update monitor status failed for monitor id: {}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, BASIC + " " + encodedAuth);
|
||||
}
|
||||
Instant now = Instant.now();
|
||||
|
||||
+4
-6
@@ -40,7 +40,6 @@ 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;
|
||||
@@ -49,6 +48,7 @@ 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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
@@ -262,8 +262,7 @@ 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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)),
|
||||
StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
|
||||
+ SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
@@ -358,8 +357,7 @@ 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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)),
|
||||
StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
|
||||
+ SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
|
||||
+5
-5
@@ -38,7 +38,6 @@ 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;
|
||||
@@ -47,6 +46,7 @@ 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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
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 = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC
|
||||
+ SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
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 |
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
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 |
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
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 | 字节 | 通过网络发送的字节数 |
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
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连接总数 |
|
||||
@@ -189,6 +189,8 @@
|
||||
"help/clickhouse",
|
||||
"help/doris_be",
|
||||
"help/doris_fe",
|
||||
"help/starrocks_be",
|
||||
"help/starrocks_fe",
|
||||
"help/elasticsearch",
|
||||
"help/flink",
|
||||
"help/flink_on_yarn",
|
||||
|
||||
+250
-1028
File diff suppressed because it is too large
Load Diff
+166
-9
@@ -1635,7 +1635,7 @@
|
||||
"@docusaurus/theme-search-algolia" "2.3.1"
|
||||
"@docusaurus/types" "2.3.1"
|
||||
|
||||
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
"@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,6 +2248,11 @@
|
||||
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"
|
||||
@@ -3331,6 +3336,13 @@ 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"
|
||||
@@ -4256,6 +4268,11 @@ 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"
|
||||
@@ -4461,6 +4478,11 @@ 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"
|
||||
@@ -4694,6 +4716,17 @@ 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"
|
||||
@@ -4779,6 +4812,13 @@ 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"
|
||||
@@ -5045,6 +5085,18 @@ 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"
|
||||
@@ -5407,6 +5459,11 @@ 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"
|
||||
@@ -5838,6 +5895,13 @@ 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"
|
||||
@@ -5846,13 +5910,6 @@ 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"
|
||||
@@ -5900,6 +5957,11 @@ 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"
|
||||
@@ -5958,6 +6020,13 @@ 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"
|
||||
@@ -6089,6 +6158,48 @@ 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"
|
||||
@@ -6137,6 +6248,11 @@ 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"
|
||||
@@ -6174,7 +6290,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.2, micromatch@^4.0.4, micromatch@^4.0.5:
|
||||
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==
|
||||
@@ -6182,6 +6298,14 @@ 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"
|
||||
@@ -6723,6 +6847,11 @@ 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"
|
||||
@@ -7140,6 +7269,11 @@ 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"
|
||||
@@ -7346,6 +7480,14 @@ 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"
|
||||
@@ -8171,6 +8313,11 @@ 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"
|
||||
@@ -8700,6 +8847,11 @@ 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"
|
||||
@@ -8748,6 +8900,11 @@ 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"
|
||||
|
||||
@@ -352,8 +352,9 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
|
||||
|
||||
@@ -352,8 +352,9 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
|
||||
|
||||
@@ -284,8 +284,9 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-api/0.12.0 Apache-2.0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user