mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3e6825d0c | ||
|
|
e7e38f3aa3 |
@@ -2325,87 +2325,6 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JuJinPark",
|
||||
"name": "jujin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/44892459?v=4",
|
||||
"profile": "https://github.com/JuJinPark",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LL-LIN",
|
||||
"name": "LL-LIN",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/43002118?v=4",
|
||||
"profile": "https://github.com/LL-LIN",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "bigcyy",
|
||||
"name": "Yang Chen",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73413979?v=4",
|
||||
"profile": "https://bigcyy.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sarthakeash",
|
||||
"name": "Sarthak Arora",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/74091160?v=4",
|
||||
"profile": "https://github.com/sarthakeash",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "PengJingzhao",
|
||||
"name": "彭镜肇",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/97368949?v=4",
|
||||
"profile": "https://github.com/PengJingzhao",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gagaradio",
|
||||
"name": "Walter Jia",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18532370?v=4",
|
||||
"profile": "https://github.com/gagaradio",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "boyucjz",
|
||||
"name": "boyucjz",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18730041?v=4",
|
||||
"profile": "https://github.com/boyucjz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Cyanty",
|
||||
"name": "Cyanty",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/153884653?v=4",
|
||||
"profile": "https://github.com/Cyanty",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "KevinLLF",
|
||||
"name": "Jay丿167",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/85452733?v=4",
|
||||
"profile": "https://github.com/KevinLLF",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
/home export-ignore
|
||||
/hip export-ignore
|
||||
/template-marketplace export-ignore
|
||||
/.github export-ignore
|
||||
/.idea export-ignore
|
||||
/.devcontainer export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
@@ -31,8 +31,6 @@ name: "CodeQL"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '21 13 * * 4'
|
||||
push:
|
||||
branches: [ action* ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
||||
@@ -19,7 +19,7 @@ name: DOC CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev, action* ]
|
||||
branches: [ master, dev ]
|
||||
paths:
|
||||
- 'home/**'
|
||||
pull_request:
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
|
||||
name: 'HertzBeat Doc Pdf Builder'
|
||||
on:
|
||||
push:
|
||||
branches: [ page_build* ]
|
||||
page_build
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -22,7 +22,7 @@ name: Frontend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev, action* ]
|
||||
branches: [ master, dev ]
|
||||
paths:
|
||||
- 'web-app/**'
|
||||
pull_request:
|
||||
|
||||
@@ -22,7 +22,7 @@ name: License Checker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev, action* ]
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
run: |
|
||||
mvn clean install
|
||||
mvn clean package -Prelease -DskipTests
|
||||
cd hertzbeat-collector
|
||||
cd collector
|
||||
mvn clean package -Pcluster -DskipTests
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -13,7 +13,6 @@ mvnw.bat
|
||||
package-lock.json
|
||||
*.zip
|
||||
jdk/
|
||||
data/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
@@ -55,5 +54,3 @@ node_modules
|
||||
application-dev.yml
|
||||
application-mysql.yml
|
||||
application-pg.yml
|
||||
/package.json
|
||||
/yarn.lock
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Apache HertzBeat (incubating)
|
||||
Copyright 2024-2025 The Apache Software Foundation
|
||||
Copyright 2024 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
@@ -510,17 +510,6 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yasminvo"><img src="https://avatars.githubusercontent.com/u/107528848?v=4?s=100" width="100px;" alt="yasminvo"/><br /><sub><b>yasminvo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yasminvo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/notbugggg"><img src="https://avatars.githubusercontent.com/u/147966331?v=4?s=100" width="100px;" alt="不关银渐层的事哦"/><br /><sub><b>不关银渐层的事哦</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=notbugggg" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yyahang"><img src="https://avatars.githubusercontent.com/u/90464876?v=4?s=100" width="100px;" alt="yyahang"/><br /><sub><b>yyahang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yyahang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JuJinPark"><img src="https://avatars.githubusercontent.com/u/44892459?v=4?s=100" width="100px;" alt="jujin"/><br /><sub><b>jujin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LL-LIN"><img src="https://avatars.githubusercontent.com/u/43002118?v=4?s=100" width="100px;" alt="LL-LIN"/><br /><sub><b>LL-LIN</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LL-LIN" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bigcyy.github.io/"><img src="https://avatars.githubusercontent.com/u/73413979?v=4?s=100" width="100px;" alt="Yang Chen"/><br /><sub><b>Yang Chen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sarthakeash"><img src="https://avatars.githubusercontent.com/u/74091160?v=4?s=100" width="100px;" alt="Sarthak Arora"/><br /><sub><b>Sarthak Arora</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PengJingzhao"><img src="https://avatars.githubusercontent.com/u/97368949?v=4?s=100" width="100px;" alt="彭镜肇"/><br /><sub><b>彭镜肇</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PengJingzhao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gagaradio"><img src="https://avatars.githubusercontent.com/u/18532370?v=4?s=100" width="100px;" alt="Walter Jia"/><br /><sub><b>Walter Jia</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gagaradio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -509,17 +509,6 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yasminvo"><img src="https://avatars.githubusercontent.com/u/107528848?v=4?s=100" width="100px;" alt="yasminvo"/><br /><sub><b>yasminvo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yasminvo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/notbugggg"><img src="https://avatars.githubusercontent.com/u/147966331?v=4?s=100" width="100px;" alt="不关银渐层的事哦"/><br /><sub><b>不关银渐层的事哦</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=notbugggg" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yyahang"><img src="https://avatars.githubusercontent.com/u/90464876?v=4?s=100" width="100px;" alt="yyahang"/><br /><sub><b>yyahang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yyahang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JuJinPark"><img src="https://avatars.githubusercontent.com/u/44892459?v=4?s=100" width="100px;" alt="jujin"/><br /><sub><b>jujin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LL-LIN"><img src="https://avatars.githubusercontent.com/u/43002118?v=4?s=100" width="100px;" alt="LL-LIN"/><br /><sub><b>LL-LIN</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LL-LIN" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bigcyy.github.io/"><img src="https://avatars.githubusercontent.com/u/73413979?v=4?s=100" width="100px;" alt="Yang Chen"/><br /><sub><b>Yang Chen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sarthakeash"><img src="https://avatars.githubusercontent.com/u/74091160?v=4?s=100" width="100px;" alt="Sarthak Arora"/><br /><sub><b>Sarthak Arora</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PengJingzhao"><img src="https://avatars.githubusercontent.com/u/97368949?v=4?s=100" width="100px;" alt="彭镜肇"/><br /><sub><b>彭镜肇</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PengJingzhao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gagaradio"><img src="https://avatars.githubusercontent.com/u/18532370?v=4?s=100" width="100px;" alt="Walter Jia"/><br /><sub><b>Walter Jia</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gagaradio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -73,11 +73,6 @@
|
||||
"field": "successCode",
|
||||
"type": 4,
|
||||
"paramValue": "200, 201"
|
||||
},
|
||||
{
|
||||
"field": "enableUrlEncoding",
|
||||
"type": 1,
|
||||
"paramValue": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+5
-30
@@ -17,14 +17,11 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.calculate;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -65,14 +62,12 @@ public class RealTimeAlertCalculator {
|
||||
private static final String KEY_PRIORITY = "__priority__";
|
||||
private static final String KEY_CODE = "__code__";
|
||||
private static final String KEY_AVAILABLE = "__available__";
|
||||
private static final String KEY_LABELS = "__labels__";
|
||||
private static final String UP = "up";
|
||||
private static final String DOWN = "down";
|
||||
private static final String KEY_ROW = "__row__";
|
||||
|
||||
private static final Pattern APP_PATTERN = Pattern.compile("equals\\(__app__,\"([^\"]+)\"\\)");
|
||||
private static final Pattern AVAILABLE_PATTERN = Pattern.compile("equals\\(__available__,\"([^\"]+)\"\\)");
|
||||
private static final Pattern LABEL_PATTERN = Pattern.compile("contains\\(__labels__,\\s*\"([^\"]+)\"\\)");
|
||||
private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\"(\\d+)\"\\)");
|
||||
private static final Pattern METRICS_PATTERN = Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)");
|
||||
|
||||
@@ -141,8 +136,8 @@ public class RealTimeAlertCalculator {
|
||||
Map<String, String> labels = metricsData.getLabels();
|
||||
Map<String, String> annotations = metricsData.getAnnotations();
|
||||
List<AlertDefine> thresholds = this.alertDefineService.getRealTimeAlertDefines();
|
||||
// Filter thresholds by app, metrics, labels and instance
|
||||
thresholds = filterThresholdsByAppAndMetrics(thresholds, app, metrics, labels, instance, priority);
|
||||
// Filter thresholds by app, metrics and instance
|
||||
thresholds = filterThresholdsByAppAndMetrics(thresholds, app, metrics, instance, priority);
|
||||
if (thresholds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +149,6 @@ public class RealTimeAlertCalculator {
|
||||
commonContext.put(KEY_PRIORITY, priority);
|
||||
commonContext.put(KEY_CODE, code);
|
||||
commonContext.put(KEY_METRICS, metrics);
|
||||
commonContext.put(KEY_LABELS, String.join(",", kvLabelsToKvStringSet(labels)));
|
||||
if (priority == 0) {
|
||||
commonContext.put(KEY_AVAILABLE, metricsData.getCode() == CollectRep.Code.SUCCESS ? UP : DOWN);
|
||||
}
|
||||
@@ -270,7 +264,7 @@ public class RealTimeAlertCalculator {
|
||||
* @param priority Current priority
|
||||
* @return Filtered alert definitions
|
||||
*/
|
||||
private List<AlertDefine> filterThresholdsByAppAndMetrics(List<AlertDefine> thresholds, String app, String metrics, Map<String, String> labels, String instance, int priority) {
|
||||
private List<AlertDefine> filterThresholdsByAppAndMetrics(List<AlertDefine> thresholds, String app, String metrics, String instance, int priority) {
|
||||
return thresholds.stream()
|
||||
.filter(define -> {
|
||||
if (StringUtils.isBlank(define.getExpr())) {
|
||||
@@ -300,29 +294,19 @@ public class RealTimeAlertCalculator {
|
||||
|
||||
// Extract and check instance - optional with multiple values
|
||||
Matcher instanceMatcher = INSTANCE_PATTERN.matcher(expr);
|
||||
Matcher labelMatcher = LABEL_PATTERN.matcher(expr);
|
||||
// If no instance and instance labels specified in expr, accept all instances
|
||||
if (!instanceMatcher.find() && !labelMatcher.find()) {
|
||||
// If no instance specified in expr, accept all instances
|
||||
if (!instanceMatcher.find()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reset matcher to check all instances
|
||||
instanceMatcher.reset();
|
||||
labelMatcher.reset();
|
||||
// If instances specified, current instance must match one of them
|
||||
while (instanceMatcher.find()) {
|
||||
if (Objects.equals(instance, instanceMatcher.group(1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If instance labels specified, current instance must match one of them
|
||||
Set<String> labelKvStringSet = kvLabelsToKvStringSet(labels);
|
||||
while (labelMatcher.find()) {
|
||||
String label = labelMatcher.group(1);
|
||||
if (labelKvStringSet.contains(label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@@ -433,13 +417,4 @@ public class RealTimeAlertCalculator {
|
||||
return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
|
||||
+ Arrays.hashCode(valueList.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private Set<String> kvLabelsToKvStringSet(Map<String, String> labels) {
|
||||
if (labels == null || labels.isEmpty()) {
|
||||
return Collections.singleton("");
|
||||
}
|
||||
return labels.entrySet().stream()
|
||||
.map(item -> item.getKey() + ":" + item.getValue())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
-9
@@ -17,39 +17,30 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Alibaba Cloud SMS configuration properties
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AlibabaSmsProperties {
|
||||
/**
|
||||
* Alibaba Cloud access key id
|
||||
*/
|
||||
@NotBlank(message = "AccessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* Alibaba Cloud access key secret
|
||||
*/
|
||||
@NotBlank(message = "AccessKeySecret cannot be empty")
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* SMS signature
|
||||
*/
|
||||
@NotBlank(message = "SignName cannot be empty")
|
||||
private String signName;
|
||||
|
||||
/**
|
||||
* SMS template Code
|
||||
*/
|
||||
@NotBlank(message = "TemplateCode cannot be null")
|
||||
private String templateCode;
|
||||
}
|
||||
|
||||
-4
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -28,19 +27,16 @@ public class AwsSmsProperties {
|
||||
/**
|
||||
* AWS Cloud access key id
|
||||
*/
|
||||
@NotBlank(message = "AccessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* AWS Cloud access key secret
|
||||
*/
|
||||
@NotBlank(message = "AccessKeySecret cannot be empty")
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* AWS Cloud region
|
||||
*/
|
||||
@NotBlank(message = "region cannot be empty")
|
||||
private String region;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -28,8 +25,6 @@ import org.springframework.stereotype.Component;
|
||||
* SMS configuration
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "alerter.sms")
|
||||
public class SmsConfig {
|
||||
@@ -42,7 +37,6 @@ public class SmsConfig {
|
||||
/**
|
||||
* sms service provider
|
||||
*/
|
||||
@NotBlank(message = "Type cannot be empty")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
@@ -64,12 +58,6 @@ public class SmsConfig {
|
||||
* Aws configuration
|
||||
*/
|
||||
private AwsSmsProperties aws;
|
||||
|
||||
/**
|
||||
* Twilio SMS configuration
|
||||
*/
|
||||
private TwilioSmsProperties twilio;
|
||||
|
||||
/**
|
||||
* Smslocal SMS configuration
|
||||
*/
|
||||
|
||||
-4
@@ -18,16 +18,12 @@
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Smslocal SMS Properties
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmslocalSmsProperties {
|
||||
/**
|
||||
* SmsLocal account api key
|
||||
|
||||
-10
@@ -17,45 +17,35 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Tencent Cloud SMS properties
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TencentSmsProperties {
|
||||
/**
|
||||
* Tencent cloud account secret id
|
||||
*/
|
||||
@NotBlank(message = "SecretId cannot be empty")
|
||||
private String secretId;
|
||||
|
||||
/**
|
||||
* Tencent cloud account secret key
|
||||
*/
|
||||
@NotBlank(message = "SecretKey cannot be empty")
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* SMS app id
|
||||
*/
|
||||
@NotBlank(message = "AppId cannot be null")
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* SMS signature
|
||||
*/
|
||||
@NotBlank(message = "SignName cannot be empty")
|
||||
private String signName;
|
||||
|
||||
/**
|
||||
* SMS template ID
|
||||
*/
|
||||
@NotBlank(message = "templateId cannot be null")
|
||||
private String templateId;
|
||||
}
|
||||
-9
@@ -17,22 +17,16 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* UniSMS properties
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UniSmsProperties {
|
||||
/**
|
||||
* UniSMS access key id
|
||||
*/
|
||||
@NotBlank(message = "accessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
@@ -43,18 +37,15 @@ public class UniSmsProperties {
|
||||
/**
|
||||
* SMS signature
|
||||
*/
|
||||
@NotBlank(message = "signature cannot be null")
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* SMS template ID
|
||||
*/
|
||||
@NotBlank(message = "templateId cannot be null")
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* Authentication mode: simple or hmac, default is simple
|
||||
*/
|
||||
@NotBlank(message = "authMode cannot be null")
|
||||
private String authMode = "simple";
|
||||
}
|
||||
+2
-2
@@ -118,11 +118,11 @@ public class TencentCloudExternAlert {
|
||||
/**
|
||||
* Transaction alarm
|
||||
*/
|
||||
public static final String EVENT = "event";
|
||||
private static final String EVENT = "event";
|
||||
|
||||
/**
|
||||
* Indicator alarm
|
||||
*/
|
||||
public static final String METRIC = "metric";
|
||||
private static final String METRIC = "metric";
|
||||
|
||||
}
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Generated from AlertExpression.g4 by ANTLR 4.13.2
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
|
||||
|
||||
/**
|
||||
* This class provides an empty implementation of {@link AlertExpressionVisitor},
|
||||
* which can be extended to create a visitor which only needs to handle a subset
|
||||
* of the available methods.
|
||||
*
|
||||
* @param <T> The return type of the visit operation. Use {@link Void} for
|
||||
* operations with no return type.
|
||||
*/
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public class AlertExpressionBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements AlertExpressionVisitor<T> {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitExpression(AlertExpressionParser.ExpressionContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitAndExpr(AlertExpressionParser.AndExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitQueryExpr(AlertExpressionParser.QueryExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitComparisonExpr(AlertExpressionParser.ComparisonExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitUnlessExpr(AlertExpressionParser.UnlessExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitLiteralExpr(AlertExpressionParser.LiteralExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitParenExpr(AlertExpressionParser.ParenExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitOrExpr(AlertExpressionParser.OrExprContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitIdentifier(AlertExpressionParser.IdentifierContext ctx) { return visitChildren(ctx); }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitNumber(AlertExpressionParser.NumberContext ctx) { return visitChildren(ctx); }
|
||||
}
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Alert expression visitor implement
|
||||
*/
|
||||
public class AlertExpressionEvalVisitor extends AlertExpressionBaseVisitor<List<Map<String, Object>>> {
|
||||
|
||||
private static final String THRESHOLD = "__threshold__";
|
||||
private static final String VALUE = "__value__";
|
||||
|
||||
private final QueryExecutor executor;
|
||||
|
||||
public AlertExpressionEvalVisitor(QueryExecutor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitExpression(AlertExpressionParser.ExpressionContext ctx) {
|
||||
return visit(ctx.expr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitParenExpr(AlertExpressionParser.ParenExprContext ctx) {
|
||||
return visit(ctx.expr());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitComparisonExpr(AlertExpressionParser.ComparisonExprContext ctx) {
|
||||
List<Map<String, Object>> leftResult = visit(ctx.left);
|
||||
List<Map<String, Object>> rightResult = visit(ctx.right);
|
||||
if (rightResult.size() == 1 && rightResult.get(0).containsKey(THRESHOLD)) {
|
||||
double threshold = (double) rightResult.get(0).get(THRESHOLD);
|
||||
String operator = ctx.op.getText();
|
||||
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> item : leftResult) {
|
||||
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, operator, 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));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitAndExpr(AlertExpressionParser.AndExprContext ctx) {
|
||||
List<Map<String, Object>> leftOperand = visit(ctx.left);
|
||||
List<Map<String, Object>> rightOperand = visit(ctx.right);
|
||||
|
||||
Map<String, Object> leftMap = null;
|
||||
boolean leftMatch = false;
|
||||
Map<String, Object> rightMap = null;
|
||||
boolean rightMatch = false;
|
||||
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);
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
} else if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
return new LinkedList<>(List.of(leftMap));
|
||||
} else if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
}
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitOrExpr(AlertExpressionParser.OrExprContext ctx) {
|
||||
List<Map<String, Object>> leftOperand = visit(ctx.left);
|
||||
List<Map<String, Object>> rightOperand = visit(ctx.right);
|
||||
|
||||
Map<String, Object> leftMap = null;
|
||||
boolean leftMatch = false;
|
||||
Map<String, Object> rightMap = null;
|
||||
boolean rightMatch = false;
|
||||
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);
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
} else if (leftMatch) {
|
||||
return new LinkedList<>(List.of(leftMap));
|
||||
} else if (rightMatch) {
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
} else {
|
||||
if (leftMap != null && rightMap != null) {
|
||||
rightMap.putAll(leftMap);
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
} else if (leftMap != null) {
|
||||
return new LinkedList<>(List.of(leftMap));
|
||||
} else if (rightMap != null) {
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
}
|
||||
}
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitUnlessExpr(AlertExpressionParser.UnlessExprContext ctx) {
|
||||
List<Map<String, Object>> leftOperand = visit(ctx.left);
|
||||
List<Map<String, Object>> rightOperand = visit(ctx.right);
|
||||
Map<String, Object> leftMap = null;
|
||||
boolean leftMatch = false;
|
||||
Map<String, Object> rightMap = null;
|
||||
boolean rightMatch = false;
|
||||
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) {
|
||||
return new LinkedList<>(List.of(leftMap));
|
||||
} else {
|
||||
if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
return new LinkedList<>(List.of(leftMap));
|
||||
} else {
|
||||
if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
return new LinkedList<>(List.of(rightMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitQueryExpr(AlertExpressionParser.QueryExprContext ctx) {
|
||||
String query = ctx.identifier().getText();
|
||||
return executor.execute(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> visitLiteralExpr(AlertExpressionParser.LiteralExprContext ctx) {
|
||||
double value = Double.parseDouble(ctx.number().getText());
|
||||
List<Map<String, Object>> numAsList = new ArrayList<>();
|
||||
Map<String, Object> valueMap = new HashMap<>();
|
||||
valueMap.put(THRESHOLD, value);
|
||||
numAsList.add(valueMap);
|
||||
return numAsList;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Generated from AlertExpression.g4 by ANTLR 4.13.2
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.RuntimeMetaData;
|
||||
import org.antlr.v4.runtime.Vocabulary;
|
||||
import org.antlr.v4.runtime.VocabularyImpl;
|
||||
import org.antlr.v4.runtime.atn.ATN;
|
||||
import org.antlr.v4.runtime.atn.ATNDeserializer;
|
||||
import org.antlr.v4.runtime.atn.LexerATNSimulator;
|
||||
import org.antlr.v4.runtime.atn.PredictionContextCache;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
|
||||
public class AlertExpressionLexer extends Lexer {
|
||||
static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); }
|
||||
|
||||
protected static final DFA[] _decisionToDFA;
|
||||
protected static final PredictionContextCache _sharedContextCache =
|
||||
new PredictionContextCache();
|
||||
public static final int
|
||||
AND=1, OR=2, UNLESS=3, GT=4, GE=5, LT=6, LE=7, EQ=8, NE=9, LPAREN=10,
|
||||
RPAREN=11, IDENTIFIER=12, NUMBER=13, WS=14;
|
||||
public static String[] channelNames = {
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
||||
};
|
||||
|
||||
public static String[] modeNames = {
|
||||
"DEFAULT_MODE"
|
||||
};
|
||||
|
||||
private static String[] makeRuleNames() {
|
||||
return new String[] {
|
||||
"AND", "OR", "UNLESS", "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN",
|
||||
"RPAREN", "IDENTIFIER", "NUMBER", "WS"
|
||||
};
|
||||
}
|
||||
public static final String[] ruleNames = makeRuleNames();
|
||||
|
||||
private static String[] makeLiteralNames() {
|
||||
return new String[] {
|
||||
null, "'and'", "'or'", "'unless'", "'>'", "'>='", "'<'", "'<='", "'=='",
|
||||
"'!='", "'('", "')'"
|
||||
};
|
||||
}
|
||||
private static final String[] _LITERAL_NAMES = makeLiteralNames();
|
||||
private static String[] makeSymbolicNames() {
|
||||
return new String[] {
|
||||
null, "AND", "OR", "UNLESS", "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN",
|
||||
"RPAREN", "IDENTIFIER", "NUMBER", "WS"
|
||||
};
|
||||
}
|
||||
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
|
||||
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #VOCABULARY} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String[] tokenNames;
|
||||
static {
|
||||
tokenNames = new String[_SYMBOLIC_NAMES.length];
|
||||
for (int i = 0; i < tokenNames.length; i++) {
|
||||
tokenNames[i] = VOCABULARY.getLiteralName(i);
|
||||
if (tokenNames[i] == null) {
|
||||
tokenNames[i] = VOCABULARY.getSymbolicName(i);
|
||||
}
|
||||
|
||||
if (tokenNames[i] == null) {
|
||||
tokenNames[i] = "<INVALID>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String[] getTokenNames() {
|
||||
return tokenNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public Vocabulary getVocabulary() {
|
||||
return VOCABULARY;
|
||||
}
|
||||
|
||||
|
||||
public AlertExpressionLexer(CharStream input) {
|
||||
super(input);
|
||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGrammarFileName() { return "AlertExpression.g4"; }
|
||||
|
||||
@Override
|
||||
public String[] getRuleNames() { return ruleNames; }
|
||||
|
||||
@Override
|
||||
public String getSerializedATN() { return _serializedATN; }
|
||||
|
||||
@Override
|
||||
public String[] getChannelNames() { return channelNames; }
|
||||
|
||||
@Override
|
||||
public String[] getModeNames() { return modeNames; }
|
||||
|
||||
@Override
|
||||
public ATN getATN() { return _ATN; }
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\u0004\u0000\u000eZ\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002\u0001"+
|
||||
"\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004"+
|
||||
"\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007"+
|
||||
"\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b"+
|
||||
"\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0001\u0000\u0001\u0000\u0001"+
|
||||
"\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001"+
|
||||
"\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001"+
|
||||
"\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001"+
|
||||
"\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001"+
|
||||
"\u0007\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001\n\u0001\n\u0001\u000b"+
|
||||
"\u0001\u000b\u0005\u000bB\b\u000b\n\u000b\f\u000bE\t\u000b\u0001\f\u0004"+
|
||||
"\fH\b\f\u000b\f\f\fI\u0001\f\u0001\f\u0004\fN\b\f\u000b\f\f\fO\u0003\f"+
|
||||
"R\b\f\u0001\r\u0004\rU\b\r\u000b\r\f\rV\u0001\r\u0001\r\u0000\u0000\u000e"+
|
||||
"\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t\u0005\u000b\u0006\r"+
|
||||
"\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f\u0019\r\u001b\u000e"+
|
||||
"\u0001\u0000\u0004\u0003\u0000AZ__az\n\u0000\"\"..09==A[]]__a{}}\u8000"+
|
||||
"\uff5e\u8000\uff5e\u0001\u000009\u0003\u0000\t\n\r\r ^\u0000\u0001\u0001"+
|
||||
"\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000\u0005\u0001"+
|
||||
"\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001\u0000"+
|
||||
"\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000\u0000"+
|
||||
"\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011\u0001\u0000\u0000"+
|
||||
"\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015\u0001\u0000\u0000"+
|
||||
"\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000\u0019\u0001\u0000\u0000"+
|
||||
"\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0001\u001d\u0001\u0000\u0000"+
|
||||
"\u0000\u0003!\u0001\u0000\u0000\u0000\u0005$\u0001\u0000\u0000\u0000\u0007"+
|
||||
"+\u0001\u0000\u0000\u0000\t-\u0001\u0000\u0000\u0000\u000b0\u0001\u0000"+
|
||||
"\u0000\u0000\r2\u0001\u0000\u0000\u0000\u000f5\u0001\u0000\u0000\u0000"+
|
||||
"\u00118\u0001\u0000\u0000\u0000\u0013;\u0001\u0000\u0000\u0000\u0015="+
|
||||
"\u0001\u0000\u0000\u0000\u0017?\u0001\u0000\u0000\u0000\u0019G\u0001\u0000"+
|
||||
"\u0000\u0000\u001bT\u0001\u0000\u0000\u0000\u001d\u001e\u0005a\u0000\u0000"+
|
||||
"\u001e\u001f\u0005n\u0000\u0000\u001f \u0005d\u0000\u0000 \u0002\u0001"+
|
||||
"\u0000\u0000\u0000!\"\u0005o\u0000\u0000\"#\u0005r\u0000\u0000#\u0004"+
|
||||
"\u0001\u0000\u0000\u0000$%\u0005u\u0000\u0000%&\u0005n\u0000\u0000&\'"+
|
||||
"\u0005l\u0000\u0000\'(\u0005e\u0000\u0000()\u0005s\u0000\u0000)*\u0005"+
|
||||
"s\u0000\u0000*\u0006\u0001\u0000\u0000\u0000+,\u0005>\u0000\u0000,\b\u0001"+
|
||||
"\u0000\u0000\u0000-.\u0005>\u0000\u0000./\u0005=\u0000\u0000/\n\u0001"+
|
||||
"\u0000\u0000\u000001\u0005<\u0000\u00001\f\u0001\u0000\u0000\u000023\u0005"+
|
||||
"<\u0000\u000034\u0005=\u0000\u00004\u000e\u0001\u0000\u0000\u000056\u0005"+
|
||||
"=\u0000\u000067\u0005=\u0000\u00007\u0010\u0001\u0000\u0000\u000089\u0005"+
|
||||
"!\u0000\u00009:\u0005=\u0000\u0000:\u0012\u0001\u0000\u0000\u0000;<\u0005"+
|
||||
"(\u0000\u0000<\u0014\u0001\u0000\u0000\u0000=>\u0005)\u0000\u0000>\u0016"+
|
||||
"\u0001\u0000\u0000\u0000?C\u0007\u0000\u0000\u0000@B\u0007\u0001\u0000"+
|
||||
"\u0000A@\u0001\u0000\u0000\u0000BE\u0001\u0000\u0000\u0000CA\u0001\u0000"+
|
||||
"\u0000\u0000CD\u0001\u0000\u0000\u0000D\u0018\u0001\u0000\u0000\u0000"+
|
||||
"EC\u0001\u0000\u0000\u0000FH\u0007\u0002\u0000\u0000GF\u0001\u0000\u0000"+
|
||||
"\u0000HI\u0001\u0000\u0000\u0000IG\u0001\u0000\u0000\u0000IJ\u0001\u0000"+
|
||||
"\u0000\u0000JQ\u0001\u0000\u0000\u0000KM\u0005.\u0000\u0000LN\u0007\u0002"+
|
||||
"\u0000\u0000ML\u0001\u0000\u0000\u0000NO\u0001\u0000\u0000\u0000OM\u0001"+
|
||||
"\u0000\u0000\u0000OP\u0001\u0000\u0000\u0000PR\u0001\u0000\u0000\u0000"+
|
||||
"QK\u0001\u0000\u0000\u0000QR\u0001\u0000\u0000\u0000R\u001a\u0001\u0000"+
|
||||
"\u0000\u0000SU\u0007\u0003\u0000\u0000TS\u0001\u0000\u0000\u0000UV\u0001"+
|
||||
"\u0000\u0000\u0000VT\u0001\u0000\u0000\u0000VW\u0001\u0000\u0000\u0000"+
|
||||
"WX\u0001\u0000\u0000\u0000XY\u0006\r\u0000\u0000Y\u001c\u0001\u0000\u0000"+
|
||||
"\u0000\u0006\u0000CIOQV\u0001\u0006\u0000\u0000";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
|
||||
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
|
||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
-570
@@ -1,570 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Generated from AlertExpression.g4 by ANTLR 4.13.2
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.antlr.v4.runtime.FailedPredicateException;
|
||||
import org.antlr.v4.runtime.NoViableAltException;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.RuleContext;
|
||||
import org.antlr.v4.runtime.RuntimeMetaData;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenStream;
|
||||
import org.antlr.v4.runtime.Vocabulary;
|
||||
import org.antlr.v4.runtime.VocabularyImpl;
|
||||
import org.antlr.v4.runtime.atn.ATN;
|
||||
import org.antlr.v4.runtime.atn.ATNDeserializer;
|
||||
import org.antlr.v4.runtime.atn.ParserATNSimulator;
|
||||
import org.antlr.v4.runtime.atn.PredictionContextCache;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue", "this-escape"})
|
||||
public class AlertExpressionParser extends Parser {
|
||||
static { RuntimeMetaData.checkVersion("4.13.2", RuntimeMetaData.VERSION); }
|
||||
|
||||
protected static final DFA[] _decisionToDFA;
|
||||
protected static final PredictionContextCache _sharedContextCache =
|
||||
new PredictionContextCache();
|
||||
public static final int
|
||||
AND=1, OR=2, UNLESS=3, GT=4, GE=5, LT=6, LE=7, EQ=8, NE=9, LPAREN=10,
|
||||
RPAREN=11, IDENTIFIER=12, NUMBER=13, WS=14;
|
||||
public static final int
|
||||
RULE_expression = 0, RULE_expr = 1, RULE_identifier = 2, RULE_number = 3;
|
||||
private static String[] makeRuleNames() {
|
||||
return new String[] {
|
||||
"expression", "expr", "identifier", "number"
|
||||
};
|
||||
}
|
||||
public static final String[] ruleNames = makeRuleNames();
|
||||
|
||||
private static String[] makeLiteralNames() {
|
||||
return new String[] {
|
||||
null, "'and'", "'or'", "'unless'", "'>'", "'>='", "'<'", "'<='", "'=='",
|
||||
"'!='", "'('", "')'"
|
||||
};
|
||||
}
|
||||
private static final String[] _LITERAL_NAMES = makeLiteralNames();
|
||||
private static String[] makeSymbolicNames() {
|
||||
return new String[] {
|
||||
null, "AND", "OR", "UNLESS", "GT", "GE", "LT", "LE", "EQ", "NE", "LPAREN",
|
||||
"RPAREN", "IDENTIFIER", "NUMBER", "WS"
|
||||
};
|
||||
}
|
||||
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
|
||||
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #VOCABULARY} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String[] tokenNames;
|
||||
static {
|
||||
tokenNames = new String[_SYMBOLIC_NAMES.length];
|
||||
for (int i = 0; i < tokenNames.length; i++) {
|
||||
tokenNames[i] = VOCABULARY.getLiteralName(i);
|
||||
if (tokenNames[i] == null) {
|
||||
tokenNames[i] = VOCABULARY.getSymbolicName(i);
|
||||
}
|
||||
|
||||
if (tokenNames[i] == null) {
|
||||
tokenNames[i] = "<INVALID>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String[] getTokenNames() {
|
||||
return tokenNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public Vocabulary getVocabulary() {
|
||||
return VOCABULARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGrammarFileName() { return "AlertExpression.g4"; }
|
||||
|
||||
@Override
|
||||
public String[] getRuleNames() { return ruleNames; }
|
||||
|
||||
@Override
|
||||
public String getSerializedATN() { return _serializedATN; }
|
||||
|
||||
@Override
|
||||
public ATN getATN() { return _ATN; }
|
||||
|
||||
public AlertExpressionParser(TokenStream input) {
|
||||
super(input);
|
||||
_interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
||||
}
|
||||
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class ExpressionContext extends ParserRuleContext {
|
||||
public ExprContext expr() {
|
||||
return getRuleContext(ExprContext.class,0);
|
||||
}
|
||||
public TerminalNode EOF() { return getToken(AlertExpressionParser.EOF, 0); }
|
||||
public ExpressionContext(ParserRuleContext parent, int invokingState) {
|
||||
super(parent, invokingState);
|
||||
}
|
||||
@Override public int getRuleIndex() { return RULE_expression; }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitExpression(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final ExpressionContext expression() throws RecognitionException {
|
||||
ExpressionContext _localctx = new ExpressionContext(_ctx, getState());
|
||||
enterRule(_localctx, 0, RULE_expression);
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(8);
|
||||
expr(0);
|
||||
setState(9);
|
||||
match(EOF);
|
||||
}
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
_localctx.exception = re;
|
||||
_errHandler.reportError(this, re);
|
||||
_errHandler.recover(this, re);
|
||||
}
|
||||
finally {
|
||||
exitRule();
|
||||
}
|
||||
return _localctx;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class ExprContext extends ParserRuleContext {
|
||||
public ExprContext(ParserRuleContext parent, int invokingState) {
|
||||
super(parent, invokingState);
|
||||
}
|
||||
@Override public int getRuleIndex() { return RULE_expr; }
|
||||
|
||||
public ExprContext() { }
|
||||
public void copyFrom(ExprContext ctx) {
|
||||
super.copyFrom(ctx);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class AndExprContext extends ExprContext {
|
||||
public ExprContext left;
|
||||
public ExprContext right;
|
||||
public TerminalNode AND() { return getToken(AlertExpressionParser.AND, 0); }
|
||||
public List<ExprContext> expr() {
|
||||
return getRuleContexts(ExprContext.class);
|
||||
}
|
||||
public ExprContext expr(int i) {
|
||||
return getRuleContext(ExprContext.class,i);
|
||||
}
|
||||
public AndExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitAndExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class QueryExprContext extends ExprContext {
|
||||
public IdentifierContext identifier() {
|
||||
return getRuleContext(IdentifierContext.class,0);
|
||||
}
|
||||
public QueryExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitQueryExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class ComparisonExprContext extends ExprContext {
|
||||
public ExprContext left;
|
||||
public Token op;
|
||||
public ExprContext right;
|
||||
public List<ExprContext> expr() {
|
||||
return getRuleContexts(ExprContext.class);
|
||||
}
|
||||
public ExprContext expr(int i) {
|
||||
return getRuleContext(ExprContext.class,i);
|
||||
}
|
||||
public TerminalNode GE() { return getToken(AlertExpressionParser.GE, 0); }
|
||||
public TerminalNode LE() { return getToken(AlertExpressionParser.LE, 0); }
|
||||
public TerminalNode GT() { return getToken(AlertExpressionParser.GT, 0); }
|
||||
public TerminalNode LT() { return getToken(AlertExpressionParser.LT, 0); }
|
||||
public TerminalNode EQ() { return getToken(AlertExpressionParser.EQ, 0); }
|
||||
public TerminalNode NE() { return getToken(AlertExpressionParser.NE, 0); }
|
||||
public ComparisonExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitComparisonExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class UnlessExprContext extends ExprContext {
|
||||
public ExprContext left;
|
||||
public ExprContext right;
|
||||
public TerminalNode UNLESS() { return getToken(AlertExpressionParser.UNLESS, 0); }
|
||||
public List<ExprContext> expr() {
|
||||
return getRuleContexts(ExprContext.class);
|
||||
}
|
||||
public ExprContext expr(int i) {
|
||||
return getRuleContext(ExprContext.class,i);
|
||||
}
|
||||
public UnlessExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitUnlessExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class LiteralExprContext extends ExprContext {
|
||||
public NumberContext number() {
|
||||
return getRuleContext(NumberContext.class,0);
|
||||
}
|
||||
public LiteralExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitLiteralExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class ParenExprContext extends ExprContext {
|
||||
public TerminalNode LPAREN() { return getToken(AlertExpressionParser.LPAREN, 0); }
|
||||
public ExprContext expr() {
|
||||
return getRuleContext(ExprContext.class,0);
|
||||
}
|
||||
public TerminalNode RPAREN() { return getToken(AlertExpressionParser.RPAREN, 0); }
|
||||
public ParenExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitParenExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class OrExprContext extends ExprContext {
|
||||
public ExprContext left;
|
||||
public ExprContext right;
|
||||
public TerminalNode OR() { return getToken(AlertExpressionParser.OR, 0); }
|
||||
public List<ExprContext> expr() {
|
||||
return getRuleContexts(ExprContext.class);
|
||||
}
|
||||
public ExprContext expr(int i) {
|
||||
return getRuleContext(ExprContext.class,i);
|
||||
}
|
||||
public OrExprContext(ExprContext ctx) { copyFrom(ctx); }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitOrExpr(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final ExprContext expr() throws RecognitionException {
|
||||
return expr(0);
|
||||
}
|
||||
|
||||
private ExprContext expr(int _p) throws RecognitionException {
|
||||
ParserRuleContext _parentctx = _ctx;
|
||||
int _parentState = getState();
|
||||
ExprContext _localctx = new ExprContext(_ctx, _parentState);
|
||||
ExprContext _prevctx = _localctx;
|
||||
int _startState = 2;
|
||||
enterRecursionRule(_localctx, 2, RULE_expr, _p);
|
||||
int _la;
|
||||
try {
|
||||
int _alt;
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(18);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case LPAREN:
|
||||
{
|
||||
_localctx = new ParenExprContext(_localctx);
|
||||
_ctx = _localctx;
|
||||
_prevctx = _localctx;
|
||||
|
||||
setState(12);
|
||||
match(LPAREN);
|
||||
setState(13);
|
||||
expr(0);
|
||||
setState(14);
|
||||
match(RPAREN);
|
||||
}
|
||||
break;
|
||||
case IDENTIFIER:
|
||||
{
|
||||
_localctx = new QueryExprContext(_localctx);
|
||||
_ctx = _localctx;
|
||||
_prevctx = _localctx;
|
||||
setState(16);
|
||||
identifier();
|
||||
}
|
||||
break;
|
||||
case NUMBER:
|
||||
{
|
||||
_localctx = new LiteralExprContext(_localctx);
|
||||
_ctx = _localctx;
|
||||
_prevctx = _localctx;
|
||||
setState(17);
|
||||
number();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
_ctx.stop = _input.LT(-1);
|
||||
setState(34);
|
||||
_errHandler.sync(this);
|
||||
_alt = getInterpreter().adaptivePredict(_input,2,_ctx);
|
||||
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
|
||||
if ( _alt==1 ) {
|
||||
if ( _parseListeners!=null ) triggerExitRuleEvent();
|
||||
_prevctx = _localctx;
|
||||
{
|
||||
setState(32);
|
||||
_errHandler.sync(this);
|
||||
switch ( getInterpreter().adaptivePredict(_input,1,_ctx) ) {
|
||||
case 1:
|
||||
{
|
||||
_localctx = new ComparisonExprContext(new ExprContext(_parentctx, _parentState));
|
||||
((ComparisonExprContext)_localctx).left = _prevctx;
|
||||
pushNewRecursionContext(_localctx, _startState, RULE_expr);
|
||||
setState(20);
|
||||
if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)");
|
||||
setState(21);
|
||||
((ComparisonExprContext)_localctx).op = _input.LT(1);
|
||||
_la = _input.LA(1);
|
||||
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 1008L) != 0)) ) {
|
||||
((ComparisonExprContext)_localctx).op = (Token)_errHandler.recoverInline(this);
|
||||
}
|
||||
else {
|
||||
if ( _input.LA(1)==Token.EOF ) matchedEOF = true;
|
||||
_errHandler.reportMatch(this);
|
||||
consume();
|
||||
}
|
||||
setState(22);
|
||||
((ComparisonExprContext)_localctx).right = expr(7);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
_localctx = new AndExprContext(new ExprContext(_parentctx, _parentState));
|
||||
((AndExprContext)_localctx).left = _prevctx;
|
||||
pushNewRecursionContext(_localctx, _startState, RULE_expr);
|
||||
setState(23);
|
||||
if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)");
|
||||
setState(24);
|
||||
match(AND);
|
||||
setState(25);
|
||||
((AndExprContext)_localctx).right = expr(6);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
_localctx = new UnlessExprContext(new ExprContext(_parentctx, _parentState));
|
||||
((UnlessExprContext)_localctx).left = _prevctx;
|
||||
pushNewRecursionContext(_localctx, _startState, RULE_expr);
|
||||
setState(26);
|
||||
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
|
||||
setState(27);
|
||||
match(UNLESS);
|
||||
setState(28);
|
||||
((UnlessExprContext)_localctx).right = expr(5);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
_localctx = new OrExprContext(new ExprContext(_parentctx, _parentState));
|
||||
((OrExprContext)_localctx).left = _prevctx;
|
||||
pushNewRecursionContext(_localctx, _startState, RULE_expr);
|
||||
setState(29);
|
||||
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
|
||||
setState(30);
|
||||
match(OR);
|
||||
setState(31);
|
||||
((OrExprContext)_localctx).right = expr(4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(36);
|
||||
_errHandler.sync(this);
|
||||
_alt = getInterpreter().adaptivePredict(_input,2,_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
_localctx.exception = re;
|
||||
_errHandler.reportError(this, re);
|
||||
_errHandler.recover(this, re);
|
||||
}
|
||||
finally {
|
||||
unrollRecursionContexts(_parentctx);
|
||||
}
|
||||
return _localctx;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class IdentifierContext extends ParserRuleContext {
|
||||
public TerminalNode IDENTIFIER() { return getToken(AlertExpressionParser.IDENTIFIER, 0); }
|
||||
public IdentifierContext(ParserRuleContext parent, int invokingState) {
|
||||
super(parent, invokingState);
|
||||
}
|
||||
@Override public int getRuleIndex() { return RULE_identifier; }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitIdentifier(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final IdentifierContext identifier() throws RecognitionException {
|
||||
IdentifierContext _localctx = new IdentifierContext(_ctx, getState());
|
||||
enterRule(_localctx, 4, RULE_identifier);
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(37);
|
||||
match(IDENTIFIER);
|
||||
}
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
_localctx.exception = re;
|
||||
_errHandler.reportError(this, re);
|
||||
_errHandler.recover(this, re);
|
||||
}
|
||||
finally {
|
||||
exitRule();
|
||||
}
|
||||
return _localctx;
|
||||
}
|
||||
|
||||
@SuppressWarnings("CheckReturnValue")
|
||||
public static class NumberContext extends ParserRuleContext {
|
||||
public TerminalNode NUMBER() { return getToken(AlertExpressionParser.NUMBER, 0); }
|
||||
public NumberContext(ParserRuleContext parent, int invokingState) {
|
||||
super(parent, invokingState);
|
||||
}
|
||||
@Override public int getRuleIndex() { return RULE_number; }
|
||||
@Override
|
||||
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
|
||||
if ( visitor instanceof AlertExpressionVisitor ) return ((AlertExpressionVisitor<? extends T>)visitor).visitNumber(this);
|
||||
else return visitor.visitChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final NumberContext number() throws RecognitionException {
|
||||
NumberContext _localctx = new NumberContext(_ctx, getState());
|
||||
enterRule(_localctx, 6, RULE_number);
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(39);
|
||||
match(NUMBER);
|
||||
}
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
_localctx.exception = re;
|
||||
_errHandler.reportError(this, re);
|
||||
_errHandler.recover(this, re);
|
||||
}
|
||||
finally {
|
||||
exitRule();
|
||||
}
|
||||
return _localctx;
|
||||
}
|
||||
|
||||
public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
|
||||
switch (ruleIndex) {
|
||||
case 1:
|
||||
return expr_sempred((ExprContext)_localctx, predIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private boolean expr_sempred(ExprContext _localctx, int predIndex) {
|
||||
switch (predIndex) {
|
||||
case 0:
|
||||
return precpred(_ctx, 6);
|
||||
case 1:
|
||||
return precpred(_ctx, 5);
|
||||
case 2:
|
||||
return precpred(_ctx, 4);
|
||||
case 3:
|
||||
return precpred(_ctx, 3);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\u0004\u0001\u000e*\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
|
||||
"\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0001\u0000\u0001\u0000\u0001"+
|
||||
"\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+
|
||||
"\u0001\u0001\u0001\u0003\u0001\u0013\b\u0001\u0001\u0001\u0001\u0001\u0001"+
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0001!\b\u0001\n\u0001"+
|
||||
"\f\u0001$\t\u0001\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001"+
|
||||
"\u0003\u0000\u0001\u0002\u0004\u0000\u0002\u0004\u0006\u0000\u0001\u0001"+
|
||||
"\u0000\u0004\t+\u0000\b\u0001\u0000\u0000\u0000\u0002\u0012\u0001\u0000"+
|
||||
"\u0000\u0000\u0004%\u0001\u0000\u0000\u0000\u0006\'\u0001\u0000\u0000"+
|
||||
"\u0000\b\t\u0003\u0002\u0001\u0000\t\n\u0005\u0000\u0000\u0001\n\u0001"+
|
||||
"\u0001\u0000\u0000\u0000\u000b\f\u0006\u0001\uffff\uffff\u0000\f\r\u0005"+
|
||||
"\n\u0000\u0000\r\u000e\u0003\u0002\u0001\u0000\u000e\u000f\u0005\u000b"+
|
||||
"\u0000\u0000\u000f\u0013\u0001\u0000\u0000\u0000\u0010\u0013\u0003\u0004"+
|
||||
"\u0002\u0000\u0011\u0013\u0003\u0006\u0003\u0000\u0012\u000b\u0001\u0000"+
|
||||
"\u0000\u0000\u0012\u0010\u0001\u0000\u0000\u0000\u0012\u0011\u0001\u0000"+
|
||||
"\u0000\u0000\u0013\"\u0001\u0000\u0000\u0000\u0014\u0015\n\u0006\u0000"+
|
||||
"\u0000\u0015\u0016\u0007\u0000\u0000\u0000\u0016!\u0003\u0002\u0001\u0007"+
|
||||
"\u0017\u0018\n\u0005\u0000\u0000\u0018\u0019\u0005\u0001\u0000\u0000\u0019"+
|
||||
"!\u0003\u0002\u0001\u0006\u001a\u001b\n\u0004\u0000\u0000\u001b\u001c"+
|
||||
"\u0005\u0003\u0000\u0000\u001c!\u0003\u0002\u0001\u0005\u001d\u001e\n"+
|
||||
"\u0003\u0000\u0000\u001e\u001f\u0005\u0002\u0000\u0000\u001f!\u0003\u0002"+
|
||||
"\u0001\u0004 \u0014\u0001\u0000\u0000\u0000 \u0017\u0001\u0000\u0000\u0000"+
|
||||
" \u001a\u0001\u0000\u0000\u0000 \u001d\u0001\u0000\u0000\u0000!$\u0001"+
|
||||
"\u0000\u0000\u0000\" \u0001\u0000\u0000\u0000\"#\u0001\u0000\u0000\u0000"+
|
||||
"#\u0003\u0001\u0000\u0000\u0000$\"\u0001\u0000\u0000\u0000%&\u0005\f\u0000"+
|
||||
"\u0000&\u0005\u0001\u0000\u0000\u0000\'(\u0005\r\u0000\u0000(\u0007\u0001"+
|
||||
"\u0000\u0000\u0000\u0003\u0012 \"";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
|
||||
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
|
||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Generated from AlertExpression.g4 by ANTLR 4.13.2
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
|
||||
|
||||
/**
|
||||
* This interface defines a complete generic visitor for a parse tree produced
|
||||
* by {@link AlertExpressionParser}.
|
||||
*
|
||||
* @param <T> The return type of the visit operation. Use {@link Void} for
|
||||
* operations with no return type.
|
||||
*/
|
||||
public interface AlertExpressionVisitor<T> extends ParseTreeVisitor<T> {
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by {@link AlertExpressionParser#expression}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitExpression(AlertExpressionParser.ExpressionContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code AndExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitAndExpr(AlertExpressionParser.AndExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code QueryExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitQueryExpr(AlertExpressionParser.QueryExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code ComparisonExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitComparisonExpr(AlertExpressionParser.ComparisonExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code UnlessExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitUnlessExpr(AlertExpressionParser.UnlessExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code LiteralExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitLiteralExpr(AlertExpressionParser.LiteralExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code ParenExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitParenExpr(AlertExpressionParser.ParenExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code OrExpr}
|
||||
* labeled alternative in AlertExpressionParser#expr
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitOrExpr(AlertExpressionParser.OrExprContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by {@link AlertExpressionParser#identifier}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitIdentifier(AlertExpressionParser.IdentifierContext ctx);
|
||||
|
||||
/**
|
||||
* Visit a parse tree produced by {@link AlertExpressionParser#number}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitNumber(AlertExpressionParser.NumberContext ctx);
|
||||
}
|
||||
-3
@@ -48,7 +48,6 @@ final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
TelegramBotNotifyDTO notifyBody = TelegramBotNotifyDTO.builder()
|
||||
.chatId(receiver.getTgUserId())
|
||||
.text(renderContent(noticeTemplate, alert))
|
||||
.messageThreadId(receiver.getTgMessageThreadId())
|
||||
.disableWebPagePreview(true)
|
||||
.build();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -85,8 +84,6 @@ final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
private String text;
|
||||
@JsonProperty("disable_web_page_preview")
|
||||
private Boolean disableWebPagePreview;
|
||||
@JsonProperty("message_thread_id")
|
||||
private String messageThreadId;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
|
||||
+1
-6
@@ -23,7 +23,6 @@ import org.apache.hertzbeat.alert.config.SmsConfig;
|
||||
import org.apache.hertzbeat.alert.service.impl.SmsLocalSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.AwsSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.TencentSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.TwilioSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.UniSmsClientImpl;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlibabaSmsClientImpl;
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
@@ -36,7 +35,6 @@ import org.springframework.stereotype.Component;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.ALIBABA;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.AWS;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.TENCENT;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.TWILIO;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.UNISMS;
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.SMSLOCAL;
|
||||
|
||||
@@ -143,12 +141,9 @@ public class SmsClientFactory {
|
||||
case AWS:
|
||||
currentSmsClient = new AwsSmsClientImpl(smsConfig.getAws());
|
||||
break;
|
||||
case TWILIO:
|
||||
currentSmsClient = new TwilioSmsClientImpl(smsConfig.getTwilio());
|
||||
break;
|
||||
default:
|
||||
log.warn("[SmsClientFactory] Unsupported SMS provider type: {}", smsConfig.getType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+285
-42
@@ -17,26 +17,21 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Stack;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.apache.hertzbeat.alert.expr.AlertExpressionEvalVisitor;
|
||||
import org.apache.hertzbeat.alert.expr.AlertExpressionLexer;
|
||||
import org.apache.hertzbeat.alert.expr.AlertExpressionParser;
|
||||
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 org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* datasource service
|
||||
@@ -44,25 +39,14 @@ import java.util.concurrent.TimeUnit;
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
@Setter
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<QueryExecutor> executors;
|
||||
|
||||
@Getter
|
||||
private final Cache<String, ParseTree> expressionCache = Caffeine.newBuilder()
|
||||
.maximumSize(256)
|
||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||
.recordStats()
|
||||
.build();
|
||||
|
||||
@Getter
|
||||
private final Cache<String, CommonTokenStream> tokenStreamCache = Caffeine.newBuilder()
|
||||
.maximumSize(512)
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.recordStats()
|
||||
.build();
|
||||
|
||||
|
||||
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>> calculate(String datasource, String expr) {
|
||||
if (!StringUtils.hasText(expr)) {
|
||||
@@ -72,7 +56,6 @@ public class DataSourceServiceImpl implements DataSourceService {
|
||||
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);
|
||||
}
|
||||
@@ -87,18 +70,278 @@ public class DataSourceServiceImpl implements DataSourceService {
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> evaluate(String expr, QueryExecutor executor) {
|
||||
ParseTree tree = expressionCache.get(expr, e -> {
|
||||
CommonTokenStream tokens = tokenStreamCache.get(e, this::createTokenStream);
|
||||
AlertExpressionParser parser = new AlertExpressionParser(tokens);
|
||||
return parser.expr();
|
||||
});
|
||||
AlertExpressionEvalVisitor visitor = new AlertExpressionEvalVisitor(executor);
|
||||
return visitor.visit(tree);
|
||||
|
||||
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 CommonTokenStream createTokenStream(String expr) {
|
||||
AlertExpressionLexer lexer = new AlertExpressionLexer(CharStreams.fromString(expr));
|
||||
return new CommonTokenStream(lexer);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-174
@@ -18,22 +18,14 @@
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.alert.dto.TencentCloudExternAlert;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Default external alarm service impl
|
||||
*/
|
||||
@@ -51,7 +43,8 @@ public class TencentExternAlertService implements ExternAlertService {
|
||||
log.warn("parse extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
SingleAlert alert = new TencentCloudAlertConverter().convert(report);
|
||||
// todo convert TenCloudAlertReport to SingleAlert
|
||||
SingleAlert alert = new SingleAlert();
|
||||
alarmCommonReduce.reduceAndSendAlarm(alert);
|
||||
}
|
||||
|
||||
@@ -59,169 +52,4 @@ public class TencentExternAlertService implements ExternAlertService {
|
||||
public String supportSource() {
|
||||
return "tencent";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter: TencentCloud alert to SingleAlert
|
||||
*/
|
||||
public static class TencentCloudAlertConverter {
|
||||
|
||||
/**
|
||||
* Metric alert content template
|
||||
*/
|
||||
private final String metricTemplate = "Tencent Cloud Metric Alert: {metricShowName} {calcType} {calcValue}{calcUnit}, Current: {currentValue}{unit}";
|
||||
|
||||
/**
|
||||
* Event alert content template
|
||||
*/
|
||||
private final String eventTemplate = "Tencent Cloud Event Alert: {productShowName} - {eventShowName}";
|
||||
|
||||
|
||||
/**
|
||||
* Convert TencentCloud alert to SingleAlert
|
||||
*/
|
||||
public SingleAlert convert(TencentCloudExternAlert tencentAlert) {
|
||||
// Building basic information
|
||||
SingleAlert.SingleAlertBuilder builder = SingleAlert.builder()
|
||||
.status(convertStatus(tencentAlert.getAlarmStatus()))
|
||||
.startAt(parseTime(tencentAlert.getFirstOccurTime()))
|
||||
.activeAt(parseTime(tencentAlert.getFirstOccurTime()))
|
||||
.triggerTimes(1);
|
||||
// If it is a recovery state, set the end time.
|
||||
if ("0".equals(tencentAlert.getAlarmStatus())) {
|
||||
builder.endAt(parseTime(tencentAlert.getRecoverTime()));
|
||||
}
|
||||
|
||||
// Build labels
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
buildLabels(labels, tencentAlert);
|
||||
|
||||
// Build annotations
|
||||
Map<String, String> annotations = new HashMap<>();
|
||||
buildAnnotations(annotations, tencentAlert);
|
||||
|
||||
// Build content
|
||||
String content = generateContent(tencentAlert);
|
||||
|
||||
return builder
|
||||
.labels(labels)
|
||||
.annotations(annotations)
|
||||
.content(content)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void buildLabels(Map<String, String> labels, TencentCloudExternAlert alert) {
|
||||
labels.put("__source__", "tencent_cloud");
|
||||
labels.put("alert_type", alert.getAlarmType());
|
||||
if (TencentCloudExternAlert.METRIC.equals(alert.getAlarmType())) {
|
||||
labels.put("metric_name", alert.getAlarmPolicyInfo().getConditions().getMetricName());
|
||||
labels.put("namespace", alert.getAlarmObjInfo().getNamespace());
|
||||
} else {
|
||||
labels.put("event_name", alert.getAlarmPolicyInfo().getConditions().getEventName());
|
||||
labels.put("product_name", alert.getAlarmPolicyInfo().getConditions().getProductName());
|
||||
}
|
||||
}
|
||||
|
||||
private void buildAnnotations(Map<String, String> annotations, TencentCloudExternAlert alert) {
|
||||
TencentCloudExternAlert.AlarmPolicyInfo alarmPolicyInfo = alert.getAlarmPolicyInfo();
|
||||
TencentCloudExternAlert.AlarmObjInfo alarmObjInfo = alert.getAlarmObjInfo();
|
||||
TencentCloudExternAlert.Dimensions dimensions = alert.getAlarmObjInfo().getDimensions();
|
||||
|
||||
putIfNotNull(annotations, "policy_id", alarmPolicyInfo.getPolicyId());
|
||||
putIfNotNull(annotations, "policy_type", alarmPolicyInfo.getPolicyType());
|
||||
putIfNotNull(annotations, "policy_name", alarmPolicyInfo.getPolicyName());
|
||||
putIfNotNull(annotations, "policy_type_cname", alarmPolicyInfo.getPolicyTypeCname());
|
||||
|
||||
putIfNotNull(annotations, "namespace", alarmObjInfo.getNamespace());
|
||||
putIfNotNull(annotations, "region", alarmObjInfo.getRegion());
|
||||
putIfNotNull(annotations, "app_id", alarmObjInfo.getAppId());
|
||||
putIfNotNull(annotations, "uin", alarmObjInfo.getUin());
|
||||
|
||||
putIfNotNull(annotations, "instance_id", dimensions.getUnInstanceId());
|
||||
putIfNotNull(annotations, "obj_id", dimensions.getObjId());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate alert content with template
|
||||
*/
|
||||
private String generateContent(TencentCloudExternAlert alert) {
|
||||
if (TencentCloudExternAlert.METRIC.equals(alert.getAlarmType())) {
|
||||
return generateMetricContent(alert);
|
||||
} else {
|
||||
return generateEventContent(alert);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate metric alert content
|
||||
*/
|
||||
private String generateMetricContent(TencentCloudExternAlert alert) {
|
||||
TencentCloudExternAlert.Conditions conditions = alert.getAlarmPolicyInfo().getConditions();
|
||||
Map<String, String> params = new HashMap<>();
|
||||
|
||||
params.put("metricShowName", conditions.getMetricShowName());
|
||||
|
||||
if (StringUtils.isNotEmpty(conditions.getCalcType())
|
||||
&& StringUtils.isNotEmpty(conditions.getCalcValue())) {
|
||||
params.put("calcType", conditions.getCalcType());
|
||||
params.put("calcValue", conditions.getCalcValue());
|
||||
params.put("calcUnit", Objects.toString(conditions.getCalcUnit(), ""));
|
||||
} else {
|
||||
params.put("calcType", "");
|
||||
params.put("calcValue", "N/A");
|
||||
params.put("calcUnit", "");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(conditions.getCurrentValue())) {
|
||||
params.put("currentValue", conditions.getCurrentValue());
|
||||
params.put("unit", Objects.toString(conditions.getUnit(), ""));
|
||||
} else {
|
||||
params.put("currentValue", "N/A");
|
||||
params.put("unit", "");
|
||||
}
|
||||
return replacePlaceholders(metricTemplate, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate event alert content
|
||||
*/
|
||||
private String generateEventContent(TencentCloudExternAlert alert) {
|
||||
TencentCloudExternAlert.Conditions conditions = alert.getAlarmPolicyInfo().getConditions();
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("productShowName", conditions.getProductShowName());
|
||||
params.put("eventShowName", conditions.getEventShowName());
|
||||
return replacePlaceholders(eventTemplate, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace placeholders in template with actual values
|
||||
*/
|
||||
private String replacePlaceholders(String template, Map<String, String> params) {
|
||||
String result = template;
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
result = result.replace("{" + entry.getKey() + "}", Objects.toString(entry.getValue(), "NULL"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String convertStatus(String alarmStatus) {
|
||||
return "1".equals(alarmStatus) ? CommonConstants.ALERT_STATUS_FIRING : CommonConstants.ALERT_STATUS_RESOLVED;
|
||||
}
|
||||
|
||||
private Long parseTime(String timeStr) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.parse(timeStr).getTime();
|
||||
} catch (ParseException e) {
|
||||
log.error("Failed to parse time: {}", timeStr);
|
||||
throw new IllegalArgumentException("Failed to parse time: " + timeStr, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void putIfNotNull(Map<String, String> map, String key, String value){
|
||||
if (StringUtils.isNotEmpty(value)){
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import jakarta.xml.bind.DatatypeConverter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.config.TencentSmsProperties;
|
||||
import org.apache.hertzbeat.alert.service.SmsClient;
|
||||
@@ -35,6 +34,7 @@ import org.apache.http.util.EntityUtils;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.config.TwilioSmsProperties;
|
||||
import org.apache.hertzbeat.alert.service.SmsClient;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.support.exception.SendMessageException;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.SmsConstants.TWILIO;
|
||||
|
||||
/**
|
||||
* Twilio SMS Client Implementation<br>
|
||||
* API doc: <a href=
|
||||
* "https://www.twilio.com/docs/sms/api">https://www.twilio.com/docs/sms/api</a>
|
||||
*/
|
||||
@Slf4j
|
||||
public class TwilioSmsClientImpl implements SmsClient {
|
||||
|
||||
private static final String API_URL_FORMAT = "https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json";
|
||||
private static final String MESSAGE_TEMPLATE = "Instance: %s, Priority: %s, Content: %s";
|
||||
|
||||
private final String accountSid;
|
||||
private final String authToken;
|
||||
private final String twilioPhoneNumber;
|
||||
|
||||
public TwilioSmsClientImpl(TwilioSmsProperties config) {
|
||||
if (config != null) {
|
||||
this.accountSid = config.getAccountSid();
|
||||
this.authToken = config.getAuthToken();
|
||||
this.twilioPhoneNumber = config.getTwilioPhoneNumber();
|
||||
} else {
|
||||
this.accountSid = "";
|
||||
this.authToken = "";
|
||||
this.twilioPhoneNumber = "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
String instance = null;
|
||||
String priority = null;
|
||||
String content = null;
|
||||
if (alert.getCommonLabels() != null) {
|
||||
instance = alert.getCommonLabels().get("instance") == null ? alert.getGroupKey()
|
||||
: alert.getCommonLabels().get("instance");
|
||||
priority = alert.getCommonLabels().get("priority") == null ? "unknown"
|
||||
: alert.getCommonLabels().get("priority");
|
||||
content = alert.getCommonAnnotations().get("summary");
|
||||
content = content == null ? alert.getCommonAnnotations().get("description") : content;
|
||||
if (content == null) {
|
||||
content = alert.getCommonAnnotations().values().stream().findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
this.send(receiver.getPhone(), createMessage(instance, priority, content));
|
||||
}
|
||||
|
||||
private String createMessage(String instance, String priority, String content) {
|
||||
return String.format(MESSAGE_TEMPLATE, instance, priority, content);
|
||||
}
|
||||
|
||||
private void send(String phoneNumber, String message) {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
String endpoint = String.format(API_URL_FORMAT, accountSid);
|
||||
URI requestUri = new URI(endpoint);
|
||||
|
||||
HttpPost httpPost = createHttpPost(requestUri, phoneNumber, message);
|
||||
log.info("Sending Twilio SMS request to {}", requestUri);
|
||||
executeRequest(httpClient, httpPost, phoneNumber);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to send SMS: {}", e.getMessage());
|
||||
throw new SendMessageException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private HttpPost createHttpPost(URI requestUri, String toNumber, String message) {
|
||||
HttpPost httpPost = new HttpPost(requestUri);
|
||||
|
||||
String auth = accountSid + ":" + authToken;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
|
||||
httpPost.setHeader("Authorization", "Basic " + encodedAuth);
|
||||
|
||||
List<BasicNameValuePair> parameters = new ArrayList<>();
|
||||
parameters.add(new BasicNameValuePair("To", toNumber));
|
||||
parameters.add(new BasicNameValuePair("From", twilioPhoneNumber));
|
||||
parameters.add(new BasicNameValuePair("Body", message));
|
||||
|
||||
try {
|
||||
httpPost.setEntity(new UrlEncodedFormEntity(parameters));
|
||||
return httpPost;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create HTTP request: {}", e.getMessage());
|
||||
throw new SendMessageException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void executeRequest(CloseableHttpClient httpClient, HttpPost httpPost, String phoneNumber)
|
||||
throws Exception {
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String responseBody = EntityUtils.toString(response.getEntity());
|
||||
log.info("SMS response status: {}, body: {}", statusCode, responseBody);
|
||||
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
|
||||
if (responseBody.contains("21608")) {
|
||||
throw new SendMessageException(
|
||||
"The Twilio trial account can only send SMS to verified phone numbers");
|
||||
} else {
|
||||
throw new SendMessageException(
|
||||
"HTTP request failed with status code: " + statusCode + ", response: " + responseBody);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
|
||||
if (jsonResponse == null) {
|
||||
throw new SendMessageException(statusCode + ":" + responseBody);
|
||||
}
|
||||
|
||||
JsonNode sidNode = jsonResponse.get("sid");
|
||||
if (sidNode == null) {
|
||||
throw new SendMessageException(statusCode + ":" + responseBody);
|
||||
}
|
||||
|
||||
String sid = sidNode.asText();
|
||||
log.info("Successfully sent SMS to phone: {}, sid: {}", phoneNumber, sid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TWILIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConfig() {
|
||||
return !(accountSid.isBlank() || authToken.isBlank() || twilioPhoneNumber.isBlank());
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
grammar AlertExpression;
|
||||
|
||||
expression
|
||||
: expr EOF
|
||||
;
|
||||
|
||||
expr
|
||||
: '(' expr ')' # ParenExpr
|
||||
| left=expr op=('>='|'<='|'>'|'<'|'=='|'!=') right=expr # ComparisonExpr
|
||||
| left=expr 'and' right=expr # AndExpr
|
||||
| left=expr 'unless' right=expr # UnlessExpr
|
||||
| left=expr 'or' right=expr # OrExpr
|
||||
| identifier # QueryExpr
|
||||
| number # LiteralExpr
|
||||
;
|
||||
|
||||
// Lexer rules
|
||||
AND : 'and' ;
|
||||
OR : 'or' ;
|
||||
UNLESS : 'unless' ;
|
||||
GT : '>' ;
|
||||
GE : '>=' ;
|
||||
LT : '<' ;
|
||||
LE : '<=' ;
|
||||
EQ : '==' ;
|
||||
NE : '!=' ;
|
||||
LPAREN : '(' ;
|
||||
RPAREN : ')' ;
|
||||
|
||||
identifier
|
||||
: IDENTIFIER
|
||||
;
|
||||
|
||||
number
|
||||
: NUMBER
|
||||
;
|
||||
|
||||
IDENTIFIER : [a-zA-Z_] [a-zA-Z0-9_=~{}[\]".]*;
|
||||
NUMBER : [0-9]+ ('.' [0-9]+)? ;
|
||||
WS : [ \t\r\n]+ -> skip ;
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
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.AlertInhibitService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* test case for {@link AlertInhibitControllerTest}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertInhibitControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private AlertInhibitService alertInhibitService;
|
||||
|
||||
@InjectMocks
|
||||
private AlertInhibitController alertInhibitController;
|
||||
|
||||
private AlertInhibit alertInhibit;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.mockMvc = standaloneSetup(alertInhibitController).build();
|
||||
|
||||
HashMap<String, String> sourceLabels = new HashMap<>();
|
||||
HashMap<String, String> targetLabels = new HashMap<>();
|
||||
|
||||
alertInhibit = AlertInhibit.builder()
|
||||
.id(1L)
|
||||
.name("test")
|
||||
.sourceLabels(sourceLabels)
|
||||
.targetLabels(targetLabels)
|
||||
.creator("test")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddNewAlertInhibit() throws Exception {
|
||||
|
||||
doNothing().when(alertInhibitService).validate(any(AlertInhibit.class), eq(false));
|
||||
doNothing().when(alertInhibitService).addAlertInhibit(any(AlertInhibit.class));
|
||||
|
||||
mockMvc.perform(post("/api/alert/inhibit")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertInhibit)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Add success"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModifyAlertInhibit() throws Exception {
|
||||
|
||||
doNothing().when(alertInhibitService).validate(any(AlertInhibit.class), eq(true));
|
||||
doNothing().when(alertInhibitService).modifyAlertInhibit(any(AlertInhibit.class));
|
||||
|
||||
mockMvc.perform(put("/api/alert/inhibit")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertInhibit)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Modify success"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAlertInhibitExists() throws Exception {
|
||||
|
||||
when(alertInhibitService.getAlertInhibit(1L)).thenReturn(alertInhibit);
|
||||
|
||||
mockMvc.perform(get("/api/alert/inhibit/{id}", 1L)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.id").value(alertInhibit.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAlertInhibitNotExists() throws Exception {
|
||||
|
||||
when(alertInhibitService.getAlertInhibit(1L)).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/alert/inhibit/{id}", 1L)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("AlertInhibit not exist."));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.expr;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* test case for {@link AlertExpressionEvalVisitor}
|
||||
*/
|
||||
class AlertExpressionEvalVisitorTest {
|
||||
|
||||
private QueryExecutor mockExecutor;
|
||||
private AlertExpressionEvalVisitor visitor;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
visitor = new AlertExpressionEvalVisitor(mockExecutor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGreaterThan() {
|
||||
when(mockExecutor.execute("cpu")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 80.0))));
|
||||
List<Map<String, Object>> result = evaluate("cpu > 70");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(80.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGreaterThanWithInteger() {
|
||||
when(mockExecutor.execute("cpu")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 80))));
|
||||
List<Map<String, Object>> result = evaluate("cpu > 70");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(80, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLessThan() {
|
||||
when(mockExecutor.execute("memory")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 65.0))));
|
||||
List<Map<String, Object>> result = evaluate("memory < 70");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(65.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualWithTolerance() {
|
||||
when(mockExecutor.execute("disk")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 99.999))));
|
||||
List<Map<String, Object>> result = evaluate("disk == 100");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotEqual() {
|
||||
when(mockExecutor.execute("network")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 50.0))));
|
||||
List<Map<String, Object>> result = evaluate("network != 60");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(50.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExactlyEqual() {
|
||||
when(mockExecutor.execute("threshold")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 100.0))));
|
||||
List<Map<String, Object>> result = evaluate("threshold == 100");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxValueBoundary() {
|
||||
when(mockExecutor.execute("max_val")).thenReturn(List.of(new HashMap<>(Map.of("__value__", Double.MAX_VALUE))));
|
||||
List<Map<String, Object>> result = evaluate("max_val > 100");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(Double.MAX_VALUE, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMinValueBoundary() {
|
||||
when(mockExecutor.execute("min_val")).thenReturn(List.of(new HashMap<>(Map.of("__value__", Double.MIN_VALUE))));
|
||||
List<Map<String, Object>> result = evaluate("min_val > 0");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(Double.MIN_VALUE, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyMetricName() {
|
||||
when(mockExecutor.execute("")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 50.0))));
|
||||
List<Map<String, Object>> result = evaluate(" > 40");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListValueWithMax() {
|
||||
when(mockExecutor.execute("multi_val")).thenReturn(List.of(new HashMap<>(Map.of("__value__", List.of(10.0, 20.0, 30.0)))));
|
||||
List<Map<String, Object>> result = evaluate("multi_val > 25");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(30.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListValueWithMin() {
|
||||
when(mockExecutor.execute("multi_val")).thenReturn(List.of(new HashMap<>(Map.of("__value__", List.of(10.0, 20.0, 30.0)))));
|
||||
List<Map<String, Object>> result = evaluate("multi_val < 15");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(10.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyListValue() {
|
||||
when(mockExecutor.execute("empty_list")).thenReturn(List.of(new HashMap<>(Map.of("__value__", List.of()))));
|
||||
List<Map<String, Object>> result = evaluate("empty_list > 50");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedParentheses() {
|
||||
when(mockExecutor.execute("a")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 10.0))));
|
||||
when(mockExecutor.execute("b")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 20.0))));
|
||||
when(mockExecutor.execute("c")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 30.0))));
|
||||
List<Map<String, Object>> result = evaluate("(a > 5) and (b > 15 or c < 25)");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(10.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleUnlessConditions() {
|
||||
when(mockExecutor.execute("metric1")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 40.0))));
|
||||
when(mockExecutor.execute("metric2")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 50.0))));
|
||||
when(mockExecutor.execute("metric3")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 60.0))));
|
||||
List<Map<String, Object>> result = evaluate("metric1 > 30 unless metric2 > 45 unless metric3 < 70");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComplexExpressionWithMixedOperators() {
|
||||
when(mockExecutor.execute("cpu_temp")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 75.0))));
|
||||
when(mockExecutor.execute("gpu_temp")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 85.0))));
|
||||
when(mockExecutor.execute("fan_speed")).thenReturn(List.of(new HashMap<>(Map.of("__value__", 2000.0))));
|
||||
|
||||
List<Map<String, Object>> result = evaluate("(cpu_temp > 70 and gpu_temp < 90) or fan_speed > 1500");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(75.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> evaluate(String expression) {
|
||||
AlertExpressionLexer lexer = new AlertExpressionLexer(CharStreams.fromString(expression));
|
||||
AlertExpressionParser parser = new AlertExpressionParser(new CommonTokenStream(lexer));
|
||||
return visitor.visit(parser.expression());
|
||||
}
|
||||
}
|
||||
+20
-92
@@ -15,23 +15,38 @@
|
||||
* 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 com.github.benmanes.caffeine.cache.Cache;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.apache.hertzbeat.alert.service.impl.DataSourceServiceImpl;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
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}
|
||||
@@ -529,91 +544,4 @@ class DataSourceServiceTest {
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(200.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void expressionCacheShouldHitForSameExpression() {
|
||||
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));
|
||||
String expr = "node_cpu_seconds_total > 50";
|
||||
Cache<String, ParseTree> expressionCache = dataSourceService.getExpressionCache();
|
||||
expressionCache.invalidateAll();
|
||||
long beforeHits = expressionCache.stats().hitCount();
|
||||
dataSourceService.calculate("promql", expr); //first execution miss cache
|
||||
dataSourceService.calculate("promql", expr); //second execution hits cache
|
||||
long actualHits = expressionCache.stats().hitCount() - beforeHits;
|
||||
assertEquals(1, actualHits, "expression cache should hit but miss");
|
||||
}
|
||||
|
||||
@Test
|
||||
void tokenCacheShouldHitForSameExpression() {
|
||||
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));
|
||||
String expr = "node_cpu_seconds_total > 50";
|
||||
Cache<String, ParseTree> expressionCache = dataSourceService.getExpressionCache();
|
||||
Cache<String, CommonTokenStream> tokenStreamCache = dataSourceService.getTokenStreamCache();
|
||||
expressionCache.invalidateAll();
|
||||
tokenStreamCache.invalidateAll();
|
||||
long beforeHits = tokenStreamCache.stats().hitCount();
|
||||
dataSourceService.calculate("promql", expr);
|
||||
expressionCache.invalidateAll();
|
||||
dataSourceService.calculate("promql", expr);
|
||||
long actualHits = tokenStreamCache.stats().hitCount() - beforeHits;
|
||||
assertEquals(1, actualHits, "expression cache should hit but miss");
|
||||
}
|
||||
|
||||
@Test
|
||||
void expressCacheShouldNotHitForDifferentExpressions() {
|
||||
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));
|
||||
dataSourceService.calculate("promql", "node_cpu_seconds_total > 50");
|
||||
Cache<String, ParseTree> expressionCache = dataSourceService.getExpressionCache();
|
||||
Cache<String, CommonTokenStream> tokenStreamCache = dataSourceService.getTokenStreamCache();
|
||||
expressionCache.invalidateAll();
|
||||
tokenStreamCache.invalidateAll();
|
||||
String expr1 = "node_cpu_seconds_total > 30";
|
||||
String expr2 = "node_cpu_seconds_total > 50";
|
||||
long beforeHits = expressionCache.stats().hitCount();
|
||||
dataSourceService.calculate("promql", expr1);
|
||||
dataSourceService.calculate("promql", expr2);
|
||||
long actualHits = expressionCache.stats().hitCount() - beforeHits;
|
||||
assertEquals(0, actualHits, "expression cache should miss but hit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void tokenCacheShouldNotHitForDifferentExpressions() {
|
||||
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));
|
||||
dataSourceService.calculate("promql", "node_cpu_seconds_total > 50");
|
||||
Cache<String, ParseTree> expressionCache = dataSourceService.getExpressionCache();
|
||||
Cache<String, CommonTokenStream> tokenStreamCache = dataSourceService.getTokenStreamCache();
|
||||
expressionCache.invalidateAll();
|
||||
tokenStreamCache.invalidateAll();
|
||||
String expr1 = "node_cpu_seconds_total > 30";
|
||||
String expr2 = "node_cpu_seconds_total > 50";
|
||||
long beforeHits = tokenStreamCache.stats().hitCount();
|
||||
dataSourceService.calculate("promql", expr1);
|
||||
dataSourceService.calculate("promql", expr2);
|
||||
long actualHits = tokenStreamCache.stats().hitCount() - beforeHits;
|
||||
assertEquals(0, actualHits, "expression cache should miss but hit");
|
||||
}
|
||||
}
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service;
|
||||
|
||||
import org.apache.hertzbeat.alert.config.TwilioSmsProperties;
|
||||
import org.apache.hertzbeat.alert.service.impl.TwilioSmsClientImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Test case for {@link TwilioSmsClientImpl}
|
||||
*/
|
||||
class TwilioSmsClientImplTest {
|
||||
|
||||
@Test
|
||||
void getType() {
|
||||
TwilioSmsProperties twilioSmsProperties = new TwilioSmsProperties();
|
||||
twilioSmsProperties.setAccountSid("accountSid");
|
||||
twilioSmsProperties.setAuthToken("authToken");
|
||||
twilioSmsProperties.setTwilioPhoneNumber("twilioPhoneNumber");
|
||||
TwilioSmsClientImpl twilioSmsClient = new TwilioSmsClientImpl(twilioSmsProperties);
|
||||
|
||||
assertEquals("twilio", twilioSmsClient.getType());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"accountSid, authToken, twilioPhoneNumber, true",
|
||||
"accountSid, authToken, '', false",
|
||||
"accountSid, '', twilioPhoneNumber, false",
|
||||
"accountSid, '', '', false",
|
||||
"'', authToken, twilioPhoneNumber, false",
|
||||
"'', authToken, '', false",
|
||||
"'', '', twilioPhoneNumber, false",
|
||||
"'', '', '', false",
|
||||
})
|
||||
void checkConfig(String accountSid, String authToken, String twilioPhoneNumber, boolean expected) {
|
||||
TwilioSmsProperties twilioSmsProperties = new TwilioSmsProperties();
|
||||
twilioSmsProperties.setAccountSid(accountSid);
|
||||
twilioSmsProperties.setAuthToken(authToken);
|
||||
twilioSmsProperties.setTwilioPhoneNumber(twilioPhoneNumber);
|
||||
TwilioSmsClientImpl twilioSmsClient = new TwilioSmsClientImpl(twilioSmsProperties);
|
||||
|
||||
assertEquals(expected, twilioSmsClient.checkConfig());
|
||||
}
|
||||
}
|
||||
@@ -155,11 +155,6 @@
|
||||
<artifactId>plc4j-driver-modbus</artifactId>
|
||||
<version>0.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.plc4x</groupId>
|
||||
<artifactId>plc4j-driver-s7</artifactId>
|
||||
<version>0.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
|
||||
+10
-36
@@ -63,25 +63,6 @@ public class JdbcCommonCollect extends AbstractCollect {
|
||||
|
||||
private static final String[] VULNERABLE_KEYWORDS = {"allowLoadLocalInfile", "allowLoadLocalInfileInPath", "useLocalInfile"};
|
||||
|
||||
private static final String[] BLACK_LIST = {
|
||||
// dangerous SQL commands - may cause database structure damage or data leakage
|
||||
"create trigger", "create alias", "runscript from", "shutdown", "drop table",
|
||||
"drop database", "create function", "alter system", "grant all", "revoke all",
|
||||
|
||||
// file IO related - may cause server files to be read or written
|
||||
"allowloadlocalinfile", "allowloadlocalinfileinpath", "uselocalinfile",
|
||||
|
||||
// code execution related - may result in remote code execution
|
||||
"init=", "javaobjectserializer=", "runscript", "serverstatusdiffinterceptor",
|
||||
"queryinterceptors=", "statementinterceptors=", "exceptioninterceptors=",
|
||||
|
||||
// multiple statement execution - may lead to SQL injection
|
||||
"allowmultiqueries",
|
||||
|
||||
// deserialization related - may result in remote code execution
|
||||
"autodeserialize", "detectcustomcollations",
|
||||
};
|
||||
|
||||
private final GlobalConnectionCache connectionCommonCache = GlobalConnectionCache.getInstance();
|
||||
|
||||
|
||||
@@ -350,24 +331,17 @@ public class JdbcCommonCollect extends AbstractCollect {
|
||||
if (Objects.nonNull(jdbcProtocol.getUrl())
|
||||
&& !Objects.equals("", jdbcProtocol.getUrl())
|
||||
&& jdbcProtocol.getUrl().startsWith("jdbc")) {
|
||||
// limit url length
|
||||
if (jdbcProtocol.getUrl().length() > 2048) {
|
||||
throw new IllegalArgumentException("JDBC URL length exceeds maximum limit of 2048 characters");
|
||||
// convert the URL to lowercase for case-insensitive checking
|
||||
String url = jdbcProtocol.getUrl().toLowerCase();
|
||||
// check whether the parameter is valid
|
||||
if (url.contains("create trigger") || url.contains("create alias") || url.contains("runscript from")
|
||||
|| url.contains("allowloadlocalinfile") || url.contains("allowloadlocalinfileinpath")
|
||||
|| url.contains("uselocalinfile") || url.contains("autodeserialize") || url.contains("detectcustomcollations")
|
||||
|| url.contains("serverstatusdiffinterceptor")) {
|
||||
throw new IllegalArgumentException("Invalid JDBC URL: contains malicious characters.");
|
||||
}
|
||||
// remove special characters
|
||||
String cleanedUrl = jdbcProtocol.getUrl().replaceAll("[\\x00-\\x1F\\x7F]", "");
|
||||
String url = cleanedUrl.toLowerCase();
|
||||
// backlist check
|
||||
for (String keyword : BLACK_LIST) {
|
||||
if (url.contains(keyword)) {
|
||||
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious parameter: " + keyword);
|
||||
}
|
||||
}
|
||||
// url format check
|
||||
if (!url.matches("^jdbc:[a-zA-Z0-9]+://[^\\s]+$")) {
|
||||
throw new IllegalArgumentException("Invalid JDBC URL format");
|
||||
}
|
||||
return cleanedUrl;
|
||||
// when has config jdbc url, use it
|
||||
return jdbcProtocol.getUrl();
|
||||
}
|
||||
return switch (jdbcProtocol.getPlatform()) {
|
||||
case "mysql", "mariadb" -> "jdbc:mysql://" + host + ":" + port
|
||||
|
||||
+55
-302
@@ -18,14 +18,11 @@
|
||||
package org.apache.hertzbeat.collector.collect.http;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.SignConstants.RIGHT_DASH;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.ConnectException;
|
||||
@@ -37,22 +34,19 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.PrometheusParseCreator;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.parser.MetricFamily;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.parser.OnlineParser;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.exporter.ExporterParser;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.exporter.MetricFamily;
|
||||
import org.apache.hertzbeat.collector.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
@@ -69,9 +63,11 @@ import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.client.AuthCache;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
@@ -81,6 +77,8 @@ import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.auth.DigestScheme;
|
||||
import org.apache.http.impl.client.BasicAuthCache;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
@@ -88,30 +86,20 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* http https collect
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpCollectImpl extends AbstractCollect {
|
||||
private final Set<Integer> defaultSuccessStatusCodes = Set.of(
|
||||
HttpStatus.SC_OK,
|
||||
HttpStatus.SC_CREATED,
|
||||
HttpStatus.SC_ACCEPTED,
|
||||
HttpStatus.SC_MULTIPLE_CHOICES,
|
||||
HttpStatus.SC_MOVED_PERMANENTLY,
|
||||
HttpStatus.SC_MOVED_TEMPORARILY);
|
||||
private static final Map<Long, ExporterParser> EXPORTER_PARSER_TABLE = new ConcurrentHashMap<>();
|
||||
private final Set<Integer> defaultSuccessStatusCodes = Stream.of(HttpStatus.SC_OK, HttpStatus.SC_CREATED,
|
||||
HttpStatus.SC_ACCEPTED, HttpStatus.SC_MULTIPLE_CHOICES, HttpStatus.SC_MOVED_PERMANENTLY,
|
||||
HttpStatus.SC_MOVED_TEMPORARILY).collect(Collectors.toSet());
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
@@ -144,11 +132,10 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
builder.setMsg(NetworkConstants.STATUS_CODE + SignConstants.BLANK + statusCode);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
this could create large objects, potentially impacting JVM memory space significantly.
|
||||
Option 1: Parse using InputStream, but this requires significant code changes;
|
||||
Option 2: Manually trigger garbage collection, similar to how it's done in Dubbo for large inputs.
|
||||
*/
|
||||
// todo This code converts an InputStream directly to a String. For large data in Prometheus exporters,
|
||||
// this could create large objects, potentially impacting JVM memory space significantly.
|
||||
// Option 1: Parse using InputStream, but this requires significant code changes;
|
||||
// Option 2: Manually trigger garbage collection, similar to how it's done in Dubbo for large inputs.
|
||||
String resp = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
if (!StringUtils.hasText(resp)) {
|
||||
log.info("http response entity is empty, status: {}.", statusCode);
|
||||
@@ -162,17 +149,15 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
case DispatchConstants.PARSE_PROM_QL ->
|
||||
parseResponseByPromQl(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
|
||||
case DispatchConstants.PARSE_PROMETHEUS ->
|
||||
parseResponseByPrometheusExporter(response.getEntity().getContent(), metrics.getAliasFields(), builder);
|
||||
parseResponseByPrometheusExporter(resp, metrics.getAliasFields(), builder);
|
||||
case DispatchConstants.PARSE_XML_PATH ->
|
||||
parseResponseByXmlPath(resp, metrics, builder, responseTime);
|
||||
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
|
||||
case DispatchConstants.PARSE_WEBSITE ->
|
||||
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime);
|
||||
case DispatchConstants.PARSE_SITE_MAP ->
|
||||
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
|
||||
case DispatchConstants.PARSE_HEADER ->
|
||||
parseResponseByHeader(builder, metrics.getAliasFields(), response);
|
||||
case DispatchConstants.PARSE_CONFIG ->
|
||||
parseResponseByConfig(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
|
||||
default ->
|
||||
parseResponseByDefault(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
|
||||
}
|
||||
@@ -343,207 +328,8 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseByXmlPath(String resp, Metrics metrics,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
HttpProtocol http = metrics.getHttp();
|
||||
List<String> aliasFields = metrics.getAliasFields();
|
||||
String xpathExpression = http.getParseScript();
|
||||
if (!StringUtils.hasText(xpathExpression)) {
|
||||
log.warn("Http collect parse type is xmlPath, but the xpath expression is empty.");
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("XPath expression is empty");
|
||||
return;
|
||||
}
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
dbf.setXIncludeAware(false);
|
||||
dbf.setExpandEntityReferences(false);
|
||||
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document document = db.parse(new InputSource(new StringReader(resp)));
|
||||
|
||||
XPathFactory xpathFactory = XPathFactory.newInstance();
|
||||
XPath xpath = xpathFactory.newXPath();
|
||||
|
||||
NodeList nodeList = (NodeList) xpath.evaluate(xpathExpression, document, XPathConstants.NODESET);
|
||||
|
||||
if (nodeList == null || nodeList.getLength() == 0) {
|
||||
log.debug("XPath expression '{}' returned no nodes.", xpathExpression);
|
||||
boolean requestedSummaryFields = aliasFields.stream()
|
||||
.anyMatch(alias -> NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)
|
||||
|| CollectorConstants.KEYWORD.equalsIgnoreCase(alias));
|
||||
|
||||
if (requestedSummaryFields) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(Integer.toString(keywordNum));
|
||||
} else {
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
|
||||
for (String alias : aliasFields) {
|
||||
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(Integer.toString(keywordNum));
|
||||
} else {
|
||||
try {
|
||||
String value = (String) xpath.evaluate(alias, node, XPathConstants.STRING);
|
||||
valueRowBuilder.addColumn(StringUtils.hasText(value) ? value : CommonConstants.NULL_VALUE);
|
||||
} catch (XPathExpressionException e) {
|
||||
log.warn("Failed to evaluate XPath '{}' for node [{}]: {}", alias, node.getNodeName(), e.getMessage());
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse XML response with XPath '{}': {}", xpathExpression, e.getMessage(), e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Failed to parse XML response: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parses the response body in Properties/Config format.
|
||||
* Two modes are supported:
|
||||
* 1. single-object mode: if http.parseScript is null, aliasFields are treated as indicator names.
|
||||
* - If there is a locator in the indicator definition, use the locator as the key of the Properties.
|
||||
* - Otherwise, use aliasField (metric name) as the key for Properties.
|
||||
* Generate a single row of data.
|
||||
* 2. array mode: if http.parseScript is not empty (e.g. “users”), treat it as an array base path.
|
||||
* Treat aliasFields as the attribute name of an array element, and generate a single row of data for each array index. locator is invalid in this mode.
|
||||
*
|
||||
* @param resp Response body string
|
||||
* @param aliasFields List of metrics aliases (i.e., the list of fields in metrics.fields).
|
||||
* @param http http protocol configuration
|
||||
* @param builder The metrics data builder.
|
||||
* @param responseTime response time
|
||||
*/
|
||||
private void parseResponseByConfig(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
if (!StringUtils.hasText(resp)) {
|
||||
log.warn("Http collect parse type is config, but response body is empty.");
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Response body is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
Properties properties = new Properties();
|
||||
try (StringReader reader = new StringReader(resp)) {
|
||||
properties.load(reader);
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to parse config response: {}", e.getMessage(), e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Failed to parse config response: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
String arrayBasePath = http.getParseScript();
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
|
||||
if (!StringUtils.hasText(arrayBasePath)) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(Integer.toString(keywordNum));
|
||||
} else {
|
||||
String value = properties.getProperty(alias);
|
||||
valueRowBuilder.addColumn(value != null ? value : CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
CollectRep.ValueRow valueRow = valueRowBuilder.build();
|
||||
if (hasMeaningfulDataInRow(valueRow, aliasFields)) {
|
||||
builder.addValueRow(valueRow);
|
||||
} else {
|
||||
log.warn("No meaningful data found in single config object response for aliasFields: {}", aliasFields);
|
||||
}
|
||||
} else {
|
||||
Pattern pattern = Pattern.compile("^" + Pattern.quote(arrayBasePath) + "\\[(\\d+)]\\.");
|
||||
Set<Integer> existingIndices = new HashSet<>();
|
||||
for (String key : properties.stringPropertyNames()) {
|
||||
Matcher matcher = pattern.matcher(key);
|
||||
if (matcher.find()) {
|
||||
try {
|
||||
int index = Integer.parseInt(matcher.group(1));
|
||||
existingIndices.add(index);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Could not parse index from key: {}", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existingIndices.isEmpty()) {
|
||||
log.warn("Could not find any array elements for base path '{}' in config response.", arrayBasePath);
|
||||
return;
|
||||
}
|
||||
List<Integer> sortedIndices = new ArrayList<>(existingIndices);
|
||||
Collections.sort(sortedIndices);
|
||||
for (int i : sortedIndices) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(Integer.toString(keywordNum));
|
||||
} else {
|
||||
String currentKey = arrayBasePath + "[" + i + "]." + alias;
|
||||
String value = properties.getProperty(currentKey);
|
||||
valueRowBuilder.addColumn(value != null ? value : CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
CollectRep.ValueRow valueRow = valueRowBuilder.build();
|
||||
if (hasMeaningfulDataInRow(valueRow, aliasFields)) {
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasMeaningfulDataInRow(CollectRep.ValueRow valueRow, List<String> aliasFields) {
|
||||
if (valueRow.getColumnsCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (valueRow.getColumnsCount() != aliasFields.size()) {
|
||||
log.error("Column count ({}) mismatch with aliasFields size ({}) when checking meaningful data.",
|
||||
valueRow.getColumnsCount(), aliasFields.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasMeaningfulData = false;
|
||||
for (int i = 0; i < valueRow.getColumnsCount(); i++) {
|
||||
String columnValue = valueRow.getColumns(i);
|
||||
String alias = aliasFields.get(i);
|
||||
if (!CommonConstants.NULL_VALUE.equals(columnValue) && (!NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias) && !CollectorConstants.KEYWORD.equalsIgnoreCase(alias))) {
|
||||
hasMeaningfulData = true;
|
||||
break;
|
||||
}
|
||||
if ((NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias) || CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) && !CommonConstants.NULL_VALUE.equals(columnValue)) {
|
||||
hasMeaningfulData = true;
|
||||
}
|
||||
}
|
||||
return hasMeaningfulData;
|
||||
private void parseResponseByXmlPath(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
}
|
||||
|
||||
private void parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
@@ -600,22 +386,36 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
prometheusParser.handle(resp, aliasFields, http, builder);
|
||||
}
|
||||
|
||||
private void parseResponseByPrometheusExporter(InputStream content, List<String> aliasFields, CollectRep.MetricsData.Builder builder) throws IOException {
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(content);
|
||||
if (metricFamilyMap == null || metricFamilyMap.isEmpty()) {
|
||||
return;
|
||||
private void parseResponseByPrometheusExporter(String resp, List<String> aliasFields,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
if (!EXPORTER_PARSER_TABLE.containsKey(builder.getId())) {
|
||||
EXPORTER_PARSER_TABLE.put(builder.getId(), new ExporterParser());
|
||||
}
|
||||
ExporterParser parser = EXPORTER_PARSER_TABLE.get(builder.getId());
|
||||
Map<String, MetricFamily> metricFamilyMap = parser.textToMetric(resp);
|
||||
String metrics = builder.getMetrics();
|
||||
if (metricFamilyMap.containsKey(metrics)) {
|
||||
MetricFamily metricFamily = metricFamilyMap.get(metrics);
|
||||
for (MetricFamily.Metric metric : metricFamily.getMetricList()) {
|
||||
Map<String, String> labelMap = metric.getLabels()
|
||||
Map<String, String> labelMap = metric.getLabelPair()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String aliasField : aliasFields) {
|
||||
if ("value".equals(aliasField)) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getValue()));
|
||||
if (metric.getCounter() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getCounter().getValue()));
|
||||
} else if (metric.getGauge() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getGauge().getValue()));
|
||||
} else if (metric.getUntyped() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getUntyped().getValue()));
|
||||
} else if (metric.getInfo() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getInfo().getValue()));
|
||||
} else if (metric.getSummary() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getSummary().getValue()));
|
||||
} else if (metric.getHistogram() != null) {
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getHistogram().getValue()));
|
||||
}
|
||||
} else {
|
||||
String columnValue = labelMap.get(aliasField);
|
||||
valueRowBuilder.addColumn(columnValue == null ? CommonConstants.NULL_VALUE : columnValue);
|
||||
@@ -672,10 +472,11 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
CredentialsProvider provider = new BasicCredentialsProvider();
|
||||
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getDigestAuthUsername(),
|
||||
auth.getDigestAuthPassword());
|
||||
AuthScope authScope = new AuthScope(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort()));
|
||||
provider.setCredentials(authScope, credentials);
|
||||
|
||||
provider.setCredentials(AuthScope.ANY, credentials);
|
||||
AuthCache authCache = new BasicAuthCache();
|
||||
authCache.put(new HttpHost(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort())), new DigestScheme());
|
||||
clientContext.setCredentialsProvider(provider);
|
||||
clientContext.setAuthCache(authCache);
|
||||
return clientContext;
|
||||
}
|
||||
}
|
||||
@@ -708,37 +509,13 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
// params
|
||||
Map<String, String> params = httpProtocol.getParams();
|
||||
boolean enableUrlEncoding = Boolean.parseBoolean(httpProtocol.getEnableUrlEncoding());
|
||||
StringBuilder queryParams = new StringBuilder();
|
||||
|
||||
if (params != null && !params.isEmpty()) {
|
||||
for (Map.Entry<String, String> param : params.entrySet()) {
|
||||
String key = param.getKey();
|
||||
String value = param.getValue();
|
||||
|
||||
if (!StringUtils.hasText(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!queryParams.isEmpty()) {
|
||||
queryParams.append("&");
|
||||
}
|
||||
|
||||
if (enableUrlEncoding) {
|
||||
key = UriUtils.encodeQueryParam(key, "UTF-8");
|
||||
}
|
||||
queryParams.append(key);
|
||||
|
||||
if (StringUtils.hasText(value)) {
|
||||
String calculatedValue = TimeExpressionUtil.calculate(value);
|
||||
if (enableUrlEncoding) {
|
||||
calculatedValue = UriUtils.encodeQueryParam(calculatedValue, "UTF-8");
|
||||
}
|
||||
queryParams.append("=").append(calculatedValue);
|
||||
if (StringUtils.hasText(param.getValue())) {
|
||||
requestBuilder.addParameter(param.getKey(), TimeExpressionUtil.calculate(param.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default request header can be overridden if customized
|
||||
// keep-alive
|
||||
requestBuilder.addHeader(HttpHeaders.CONNECTION, NetworkConstants.KEEP_ALIVE);
|
||||
@@ -748,7 +525,8 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
if (StringUtils.hasText(header.getValue())) {
|
||||
requestBuilder.addHeader(header.getKey(), header.getValue());
|
||||
requestBuilder.addHeader(CollectUtil.replaceUriSpecialChar(header.getKey()),
|
||||
CollectUtil.replaceUriSpecialChar(header.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -779,32 +557,14 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
|
||||
// if it has payload, would override post params
|
||||
if (StringUtils.hasLength(httpProtocol.getPayload()) && (HttpMethod.POST.matches(httpMethod) || HttpMethod.PUT.matches(httpMethod))) {
|
||||
requestBuilder.setEntity(new StringEntity(TimeExpressionUtil.calculate(httpProtocol.getPayload()), StandardCharsets.UTF_8));
|
||||
requestBuilder.setEntity(new StringEntity(httpProtocol.getPayload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// uri encode
|
||||
String uri;
|
||||
if (enableUrlEncoding) {
|
||||
// if the url contains parameters directly
|
||||
if (httpProtocol.getUrl().contains("?")) {
|
||||
String path = httpProtocol.getUrl().substring(0, httpProtocol.getUrl().indexOf("?"));
|
||||
String query = httpProtocol.getUrl().substring(httpProtocol.getUrl().indexOf("?") + 1);
|
||||
uri = UriUtils.encodePath(path, "UTF-8") + "?" + UriUtils.encodeQuery(query, "UTF-8");
|
||||
} else {
|
||||
uri = UriUtils.encodePath(httpProtocol.getUrl(), "UTF-8");
|
||||
}
|
||||
} else {
|
||||
uri = httpProtocol.getUrl();
|
||||
}
|
||||
|
||||
// append query params
|
||||
if (!queryParams.isEmpty()) {
|
||||
uri += (uri.contains("?") ? "&" : "?") + queryParams;
|
||||
}
|
||||
|
||||
String finalUri;
|
||||
// uri
|
||||
String uri = CollectUtil.replaceUriSpecialChar(httpProtocol.getUrl());
|
||||
if (IpDomainUtil.isHasSchema(httpProtocol.getHost())) {
|
||||
finalUri = httpProtocol.getHost() + ":" + httpProtocol.getPort() + uri;
|
||||
|
||||
requestBuilder.setUri(httpProtocol.getHost() + ":" + httpProtocol.getPort() + uri);
|
||||
} else {
|
||||
String ipAddressType = IpDomainUtil.checkIpAddressType(httpProtocol.getHost());
|
||||
String baseUri = NetworkConstants.IPV6.equals(ipAddressType)
|
||||
@@ -812,19 +572,12 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
: String.format("%s:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri);
|
||||
boolean ssl = Boolean.parseBoolean(httpProtocol.getSsl());
|
||||
if (ssl) {
|
||||
finalUri = NetworkConstants.HTTPS_HEADER + baseUri;
|
||||
requestBuilder.setUri(NetworkConstants.HTTPS_HEADER + baseUri);
|
||||
} else {
|
||||
finalUri = NetworkConstants.HTTP_HEADER + baseUri;
|
||||
requestBuilder.setUri(NetworkConstants.HTTP_HEADER + baseUri);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
requestBuilder.setUri(finalUri);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid URI with illegal characters: {}. User has disabled URL encoding, not applying any encoding.", finalUri);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// custom timeout
|
||||
int timeout = CollectUtil.getTimeout(httpProtocol.getTimeout(), 0);
|
||||
if (timeout > 0) {
|
||||
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* 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.http.promethus.exporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.ParseException;
|
||||
import org.apache.hertzbeat.common.util.StrBuffer;
|
||||
|
||||
/**
|
||||
* Resolves the data passed by prometheus's exporter interface http:xxx/metrics
|
||||
* Reference: prometheus text_parse.go code, entry: TextToMetricFamilies
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExporterParser {
|
||||
private static final String HELP = "HELP";
|
||||
private static final String TYPE = "TYPE";
|
||||
private static final String EOF = "EOF";
|
||||
private static final String METRIC_NAME_LABEL = ".name";
|
||||
private static final String QUANTILE_LABEL = "quantile";
|
||||
private static final String BUCKET_LABEL = "le";
|
||||
private static final String NAME_LABEL = "__name__";
|
||||
private static final String SUM_SUFFIX = "_sum";
|
||||
private static final String COUNT_SUFFIX = "_count";
|
||||
|
||||
private static final char LEFT_CURLY_BRACKET = '{';
|
||||
private static final char RIGHT_CURLY_BRACKET = '}';
|
||||
private static final char EQUALS = '=';
|
||||
private static final char QUOTES = '"';
|
||||
private static final char ENTER = '\n';
|
||||
private static final char SPACE = ' ';
|
||||
private static final char COMMA = ',';
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private MetricFamily currentMetricFamily;
|
||||
private String currentQuantile;
|
||||
private String currentBucket;
|
||||
|
||||
public Map<String, MetricFamily> textToMetric(String resp) {
|
||||
// key: metric name, value: metric family
|
||||
Map<String, MetricFamily> metricMap = new ConcurrentHashMap<>(10);
|
||||
lock.lock();
|
||||
try {
|
||||
String[] lines = resp.split("\n");
|
||||
for (String line : lines) {
|
||||
this.parseLine(metricMap, new StrBuffer(line));
|
||||
}
|
||||
return metricMap;
|
||||
} catch (Exception e) {
|
||||
log.error("parse prometheus exporter data error, msg: {}", e.getMessage());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return metricMap;
|
||||
}
|
||||
|
||||
private void parseLine(Map<String, MetricFamily> metricMap, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
switch (buffer.charAt(0)) {
|
||||
case '#' -> {
|
||||
buffer.read();
|
||||
this.currentMetricFamily = null;
|
||||
this.parseComment(metricMap, buffer);
|
||||
}
|
||||
case ENTER -> {
|
||||
}
|
||||
default -> {
|
||||
this.currentBucket = null;
|
||||
this.currentQuantile = null;
|
||||
this.parseMetric(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseComment(Map<String, MetricFamily> metricMap, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String token = this.readTokenUnitWhitespace(buffer);
|
||||
if (EOF.equals(token)) {
|
||||
return;
|
||||
}
|
||||
if (!HELP.equals(token) && !TYPE.equals(token)) {
|
||||
log.error("parse comment error {}, start without {} or {}", buffer.toStr(), HELP, TYPE);
|
||||
return;
|
||||
}
|
||||
String metricName = this.readTokenAsMetricName(buffer);
|
||||
this.currentMetricFamily = metricMap.computeIfAbsent(metricName, key -> new MetricFamily());
|
||||
this.currentMetricFamily.setName(metricName);
|
||||
switch (token) {
|
||||
case HELP -> this.parseHelp(buffer);
|
||||
case TYPE -> this.parseType(buffer);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHelp(StrBuffer line) {
|
||||
line.skipBlankTabs();
|
||||
this.currentMetricFamily.setHelp(line.toStr());
|
||||
}
|
||||
|
||||
private void parseType(StrBuffer line) {
|
||||
line.skipBlankTabs();
|
||||
String type = line.toStr().toLowerCase();
|
||||
MetricType metricType = MetricType.getType(type);
|
||||
if (metricType == null) {
|
||||
throw new ParseException("pare type error");
|
||||
}
|
||||
this.currentMetricFamily.setMetricType(metricType);
|
||||
}
|
||||
|
||||
private void parseMetric(StrBuffer buffer) {
|
||||
String metricName = this.readTokenAsMetricName(buffer);
|
||||
MetricFamily.Label label = new MetricFamily.Label();
|
||||
label.setName(METRIC_NAME_LABEL);
|
||||
label.setValue(metricName);
|
||||
|
||||
if (metricName.isEmpty()) {
|
||||
log.error("error parse metric, metric name is null, line: {}", buffer.toStr());
|
||||
return;
|
||||
}
|
||||
|
||||
List<MetricFamily.Metric> metricList = this.currentMetricFamily.getMetricList();
|
||||
if (metricList == null) {
|
||||
metricList = new ArrayList<>();
|
||||
this.currentMetricFamily.setMetricList(metricList);
|
||||
}
|
||||
|
||||
// TODO For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Metric metric = new MetricFamily.Metric();
|
||||
metricList.add(metric);
|
||||
|
||||
metric.setLabelPair(new ArrayList<>());
|
||||
metric.getLabelPair().add(label);
|
||||
this.readLabels(metric, buffer);
|
||||
}
|
||||
|
||||
private void readLabels(MetricFamily.Metric metric, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (buffer.charAt(0) == LEFT_CURLY_BRACKET) {
|
||||
buffer.read();
|
||||
this.startReadLabelName(metric, buffer);
|
||||
} else {
|
||||
this.readLabelValue(metric, null, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void startReadLabelName(MetricFamily.Metric metric, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (buffer.charAt(0) == RIGHT_CURLY_BRACKET) {
|
||||
buffer.read();
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.readLabelValue(metric, new MetricFamily.Label(), buffer);
|
||||
return;
|
||||
}
|
||||
String labelName = this.readTokenAsLabelName(buffer);
|
||||
if (labelName.isEmpty() || labelName.equals(NAME_LABEL)) {
|
||||
throw new ParseException("invalid label name" + labelName + ", label name size = 0 or label name equals " + NAME_LABEL);
|
||||
}
|
||||
MetricFamily.Label label = new MetricFamily.Label();
|
||||
label.setName(labelName);
|
||||
if (buffer.read() != EQUALS) {
|
||||
throw new ParseException("parse error, not match the format of labelName=labelValue");
|
||||
}
|
||||
this.startReadLabelValue(metric, label, buffer);
|
||||
}
|
||||
|
||||
private void startReadLabelValue(MetricFamily.Metric metric, MetricFamily.Label label, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
char c = buffer.read();
|
||||
if (c != QUOTES) {
|
||||
throw new ParseException("expected '\"' at start of label value, line: " + buffer.toStr());
|
||||
}
|
||||
String labelValue = this.readTokenAsLabelValue(buffer);
|
||||
label.setValue(labelValue);
|
||||
if (!this.isValidLabelValue(labelValue)) {
|
||||
throw new ParseException("no valid label value: " + labelValue);
|
||||
}
|
||||
if (this.currentMetricFamily.getMetricType().equals(MetricType.SUMMARY) && label.getName().equals(QUANTILE_LABEL)) {
|
||||
this.currentQuantile = labelValue;
|
||||
} else if (this.currentMetricFamily.getMetricType().equals(MetricType.HISTOGRAM) && label.getName().equals(BUCKET_LABEL)) {
|
||||
this.currentBucket = labelValue;
|
||||
}
|
||||
metric.getLabelPair().add(label);
|
||||
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
c = buffer.read();
|
||||
switch (c) {
|
||||
case COMMA -> this.startReadLabelName(metric, buffer);
|
||||
case RIGHT_CURLY_BRACKET -> this.readLabelValue(metric, label, buffer);
|
||||
default -> throw new ParseException("expected '}' or ',' at end of label value, line: " + buffer.toStr());
|
||||
}
|
||||
}
|
||||
|
||||
private void readLabelValue(MetricFamily.Metric metric, MetricFamily.Label label, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
switch (this.currentMetricFamily.getMetricType()) {
|
||||
case INFO -> {
|
||||
MetricFamily.Info info = new MetricFamily.Info();
|
||||
info.setValue(buffer.toDouble());
|
||||
metric.setInfo(info);
|
||||
}
|
||||
case COUNTER -> {
|
||||
MetricFamily.Counter counter = new MetricFamily.Counter();
|
||||
counter.setValue(buffer.toDouble());
|
||||
metric.setCounter(counter);
|
||||
}
|
||||
case GAUGE -> {
|
||||
MetricFamily.Gauge gauge = new MetricFamily.Gauge();
|
||||
gauge.setValue(buffer.toDouble());
|
||||
metric.setGauge(gauge);
|
||||
}
|
||||
case UNTYPED -> {
|
||||
MetricFamily.Untyped untyped = new MetricFamily.Untyped();
|
||||
untyped.setValue(buffer.toDouble());
|
||||
metric.setUntyped(untyped);
|
||||
}
|
||||
case SUMMARY -> {
|
||||
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Summary summary = new MetricFamily.Summary();
|
||||
summary.setValue(buffer.toDouble());
|
||||
metric.setSummary(summary);
|
||||
}
|
||||
case HISTOGRAM -> {
|
||||
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Histogram histogram = new MetricFamily.Histogram();
|
||||
histogram.setValue(buffer.toDouble());
|
||||
metric.setHistogram(histogram);
|
||||
}
|
||||
default -> throw new ParseException("no such type in metricFamily");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the token before the first whitespace
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return token unit
|
||||
*/
|
||||
private String readTokenUnitWhitespace(StrBuffer buffer) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
if (c == SPACE) {
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the metric
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return token name
|
||||
*/
|
||||
private String readTokenAsMetricName(StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (this.isValidMetricNameStart(buffer.charAt(0))) {
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
if (!this.isValidMetricNameContinuation(c)) {
|
||||
buffer.rollback();
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
throw new ParseException("parse metric name error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the label
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return label name
|
||||
*/
|
||||
private String readTokenAsLabelName(StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
char c = buffer.read();
|
||||
if (this.isValidLabelNameStart(c)) {
|
||||
builder.append(c);
|
||||
while (!buffer.isEmpty()) {
|
||||
c = buffer.read();
|
||||
if (!this.isValidLabelNameContinuation(c)) {
|
||||
buffer.rollback();
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
throw new ParseException("parse label name error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the label
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return label value
|
||||
*/
|
||||
private String readTokenAsLabelValue(StrBuffer buffer) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean escaped = false;
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
// Handle '\\' escape sequences
|
||||
if (escaped) {
|
||||
switch (c) {
|
||||
case QUOTES, '\\' -> builder.append(c);
|
||||
case 'n' -> builder.append('\n');
|
||||
default -> throw new ParseException("parse label value error");
|
||||
}
|
||||
escaped = false;
|
||||
} else {
|
||||
switch (c) {
|
||||
case QUOTES -> {
|
||||
return builder.toString();
|
||||
}
|
||||
case ENTER -> throw new ParseException("parse label value error, next line");
|
||||
case '\\' -> escaped = true;
|
||||
default -> builder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to the first character rule for metric names
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidMetricNameStart(char c) {
|
||||
return isValidLabelNameStart(c) || c == ':';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to rules for metric name characters other than the first
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidMetricNameContinuation(char c) {
|
||||
return isValidLabelNameContinuation(c) || c == ':';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to the first character rule for label names
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelNameStart(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to rules for label name characters other than the first
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelNameContinuation(char c) {
|
||||
return isValidLabelNameStart(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid UTF-8 encoded string
|
||||
*
|
||||
* @param s label value
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelValue(String s) {
|
||||
return s != null;
|
||||
}
|
||||
|
||||
private boolean isSum(String s) {
|
||||
return s != null && s.endsWith(SUM_SUFFIX);
|
||||
}
|
||||
|
||||
private boolean isCount(String s) {
|
||||
return s != null && s.endsWith(COUNT_SUFFIX);
|
||||
}
|
||||
|
||||
}
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* 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.http.promethus.exporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* MetricFamily.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class MetricFamily {
|
||||
/**
|
||||
* metric name
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* metric help
|
||||
*/
|
||||
private String help;
|
||||
|
||||
/**
|
||||
* metric type
|
||||
*/
|
||||
private MetricType metricType;
|
||||
|
||||
/**
|
||||
* Specific metric
|
||||
*/
|
||||
private List<Metric> metricList;
|
||||
|
||||
/**
|
||||
* Metric
|
||||
*/
|
||||
@Data
|
||||
public static class Metric {
|
||||
|
||||
/**
|
||||
* Label data, mainly corresponding to the content within {}
|
||||
*/
|
||||
private List<Label> labelPair;
|
||||
|
||||
/**
|
||||
* info
|
||||
*/
|
||||
private Info info;
|
||||
|
||||
/**
|
||||
* gauge
|
||||
*/
|
||||
private Gauge gauge;
|
||||
|
||||
/**
|
||||
* counter
|
||||
*/
|
||||
private Counter counter;
|
||||
|
||||
/**
|
||||
* summary
|
||||
*/
|
||||
private Summary summary;
|
||||
|
||||
/**
|
||||
* untyped
|
||||
*/
|
||||
private Untyped untyped;
|
||||
|
||||
/**
|
||||
* histogram
|
||||
*/
|
||||
private Histogram histogram;
|
||||
|
||||
/**
|
||||
* timestampMs
|
||||
*/
|
||||
private Long timestampMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label
|
||||
*/
|
||||
@Data
|
||||
public static class Label {
|
||||
|
||||
/**
|
||||
* name
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private String value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Info
|
||||
*/
|
||||
@Data
|
||||
public static class Info {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Counter
|
||||
*/
|
||||
@Data
|
||||
public static class Counter {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
// Exemplar
|
||||
}
|
||||
|
||||
/**
|
||||
* Gauge
|
||||
*/
|
||||
@Data
|
||||
public static class Gauge {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* untyped
|
||||
*/
|
||||
@Data
|
||||
public static class Untyped {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary
|
||||
*/
|
||||
@Data
|
||||
public static class Summary {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* count
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* sum
|
||||
*/
|
||||
private double sum;
|
||||
|
||||
/**
|
||||
* quantileList
|
||||
*/
|
||||
private List<Quantile> quantileList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quantile
|
||||
*/
|
||||
@Data
|
||||
public static class Quantile {
|
||||
/**
|
||||
* Corresponding to the quantile field in Prometheus
|
||||
*/
|
||||
private double xLabel;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Histogram
|
||||
*/
|
||||
@Data
|
||||
public static class Histogram {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* count
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* sum
|
||||
*/
|
||||
private double sum;
|
||||
|
||||
/**
|
||||
* bucketList
|
||||
*/
|
||||
private List<Bucket> bucketList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bucket
|
||||
*/
|
||||
@Data
|
||||
public static class Bucket {
|
||||
|
||||
/**
|
||||
* cumulativeCount
|
||||
*/
|
||||
private long cumulativeCount;
|
||||
|
||||
/**
|
||||
* upperBound
|
||||
*/
|
||||
private double upperBound;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.http.promethus.exporter;
|
||||
|
||||
/**
|
||||
* prometheus metrics type
|
||||
*/
|
||||
public enum MetricType {
|
||||
// for string metric info
|
||||
INFO("info"),
|
||||
// Represents a monotonically increasing counter, e.g., counting occurrences
|
||||
COUNTER("counter"),
|
||||
// A metric type that can fluctuate up and down, e.g., CPU usage rate
|
||||
GAUGE("gauge"),
|
||||
SUMMARY("summary"),
|
||||
UNTYPED("untyped"),
|
||||
HISTOGRAM("histogram");
|
||||
|
||||
private final String value;
|
||||
|
||||
MetricType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static MetricType getType(String value) {
|
||||
for (MetricType metricType : values()) {
|
||||
if (metricType.getValue().equals(value)) {
|
||||
return metricType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+23
-12
@@ -19,7 +19,6 @@ package org.apache.hertzbeat.collector.collect.prometheus;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.SignConstants.RIGHT_DASH;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -35,6 +34,7 @@ import javax.net.ssl.SSLException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
@@ -46,7 +46,6 @@ 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.hertzbeat.collector.collect.prometheus.parser.OnlineParser;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpStatus;
|
||||
@@ -65,6 +64,7 @@ import org.apache.http.impl.auth.DigestScheme;
|
||||
import org.apache.http.impl.client.BasicAuthCache;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -99,12 +99,25 @@ public class PrometheusAutoCollectImpl {
|
||||
builder.setMsg(NetworkConstants.STATUS_CODE + SignConstants.BLANK + statusCode);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseResponseByPrometheusExporter(response.getEntity().getContent(), builder);
|
||||
} catch (Exception e) {
|
||||
log.info("parse error: {}.", e.getMessage(), e);
|
||||
// todo: The InputStream is directly converted to a String here
|
||||
// For large data in the Prometheus exporter, this can generate large objects, which could severely impact JVM memory space
|
||||
// todo: Option one: Use InputStream for parsing, but this requires significant code changes
|
||||
// Option two: Manually trigger garbage collection, which can be referenced from Dubbo for long i
|
||||
String resp = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
long collectTime = System.currentTimeMillis();
|
||||
builder.setTime(collectTime);
|
||||
if (resp == null || !StringUtils.hasText(resp)) {
|
||||
log.error("http response content is empty, status: {}.", statusCode);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("parse response data error:" + e.getMessage());
|
||||
builder.setMsg("http response content is empty");
|
||||
} else {
|
||||
try {
|
||||
return parseResponseByPrometheusExporter(resp, metrics.getAliasFields(), builder);
|
||||
} catch (Exception e) {
|
||||
log.info("parse error: {}.", e.getMessage(), e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("parse response data error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (ClientProtocolException e1) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e1);
|
||||
@@ -155,12 +168,10 @@ public class PrometheusAutoCollectImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private List<CollectRep.MetricsData> parseResponseByPrometheusExporter(InputStream inputStream, CollectRep.MetricsData.Builder builder) throws IOException {
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
private List<CollectRep.MetricsData> parseResponseByPrometheusExporter(String resp, List<String> aliasFields,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
Map<String, MetricFamily> metricFamilyMap = TextParser.textToMetricFamilies(resp);
|
||||
List<CollectRep.MetricsData> metricsDataList = new LinkedList<>();
|
||||
if (metricFamilyMap == null) {
|
||||
return metricsDataList;
|
||||
}
|
||||
for (Map.Entry<String, MetricFamily> entry : metricFamilyMap.entrySet()) {
|
||||
builder.clearFields();
|
||||
builder.clearValues();
|
||||
|
||||
+70
-88
@@ -17,17 +17,16 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.prometheus.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
@@ -37,47 +36,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@Slf4j
|
||||
public class OnlineParser {
|
||||
|
||||
private static final Map<Integer, Integer> escapeMap = new HashMap<>(8);
|
||||
|
||||
static {
|
||||
escapeMap.put((int) 'n', (int) '\n');
|
||||
escapeMap.put((int) 'b', (int) '\b');
|
||||
escapeMap.put((int) 't', (int) '\t');
|
||||
escapeMap.put((int) 'r', (int) '\r');
|
||||
escapeMap.put((int) 'f', (int) '\f');
|
||||
escapeMap.put((int) '\'', (int) '\'');
|
||||
escapeMap.put((int) '\"', (int) '\"');
|
||||
escapeMap.put((int) '\\', (int) '\\');
|
||||
}
|
||||
|
||||
private OnlineParser() {
|
||||
}
|
||||
|
||||
public static Map<String, MetricFamily> parseMetrics(InputStream inputStream) throws IOException {
|
||||
Map<String, MetricFamily> metricFamilyMap = new ConcurrentHashMap<>(10);
|
||||
try {
|
||||
int i = getChar(inputStream);
|
||||
while (i != -1) {
|
||||
if (i == '#' || i == '\n') {
|
||||
skipToLineEnd(inputStream).maybeEol().maybeEof().noElse();
|
||||
} else {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append((char) i);
|
||||
parseMetric(inputStream, metricFamilyMap, stringBuilder);
|
||||
}
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
log.error("prometheus parser failed because of wrong input format. {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return metricFamilyMap;
|
||||
}
|
||||
|
||||
private static class FormatException extends Exception {
|
||||
|
||||
public FormatException() {
|
||||
}
|
||||
public FormatException() {}
|
||||
|
||||
public FormatException(String message) {
|
||||
super(message);
|
||||
@@ -156,92 +117,91 @@ public class OnlineParser {
|
||||
return this.i;
|
||||
}
|
||||
|
||||
private int getInt() {
|
||||
private int getInt() throws FormatException {
|
||||
return this.i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int getChar(InputStream inputStream) throws IOException, FormatException {
|
||||
private static CharChecker parseOneChar(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
if (i == '\\') {
|
||||
i = inputStream.read();
|
||||
if (escapeMap.containsKey(i)) {
|
||||
return escapeMap.get(i);
|
||||
} else {
|
||||
throw new FormatException("Escape character failed.");
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while (i >= '0' && i <= '9' || i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z' || i == '-' || i == '+' || i == '.') {
|
||||
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || i == '-' || i == '+' || i == 'e' || i == '.') {
|
||||
stringBuilder.append((char) i);
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipOneLong(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
private static CharChecker skipOneLong(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while (i >= '0' && i <= '9') {
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == ':') {
|
||||
stringBuilder.append((char) i);
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_') {
|
||||
stringBuilder.append((char) i);
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseLabelValue(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
int i = inputStream.read();
|
||||
while (i != '"' && i != -1) {
|
||||
if (i == '\\') {
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
switch (i) {
|
||||
case 'n' -> stringBuilder.append('\n');
|
||||
case '\\' -> stringBuilder.append('\\');
|
||||
case '\"' -> stringBuilder.append('\"');
|
||||
default -> throw new FormatException();
|
||||
case 'n':
|
||||
stringBuilder.append('\n');
|
||||
break;
|
||||
case '\\':
|
||||
stringBuilder.append('\\');
|
||||
break;
|
||||
case '\"':
|
||||
stringBuilder.append('\"');
|
||||
break;
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
stringBuilder.append((char) i);
|
||||
}
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipSpaces(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
private static CharChecker skipSpaces(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while (i == ' ') {
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
while (i != '\n' && i != -1) {
|
||||
i = getChar(inputStream);
|
||||
i = inputStream.read();
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
@@ -296,7 +256,7 @@ public class OnlineParser {
|
||||
}
|
||||
|
||||
private static CharChecker parseMetric(InputStream inputStream, Map<String, MetricFamily> metricFamilyMap, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
MetricFamily metricFamily;
|
||||
MetricFamily metricFamily = null;
|
||||
MetricFamily.Metric metric = new MetricFamily.Metric();
|
||||
int i = parseMetricName(inputStream, stringBuilder).maybeSpace().maybeLeftBracket().noElse();
|
||||
String metricName = stringBuilder.toString();
|
||||
@@ -307,18 +267,18 @@ public class OnlineParser {
|
||||
metricFamily.setMetricList(new ArrayList<>());
|
||||
metricFamily.setName(metricName);
|
||||
metricFamilyMap.put(metricName, metricFamily);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
metricFamily = metricFamilyMap.get(metricName);
|
||||
}
|
||||
|
||||
if (i == ' ') {
|
||||
i = skipSpaces(inputStream).getInt();
|
||||
}
|
||||
|
||||
List<MetricFamily.Label> labelList = new LinkedList<>();
|
||||
metric.setLabels(labelList);
|
||||
if (i == '{') {
|
||||
List<MetricFamily.Label> labelList = new LinkedList<>();
|
||||
parseLabels(inputStream, stringBuilder, labelList);
|
||||
metric.setLabels(labelList);
|
||||
i = skipSpaces(inputStream).getInt();
|
||||
}
|
||||
|
||||
@@ -344,4 +304,26 @@ public class OnlineParser {
|
||||
metricFamily.getMetricList().add(metric);
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
public static Map<String, MetricFamily> parseMetrics(InputStream inputStream) throws IOException {
|
||||
Map<String, MetricFamily> metricFamilyMap = new ConcurrentHashMap<>(10);
|
||||
int i = inputStream.read();
|
||||
try {
|
||||
while (i != -1) {
|
||||
if (i == '#' || i == '\n') {
|
||||
skipToLineEnd(inputStream).maybeEol().maybeEof().noElse();
|
||||
} else {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append((char) i);
|
||||
parseMetric(inputStream, metricFamilyMap, stringBuilder);
|
||||
}
|
||||
i = inputStream.read();
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
log.error("prometheus parser failed because of wrong input format. {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return metricFamilyMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -29,10 +29,8 @@ import org.apache.hertzbeat.common.util.StrBuffer;
|
||||
/**
|
||||
* Resolves the data passed by prometheus's exporter interface http:xxx/metrics
|
||||
* Reference: prometheus text_parse.go code, entry: TextToMetricFamilies
|
||||
* recommend use OnlineParser
|
||||
*/
|
||||
@Slf4j
|
||||
@Deprecated(since = "1.7.0")
|
||||
public class TextParser {
|
||||
private static final String NAME_LABEL = "__name__";
|
||||
private static final char LEFT_CURLY_BRACKET = '{';
|
||||
@@ -46,6 +44,7 @@ public class TextParser {
|
||||
|
||||
/**
|
||||
* parser prometheus exporter text metrics data
|
||||
* todo use inputStream bytebuffer instead of resp string
|
||||
* @param resp txt data
|
||||
* @return metrics family
|
||||
*/
|
||||
|
||||
+6
-6
@@ -58,13 +58,13 @@ public class PushCollectImpl extends AbstractCollect {
|
||||
private static final Map<Long, Long> timeMap = new ConcurrentHashMap<>();
|
||||
|
||||
// ms
|
||||
private static final Integer DEFAULT_TIMEOUT = 3000;
|
||||
private static final Integer timeout = 3000;
|
||||
|
||||
private static final Integer SUCCESS_CODE = 200;
|
||||
|
||||
// It's hard to determine how long ago the first data collection was, because there's no way to know when the last collection occurred.
|
||||
// This makes it difficult to avoid re-collecting data after a restart. The default is 30 seconds
|
||||
private static final Integer FIRST_COLLECT_INTERVAL = 30000;
|
||||
private static final Integer firstCollectInterval = 30000;
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
@@ -80,7 +80,7 @@ public class PushCollectImpl extends AbstractCollect {
|
||||
long monitorId = builder.getId();
|
||||
PushProtocol pushProtocol = metrics.getPush();
|
||||
|
||||
Long time = timeMap.getOrDefault(monitorId, curTime - FIRST_COLLECT_INTERVAL);
|
||||
Long time = timeMap.getOrDefault(monitorId, curTime - firstCollectInterval);
|
||||
timeMap.put(monitorId, curTime);
|
||||
|
||||
HttpContext httpContext = createHttpContext(pushProtocol);
|
||||
@@ -145,10 +145,10 @@ public class PushCollectImpl extends AbstractCollect {
|
||||
|
||||
//requestBuilder.setUri(pushProtocol.getUri());
|
||||
|
||||
if (DEFAULT_TIMEOUT > 0) {
|
||||
if (timeout > 0) {
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(DEFAULT_TIMEOUT)
|
||||
.setSocketTimeout(DEFAULT_TIMEOUT)
|
||||
.setConnectTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.setRedirectsEnabled(true)
|
||||
.build();
|
||||
requestBuilder.setConfig(requestConfig);
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.s7;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.plc.AbstractPlcCollectImpl;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.PlcProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.S7Protocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.plc4x.java.api.PlcConnection;
|
||||
import org.apache.plc4x.java.api.messages.PlcReadRequest;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* plc collect
|
||||
*/
|
||||
@Slf4j
|
||||
public class S7CollectImpl extends AbstractPlcCollectImpl {
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
S7Protocol s7 = metrics.getS7();
|
||||
List<String> registerAddressList = s7.getRegisterAddresses();
|
||||
// check RackId
|
||||
if (!StringUtils.hasText(s7.getRackId())) {
|
||||
s7.setRackId("0");
|
||||
}
|
||||
// check SlotId
|
||||
if (!StringUtils.hasText(s7.getSlotId())) {
|
||||
s7.setSlotId("0");
|
||||
}
|
||||
// check controllerType
|
||||
if (!StringUtils.hasText(s7.getControllerType())) {
|
||||
s7.setControllerType("S7_1500");
|
||||
}
|
||||
if (!StringUtils.hasText(s7.getTimeout())) {
|
||||
s7.setTimeout("5000");
|
||||
}
|
||||
PlcProtocol plc = metrics.getPlc() == null ? new PlcProtocol() : metrics.getPlc();
|
||||
plc.setRegisterAddresses(registerAddressList);
|
||||
BeanUtils.copyProperties(s7, plc);
|
||||
metrics.setPlc(plc);
|
||||
super.preCheck(metrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
super.collect(builder, metrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_S7;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConnectionString(Metrics metrics) {
|
||||
S7Protocol s7Protocol = metrics.getS7();
|
||||
return "s7://" + s7Protocol.getHost() + ":" + s7Protocol.getPort() + "?remote-rack:" + s7Protocol.getRackId()
|
||||
+ "&remote-slot:" + s7Protocol.getSlotId() + "&controller-type=" + s7Protocol.getControllerType();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlcReadRequest buildRequest(Metrics metrics, PlcConnection connection) {
|
||||
S7Protocol s7Protocol = metrics.getS7();
|
||||
List<String> registerAddressList = s7Protocol.getRegisterAddresses();
|
||||
// Create a new read request:
|
||||
PlcReadRequest.Builder requestBuilder = connection.readRequestBuilder();
|
||||
for (int i = 0; i < registerAddressList.size(); i++) {
|
||||
String s1 = registerAddressList.get(i);
|
||||
requestBuilder.addTagAddress(metrics.getS7().getAddressSyntax() + ":" + i, s1);
|
||||
}
|
||||
return requestBuilder.build();
|
||||
}
|
||||
}
|
||||
+31
-23
@@ -47,28 +47,31 @@ public class DnsCollectImplTest {
|
||||
.address("www.google.com")
|
||||
.timeout("3000")
|
||||
.port("53")
|
||||
.tcp("tcp")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreCheck() {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
//metrics is null
|
||||
dnsCollect.collect(builder, null);
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setName("question");
|
||||
metrics.setDns(dnsProtocol);
|
||||
dnsCollect.preCheck(metrics);
|
||||
});
|
||||
|
||||
//invalid DnsProtocol
|
||||
Metrics metrics = new Metrics();
|
||||
//query class is blank
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
DnsProtocol dns = DnsProtocol.builder().build();
|
||||
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setDns(dns);
|
||||
dnsCollect.preCheck(metrics);
|
||||
});
|
||||
|
||||
//validated DnsProtocol
|
||||
// no exception throws
|
||||
assertDoesNotThrow(() -> {
|
||||
metrics.setName("question");
|
||||
dnsProtocol.setTcp("tcp");
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setDns(dnsProtocol);
|
||||
dnsCollect.preCheck(metrics);
|
||||
});
|
||||
@@ -77,23 +80,28 @@ public class DnsCollectImplTest {
|
||||
@Test
|
||||
public void testCollect() {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
Metrics metrics0 = Metrics.builder()
|
||||
.name("question")
|
||||
.dns(dnsProtocol)
|
||||
.aliasFields(Collections.singletonList("section"))
|
||||
.build();
|
||||
dnsCollect.collect(builder, metrics0);
|
||||
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
|
||||
long monitorId = 666;
|
||||
String app = "testDNS";
|
||||
Metrics metrics = new Metrics();
|
||||
metrics.setName("question");
|
||||
metrics.setDns(dnsProtocol);
|
||||
metrics.setAliasFields(Collections.singletonList("section"));
|
||||
dnsCollect.collect(builder, metrics);
|
||||
assertNotNull(builder.getValues(0).getColumns(0));
|
||||
|
||||
// dns is null, no exception throws
|
||||
assertDoesNotThrow(() -> {
|
||||
dnsCollect.collect(builder, null);
|
||||
});
|
||||
|
||||
// metric name is header
|
||||
Metrics metrics1 = Metrics.builder()
|
||||
.name("header")
|
||||
.dns(dnsProtocol)
|
||||
.aliasFields(Collections.singletonList("section"))
|
||||
.build();
|
||||
dnsCollect.collect(builder, metrics1);
|
||||
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
|
||||
assertDoesNotThrow(() -> {
|
||||
Metrics metrics1 = new Metrics();
|
||||
metrics1.setName("header");
|
||||
metrics1.setDns(dnsProtocol);
|
||||
metrics1.setAliasFields(Collections.singletonList("section"));
|
||||
dnsCollect.collect(builder, metrics1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-96
@@ -18,14 +18,7 @@
|
||||
package org.apache.hertzbeat.collector.collect.http;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
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;
|
||||
@@ -48,7 +41,7 @@ class HttpCollectImplTest {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
httpCollectImpl.preCheck(null);
|
||||
});
|
||||
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
Metrics metrics = Metrics.builder().build();
|
||||
httpCollectImpl.preCheck(metrics);
|
||||
@@ -72,92 +65,4 @@ class HttpCollectImplTest {
|
||||
String protocol = httpCollectImpl.supportProtocol();
|
||||
assert "http".equals(protocol);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseResponseByXmlPath() throws Exception {
|
||||
// Create a sample XML response
|
||||
String xmlResponse = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<server>
|
||||
<name>Server1</name>
|
||||
<status>Running</status>
|
||||
<metrics>
|
||||
<cpu>75.5</cpu>
|
||||
<memory>1024</memory>
|
||||
<disk>500</disk>
|
||||
</metrics>
|
||||
</server>
|
||||
<server>
|
||||
<name>Server2</name>
|
||||
<status>Stopped</status>
|
||||
<metrics>
|
||||
<cpu>0.0</cpu>
|
||||
<memory>0</memory>
|
||||
<disk>500</disk>
|
||||
</metrics>
|
||||
</server>
|
||||
</root>
|
||||
""";
|
||||
|
||||
// Set up HttpProtocol with XML path parsing
|
||||
HttpProtocol http = HttpProtocol.builder()
|
||||
.parseType(DispatchConstants.PARSE_XML_PATH)
|
||||
.parseScript("//server") // XPath to select all server nodes
|
||||
.build();
|
||||
|
||||
// Set up Metrics with fields that have XPath expressions
|
||||
List<Metrics.Field> fields = new ArrayList<>();
|
||||
fields.add(Metrics.Field.builder().field("name").build());
|
||||
fields.add(Metrics.Field.builder().field("status").build());
|
||||
fields.add(Metrics.Field.builder().field("metrics/cpu").build());
|
||||
fields.add(Metrics.Field.builder().field("metrics/memory").build());
|
||||
|
||||
Metrics metrics = Metrics.builder()
|
||||
.http(http)
|
||||
.fields(fields)
|
||||
.aliasFields(Arrays.asList("name", "status", "metrics/cpu", "metrics/memory"))
|
||||
.build();
|
||||
|
||||
// Create a custom builder that captures added rows
|
||||
List<CollectRep.ValueRow> capturedRows = new ArrayList<>();
|
||||
CollectRep.MetricsData.Builder builder = new CollectRep.MetricsData.Builder() {
|
||||
@Override
|
||||
public CollectRep.MetricsData.Builder addValueRow(CollectRep.ValueRow valueRow) {
|
||||
capturedRows.add(valueRow);
|
||||
return super.addValueRow(valueRow);
|
||||
}
|
||||
};
|
||||
|
||||
// Use reflection to access the private parseResponseByXmlPath method
|
||||
Method parseMethod = HttpCollectImpl.class.getDeclaredMethod(
|
||||
"parseResponseByXmlPath",
|
||||
String.class,
|
||||
Metrics.class,
|
||||
CollectRep.MetricsData.Builder.class,
|
||||
Long.class);
|
||||
parseMethod.setAccessible(true);
|
||||
|
||||
// Call the method
|
||||
parseMethod.invoke(httpCollectImpl, xmlResponse, metrics, builder, 100L);
|
||||
|
||||
// Verify the results
|
||||
assertEquals(2, capturedRows.size(), "Should have parsed 2 server nodes");
|
||||
|
||||
// Check first server
|
||||
CollectRep.ValueRow firstRow = capturedRows.get(0);
|
||||
assertEquals(4, firstRow.getColumnsCount(), "First row should have 4 columns");
|
||||
assertEquals("Server1", firstRow.getColumns(0), "First server name should be Server1");
|
||||
assertEquals("Running", firstRow.getColumns(1), "First server status should be Running");
|
||||
assertEquals("75.5", firstRow.getColumns(2), "First server CPU should be 75.5");
|
||||
assertEquals("1024", firstRow.getColumns(3), "First server memory should be 1024");
|
||||
|
||||
// Check second server
|
||||
CollectRep.ValueRow secondRow = capturedRows.get(1);
|
||||
assertEquals(4, secondRow.getColumnsCount(), "Second row should have 4 columns");
|
||||
assertEquals("Server2", secondRow.getColumns(0), "Second server name should be Server2");
|
||||
assertEquals("Stopped", secondRow.getColumns(1), "Second server status should be Stopped");
|
||||
assertEquals("0.0", secondRow.getColumns(2), "Second server CPU should be 0.0");
|
||||
assertEquals("0", secondRow.getColumns(3), "Second server memory should be 0");
|
||||
}
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.http.promethus.exporter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test case for {@link ExporterParser}
|
||||
*/
|
||||
class ExporterParserTest {
|
||||
|
||||
@Test
|
||||
void textToMetric() {
|
||||
String resp = """
|
||||
# HELP disk_total_bytes Total space for path
|
||||
# TYPE disk_total_bytes gauge
|
||||
disk_total_bytes{path="C:\\\\hertzbeat\\\\repo\\\\testpath",} 4.29496725504E11
|
||||
# HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
|
||||
# TYPE go_gc_cycles_automatic_gc_cycles_total counter
|
||||
go_gc_cycles_automatic_gc_cycles_total 0
|
||||
# HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
|
||||
# TYPE go_gc_cycles_forced_gc_cycles_total counter
|
||||
go_gc_cycles_forced_gc_cycles_total 0
|
||||
# HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
|
||||
# TYPE go_gc_cycles_total_gc_cycles_total counter
|
||||
go_gc_cycles_total_gc_cycles_total 0
|
||||
# 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"} 0
|
||||
go_gc_duration_seconds{quantile="0.25"} 0
|
||||
go_gc_duration_seconds{quantile="0.5"} 0
|
||||
go_gc_duration_seconds{quantile="0.75"} 0
|
||||
go_gc_duration_seconds{quantile="1"} 0
|
||||
# TYPE jvm info
|
||||
# HELP jvm VM version info
|
||||
jvm_info{runtime="OpenJDK Runtime Environment",vendor="Azul Systems, Inc.",version="11.0.13+8-LTS"} 1.0
|
||||
# TYPE jvm_gc_collection_seconds summary
|
||||
# HELP jvm_gc_collection_seconds Time spent in a given JVM garbage collector in seconds.
|
||||
jvm_gc_collection_seconds_count{gc="G1 Young Generation"} 10.0
|
||||
jvm_gc_collection_seconds_sum{gc="G1 Young Generation"} 0.051
|
||||
jvm_gc_collection_seconds_count{gc="G1 Old Generation"} 0.0
|
||||
jvm_gc_collection_seconds_sum{gc="G1 Old Generation"} 0.0
|
||||
# TYPE resource_group_aggregate_usage_secs summary
|
||||
resource_group_aggregate_usage_secs{cluster="standalone",quantile="0.5"} 2.69245E-4
|
||||
resource_group_aggregate_usage_secs{cluster="standalone",quantile="0.9"} 3.49601E-4
|
||||
resource_group_aggregate_usage_secs_count{cluster="standalone"} 13.0
|
||||
resource_group_aggregate_usage_secs_sum{cluster="standalone"} 0.004832498
|
||||
resource_group_aggregate_usage_secs_created{cluster="standalone"} 1.715842140749E9
|
||||
# TYPE metadata_store_ops_latency_ms histogram
|
||||
metadata_store_ops_latency_ms_bucket{cluster="standalone",name="metadata-store",type="get",status="success",le="1.0"} 59.0
|
||||
metadata_store_ops_latency_ms_bucket{cluster="standalone",name="metadata-store",type="get",status="success",le="3.0"} 61.0
|
||||
metadata_store_ops_latency_ms_bucket{cluster="standalone",name="metadata-store",type="get",status="success",le="5.0"} 61.0
|
||||
# EOF""";
|
||||
|
||||
ExporterParser parser = new ExporterParser();
|
||||
Map<String, MetricFamily> metricFamilyMap = parser.textToMetric(resp);
|
||||
assertEquals(5, metricFamilyMap.get("resource_group_aggregate_usage_secs").getMetricList().size());
|
||||
assertEquals(3, metricFamilyMap.get("metadata_store_ops_latency_ms").getMetricList().size());
|
||||
assertEquals(5, metricFamilyMap.get("go_gc_duration_seconds").getMetricList().size());
|
||||
assertEquals(9, metricFamilyMap.size());
|
||||
}
|
||||
}
|
||||
+7
-30
@@ -25,12 +25,8 @@ import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
class OnlineParserTest {
|
||||
|
||||
@@ -48,13 +44,13 @@ class OnlineParserTest {
|
||||
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
|
||||
go_gc_duration_seconds{quantile="0.25"} 6.6917e-05
|
||||
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 = "0.75"} +Inf
|
||||
go_gc_duration_seconds{quantile="1"} NaN
|
||||
go_gc_duration_seconds_sum 0.001134793
|
||||
go_gc_duration_seconds_count 5
|
||||
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
|
||||
@@ -77,26 +73,7 @@ class OnlineParserTest {
|
||||
# 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> metricFamilyMap1 = OnlineParser.parseMetrics(inputStream);
|
||||
Map<String, MetricFamily> metricFamilyMap2 = TextParser.textToMetricFamilies(str);
|
||||
assertNotNull(metricFamilyMap1);
|
||||
assertNotNull(metricFamilyMap2);
|
||||
assertEquals(metricFamilyMap1.size(), metricFamilyMap2.size());
|
||||
metricFamilyMap2.forEach((metricFamilyName, metricFamily2) -> {
|
||||
if (!metricFamilyMap1.containsKey(metricFamilyName)) {
|
||||
fail("parse failed, different result from two parser.");
|
||||
}
|
||||
MetricFamily metricFamily1 = metricFamilyMap1.get(metricFamilyName);
|
||||
assertEquals(metricFamily1.getName(), metricFamily1.getName());
|
||||
Set<Double> metricValueSet = metricFamily2.getMetricList().stream().map(MetricFamily.Metric::getValue).collect(Collectors.toSet());
|
||||
metricFamily1.getMetricList().forEach(metric -> {
|
||||
// this is for something different between two algorithms above, and both of them is current on this parsing behavior.
|
||||
if (!(metric.getValue() == Double.POSITIVE_INFINITY || metric.getValue() == Double.NEGATIVE_INFINITY)) {
|
||||
if (!metricValueSet.contains(metric.getValue())) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
assertNotNull(metricFamilyMap);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -302,7 +302,7 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
|
||||
value = String.valueOf(objValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[calculates execute warning, use original value.] {}", e.getMessage());
|
||||
log.info("[calculates execute warning] {}.", e.getMessage());
|
||||
value = Optional.ofNullable(fieldValueMap.get(expression.getSourceText()))
|
||||
.map(String::valueOf)
|
||||
.orElse(null);
|
||||
|
||||
+1
-2
@@ -29,5 +29,4 @@ org.apache.hertzbeat.collector.collect.mqtt.MqttCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.ipmi2.IpmiCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.kafka.KafkaCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.HttpSdCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.modbus.ModbusCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.s7.S7CollectImpl
|
||||
org.apache.hertzbeat.collector.collect.modbus.ModbusCollectImpl
|
||||
+1
-1
@@ -39,7 +39,7 @@ spring:
|
||||
|
||||
collector:
|
||||
info:
|
||||
version: ${COLLECTOR_VERSION:1.7.0}
|
||||
version: ${COLLECTOR_VERSION:1.6.1}
|
||||
ip: ${COLLECTOR_IP:}
|
||||
dispatch:
|
||||
entrance:
|
||||
|
||||
-9
@@ -202,10 +202,6 @@ public interface DispatchConstants {
|
||||
* Parsing method prometheus exporter data
|
||||
*/
|
||||
String PARSE_PROMETHEUS = "prometheus";
|
||||
/**
|
||||
* Parse response body as config/properties format
|
||||
*/
|
||||
String PARSE_CONFIG = "config";
|
||||
/**
|
||||
* prometheus accept header
|
||||
*/
|
||||
@@ -231,9 +227,4 @@ public interface DispatchConstants {
|
||||
* protocol modbus
|
||||
*/
|
||||
String PROTOCOL_MODBUS = "modbus";
|
||||
|
||||
/**
|
||||
* protocol modbus
|
||||
*/
|
||||
String PROTOCOL_S7 = "s7";
|
||||
}
|
||||
|
||||
+7
-3
@@ -613,8 +613,9 @@ public class HashedWheelTimer implements Timer {
|
||||
HashedWheelBucket bucket = this.bucket;
|
||||
if (bucket != null) {
|
||||
bucket.remove(this);
|
||||
} else {
|
||||
timer.pendingTimeouts.decrementAndGet();
|
||||
}
|
||||
timer.pendingTimeouts.decrementAndGet();
|
||||
}
|
||||
|
||||
public boolean compareAndSetState(int expected, int state) {
|
||||
@@ -641,7 +642,6 @@ public class HashedWheelTimer implements Timer {
|
||||
}
|
||||
|
||||
try {
|
||||
remove();
|
||||
task.run(this);
|
||||
} catch (Throwable t) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
@@ -718,6 +718,7 @@ public class HashedWheelTimer implements Timer {
|
||||
while (timeout != null) {
|
||||
HashedWheelTimeout next = timeout.next;
|
||||
if (timeout.remainingRounds <= 0) {
|
||||
next = remove(timeout);
|
||||
if (timeout.deadline <= deadline) {
|
||||
timeout.expire();
|
||||
} else {
|
||||
@@ -725,7 +726,9 @@ public class HashedWheelTimer implements Timer {
|
||||
throw new IllegalStateException(String.format(
|
||||
"timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));
|
||||
}
|
||||
} else if (!timeout.isCancelled()) {
|
||||
} else if (timeout.isCancelled()) {
|
||||
next = remove(timeout);
|
||||
} else {
|
||||
timeout.remainingRounds--;
|
||||
}
|
||||
timeout = next;
|
||||
@@ -758,6 +761,7 @@ public class HashedWheelTimer implements Timer {
|
||||
timeout.prev = null;
|
||||
timeout.next = null;
|
||||
timeout.bucket = null;
|
||||
timeout.timer.pendingTimeouts.decrementAndGet();
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -242,7 +242,6 @@ public class KafkaCollectImpl extends AbstractCollect {
|
||||
boolean isKafkaCommand = SupportedCommand.isKafkaCommand(command);
|
||||
if (!isKafkaCommand) {
|
||||
log.error("Unsupported command: {}", command);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,7 +273,7 @@ public class KafkaCollectImpl extends AbstractCollect {
|
||||
}
|
||||
}
|
||||
|
||||
protected AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
|
||||
private AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
|
||||
CacheIdentifier kafkaAdminClientIdentifier = CacheIdentifier.builder()
|
||||
.ip(kafkaProtocol.getHost()).port(kafkaProtocol.getPort())
|
||||
.build();
|
||||
|
||||
+21
-59
@@ -17,24 +17,12 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.kafka;
|
||||
|
||||
import org.apache.hertzbeat.collector.collect.kafka.constants.SupportedCommand;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.KafkaProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.kafka.clients.admin.AdminClient;
|
||||
import org.apache.kafka.clients.admin.ListTopicsOptions;
|
||||
import org.apache.kafka.clients.admin.ListTopicsResult;
|
||||
import org.apache.kafka.common.KafkaFuture;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -43,25 +31,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
/**
|
||||
* Test case for {@link KafkaCollectImpl}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class KafkaCollectTest {
|
||||
|
||||
private static final String HOST = "127.0.0.1";
|
||||
private static final String PORT = "9092";
|
||||
|
||||
private KafkaCollectImpl collect;
|
||||
|
||||
@Mock
|
||||
private AdminClient adminClient;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
collect = new KafkaCollectImpl(){
|
||||
@Override
|
||||
protected AdminClient getAdminClient(KafkaProtocol protocol) {
|
||||
return adminClient;
|
||||
}
|
||||
};
|
||||
collect = new KafkaCollectImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,51 +59,38 @@ public class KafkaCollectTest {
|
||||
});
|
||||
// kafka port is null
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
kafka.setHost(HOST);
|
||||
kafka.setHost("127.0.0.1");
|
||||
collect.preCheck(metric);
|
||||
});
|
||||
// no exception throw
|
||||
assertDoesNotThrow(() -> {
|
||||
kafka.setPort(PORT);
|
||||
kafka.setPort("9092");
|
||||
collect.preCheck(metric);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollect() throws Exception {
|
||||
Set<String> topicSet = new HashSet<>();
|
||||
topicSet.add("test-topic");
|
||||
|
||||
ListTopicsResult listTopicsResult = Mockito.mock(ListTopicsResult.class);
|
||||
KafkaFuture<Set<String>> future = Mockito.mock(KafkaFuture.class);
|
||||
|
||||
Mockito.when(adminClient.listTopics(Mockito.any(ListTopicsOptions.class))).thenReturn(listTopicsResult);
|
||||
Mockito.when(listTopicsResult.names()).thenReturn(future);
|
||||
Mockito.when(future.get()).thenReturn(topicSet);
|
||||
|
||||
KafkaProtocol kafka = KafkaProtocol.builder()
|
||||
.host(HOST)
|
||||
.port(PORT)
|
||||
.build();
|
||||
|
||||
Metrics metrics = Metrics.builder()
|
||||
.kclient(kafka)
|
||||
.build();
|
||||
|
||||
// test if not kafka command
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
collect.collect(builder, metrics);
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
void collect() {
|
||||
// metrics is null
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
collect.collect(builder, null);
|
||||
});
|
||||
|
||||
KafkaProtocol kafka = KafkaProtocol.builder().host("127.0.0.1").port("9092").build();
|
||||
Metrics metrics = Metrics.builder().kclient(kafka).build();
|
||||
//test if not kafka command
|
||||
assertDoesNotThrow(() -> {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
collect.collect(builder, metrics);
|
||||
});
|
||||
//test kafka command
|
||||
kafka.setCommand(SupportedCommand.TOPIC_LIST.getCommand());
|
||||
builder.setCode(CollectRep.Code.forNumber(6));
|
||||
collect.collect(builder, metrics);
|
||||
assertDoesNotThrow(() -> {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
metrics.getKclient().setCommand("topic-list");
|
||||
collect.collect(builder, metrics);
|
||||
});
|
||||
|
||||
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
|
||||
assertEquals(1, builder.getValuesList().size());
|
||||
assertEquals("test-topic", builder.getValues(0).getColumns(0));
|
||||
Mockito.verify(adminClient).listTopics(Mockito.any(ListTopicsOptions.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-15
@@ -455,19 +455,4 @@ public interface CommonConstants {
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte STATUS_PAGE_INCIDENT_STATE_RESOLVED = 3;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_NORMAL = 0;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_PUSH_AUTO_CREATE = 1;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_DISCOVERY_AUTO_CREATE = 2;
|
||||
}
|
||||
|
||||
-2
@@ -65,8 +65,6 @@ public interface ConfigConstants {
|
||||
String INFO = "info";
|
||||
|
||||
String GRAFANA = "grafana";
|
||||
|
||||
String LOG = "log";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,4 @@ public interface SmsConstants {
|
||||
|
||||
// Aws cloud SMS
|
||||
String AWS = "aws";
|
||||
|
||||
// Twilio SMS
|
||||
String TWILIO = "twilio";
|
||||
}
|
||||
|
||||
-5
@@ -122,11 +122,6 @@ public class NoticeReceiver {
|
||||
example = "779294123", accessMode = READ_WRITE)
|
||||
private String tgUserId;
|
||||
|
||||
@Schema(title = "Telegram message thread id: The notification method is valid for Telegram Bot",
|
||||
description = "TTelegram message thread id: The notification method is valid for Telegram Bot",
|
||||
example = "779294123", accessMode = READ_WRITE)
|
||||
private String tgMessageThreadId;
|
||||
|
||||
@Schema(title = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
|
||||
description = "DingTalk,FeiShu,WeWork user id: The notification method is valid for DingTalk,FeiShu,WeWork Bot",
|
||||
example = "779294123", accessMode = READ_WRITE)
|
||||
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.dto.query;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Metric History Range Query Data
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Query Request Data")
|
||||
public class DatasourceQuery {
|
||||
|
||||
@Schema(title = "Ref Id, unique id for the query")
|
||||
private String refId;
|
||||
|
||||
@Schema(title = "datasource name")
|
||||
private String datasource;
|
||||
|
||||
@Schema(title = "query expr, like prometheus query")
|
||||
private String expr;
|
||||
|
||||
@Schema(title = "query expr type, like promql or sql or influxql")
|
||||
private String exprType;
|
||||
|
||||
@Schema(title = "query range type, like range or instant")
|
||||
private String timeType;
|
||||
|
||||
@Schema(title = "query range start time")
|
||||
private Long start;
|
||||
|
||||
@Schema(title = "query range end time")
|
||||
private Long end;
|
||||
|
||||
@Schema(title = "query time step, like 5m or 1h")
|
||||
private String step;
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.dto.query;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Metric History Range Query Data
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Metric Query Data")
|
||||
public class DatasourceQueryData {
|
||||
|
||||
@Schema(title = "Ref Id, unique id for the query")
|
||||
private String refId;
|
||||
|
||||
@Schema(title = "query status code, 200 for success, other for error")
|
||||
private Integer status;
|
||||
|
||||
@Schema(title = "query error message")
|
||||
private String msg;
|
||||
|
||||
@Schema(title = "query result data frames")
|
||||
private List<SchemaData> frames;
|
||||
|
||||
/**
|
||||
* Schema Data
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static final class SchemaData {
|
||||
|
||||
@Schema(title = "Metric Schema")
|
||||
private MetricSchema schema;
|
||||
|
||||
@Schema(title = "metrics row values, first is the timestamp-ts", example = "[[29,32,44],[32,34,true]]")
|
||||
private List<Object[]> data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metric Schema
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static final class MetricSchema {
|
||||
|
||||
@Schema(title = "Metrics Field")
|
||||
private List<MetricField> fields;
|
||||
|
||||
@Schema(title = "This frame labels")
|
||||
private Map<String, String> labels;
|
||||
|
||||
@Schema(title = "Meta Information")
|
||||
private Map<String, String> meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metric Field
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static final class MetricField {
|
||||
|
||||
@Schema(title = "Metric Field Name")
|
||||
private String name;
|
||||
|
||||
@Schema(title = "Field Type: number, string, time, bool")
|
||||
private String type;
|
||||
|
||||
@Schema(title = "Field Unit: %, Mb, Kbps etc.")
|
||||
private String unit;
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ import org.apache.hertzbeat.common.entity.job.protocol.PushProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RedfishProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RedisProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RocketmqProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.S7Protocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ScriptProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.SmtpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.SnmpProtocol;
|
||||
@@ -271,10 +270,6 @@ public class Metrics {
|
||||
* Monitoring configuration information using the public modBus protocol
|
||||
*/
|
||||
private ModbusProtocol modbus;
|
||||
/**
|
||||
* Monitoring configuration information using the public s7 protocol
|
||||
*/
|
||||
private S7Protocol s7;
|
||||
/**
|
||||
* collector use - Temporarily store subTask metrics response data
|
||||
*/
|
||||
|
||||
-6
@@ -95,12 +95,6 @@ public class HttpProtocol implements CommonRequestProtocol, Protocol {
|
||||
*/
|
||||
private List<String> successCodes;
|
||||
|
||||
/**
|
||||
* Whether to enable URL encoding for the path. Default is true.
|
||||
* When true, the URL path will be encoded. When false, the URL path will not be encoded.
|
||||
*/
|
||||
private String enableUrlEncoding = "true";
|
||||
|
||||
/**
|
||||
* authentication information
|
||||
*/
|
||||
|
||||
@@ -91,9 +91,6 @@ public class Monitor {
|
||||
@Max(4)
|
||||
private byte status;
|
||||
|
||||
@Schema(title = "Task type 0: Normal, 1: push auto create, 2: discovery auto create")
|
||||
private byte type;
|
||||
|
||||
@Schema(title = "task label", example = "{env:test}", accessMode = READ_WRITE)
|
||||
@Convert(converter = JsonMapAttributeConverter.class)
|
||||
@Column(length = 4096)
|
||||
|
||||
+9
-1
@@ -445,7 +445,15 @@ public final class CollectRep {
|
||||
if (value != null) {
|
||||
// Check byte array size, Arrow buffer size is 32768 bytes
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
vector.setSafe(rowIndex, bytes);
|
||||
if (bytes.length > 32700) {
|
||||
log.warn("Value too large for Arrow buffer ({}), truncating to 32700 bytes. Meta: {}",
|
||||
bytes.length, JsonUtil.toJson(metadata));
|
||||
byte[] truncatedBytes = new byte[32700];
|
||||
System.arraycopy(bytes, 0, truncatedBytes, 0, 32700);
|
||||
vector.set(rowIndex, truncatedBytes);
|
||||
} else {
|
||||
vector.set(rowIndex, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,46 +49,4 @@ public final class TimePeriodUtil {
|
||||
return Duration.parse("PT" + tokenTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transform any timestamp to milliseconds
|
||||
* @param timestamp timestamp
|
||||
* @return milliseconds
|
||||
*/
|
||||
public static long normalizeToMilliseconds(Object timestamp) {
|
||||
if (timestamp instanceof String timestampStr) {
|
||||
// string type, may be second, millisecond or decimal second
|
||||
// eg: "1672531199000", "1672531199", "1672531199.123"
|
||||
if (timestampStr.contains(".")) {
|
||||
// contains decimal point, parse as second timestamp
|
||||
double seconds = Double.parseDouble(timestampStr);
|
||||
return (long) (seconds * 1000);
|
||||
} else {
|
||||
// integer form, determine second or millisecond
|
||||
long numericTimestamp = Long.parseLong(timestampStr);
|
||||
return convertNumericTimestamp(numericTimestamp);
|
||||
}
|
||||
} else if (timestamp instanceof Number) {
|
||||
// number eg Integer、Long、Double
|
||||
if (timestamp instanceof Double || timestamp instanceof Float) {
|
||||
// float type, treat as second timestamp
|
||||
double seconds = ((Number) timestamp).doubleValue();
|
||||
return (long) (seconds * 1000);
|
||||
} else {
|
||||
// integer type, directly determine second or millisecond
|
||||
long numericTimestamp = ((Number) timestamp).longValue();
|
||||
return convertNumericTimestamp(numericTimestamp);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Not support this timestamp type: " + timestamp.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static long convertNumericTimestamp(long numericTimestamp) {
|
||||
if (String.valueOf(numericTimestamp).length() <= 10) {
|
||||
return numericTimestamp * 1000;
|
||||
} else {
|
||||
return numericTimestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-12
@@ -40,7 +40,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -72,11 +71,6 @@ public class DashboardService {
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ResponseEntity<?> createOrUpdateDashboard(String dashboardJson, Long monitorId) {
|
||||
if (!grafanaProperties.enabled()) {
|
||||
log.info("HertzBeat Grafana config not enabled");
|
||||
throw new RuntimeException("HertzBeat Grafana config not enabled");
|
||||
}
|
||||
|
||||
String token = serviceAccountService.getToken();
|
||||
String url = grafanaProperties.getPrefix() + grafanaProperties.getUrl() + CREATE_DASHBOARD_API;
|
||||
|
||||
@@ -109,12 +103,6 @@ public class DashboardService {
|
||||
log.error("create dashboard error: {}", response.getStatusCode());
|
||||
throw new RuntimeException("create dashboard error");
|
||||
}
|
||||
} catch (HttpClientErrorException.Forbidden ex) {
|
||||
log.error("Grafana Access denied to save dashboard", ex);
|
||||
throw new RuntimeException("Grafana Access denied to save dashboard", ex);
|
||||
} catch (HttpClientErrorException.NotFound ex){
|
||||
log.error("Grafana Dashboard not found", ex);
|
||||
throw new RuntimeException("Grafana Dashboard not found", ex);
|
||||
} catch (Exception ex) {
|
||||
log.error("create dashboard error", ex);
|
||||
throw new RuntimeException("create dashboard error", ex);
|
||||
|
||||
-5
@@ -61,11 +61,6 @@ public class DatasourceService {
|
||||
* Create a new datasource in Grafana.
|
||||
*/
|
||||
public void existOrCreateDatasource(String token) {
|
||||
if (!warehouseProperties.enabled()) {
|
||||
log.info("HertzBeat VictoriaMetrics config not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(token);
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hertzbeat-log</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<properties>
|
||||
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- hertzbeat common -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-common</artifactId>
|
||||
</dependency>
|
||||
<!-- hertzbeat warehouse -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-warehouse</artifactId>
|
||||
</dependency>
|
||||
<!-- OpenTelemetry -->
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-logs</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-otlp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry.instrumentation</groupId>
|
||||
<artifactId>opentelemetry-logback-appender-1.0</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.log.config;
|
||||
|
||||
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
|
||||
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* OpenTelemetryConfig is responsible for initializing OpenTelemetry with the specified service name and GrepTimeDB endpoint.
|
||||
* It ensures that the initialization is done in a thread-safe manner and includes authentication for GrepTimeDB.
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class OpenTelemetryConfig {
|
||||
|
||||
@Autowired
|
||||
private GreptimeProperties greptimeProperties;
|
||||
|
||||
/**
|
||||
* Initializes OpenTelemetry with the given service name and GrepTimeDB endpoint.
|
||||
* Includes authentication if configured in GreptimeProperties.
|
||||
*/
|
||||
@PostConstruct
|
||||
public void initializeOpenTelemetry() {
|
||||
if (greptimeProperties == null || !greptimeProperties.enabled()) {
|
||||
log.info("GrepTimeDB logging is disabled, skipping OpenTelemetry configuration.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Resource resource = Resource.getDefault()
|
||||
.merge(Resource.builder()
|
||||
.put(SERVICE_NAME, "HertzBeat")
|
||||
.build());
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
|
||||
headers.put("X-Greptime-DB-Name", "public");
|
||||
headers.put("X-Greptime-Log-Table-Name", "hzb_log");
|
||||
|
||||
addAuthenticationHeaders(headers);
|
||||
|
||||
OtlpHttpLogRecordExporter logExporter = OtlpHttpLogRecordExporter.builder()
|
||||
.setEndpoint(greptimeProperties.httpEndpoint() + "/v1/otlp/v1/logs")
|
||||
.setHeaders(()-> headers)
|
||||
.setTimeout(10, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
|
||||
.setResource(resource)
|
||||
.addLogRecordProcessor(
|
||||
BatchLogRecordProcessor.builder(logExporter)
|
||||
.setScheduleDelay(1000, TimeUnit.MILLISECONDS)
|
||||
.setMaxExportBatchSize(512)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
|
||||
.setLoggerProvider(loggerProvider)
|
||||
.build();
|
||||
|
||||
OpenTelemetryAppender.install(openTelemetry);
|
||||
log.info("OpenTelemetry successfully configured with GrepTimeDB exporter.");
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to initialize OpenTelemetry with GrepTimeDB", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds authentication headers to the provided map if username and password are configured.
|
||||
*
|
||||
* @param headers the map to which authentication headers will be added
|
||||
*/
|
||||
private void addAuthenticationHeaders(Map<String, String> headers) {
|
||||
if (StringUtils.isNotBlank(greptimeProperties.username())
|
||||
&& StringUtils.isNotBlank(greptimeProperties.password())) {
|
||||
String credentials = greptimeProperties.username() + ":" + greptimeProperties.password();
|
||||
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
|
||||
headers.put("Authorization", "Basic " + encodedCredentials);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.apache.hertzbeat.log.config.LogAutoConfiguration
|
||||
@@ -89,11 +89,6 @@
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-grafana</artifactId>
|
||||
</dependency>
|
||||
<!-- log -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-log</artifactId>
|
||||
</dependency>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -28,7 +28,6 @@ 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;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* start up class.
|
||||
@@ -42,7 +41,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
|
||||
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class Manager {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Manager.class, args);
|
||||
|
||||
+18
-13
@@ -15,21 +15,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.log.config;
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
|
||||
import org.apache.hertzbeat.common.constants.ConfigConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Log auto configuration.
|
||||
* Alibaba Sms Sender configuration dto
|
||||
*/
|
||||
@ComponentScan(basePackages = ConfigConstants.PkgConstant.PKG
|
||||
+ SignConstants.DOT
|
||||
+ ConfigConstants.FunctionModuleConstants.LOG
|
||||
)
|
||||
@EnableConfigurationProperties(GreptimeProperties.class)
|
||||
public class LogAutoConfiguration {
|
||||
@Data
|
||||
public class SmsAlibabaConfig {
|
||||
|
||||
@NotBlank(message = "AccessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
@NotBlank(message = "AccessKeySecret cannot be empty")
|
||||
private String accessKeySecret;
|
||||
|
||||
@NotBlank(message = "SignName cannot be empty")
|
||||
private String signName;
|
||||
|
||||
@NotBlank(message = "TemplateCode cannot be null")
|
||||
private String templateCode;
|
||||
}
|
||||
+15
-12
@@ -15,21 +15,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.warehouse.service;
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.DatasourceQuery;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.DatasourceQueryData;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* metrics data query service
|
||||
* AWS Sms Sender configuration dto
|
||||
*/
|
||||
public interface DatasourceQueryService {
|
||||
@Data
|
||||
public class SmsAwsConfig {
|
||||
|
||||
@NotBlank(message = "AccessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
@NotBlank(message = "AccessKeySecret cannot be empty")
|
||||
private String accessKeySecret;
|
||||
|
||||
@NotBlank(message = "region cannot be empty")
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* Query metrics data
|
||||
* @param queries query expr
|
||||
* @return data
|
||||
*/
|
||||
List<DatasourceQueryData> query(List<DatasourceQuery> queries);
|
||||
}
|
||||
+18
-20
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.config;
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,27 +23,25 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Twilio SMS configuration properties
|
||||
* Sms Sender configuration dto
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TwilioSmsProperties {
|
||||
/**
|
||||
* Twilio Account SID
|
||||
*/
|
||||
@NotBlank(message = "Account SID cannot be empty")
|
||||
private String accountSid;
|
||||
@AllArgsConstructor
|
||||
public class SmsNoticeSender {
|
||||
|
||||
@NotBlank(message = "Type cannot be empty")
|
||||
private String type;
|
||||
|
||||
private SmsTencentConfig tencent;
|
||||
|
||||
private SmsAlibabaConfig alibaba;
|
||||
|
||||
/**
|
||||
* Twilio Auth Token
|
||||
*/
|
||||
@NotBlank(message = "Auth Token cannot be empty")
|
||||
private String authToken;
|
||||
private SmsUniSmsConfig unisms;
|
||||
|
||||
/**
|
||||
* Twilio Issued Phone Number
|
||||
*/
|
||||
@NotBlank(message = "Twilio Phone Number cannot be empty")
|
||||
private String twilioPhoneNumber;
|
||||
}
|
||||
private SmslocalConfig smslocal;
|
||||
|
||||
private SmsAwsConfig aws;
|
||||
|
||||
private boolean enable = true;
|
||||
}
|
||||
+16
-27
@@ -15,45 +15,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.job.protocol;
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Modbus Protocol
|
||||
* Tencent Sms Sender configuration dto
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class S7Protocol implements Protocol {
|
||||
@AllArgsConstructor
|
||||
public class SmsTencentConfig {
|
||||
|
||||
@NotBlank(message = "SecretId cannot be empty")
|
||||
private String secretId;
|
||||
|
||||
/**
|
||||
* IP ADDRESS OR DOMAIN NAME OF THE PEER HOST
|
||||
*/
|
||||
private String host;
|
||||
/**
|
||||
* Port number
|
||||
*/
|
||||
private String port;
|
||||
@NotBlank(message = "SecretKey cannot be empty")
|
||||
private String secretKey;
|
||||
|
||||
private String driverName;
|
||||
@NotBlank(message = "SignName cannot be empty")
|
||||
private String signName;
|
||||
|
||||
private String addressSyntax;
|
||||
@NotBlank(message = "AppId cannot be null")
|
||||
private String appId;
|
||||
|
||||
private String rackId;
|
||||
@NotBlank(message = "templateId cannot be null")
|
||||
private String templateId;
|
||||
|
||||
private String slotId;
|
||||
|
||||
private String controllerType;
|
||||
|
||||
private String timeout;
|
||||
|
||||
private List<String> registerAddresses;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.manager.pojo.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.logging.log4j.core.config.plugins.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* UniSMS configuration
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmsUniSmsConfig {
|
||||
/**
|
||||
* UniSMS access key id
|
||||
*/
|
||||
@NotBlank(message = "accessKeyId cannot be empty")
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* UniSMS access key secret, required for HMAC mode
|
||||
*/
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* SMS signature
|
||||
*/
|
||||
@NotBlank(message = "signature cannot be null")
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* SMS template ID
|
||||
*/
|
||||
@NotBlank(message = "templateId cannot be null")
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* Authentication mode: simple or hmac, default is simple
|
||||
*/
|
||||
@NotBlank(message = "authMode cannot be null")
|
||||
private String authMode = "simple";
|
||||
}
|
||||
+18
-4
@@ -17,8 +17,22 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export class TwilioSmsConfig {
|
||||
accountSid!: string;
|
||||
authToken!: string;
|
||||
twilioPhoneNumber!: string;
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
/**
|
||||
* Smslocal SMS configuration
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SmslocalConfig {
|
||||
/**
|
||||
* Smslocal api key
|
||||
*/
|
||||
private String apiKey;
|
||||
}
|
||||
-1
@@ -427,7 +427,6 @@ public class CollectorJobScheduler implements CollectorScheduling, CollectJobSch
|
||||
if (jobId == null) {
|
||||
return;
|
||||
}
|
||||
jobContentCache.remove(jobId);
|
||||
for (ConsistentHash.Node node : consistentHash.getAllNodes().values()) {
|
||||
AssignJobs assignJobs = node.getAssignJobs();
|
||||
if (assignJobs.getPinnedJobs().remove(jobId)
|
||||
|
||||
+1
-2
@@ -75,8 +75,7 @@ import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Monitoring Type Management Implementation
|
||||
* Temporarily stores the monitoring configuration and parameter configuration in memory,
|
||||
* and then persists it to a storage system.
|
||||
* temporarily stores the monitoring configuration and parameter configuration in memory and then stores it in the
|
||||
*/
|
||||
@Service
|
||||
@Order(value = Ordered.HIGHEST_PRECEDENCE)
|
||||
|
||||
+1
-1
@@ -531,7 +531,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
MonitorDto monitorDto = new MonitorDto();
|
||||
List<Param> params = paramDao.findParamsByMonitorId(id);
|
||||
monitorDto.setParams(params);
|
||||
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp()) || monitor.getType() == CommonConstants.MONITOR_TYPE_PUSH_AUTO_CREATE) {
|
||||
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp())) {
|
||||
List<CollectRep.MetricsData> metricsDataList = warehouseService.queryMonitorMetricsData(id);
|
||||
List<String> metrics = metricsDataList.stream().map(CollectRep.MetricsData::getMetrics).collect(Collectors.toList());
|
||||
monitorDto.setMetrics(metrics);
|
||||
|
||||
+5
-5
@@ -22,10 +22,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.hertzbeat.alert.config.SmsConfig;
|
||||
import org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum;
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
import org.apache.hertzbeat.common.support.event.SmsConfigChangeEvent;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.SmsNoticeSender;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.stereotype.Service;
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class SmsGeneralConfigServiceImpl extends AbstractGeneralConfigServiceImpl<SmsConfig> {
|
||||
public class SmsGeneralConfigServiceImpl extends AbstractGeneralConfigServiceImpl<SmsNoticeSender> {
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class SmsGeneralConfigServiceImpl extends AbstractGeneralConfigServiceImp
|
||||
* This method is used to handle the sms configuration change event.
|
||||
*/
|
||||
@Override
|
||||
public void handler(SmsConfig smsConfig) {
|
||||
public void handler(SmsNoticeSender smsNoticeSender) {
|
||||
applicationContext.publishEvent(new SmsConfigChangeEvent(applicationContext));
|
||||
}
|
||||
|
||||
@@ -69,11 +69,11 @@ public class SmsGeneralConfigServiceImpl extends AbstractGeneralConfigServiceImp
|
||||
* a TypeReference of NoticeSender type
|
||||
*/
|
||||
@Override
|
||||
public TypeReference<SmsConfig> getTypeReference() {
|
||||
public TypeReference<SmsNoticeSender> getTypeReference() {
|
||||
return new TypeReference<>() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return SmsConfig.class;
|
||||
return SmsNoticeSender.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,22 +72,14 @@ warehouse:
|
||||
rpc-port: 6667
|
||||
username: root
|
||||
password: root
|
||||
# store real-time metrics data, enable only one below
|
||||
real-time:
|
||||
redis:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: 123456
|
||||
memory:
|
||||
enabled: true
|
||||
init-size: 16
|
||||
redis:
|
||||
enabled: false
|
||||
# redis mode: single, sentinel, cluster. Default is single
|
||||
mode: single
|
||||
# separate each address with comma when using cluster mode, eg: 127.0.0.1:6379,127.0.0.1:6380
|
||||
address: 127.0.0.1:6379
|
||||
# enter master name when using sentinel mode
|
||||
masterName: mymaster
|
||||
password: 123456
|
||||
# redis db index, default: DB0
|
||||
db: 0
|
||||
|
||||
scheduler:
|
||||
server:
|
||||
@@ -110,4 +102,4 @@ ai:
|
||||
# api key
|
||||
api-key:
|
||||
#At present, only IFLYTEK large model needs to be filled in
|
||||
api-secret:
|
||||
api-secret:
|
||||
@@ -231,10 +231,6 @@ alerter:
|
||||
access-key-id: YOUR_ACCESS_KEY_ID
|
||||
access-key-secret: YOUR_ACCESS_KEY_SECRET
|
||||
region: AWS_REGION_FOR_END_USER_MESSAGING
|
||||
twilio:
|
||||
account-sid: YOUR_ACCOUNT_SID
|
||||
auth-token: YOUR_AUTH_TOKEN
|
||||
twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
|
||||
scheduler:
|
||||
server:
|
||||
enabled: true
|
||||
|
||||
@@ -17,44 +17,71 @@
|
||||
|
||||
-- ensure every sql can rerun without error
|
||||
|
||||
-- add repair table sql
|
||||
REPAIR TABLE HZB_ALERT_DEFINE;
|
||||
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE AddOrDropColumns()
|
||||
BEGIN
|
||||
DECLARE col_exists INT;
|
||||
|
||||
-- Check and add 'app' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='app';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN app VARCHAR(255);
|
||||
END IF;
|
||||
-- Drop 'app' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='app';
|
||||
IF col_exists = 1 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE DROP COLUMN app;
|
||||
END IF;
|
||||
|
||||
-- Check and add 'metric' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='metric';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN metric VARCHAR(255);
|
||||
END IF;
|
||||
-- Drop 'metric' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='metric';
|
||||
IF col_exists = 1 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE DROP COLUMN metric;
|
||||
END IF;
|
||||
|
||||
-- Check and add 'field' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='field';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN field VARCHAR(255);
|
||||
END IF;
|
||||
-- Drop 'field' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='field';
|
||||
IF col_exists = 1 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE DROP COLUMN field;
|
||||
END IF;
|
||||
|
||||
-- Check and add 'preset' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='preset';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN preset BOOLEAN;
|
||||
END IF;
|
||||
-- Drop 'preset' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='preset';
|
||||
IF col_exists = 1 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE DROP COLUMN preset;
|
||||
END IF;
|
||||
|
||||
-- Check and add 'priority' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='priority';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN priority INTEGER;
|
||||
END IF;
|
||||
-- Drop 'priority' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='priority';
|
||||
IF col_exists = 1 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE DROP COLUMN priority;
|
||||
END IF;
|
||||
|
||||
-- Check and add 'tags' column if it does not exist
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='tags';
|
||||
IF col_exists = 0 THEN
|
||||
ALTER TABLE HZB_ALERT_DEFINE ADD COLUMN tags VARCHAR(255);
|
||||
END IF;
|
||||
-- Drop 'tags' column if it exists
|
||||
SELECT COUNT(*) INTO col_exists FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='HZB_ALERT_DEFINE' AND COLUMN_NAME='tags';
|
||||
IF col_exists = 1 THEN
|
||||
@@ -66,5 +93,4 @@ DELIMITER ;
|
||||
|
||||
|
||||
CALL AddOrDropColumns();
|
||||
DROP PROCEDURE IF EXISTS AddOrDropColumns;
|
||||
commit;
|
||||
DROP PROCEDURE IF EXISTS AddOrDropColumns;
|
||||
@@ -246,20 +246,6 @@ params:
|
||||
defaultValue: '200, 201'
|
||||
# hide param-true or false
|
||||
hide: true
|
||||
# field-param field key
|
||||
- field: enableUrlEncoding
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 启用URL编码(UTF-8)
|
||||
en-US: Enable URL Encoding(UTF-8)
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: boolean
|
||||
# required-true or false
|
||||
required: true
|
||||
# default value
|
||||
defaultValue: true
|
||||
# hide param-true or false
|
||||
hide: true
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
# metrics - summary
|
||||
@@ -300,8 +286,6 @@ metrics:
|
||||
method: ^_^httpMethod^_^
|
||||
# if enabled https
|
||||
ssl: ^_^ssl^_^
|
||||
# enable url encoding
|
||||
enableUrlEncoding: ^_^enableUrlEncoding^_^
|
||||
# http request payload
|
||||
payload: ^_^payload^_^
|
||||
# http request header content
|
||||
@@ -360,8 +344,6 @@ metrics:
|
||||
method: GET
|
||||
# if enabled https
|
||||
ssl: ^_^ssl^_^
|
||||
# enable url encoding
|
||||
enableUrlEncoding: ^_^enableUrlEncoding^_^
|
||||
authorization:
|
||||
# http auth type: Basic Auth, Digest Auth, Bearer Token
|
||||
type: ^_^authType^_^
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: dahua
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: 大华
|
||||
en-US: Dahua
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: 通过http接口监控大华设备状态,获取设备健康数据。
|
||||
en-US: Monitor Dahua devices through http interface to collect health data.
|
||||
|
||||
# Input params define for monitoring(render web ui by the definition)
|
||||
params:
|
||||
- field: host
|
||||
name:
|
||||
zh-CN: 主机Host
|
||||
en-US: Host
|
||||
type: host
|
||||
required: true
|
||||
- field: port
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
type: number
|
||||
range: '[0,65535]'
|
||||
required: true
|
||||
defaultValue: 80
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
type: number
|
||||
range: '[1000,60000]'
|
||||
required: true
|
||||
defaultValue: 5000
|
||||
- field: username
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
type: text
|
||||
required: true
|
||||
- field: password
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
type: password
|
||||
required: true
|
||||
- field: ssl
|
||||
name:
|
||||
zh-CN: 启用HTTPS
|
||||
en-US: SSL
|
||||
type: boolean
|
||||
required: false
|
||||
defaultValue: false
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
- name: network_info # 指标集合名称
|
||||
i18n:
|
||||
zh-CN: 网络信息
|
||||
en-US: Network Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/configManager.cgi?action=getConfig&name=Network
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
fields:
|
||||
- field: default_interface
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 默认网卡
|
||||
en-US: Default Interface
|
||||
- field: domain_name
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 域名
|
||||
en-US: Domain Name
|
||||
- field: hostname
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 主机名
|
||||
en-US: Hostname
|
||||
- field: eth0_ip_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 IP地址
|
||||
en-US: Interface eth0 IP Address
|
||||
- field: eth0_gateway
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 默认网关
|
||||
en-US: Interface eth0 Default Gateway
|
||||
- field: eth0_mac_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 物理地址
|
||||
en-US: Interface eth0 Physical Address
|
||||
- field: eth0_subnet_mask
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 子网掩码
|
||||
en-US: Interface eth0 Subnet Mask
|
||||
- field: eth0_mtu
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 MTU
|
||||
en-US: Interface eth0 MTU
|
||||
- field: eth0_dns1
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 DNS服务器1
|
||||
en-US: Interface eth0 DNS Server 1
|
||||
- field: eth0_dns2
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 DNS服务器2
|
||||
en-US: Interface eth0 DNS Server 2
|
||||
aliasFields:
|
||||
- table.Network.DefaultInterface
|
||||
- table.Network.Domain
|
||||
- table.Network.Hostname
|
||||
- table.Network.eth0.IPAddress
|
||||
- table.Network.eth0.DefaultGateway
|
||||
- table.Network.eth0.PhysicalAddress
|
||||
- table.Network.eth0.SubnetMask
|
||||
- table.Network.eth0.MTU
|
||||
- table.Network.eth0.DnsServers[0]
|
||||
- table.Network.eth0.DnsServers[1]
|
||||
calculates:
|
||||
- default_interface=table.Network.DefaultInterface
|
||||
- domain_name=table.Network.Domain
|
||||
- hostname=table.Network.Hostname
|
||||
- eth0_ip_address=table.Network.eth0.IPAddress
|
||||
- eth0_gateway=table.Network.eth0.DefaultGateway
|
||||
- eth0_mac_address=table.Network.eth0.PhysicalAddress
|
||||
- eth0_subnet_mask=table.Network.eth0.SubnetMask
|
||||
- eth0_mtu=table.Network.eth0.MTU
|
||||
- eth0_dns1=table.Network.eth0.DnsServers[0]
|
||||
- eth0_dns2=table.Network.eth0.DnsServers[1]
|
||||
- name: user_info
|
||||
i18n:
|
||||
zh-CN: 用户信息
|
||||
en-US: User Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/userManager.cgi?action=getActiveUserInfoAll
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
parseScript: users
|
||||
fields:
|
||||
- field: ClientAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端地址
|
||||
en-US: ClientAddress
|
||||
- field: Name
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端用户
|
||||
en-US: Name
|
||||
- field: ClientType
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端登录类型
|
||||
en-US: ClientType
|
||||
- field: LoginTime
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端登录时间
|
||||
en-US: LoginTime
|
||||
- name: ntp_info
|
||||
i18n:
|
||||
zh-CN: 校时信息
|
||||
en-US: Ntp Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/configManager.cgi?action=getConfig&name=NTP
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
fields:
|
||||
- field: ntp_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时服务器
|
||||
en-US: Ntp Address
|
||||
- field: ntp_port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时端口
|
||||
en-US: Ntp Port
|
||||
- field: ntp_update_period
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时间隔
|
||||
en-US: Ntp Update Period
|
||||
aliasFields:
|
||||
- table.NTP.Address
|
||||
- table.NTP.Port
|
||||
- table.NTP.UpdatePeriod
|
||||
calculates:
|
||||
- ntp_address=table.NTP.Address
|
||||
- ntp_port=table.NTP.Port
|
||||
- ntp_update_period=table.NTP.UpdatePeriod
|
||||
@@ -1,268 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: hikvision_isapi
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: 海康威视 ISAPI
|
||||
en-US: Hikvision ISAPI
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: 通过ISAPI接口监控海康威视设备状态,获取设备健康数据。
|
||||
en-US: Monitor Hikvision devices through ISAPI interface to collect health data.
|
||||
|
||||
# Input params define for monitoring(render web ui by the definition)
|
||||
params:
|
||||
- field: host
|
||||
name:
|
||||
zh-CN: 主机Host
|
||||
en-US: Host
|
||||
type: host
|
||||
required: true
|
||||
- field: port
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
type: number
|
||||
range: '[0,65535]'
|
||||
required: true
|
||||
defaultValue: 80
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
type: number
|
||||
range: '[1000,60000]'
|
||||
required: true
|
||||
defaultValue: 5000
|
||||
- field: username
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
type: text
|
||||
required: true
|
||||
- field: password
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
type: password
|
||||
required: true
|
||||
- field: ssl
|
||||
name:
|
||||
zh-CN: 启用HTTPS
|
||||
en-US: SSL
|
||||
type: boolean
|
||||
required: false
|
||||
defaultValue: false
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
- name: system_info
|
||||
i18n:
|
||||
zh-CN: 系统信息
|
||||
en-US: System Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /ISAPI/System/deviceInfo
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: //DeviceInfo
|
||||
fields:
|
||||
- field: deviceName
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备名称
|
||||
en-US: Device Name
|
||||
- field: deviceID
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备ID
|
||||
en-US: Device ID
|
||||
- field: firmwareVersion
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 固件版本
|
||||
en-US: Firmware Version
|
||||
- field: model
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备型号
|
||||
en-US: Device Model
|
||||
- field: macAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: mac地址
|
||||
en-US: Mac Address
|
||||
- name: status
|
||||
i18n:
|
||||
zh-CN: 设备状态
|
||||
en-US: Status
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /ISAPI/System/status
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: //DeviceStatus
|
||||
fields:
|
||||
- field: CPU_utilization
|
||||
i18n:
|
||||
zh-CN: CPU 利用率
|
||||
en-US: CPU Utilization
|
||||
type: 0
|
||||
unit: '%'
|
||||
- field: memory_usage
|
||||
i18n:
|
||||
zh-CN: 内存使用量
|
||||
en-US: Memory Usage
|
||||
type: 0
|
||||
unit: MB
|
||||
- field: memory_available
|
||||
i18n:
|
||||
zh-CN: 可用内存
|
||||
en-US: Memory Available
|
||||
type: 0
|
||||
unit: MB
|
||||
- field: cache_size
|
||||
i18n:
|
||||
zh-CN: 缓存大小
|
||||
en-US: Cache Size
|
||||
type: 0
|
||||
unit: MB
|
||||
- field: net_port_1_speed
|
||||
i18n:
|
||||
zh-CN: 网口1速度
|
||||
en-US: Net Port 1 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
- field: net_port_2_speed
|
||||
i18n:
|
||||
zh-CN: 网口2速度
|
||||
en-US: Net Port 2 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
- field: boot_time
|
||||
i18n:
|
||||
zh-CN: 启动时间
|
||||
en-US: Boot Time
|
||||
type: 1
|
||||
- field: device_uptime
|
||||
i18n:
|
||||
zh-CN: 运行时长
|
||||
en-US: Device Uptime
|
||||
type: 1
|
||||
- field: last_calibration_time
|
||||
i18n:
|
||||
zh-CN: 上次校时时间
|
||||
en-US: Last Calibration Time
|
||||
type: 1
|
||||
- field: last_calibration_time_diff
|
||||
i18n:
|
||||
zh-CN: 上次校时时间差
|
||||
en-US: Last Calibration Time Diff
|
||||
type: 0
|
||||
unit: s
|
||||
- field: avg_upload_time
|
||||
i18n:
|
||||
zh-CN: 平均上传耗时
|
||||
en-US: Avg Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
- field: max_upload_time
|
||||
i18n:
|
||||
zh-CN: 最大上传耗时
|
||||
en-US: Max Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
- field: min_upload_time
|
||||
i18n:
|
||||
zh-CN: 最小上传耗时
|
||||
en-US: Min Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
- field: last_calibration_mode
|
||||
i18n:
|
||||
zh-CN: 上次校时模式
|
||||
en-US: Last Calibration Mode
|
||||
type: 1
|
||||
- field: last_calibration_address
|
||||
i18n:
|
||||
zh-CN: 上次校时地址
|
||||
en-US: Last Calibration Address
|
||||
type: 1
|
||||
- field: response_time
|
||||
i18n:
|
||||
zh-CN: 响应时间
|
||||
en-US: Response Time
|
||||
type: 0
|
||||
unit: ms
|
||||
aliasFields:
|
||||
- CPUList/CPU/cpuUtilization
|
||||
- MemoryList/Memory/memoryUsage
|
||||
- MemoryList/Memory/memoryAvailable
|
||||
- MemoryList/Memory/cacheSize
|
||||
- NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
- NetPortStatusList/NetPortStatus[id='2']/workSpeed
|
||||
- bootTime
|
||||
- deviceUpTime
|
||||
- lastCalibrationTime
|
||||
- lastCalibrationTimeDiff
|
||||
- uploadTimeConsumingList/avgTime
|
||||
- uploadTimeConsumingList/maxTime
|
||||
- uploadTimeConsumingList/minTime
|
||||
- lastCalibrationTimeMode
|
||||
- lastCalibrationTimeAddress
|
||||
- responseTime
|
||||
calculates:
|
||||
- CPU_utilization=CPUList/CPU/cpuUtilization
|
||||
- memory_usage=MemoryList/Memory/memoryUsage
|
||||
- memory_available=MemoryList/Memory/memoryAvailable
|
||||
- cache_size=MemoryList/Memory/cacheSize
|
||||
- net_port_1_speed=NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
- net_port_2_speed=NetPortStatusList/NetPortStatus[id='2']/workSpeed
|
||||
- boot_time=bootTime
|
||||
- device_uptime=deviceUpTime
|
||||
- last_calibration_time=lastCalibrationTime
|
||||
- last_calibration_time_diff=lastCalibrationTimeDiff
|
||||
- avg_upload_time=uploadTimeConsumingList/avgTime
|
||||
- max_upload_time=uploadTimeConsumingList/maxTime
|
||||
- min_upload_time=uploadTimeConsumingList/minTime
|
||||
- last_calibration_mode=lastCalibrationTimeMode
|
||||
- last_calibration_address=lastCalibrationTimeAddress
|
||||
- response_time=responseTime
|
||||
units:
|
||||
- memory_usage=KB->MB
|
||||
- memory_available=KB->MB
|
||||
- cache_size=KB->MB
|
||||
@@ -1,263 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring mid-middleware custom-custom monitoring os-operating system monitoring
|
||||
category: service
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: s7
|
||||
# The app api i18n name
|
||||
name:
|
||||
zh-CN: s7服务器
|
||||
en-US: s7 Server
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: HertzBeat对支持s7协议的服务进行(保持寄存器和线圈)相关指标进行采集
|
||||
en-US: HertzBeat collects metrics related to maintaining registers and coils for services that support s7 protocol
|
||||
zh-TW: HertzBeat對支持s7協定的服務進行(保持寄存器和線圈)相關名額進行採集
|
||||
# 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: s7服务Host
|
||||
en-US: s7 Server Host
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: host
|
||||
# required-true or false
|
||||
required: true
|
||||
# field-param field key
|
||||
- field: port
|
||||
# name-param field display i18n name
|
||||
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]'
|
||||
# required-true or false
|
||||
required: true
|
||||
# default value
|
||||
defaultValue: 102
|
||||
- field: rackId
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: rackId
|
||||
en-US: rackId
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: false
|
||||
defaultValue: 0
|
||||
- field: slotId
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: slotId
|
||||
en-US: slotId
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: false
|
||||
defaultValue: 0
|
||||
- field: controllerType
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: controllerType
|
||||
en-US: controllerType
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: false
|
||||
defaultValue: S7_1500
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 请求超时时间(ms)
|
||||
en-US: Request Timeout(ms)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
range: '[400,200000]'
|
||||
required: false
|
||||
defaultValue: 6000
|
||||
hide: true
|
||||
- field: holdingRegisterAddresses
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 保持寄存器地址
|
||||
en-US: Holding Registers address
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: array
|
||||
# param field input placeholder
|
||||
placeholder: 'Input RegisterAddress'
|
||||
# required-true or false
|
||||
required: true
|
||||
# hide param-true or false
|
||||
# hide: true
|
||||
- field: coilRegisterAddresses
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 线圈寄存器地址
|
||||
en-US: Coil Register address
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: array
|
||||
# param field input placeholder
|
||||
placeholder: 'Input RegisterAddress'
|
||||
# required-true or false
|
||||
required: true
|
||||
# hide param-true or false
|
||||
# hide: true
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
# metrics - summary
|
||||
- name: holding-register
|
||||
i18n:
|
||||
zh-CN: 保持寄存器 统计信息
|
||||
en-US: holding-register stats
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
# collect metrics content
|
||||
fields:
|
||||
- field: responseTime
|
||||
type: 0
|
||||
unit: ms
|
||||
i18n:
|
||||
zh-CN: 响应时间
|
||||
en-US: Response Time
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: address-0
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-0
|
||||
en-US: address-0
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: address-1
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-1
|
||||
en-US: address-1
|
||||
- field: address-2
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-2
|
||||
en-US: address-2
|
||||
- field: address-3
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-3
|
||||
en-US: address-3
|
||||
# 指标别名列表,按照寄存器地址来进行命名的
|
||||
# 例如 地址为下标为0 ,值为m holding-register:0
|
||||
# 地址为下标为1,值为m[2] holding-register:1-0 holding-register:1-1
|
||||
aliasFields:
|
||||
- responseTime
|
||||
- holding-register:0
|
||||
- holding-register:1
|
||||
- holding-register:2
|
||||
- holding-register:3
|
||||
# mapping and conversion expressions, use thesand aliasField above to calculate metrics value# (可选)指标映射转换计算表达式,与上面的别名一起作用,计算出最终需要的指标值# eg: cores=core1+core2, usage=usage, waitTimeallTime-runningTime
|
||||
calculates:
|
||||
- responseTime=responseTime
|
||||
- address-0=holding-register:0
|
||||
- address-1=holding-register:1
|
||||
- address-2=holding-register:2
|
||||
- address-3=holding-register:3
|
||||
protocol: s7
|
||||
# the config content when protocol is http
|
||||
s7:
|
||||
# host
|
||||
host: ^_^host^_^
|
||||
# port
|
||||
port: ^_^port^_^
|
||||
driverName: s7
|
||||
addressSyntax: holding-register
|
||||
rackId: ^_^rackId^_^
|
||||
slotId: ^_^slotId^_^
|
||||
controllerType: ^_^controllerType^_^
|
||||
timeout: ^_^timeout^_^
|
||||
registerAddresses: [ ^_^holdingRegisterAddresses^_^ ]
|
||||
|
||||
# metrics - summary
|
||||
- name: coil
|
||||
i18n:
|
||||
zh-CN: 线圈 统计信息
|
||||
en-US: coil stats
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 1
|
||||
# collect metrics content
|
||||
fields:
|
||||
- field: responseTime
|
||||
type: 0
|
||||
unit: ms
|
||||
i18n:
|
||||
zh-CN: 响应时间
|
||||
en-US: Response Time
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: address-0
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-0
|
||||
en-US: address-0
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: address-1
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-1
|
||||
en-US: address-1
|
||||
- field: address-2
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-2
|
||||
en-US: address-2
|
||||
- field: address-3
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: address-3
|
||||
en-US: address-3
|
||||
# 指标别名列表,按照寄存器地址来进行命名的
|
||||
# 例如 地址为下标为0 ,值为m coil:0
|
||||
# 地址为下标为1,值为m[2] coil:1-0 coil:1-1
|
||||
aliasFields:
|
||||
- responseTime
|
||||
- coil:0
|
||||
- coil:1
|
||||
- coil:2
|
||||
- coil:3
|
||||
# mapping and conversion expressions, use thesand aliasField above to calculate metrics value# (可选)指标映射转换计算表达式,与上面的别名一起作用,计算出最终需要的指标值# eg: cores=core1+core2, usage=usage, waitTimeallTime-runningTime
|
||||
calculates:
|
||||
- responseTime=responseTime
|
||||
- address-0=coil:0
|
||||
- address-1=coil:1
|
||||
- address-2=coil:2
|
||||
- address-3=coil:3
|
||||
protocol: s7
|
||||
# the config content when protocol is http
|
||||
s7:
|
||||
# host
|
||||
host: ^_^host^_^
|
||||
# port
|
||||
port: ^_^port^_^
|
||||
driverName: s7
|
||||
addressSyntax: coil
|
||||
rackId: ^_^rackId^_^
|
||||
slotId: ^_^slotId^_^
|
||||
controllerType: ^_^controllerType^_^
|
||||
timeout: ^_^timeout^_^
|
||||
registerAddresses: [ ^_^coilRegisterAddresses^_^ ]
|
||||
|
||||
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: uniview
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: 宇视
|
||||
en-US: Uniview
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: 通过HTTP接口监控宇视设备状态,获取设备健康数据。
|
||||
en-US: Monitor Uniview devices through HTTP interface to collect health data.
|
||||
|
||||
# Input params define for monitoring(render web ui by the definition)
|
||||
params:
|
||||
- field: host
|
||||
name:
|
||||
zh-CN: 主机Host
|
||||
en-US: Host
|
||||
type: host
|
||||
required: true
|
||||
- field: port
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
type: number
|
||||
range: '[0,65535]'
|
||||
required: true
|
||||
defaultValue: 80
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
type: number
|
||||
range: '[1000,60000]'
|
||||
required: true
|
||||
defaultValue: 5000
|
||||
- field: username
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
type: text
|
||||
required: true
|
||||
- field: password
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
type: password
|
||||
required: true
|
||||
- field: ssl
|
||||
name:
|
||||
zh-CN: 启用HTTPS
|
||||
en-US: SSL
|
||||
type: boolean
|
||||
required: false
|
||||
defaultValue: false
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
- name: system_info
|
||||
i18n:
|
||||
zh-CN: 系统信息
|
||||
en-US: System Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /LAPI/V1.0/System/DeviceInfo
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: jsonPath
|
||||
parseScript: '$.Response.Data'
|
||||
fields:
|
||||
- field: DeviceName
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备名称
|
||||
en-US: Device Name
|
||||
- field: SerialNumber
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 序列号
|
||||
en-US: SerialNumber
|
||||
- field: FirmwareVersion
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 固件版本
|
||||
en-US: Firmware Version
|
||||
- field: DeviceModel
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备型号
|
||||
en-US: Device Model
|
||||
- name: ntp_info
|
||||
i18n:
|
||||
zh-CN: 校时信息
|
||||
en-US: NTP Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /LAPI/V1.0/System/Time/NTP
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: jsonPath
|
||||
parseScript: '$.Response.Data.NTPServerInfos[0]'
|
||||
fields:
|
||||
- field: IPAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: IP地址
|
||||
en-US: IPAddress
|
||||
- field: Port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 端口号
|
||||
en-US: Port
|
||||
- field: SynchronizeInterval
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 同步间隔
|
||||
en-US: SynchronizeInterval
|
||||
- field: Enabled
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 是否启用
|
||||
en-US: Enabled
|
||||
@@ -76,26 +76,6 @@
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- OpenTelemetry Appender for shipping logs to GrepTimeDB -->
|
||||
<appender name="OpenTelemetryAppender" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
|
||||
<!-- Capture source code information (class name, method name, line number) -->
|
||||
<captureCodeAttributes>true</captureCodeAttributes>
|
||||
|
||||
<!-- Capture MDC context values -->
|
||||
<captureMdcAttributes>
|
||||
<pattern>.*</pattern>
|
||||
</captureMdcAttributes>
|
||||
|
||||
<!-- Capture experimental attributes like exception details -->
|
||||
<captureExperimentalAttributes>true</captureExperimentalAttributes>
|
||||
|
||||
<!-- Capture marker attributes -->
|
||||
<captureMarkerAttribute>true</captureMarkerAttribute>
|
||||
|
||||
<!-- Capture logger context attributes -->
|
||||
<captureLoggerContext>true</captureLoggerContext>
|
||||
</appender>
|
||||
|
||||
<!-- Settings for this logger: for example, all output logs under the org.springframework package must be at level info or above to be output! -->
|
||||
<!-- This can avoid outputting many common debug information of the spring framework! -->
|
||||
<logger name="org.springframework" level="info"/>
|
||||
@@ -110,14 +90,12 @@
|
||||
<logger name="org.mongodb" level="warn"/>
|
||||
<logger name="io.greptime" level="warn"/>
|
||||
<logger name="org.apache.kafka" level="warn"/>
|
||||
<logger name="io.opentelemetry" level="info"/>
|
||||
|
||||
<!-- Production environment configuration -->
|
||||
<springProfile name="prod">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="SystemOutFileAppender"/>
|
||||
<appender-ref ref="ErrOutFileAppender"/>
|
||||
<appender-ref ref="OpenTelemetryAppender"/>
|
||||
</root>
|
||||
<!-- North log -->
|
||||
<Logger name="com.obs.services.AbstractClient" level="OFF"
|
||||
@@ -140,7 +118,6 @@
|
||||
<appender-ref ref="ConsoleAppender"/>
|
||||
<appender-ref ref="SystemOutFileAppender"/>
|
||||
<appender-ref ref="ErrOutFileAppender"/>
|
||||
<appender-ref ref="OpenTelemetryAppender"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
@@ -150,7 +127,6 @@
|
||||
<appender-ref ref="ConsoleAppender"/>
|
||||
<appender-ref ref="SystemOutFileAppender"/>
|
||||
<appender-ref ref="ErrOutFileAppender"/>
|
||||
<appender-ref ref="OpenTelemetryAppender"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
@@ -160,8 +136,7 @@
|
||||
<appender-ref ref="ConsoleAppender"/>
|
||||
<appender-ref ref="SystemOutFileAppender"/>
|
||||
<appender-ref ref="ErrOutFileAppender"/>
|
||||
<appender-ref ref="OpenTelemetryAppender"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
+3
-3
@@ -21,9 +21,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.hertzbeat.alert.config.SmsConfig;
|
||||
import org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum;
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.SmsNoticeSender;
|
||||
import org.apache.hertzbeat.manager.service.impl.SmsGeneralConfigServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -66,9 +66,9 @@ class SmsGeneralConfigServiceTest {
|
||||
@Test
|
||||
void testGetTypeReference() {
|
||||
|
||||
TypeReference<SmsConfig> typeReference = service.getTypeReference();
|
||||
TypeReference<SmsNoticeSender> typeReference = service.getTypeReference();
|
||||
assertNotNull(typeReference);
|
||||
assertEquals(SmsConfig.class, typeReference.getType());
|
||||
assertEquals(SmsNoticeSender.class, typeReference.getType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Hertzbeat-MCP
|
||||
|
||||
## Hertzbeat-Log-MCP
|
||||
|
||||
Log MCP Service Based on GreptimeDB.
|
||||
|
||||
- GreptimeDB log writing needs to be enabled.
|
||||
|
||||
## Claude Desktop Integration (stdio)
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hertzbeat-mcp": {
|
||||
"command": "java",
|
||||
"args": [
|
||||
"-Dspring.ai.mcp.server.stdio=true",
|
||||
"-Dspring.main.web-application-type=none",
|
||||
"-Dlogging.pattern.console=",
|
||||
"-Dgreptime.url=http://${IP}:4000",
|
||||
"-jar",
|
||||
"${PATH}/hertzbeat-mcp-2.0-SNAPSHOT.jar"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hertzbeat-mcp</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<spring-ai.version>1.0.0-M6</spring-ai.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<!-- json path parser-->
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
<compilerArgs>
|
||||
<compilerArg>-parameters</compilerArg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.mcp.server;
|
||||
|
||||
import org.apache.hertzbeat.mcp.server.service.LogService;
|
||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* MCP Server Application
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class McpServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(McpServerApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ToolCallbackProvider tools(
|
||||
LogService logService) {
|
||||
return MethodToolCallbackProvider.builder()
|
||||
.toolObjects(logService)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user