Compare commits

...
Author SHA1 Message Date
vinci 67da4b68d6 query executor 2025-04-05 20:30:47 +08:00
0385892674 [feature] add pushgateway to push module (#3204)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Signed-off-by: leo <1552443053@qq.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-05 17:28:11 +08:00
Logic 04e2321175 [test](hertzbeat-collector): add unit test for XML response parsing (#3212) 2025-04-05 09:18:52 +08:00
8373f0a247 [test] add test for controller AlertInhibitController (#3183)
Co-authored-by: yunfan24 <yunfan24@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-04-03 21:31:06 +08:00
Logic 5ad03687b2 [feature] Implementation of Hikvision camera monitoring and http monitoring xml parsing function (#3211) 2025-04-03 20:07:18 +08:00
tomsun28andaias00 7127159063 [doc] archive version docs (#3207)
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-04-03 14:30:49 +08:00
Nick Guoandtomsun28 51715797ef [improve] optimize DnsCollectTest with status code. (#3209)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-04-03 14:01:10 +08:00
liutianyouandtomsun28 47a1b13781 [doc] add doc for alarm grouping and alarm inhibit (#3206)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-04-03 13:33:15 +08:00
d5fc492b98 [feature] support plc s7 protocol for siemens (#3194)
Co-authored-by: cuijianzhong <shangyuancjz@126.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-04-02 23:09:32 +08:00
bbc30cd3db [improve] i18n Portuguese (#3193)
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-04-02 22:46:33 +08:00
6e50d1f3a7 [improve] support the use of time expressions in HTTP payloads (#3192)
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-04-02 22:32:18 +08:00
b18412254e [improve] optimize kafka collection logic and expand test coverage (#3189)
Co-authored-by: yunfan24 <yunfan24@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-04-02 22:26:37 +08:00
Yang Chenandaias00 fc0ef7773c [bugfix] fix bulletin indicator selection status error (#3201)
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-04-02 15:48:33 +08:00
aias00 adf71441f8 update download info (#3203) 2025-04-02 15:44:42 +08:00
tomsun28 e36842420f [improve] update monitor list ui (#3200)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-03-31 19:27:22 +08:00
tomsun28andaias00 a2c1123b09 [doc] update new hertzbeat ppmc (#3191)
Co-authored-by: aias00 <rokkki@163.com>
2025-03-31 14:52:37 +08:00
tomsun28 b4151a4e73 [improve] update monitor detail ui (#3199)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-03-31 14:33:29 +08:00
jujin 1c1683d9c6 [doc] add blog bout How Does Metrics Collection Work (#3195) 2025-03-30 14:08:15 +08:00
46d4fc2dd9 [bugfix] Modify inconsistent icons (#3190)
Co-authored-by: yinyijun <yingey2011>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-03-29 10:38:07 +08:00
Sarthak Aroraandtomsun28 ffa2487587 [docs] add collector user guide (#3187)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-03-29 09:13:06 +08:00
30c7b22c4c [doc] add help documentation for Alarms Center (#3181)
Co-authored-by: yunfan24 <yunfan24@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-03-29 00:06:40 +08:00
彭镜肇andtomsun28 7a3e23ec59 [improve] update import for package import (#3180)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-03-28 00:09:45 +08:00
Jast da6556641a [Doc] Modify the error records in the document (#3178) 2025-03-27 23:45:02 +08:00
Rickandrick b008f16b67 fix: Ai is a typo (#3176)
Co-authored-by: rick <LinuxSuRen@users.noreply.github.com>
2025-03-26 12:59:17 +08:00
tomsun28andaias00 b77222765c [improve] update notice copyright years (#3171)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-03-25 10:16:26 +08:00
tomsun28 aeb4188f83 [improve] update git archive export ignore (#3172)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-03-25 09:39:49 +08:00
Yang Chen 311cb131fd [bugfix] fix bind Labels are not updated when the Alarm Severity switches (#3170) 2025-03-24 22:12:06 +08:00
tomsun28andaias00 21558e9b4f [bugfix] fix postgre mount error, use mariadb instead of mysql in compose (#3168)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-03-24 15:12:56 +08:00
LL-LINandaias00 8d1f306433 [feature] implement labels-based monitors filtering in bulletin creation flow (#3161)
Co-authored-by: aias00 <rokkki@163.com>
2025-03-24 10:46:52 +08:00
tomsun28 e98af9527e [release] update release version 1.7.0 version and docs (#3165)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-03-23 10:39:00 +08:00
531 changed files with 16151 additions and 5162 deletions
+36
View File
@@ -2325,6 +2325,42 @@
"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"
]
}
],
"contributorsPerLine": 7,
+2
View File
@@ -17,7 +17,9 @@
/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
+3
View File
@@ -13,6 +13,7 @@ mvnw.bat
package-lock.json
*.zip
jdk/
data/
### STS ###
.apt_generated
@@ -54,3 +55,5 @@ node_modules
application-dev.yml
application-mysql.yml
application-pg.yml
/package.json
/yarn.lock
+1 -1
View File
@@ -1,5 +1,5 @@
Apache HertzBeat (incubating)
Copyright 2024 The Apache Software Foundation
Copyright 2024-2025 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
+6
View File
@@ -510,6 +510,12 @@ 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>
</tr>
</tbody>
</table>
+6
View File
@@ -509,6 +509,12 @@ 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>
</tr>
</tbody>
</table>
@@ -17,6 +17,7 @@
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;
@@ -34,7 +35,6 @@ 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;
@@ -0,0 +1,133 @@
/*
* 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."));
}
}
@@ -41,12 +41,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.HashMap;
import org.apache.hertzbeat.alert.service.impl.DataSourceServiceImpl;
import org.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}
@@ -155,6 +155,11 @@
<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>
@@ -40,6 +40,10 @@ 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;
@@ -63,11 +67,9 @@ 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;
@@ -77,8 +79,6 @@ 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;
@@ -91,6 +91,8 @@ import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.hertzbeat.common.entity.job.Metrics.Field;
import java.util.function.Function;
/**
* http https collect
@@ -121,7 +123,7 @@ public class HttpCollectImpl extends AbstractCollect {
if (CollectionUtils.isEmpty(httpProtocol.getSuccessCodes())) {
httpProtocol.setSuccessCodes(List.of(HttpStatus.SC_OK + ""));
}
HttpContext httpContext = createHttpContext(metrics.getHttp());
HttpUriRequest request = createHttpRequest(metrics.getHttp());
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext)) {
@@ -152,7 +154,7 @@ public class HttpCollectImpl extends AbstractCollect {
case DispatchConstants.PARSE_PROMETHEUS ->
parseResponseByPrometheusExporter(resp, metrics.getAliasFields(), builder);
case DispatchConstants.PARSE_XML_PATH ->
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
parseResponseByXmlPath(resp, metrics, builder, responseTime);
case DispatchConstants.PARSE_WEBSITE ->
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime);
case DispatchConstants.PARSE_SITE_MAP ->
@@ -329,8 +331,95 @@ public class HttpCollectImpl extends AbstractCollect {
}
}
private void parseResponseByXmlPath(String resp, List<String> aliasFields, HttpProtocol http,
CollectRep.MetricsData.Builder builder) {
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;
}
Map<String, Field> fieldMap = metrics.getFields().stream()
.collect(Collectors.toMap(Field::getField, Function.identity(), (field1, field2) -> field1));
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 {
Field field = fieldMap.get(alias);
if (field == null || !StringUtils.hasText(field.getXpath())) {
log.warn("No field definition or xpath found for alias '{}' in XML path parsing.", alias);
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
continue;
}
String relativeXpath = field.getXpath();
try {
String value = (String) xpath.evaluate(relativeXpath, node, XPathConstants.STRING);
valueRowBuilder.addColumn(StringUtils.hasText(value) ? value : CommonConstants.NULL_VALUE);
} catch (XPathExpressionException e) {
log.warn("Failed to evaluate relative XPath '{}' (from field definition) for node [{}]: {}", relativeXpath, 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());
}
}
private void parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
@@ -473,11 +562,10 @@ public class HttpCollectImpl extends AbstractCollect {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getDigestAuthUsername(),
auth.getDigestAuthPassword());
provider.setCredentials(AuthScope.ANY, credentials);
AuthCache authCache = new BasicAuthCache();
authCache.put(new HttpHost(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort())), new DigestScheme());
AuthScope authScope = new AuthScope(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort()));
provider.setCredentials(authScope, credentials);
clientContext.setCredentialsProvider(provider);
clientContext.setAuthCache(authCache);
return clientContext;
}
}
@@ -512,7 +600,7 @@ public class HttpCollectImpl extends AbstractCollect {
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();
@@ -522,7 +610,7 @@ public class HttpCollectImpl extends AbstractCollect {
continue;
}
if (queryParams.length() > 0) {
if (!queryParams.isEmpty()) {
queryParams.append("&");
}
@@ -540,7 +628,7 @@ public class HttpCollectImpl extends AbstractCollect {
}
}
}
// The default request header can be overridden if customized
// keep-alive
requestBuilder.addHeader(HttpHeaders.CONNECTION, NetworkConstants.KEEP_ALIVE);
@@ -581,7 +669,7 @@ 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(httpProtocol.getPayload(), StandardCharsets.UTF_8));
requestBuilder.setEntity(new StringEntity(TimeExpressionUtil.calculate(httpProtocol.getPayload()), StandardCharsets.UTF_8));
}
// uri encode
@@ -600,10 +688,10 @@ public class HttpCollectImpl extends AbstractCollect {
}
// append query params
if (queryParams.length() > 0) {
uri += (uri.contains("?") ? "&" : "?") + queryParams.toString();
if (!queryParams.isEmpty()) {
uri += (uri.contains("?") ? "&" : "?") + queryParams;
}
String finalUri;
if (IpDomainUtil.isHasSchema(httpProtocol.getHost())) {
finalUri = httpProtocol.getHost() + ":" + httpProtocol.getPort() + uri;
@@ -619,7 +707,7 @@ public class HttpCollectImpl extends AbstractCollect {
finalUri = NetworkConstants.HTTP_HEADER + baseUri;
}
}
try {
requestBuilder.setUri(finalUri);
} catch (IllegalArgumentException e) {
@@ -33,7 +33,7 @@ import java.util.stream.Stream;
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.common.entity.dto.MetricFamily;
import org.apache.hertzbeat.collector.collect.prometheus.parser.TextParser;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.collector.util.CollectUtil;
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.collector.collect.http.promethus.ParseException;
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
import org.apache.hertzbeat.common.util.StrBuffer;
/**
@@ -0,0 +1,95 @@
/*
* 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();
}
}
@@ -47,31 +47,28 @@ public class DnsCollectImplTest {
.address("www.google.com")
.timeout("3000")
.port("53")
.tcp("tcp")
.build();
}
@Test
public void testPreCheck() {
assertThrows(IllegalArgumentException.class, () -> {
Metrics metrics = new Metrics();
metrics.setName("question");
metrics.setDns(dnsProtocol);
dnsCollect.preCheck(metrics);
});
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
//metrics is null
dnsCollect.collect(builder, null);
assertEquals(CollectRep.Code.FAIL, builder.getCode());
//query class is blank
//invalid DnsProtocol
Metrics metrics = new Metrics();
assertThrows(IllegalArgumentException.class, () -> {
DnsProtocol dns = DnsProtocol.builder().build();
Metrics metrics = new Metrics();
metrics.setDns(dns);
dnsCollect.preCheck(metrics);
});
// no exception throws
//validated DnsProtocol
assertDoesNotThrow(() -> {
dnsProtocol.setTcp("tcp");
Metrics metrics = new Metrics();
metrics.setName("question");
metrics.setDns(dnsProtocol);
dnsCollect.preCheck(metrics);
});
@@ -80,28 +77,23 @@ public class DnsCollectImplTest {
@Test
public void testCollect() {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
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);
Metrics metrics0 = Metrics.builder()
.name("question")
.dns(dnsProtocol)
.aliasFields(Collections.singletonList("section"))
.build();
dnsCollect.collect(builder, metrics0);
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
assertNotNull(builder.getValues(0).getColumns(0));
// dns is null, no exception throws
assertDoesNotThrow(() -> {
dnsCollect.collect(builder, null);
});
// metric name is header
assertDoesNotThrow(() -> {
Metrics metrics1 = new Metrics();
metrics1.setName("header");
metrics1.setDns(dnsProtocol);
metrics1.setAliasFields(Collections.singletonList("section"));
dnsCollect.collect(builder, metrics1);
});
Metrics metrics1 = Metrics.builder()
.name("header")
.dns(dnsProtocol)
.aliasFields(Collections.singletonList("section"))
.build();
dnsCollect.collect(builder, metrics1);
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
}
@Test
@@ -18,7 +18,14 @@
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;
@@ -41,7 +48,7 @@ class HttpCollectImplTest {
assertThrows(IllegalArgumentException.class, () -> {
httpCollectImpl.preCheck(null);
});
assertThrows(IllegalArgumentException.class, () -> {
Metrics metrics = Metrics.builder().build();
httpCollectImpl.preCheck(metrics);
@@ -65,4 +72,92 @@ 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").xpath("name").build());
fields.add(Metrics.Field.builder().field("status").xpath("status").build());
fields.add(Metrics.Field.builder().field("cpu").xpath("metrics/cpu").build());
fields.add(Metrics.Field.builder().field("memory").xpath("metrics/memory").build());
Metrics metrics = Metrics.builder()
.http(http)
.fields(fields)
.aliasFields(Arrays.asList("name", "status", "cpu", "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");
}
}
@@ -17,6 +17,8 @@
package org.apache.hertzbeat.collector.collect.prometheus.parser;
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
import org.apache.hertzbeat.common.util.OnlineParser;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -29,4 +29,5 @@ 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.modbus.ModbusCollectImpl
org.apache.hertzbeat.collector.collect.s7.S7CollectImpl
@@ -39,7 +39,7 @@ spring:
collector:
info:
version: ${COLLECTOR_VERSION:1.6.1}
version: ${COLLECTOR_VERSION:1.7.0}
ip: ${COLLECTOR_IP:}
dispatch:
entrance:
@@ -227,4 +227,9 @@ public interface DispatchConstants {
* protocol modbus
*/
String PROTOCOL_MODBUS = "modbus";
/**
* protocol modbus
*/
String PROTOCOL_S7 = "s7";
}
@@ -242,6 +242,7 @@ public class KafkaCollectImpl extends AbstractCollect {
boolean isKafkaCommand = SupportedCommand.isKafkaCommand(command);
if (!isKafkaCommand) {
log.error("Unsupported command: {}", command);
builder.setCode(CollectRep.Code.FAIL);
return;
}
@@ -273,7 +274,7 @@ public class KafkaCollectImpl extends AbstractCollect {
}
}
private AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
protected AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
CacheIdentifier kafkaAdminClientIdentifier = CacheIdentifier.builder()
.ip(kafkaProtocol.getHost()).port(kafkaProtocol.getPort())
.build();
@@ -17,12 +17,24 @@
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;
@@ -31,12 +43,25 @@ 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();
collect = new KafkaCollectImpl(){
@Override
protected AdminClient getAdminClient(KafkaProtocol protocol) {
return adminClient;
}
};
}
@Test
@@ -59,38 +84,51 @@ public class KafkaCollectTest {
});
// kafka port is null
assertThrows(IllegalArgumentException.class, () -> {
kafka.setHost("127.0.0.1");
kafka.setHost(HOST);
collect.preCheck(metric);
});
// no exception throw
assertDoesNotThrow(() -> {
kafka.setPort("9092");
kafka.setPort(PORT);
collect.preCheck(metric);
});
}
@Test
void collect() {
// metrics is null
assertThrows(NullPointerException.class, () -> {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
collect.collect(builder, null);
});
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());
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
assertDoesNotThrow(() -> {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
metrics.getKclient().setCommand("topic-list");
collect.collect(builder, metrics);
});
kafka.setCommand(SupportedCommand.TOPIC_LIST.getCommand());
builder.setCode(CollectRep.Code.forNumber(6));
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
@@ -455,4 +455,19 @@ 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;
}
@@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.prometheus.parser;
package org.apache.hertzbeat.common.entity.dto;
import java.util.List;
import lombok.Data;
@@ -0,0 +1,82 @@
/*
* 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;
import java.util.List;
import java.util.Map;
/**
* Metric History Range Query Data
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Metric Query Data")
public class MetricQueryData {
@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<List<Object>> values;
/**
* Metric Schema
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static final class MetricSchema {
@Schema(title = "Metrics Field")
private List<MetricField> fields;
@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;
@Schema(title = "Whether is a label")
private Boolean label;
}
}
@@ -54,6 +54,7 @@ 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;
@@ -270,6 +271,10 @@ 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
*/
@@ -372,5 +377,10 @@ public class Metrics {
* Metric unit
*/
private String unit;
/**
* when parse type is xmlParse, use it, like NetPortStatusList/NetPortStatus[id='1']/workSpeed
*/
private String xpath;
}
}
@@ -15,19 +15,45 @@
* limitations under the License.
*/
package org.apache.hertzbeat.push.dao;
package org.apache.hertzbeat.common.entity.job.protocol;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import org.apache.hertzbeat.common.entity.push.PushMetrics;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* push metrics dao
* Modbus Protocol
*/
public interface PushMetricsDao extends JpaRepository<PushMetrics, Long> {
PushMetrics findFirstByMonitorIdOrderByTimeDesc(Long monitorId);
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class S7Protocol implements Protocol {
@Transactional(rollbackFor = Exception.class)
void deleteAllByTimeBefore(Long time);
/**
* IP ADDRESS OR DOMAIN NAME OF THE PEER HOST
*/
private String host;
/**
* Port number
*/
private String port;
private String driverName;
private String addressSyntax;
private String rackId;
private String slotId;
private String controllerType;
private String timeout;
private List<String> registerAddresses;
}
@@ -91,6 +91,9 @@ 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)
@@ -445,15 +445,7 @@ public final class CollectRep {
if (value != null) {
// Check byte array size, Arrow buffer size is 32768 bytes
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
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);
}
vector.setSafe(rowIndex, bytes);
}
}
}
@@ -15,18 +15,20 @@
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.prometheus.parser;
package org.apache.hertzbeat.common.util;
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 org.apache.hertzbeat.common.entity.dto.MetricFamily;
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;
@@ -36,9 +38,23 @@ import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class OnlineParser {
private static final Map<Integer, Integer> escapeMap = new HashMap<>();
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 static class FormatException extends Exception {
public FormatException() {}
public FormatException() {
}
public FormatException(String message) {
super(message);
@@ -123,51 +139,65 @@ public class OnlineParser {
}
private static CharChecker parseOneChar(InputStream inputStream) throws IOException {
private static int getChar(InputStream inputStream) throws IOException, FormatException {
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;
}
}
private static CharChecker parseOneChar(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
return new CharChecker(i);
}
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
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 == 'e' || i == '.') {
stringBuilder.append((char) i);
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker skipOneLong(InputStream inputStream) throws IOException {
int i = inputStream.read();
private static CharChecker skipOneLong(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
while (i >= '0' && i <= '9') {
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = getChar(inputStream);
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == ':') {
stringBuilder.append((char) i);
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = getChar(inputStream);
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_') {
stringBuilder.append((char) i);
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker parseLabelValue(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = inputStream.read();
int i = getChar(inputStream);
while (i != '"' && i != -1) {
if (i == '\\') {
i = inputStream.read();
i = getChar(inputStream);
switch (i) {
case 'n':
stringBuilder.append('\n');
@@ -181,27 +211,26 @@ public class OnlineParser {
default:
throw new FormatException();
}
}
else {
} else {
stringBuilder.append((char) i);
}
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker skipSpaces(InputStream inputStream) throws IOException {
int i = inputStream.read();
private static CharChecker skipSpaces(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
while (i == ' ') {
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException {
int i = inputStream.read();
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
while (i != '\n' && i != -1) {
i = inputStream.read();
i = getChar(inputStream);
}
return new CharChecker(i);
}
@@ -267,18 +296,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();
}
@@ -307,8 +336,8 @@ public class OnlineParser {
public static Map<String, MetricFamily> parseMetrics(InputStream inputStream) throws IOException {
Map<String, MetricFamily> metricFamilyMap = new ConcurrentHashMap<>(10);
int i = inputStream.read();
try {
int i = getChar(inputStream);
while (i != -1) {
if (i == '#' || i == '\n') {
skipToLineEnd(inputStream).maybeEol().maybeEof().noElse();
@@ -317,7 +346,7 @@ public class OnlineParser {
stringBuilder.append((char) i);
parseMetric(inputStream, metricFamilyMap, stringBuilder);
}
i = inputStream.read();
i = getChar(inputStream);
}
} catch (FormatException e) {
log.error("prometheus parser failed because of wrong input format. {}", e.getMessage());
@@ -28,6 +28,7 @@ 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.
@@ -41,6 +42,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
@EnableAsync
@EnableScheduling
public class Manager {
public static void main(String[] args) {
SpringApplication.run(Manager.class, args);
@@ -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())) {
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp()) || monitor.getType() == CommonConstants.MONITOR_TYPE_PUSH_AUTO_CREATE) {
List<CollectRep.MetricsData> metricsDataList = warehouseService.queryMonitorMetricsData(id);
List<String> metrics = metricsDataList.stream().map(CollectRep.MetricsData::getMetrics).collect(Collectors.toList());
monitorDto.setMetrics(metrics);
@@ -72,14 +72,22 @@ warehouse:
rpc-port: 6667
username: root
password: root
redis:
enabled: false
host: 127.0.0.1
port: 6379
password: 123456
# store real-time metrics data, enable only one below
real-time:
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:
@@ -102,4 +110,4 @@ ai:
# api key
api-key:
#At present, only IFLYTEK large model needs to be filled in
api-secret:
api-secret:
@@ -17,71 +17,44 @@
-- 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
@@ -93,4 +66,5 @@ DELIMITER ;
CALL AddOrDropColumns();
DROP PROCEDURE IF EXISTS AddOrDropColumns;
DROP PROCEDURE IF EXISTS AddOrDropColumns;
commit;
@@ -0,0 +1,250 @@
# 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 categoryservice-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
xpath: deviceName
- field: deviceID
type: 1
i18n:
zh-CN: 设备ID
en-US: Device ID
xpath: deviceID
- field: firmwareVersion
type: 1
i18n:
zh-CN: 固件版本
en-US: Firmware Version
xpath: firmwareVersion
- field: model
type: 1
i18n:
zh-CN: 设备型号
en-US: Device Model
xpath: model
- field: macAddress
type: 1
i18n:
zh-CN: mac地址
en-US: Mac Address
xpath: macAddress
- name: status
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: cpuUtilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
xpath: CPUList/CPU/cpuUtilization
- field: memoryUsage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
xpath: MemoryList/Memory/memoryUsage
- field: memoryAvailable
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
xpath: MemoryList/Memory/memoryAvailable
- field: cacheSize
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
xpath: MemoryList/Memory/cacheSize
- field: netPort1Speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
- field: netPort2Speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
- field: bootTime
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
xpath: bootTime
- field: deviceUpTime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
xpath: deviceUpTime
- field: lastCalibrationTime
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
xpath: lastCalibrationTime
- field: lastCalibrationTimeDiff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
xpath: lastCalibrationTimeDiff
- field: avgUploadTime
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/avgTime
- field: maxUploadTime
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/maxTime
- field: minUploadTime
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/minTime
- field: lastCalibrationMode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
xpath: lastCalibrationTimeMode
- field: lastCalibrationAddress
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
xpath: lastCalibrationTimeAddress
- field: responseTime
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
units:
- memoryUsage=KB->MB
- memoryAvailable=KB->MB
- cacheSize=KB->MB
@@ -0,0 +1,263 @@
# 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 categoryservice-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^_^ ]
@@ -0,0 +1,42 @@
/*
* 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.push.config;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;
/**
* push error request wrapper
*/
@Getter
public class PushErrorRequestWrapper extends HttpServletRequestWrapper {
private final String job;
private final String instance;
public PushErrorRequestWrapper(HttpServletRequest request, String job, String instance) {
super(request);
this.job = job;
this.instance = instance;
}
}
@@ -0,0 +1,47 @@
/*
* 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.push.config;
import org.apache.hertzbeat.push.service.PushGatewayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*/
@Configuration
public class PushFilterConfig {
@Autowired
private PushGatewayService pushGatewayService;
private static final String URI_PREFIX = "/api/push/prometheus/*";
@Bean
public FilterRegistrationBean<PushPrometheusStreamReadingFilter> contentTypeFilter() {
FilterRegistrationBean<PushPrometheusStreamReadingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new PushPrometheusStreamReadingFilter(pushGatewayService));
registrationBean.addUrlPatterns(URI_PREFIX);
registrationBean.setOrder(Integer.MIN_VALUE);
return registrationBean;
}
}
@@ -0,0 +1,82 @@
/*
* 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.push.config;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.hertzbeat.push.service.PushGatewayService;
/**
* todo
*/
public class PushPrometheusStreamReadingFilter implements Filter {
private final PushGatewayService pushGatewayService;
private final Pattern pathPattern = Pattern.compile("^/api/push/prometheus/job/([a-zA-Z0-9_]*)(?:/instance/([a-zA-Z0-9_]*))?$");
public PushPrometheusStreamReadingFilter(PushGatewayService pushGatewayService) {
this.pushGatewayService = pushGatewayService;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest httpRequest) {
String uri = httpRequest.getRequestURI();
Matcher matcher = pathPattern.matcher(uri);
String job = null;
String instance = null;
if (matcher.matches()) {
job = matcher.group(1);
instance = matcher.group(2);
boolean flag = pushGatewayService.pushPrometheusMetrics(request.getInputStream(), job, instance);
if (flag) {
PushSuccessRequestWrapper successRequestWrapper = new PushSuccessRequestWrapper(httpRequest, job, instance);
chain.doFilter(successRequestWrapper, response);
} else {
PushErrorRequestWrapper errorRequestWrapper = new PushErrorRequestWrapper(httpRequest, job, instance);
chain.doFilter(errorRequestWrapper, response);
}
} else {
chain.doFilter(request, response);
}
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {}
}
@@ -0,0 +1,41 @@
/*
* 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.push.config;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;
/**
* push success request wrapper
*/
@Getter
public class PushSuccessRequestWrapper extends HttpServletRequestWrapper {
private final String job;
private final String instance;
public PushSuccessRequestWrapper(HttpServletRequest request, String job, String instance) {
super(request);
this.job = job;
this.instance = instance;
}
}
@@ -1,62 +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.push.controller;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.apache.hertzbeat.push.service.PushService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* push controller
*/
@Tag(name = "Metrics Push API")
@RestController
@RequestMapping(value = "/api/push", produces = {APPLICATION_JSON_VALUE})
public class PushController {
@Autowired
private PushService pushService;
@PostMapping
@Operation(summary = "Push metric data to hertzbeat", description = "Push metric data to hertzbeat")
public ResponseEntity<Message<Void>> pushMetrics(@RequestBody PushMetricsDto pushMetricsDto) {
pushService.pushMetricsData(pushMetricsDto);
return ResponseEntity.ok(Message.success("Push success"));
}
@GetMapping()
@Operation(summary = "Get metric data for hertzbeat", description = "Get metric data for hertzbeat")
public ResponseEntity<Message<PushMetricsDto>> getMetrics(
@Parameter(description = "Monitor ID", example = "6565463543") @RequestParam("id") final Long id,
@Parameter(description = "Last pull time", example = "6565463543") @RequestParam("time") final Long time) {
PushMetricsDto pushMetricsDto = pushService.getPushMetricData(id, time);
return ResponseEntity.ok(Message.success(pushMetricsDto));
}
}
@@ -22,11 +22,9 @@ package org.apache.hertzbeat.push.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.apache.hertzbeat.push.service.PushGatewayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.hertzbeat.push.config.PushErrorRequestWrapper;
import org.apache.hertzbeat.push.config.PushSuccessRequestWrapper;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -37,22 +35,23 @@ import org.springframework.web.bind.annotation.RestController;
*/
@Tag(name = "Metrics Push Gateway API")
@RestController
@RequestMapping(value = "/api/push/pushgateway")
public class PushGatewayController {
@Autowired
private PushGatewayService pushGatewayService;
@RequestMapping(value = "/api/push/prometheus/**")
public class PushPrometheusController {
@PostMapping()
@Operation(summary = "Push metric data to hertzbeat pushgateway", description = "Push metric data to hertzbeat pushgateway")
public ResponseEntity<Message<Void>> pushMetrics(HttpServletRequest request) throws IOException {
InputStream inputStream = request.getInputStream();
boolean result = pushGatewayService.pushMetricsData(inputStream);
if (result) {
return ResponseEntity.ok(Message.success("Push success"));
@Operation(summary = "Prometheus push gateway", description = "Push prometheus metric data to hertzbeat")
public ResponseEntity<Message<Void>> pushMetrics(HttpServletRequest request) {
if (request instanceof PushErrorRequestWrapper error) {
return ResponseEntity.badRequest().body(Message.success(String.format("Push failed, job: %s, instance: %s",
error.getJob(), error.getInstance())));
}
else if (request instanceof PushSuccessRequestWrapper success) {
return ResponseEntity.ok(Message.success(String.format("Push success, job: %s, instance: %s",
success.getJob(), success.getInstance())));
}
else {
return ResponseEntity.ok(Message.success("Push failed"));
return ResponseEntity.badRequest()
.body(Message.success(String.format("Request %s not matched.", request.getRequestURI())));
}
}
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.push.dao;
import java.util.List;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.springframework.data.jpa.repository.JpaRepository;
@@ -24,4 +25,11 @@ import org.springframework.data.jpa.repository.JpaRepository;
* push monitor dao
*/
public interface PushMonitorDao extends JpaRepository<Monitor, Long> {
/**
* Find all monitoring entities by type
* @param type Monitoring type
* @return Monitoring entity list
*/
List<Monitor> findMonitorsByType(byte type);
}
@@ -19,17 +19,24 @@
package org.apache.hertzbeat.push.service;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.stereotype.Service;
/**
* push gateway metrics
* push gateway service
*/
@Service
public interface PushGatewayService {
boolean pushMetricsData(InputStream inputStream) throws IOException;
/**
* push prometheus metrics data
* @param inputStream input stream
* @param job job name, maybe null
* @param instance instance name, maybe null
* @return push success or not
*/
boolean pushPrometheusMetrics(InputStream inputStream, String job, String instance);
}
@@ -19,9 +19,23 @@
package org.apache.hertzbeat.push.service.impl;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.util.OnlineParser;
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
import org.apache.hertzbeat.push.dao.PushMonitorDao;
import org.apache.hertzbeat.push.service.PushGatewayService;
import org.springframework.stereotype.Service;
@@ -32,9 +46,88 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
public class PushGatewayServiceImpl implements PushGatewayService {
private final CommonDataQueue commonDataQueue;
private final PushMonitorDao pushMonitorDao;
private final Map<String, Long> jobInstanceMap;
public PushGatewayServiceImpl(CommonDataQueue commonDataQueue, PushMonitorDao pushMonitorDao) {
this.commonDataQueue = commonDataQueue;
this.pushMonitorDao = pushMonitorDao;
jobInstanceMap = new ConcurrentHashMap<>();
pushMonitorDao.findMonitorsByType((byte) 1).forEach(monitor ->
jobInstanceMap.put(monitor.getApp() + "_" + monitor.getName(), monitor.getId()));
}
@Override
public boolean pushMetricsData(InputStream inputStream) throws IOException {
return true;
public boolean pushPrometheusMetrics(InputStream inputStream, String job, String instance) {
try {
long curTime = Instant.now().toEpochMilli();
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
if (metricFamilyMap == null) {
log.error("parse prometheus metrics is null, job: {}, instance: {}", job, instance);
return false;
}
long id = 0L;
if (job != null && instance != null) {
// auto create monitor when job and instance not null
// job is app, instance is the name
id = jobInstanceMap.computeIfAbsent(job + "_" + instance, key -> {
log.info("auto create monitor by prometheus push, job: {}, instance: {}", job, instance);
long monitorId = SnowFlakeIdGenerator.generateId();
Monitor monitor = Monitor.builder()
.id(monitorId)
.app(job)
.name(instance)
.host(instance)
.type((byte) 1)
.status(CommonConstants.MONITOR_UP_CODE)
.build();
this.pushMonitorDao.save(monitor);
return monitorId;
});
}
for (Map.Entry<String, MetricFamily> entry : metricFamilyMap.entrySet()) {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
builder.setId(id);
builder.setApp(job);
builder.setTime(curTime);
String metricsName = entry.getKey();
builder.setMetrics(metricsName);
MetricFamily metricFamily = entry.getValue();
if (!metricFamily.getMetricList().isEmpty()) {
List<String> metricsFields = new LinkedList<>();
for (int index = 0; index < metricFamily.getMetricList().size(); index++) {
MetricFamily.Metric metric = metricFamily.getMetricList().get(index);
if (index == 0) {
metric.getLabels().forEach(label -> {
metricsFields.add(label.getName());
builder.addField(CollectRep.Field.newBuilder().setName(label.getName())
.setType(CommonConstants.TYPE_STRING).setLabel(true).build());
});
builder.addField(CollectRep.Field.newBuilder().setName("value")
.setType(CommonConstants.TYPE_NUMBER).setLabel(false).build());
}
Map<String, String> labelMap = metric.getLabels()
.stream()
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String field : metricsFields) {
String fieldValue = labelMap.get(field);
valueRowBuilder.addColumn(fieldValue == null ? CommonConstants.NULL_VALUE : fieldValue);
}
valueRowBuilder.addColumn(String.valueOf(metric.getValue()));
builder.addValueRow(valueRowBuilder.build());
}
commonDataQueue.sendMetricsData(builder.build());
}
}
return true;
} catch (Exception e) {
log.error("push prometheus metrics error", e);
return false;
}
}
}
@@ -1,143 +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.push.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.push.PushMetrics;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.push.dao.PushMetricsDao;
import org.apache.hertzbeat.push.dao.PushMonitorDao;
import org.apache.hertzbeat.push.service.PushService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* push service impl
*/
@Slf4j
@Service
public class PushServiceImpl implements PushService {
@Autowired
private PushMonitorDao monitorDao;
@Autowired
private PushMetricsDao metricsDao;
private final Map<Long, Long> monitorIdCache; // key: monitorId, value: time stamp of last query
private static final long cacheTimeout = 5000L; // ms
private final Map<Long, PushMetricsDto.Metrics> lastPushMetrics;
private static final long deleteMetricsPeriod = 1000 * 60 * 60 * 12L;
private static final long deleteBeforeTime = deleteMetricsPeriod / 2;
public PushServiceImpl(){
monitorIdCache = new HashMap<>();
lastPushMetrics = new HashMap<>();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
deletePeriodically();
} catch (Exception e) {
log.error("periodical deletion failed. {}", e.getMessage());
}
}
}, 1000, deleteMetricsPeriod);
}
public void deletePeriodically(){
metricsDao.deleteAllByTimeBefore(System.currentTimeMillis() - deleteBeforeTime);
}
@Override
public void pushMetricsData(PushMetricsDto pushMetricsDto) throws RuntimeException {
List<PushMetrics> pushMetricsList = new ArrayList<>();
long curTime = System.currentTimeMillis();
for (PushMetricsDto.Metrics metrics : pushMetricsDto.getMetricsList()) {
long monitorId = metrics.getMonitorId();
metrics.setTime(curTime);
if (!monitorIdCache.containsKey(monitorId) || (monitorIdCache.containsKey(monitorId) && curTime > monitorIdCache.get(monitorId) + cacheTimeout)) {
Optional<Monitor> queryOption = monitorDao.findById(monitorId);
if (queryOption.isEmpty()) {
monitorIdCache.remove(monitorId);
continue;
}
monitorIdCache.put(monitorId, curTime);
}
PushMetrics pushMetrics = PushMetrics.builder()
.monitorId(metrics.getMonitorId())
.time(curTime)
.metrics(JsonUtil.toJson(metrics.getMetrics())).build();
lastPushMetrics.put(monitorId, metrics);
pushMetricsList.add(pushMetrics);
}
metricsDao.saveAll(pushMetricsList);
}
@Override
public PushMetricsDto getPushMetricData(final Long monitorId, final Long time) {
PushMetricsDto.Metrics metrics;
PushMetricsDto pushMetricsDto = new PushMetricsDto();
if (lastPushMetrics.containsKey(monitorId)) {
metrics = lastPushMetrics.get(monitorId);
}
else {
try {
PushMetrics pushMetrics = metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId);
if (pushMetrics == null || pushMetrics.getMetrics() == null) {
return pushMetricsDto;
}
List<Map<String, String>> jsonMap = JsonUtil.fromJson(pushMetrics.getMetrics(), new TypeReference<>() {
});
metrics = PushMetricsDto.Metrics.builder().monitorId(monitorId).metrics(jsonMap).time(pushMetrics.getTime()).build();
lastPushMetrics.put(monitorId, metrics);
}
catch (Exception e) {
log.error("no metrics found, monitor id: {}, {}", monitorId, e.getMessage(), e);
return pushMetricsDto;
}
}
if (time > metrics.getTime()) {
// return void because time param is invalid
return pushMetricsDto;
}
pushMetricsDto.getMetricsList().add(metrics);
return pushMetricsDto;
}
}
@@ -1,93 +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.push.controller;
import static org.mockito.Mockito.when;
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.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.push.service.PushService;
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 org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
/**
* test case for {@link PushController}
*/
@ExtendWith(MockitoExtension.class)
class PushControllerTest {
private MockMvc mockMvc;
@Mock
private PushService pushService;
@InjectMocks
private PushController pushController;
private PushMetricsDto mockPushMetricsDto;
@BeforeEach
void setUp() {
this.mockMvc = standaloneSetup(this.pushController).build();
mockPushMetricsDto = PushMetricsDto.builder().build();
}
@Test
void testPushMetrics() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.post("/api/push")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(mockPushMetricsDto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
@Test
void testGetMetrics() throws Exception {
Long id = 6565463543L;
Long time = 6565463543L;
when(pushService.getPushMetricData(id, time)).thenReturn(mockPushMetricsDto);
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/push")
.contentType(MediaType.APPLICATION_JSON)
.param("id", id.toString())
.param("time", time.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andReturn();
}
}
@@ -22,12 +22,11 @@ package org.apache.hertzbeat.push.controller;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.InputStream;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.push.service.PushGatewayService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -38,11 +37,12 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
/**
* test case for {@link PushGatewayController}
* test case for {@link PushPrometheusController}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class PushGatewayControllerTest {
class PushPrometheusControllerTest {
private MockMvc mockMvc;
@@ -50,7 +50,7 @@ class PushGatewayControllerTest {
private PushGatewayService pushGatewayService;
@InjectMocks
private PushGatewayController gatewayController;
private PushPrometheusController gatewayController;
@BeforeEach
void setUp() {
@@ -63,14 +63,12 @@ class PushGatewayControllerTest {
String mockData = "some metric data";
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(true);
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(true);
mockMvc.perform(post("/api/push/pushgateway")
.contentType(MediaType.APPLICATION_JSON)
.content(mockData))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Push success"));
.andExpect(status().isOk());
}
@Test
@@ -78,13 +76,12 @@ class PushGatewayControllerTest {
String mockData = "some metric data";
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(false);
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(false);
mockMvc.perform(post("/api/push/pushgateway")
.contentType(MediaType.APPLICATION_JSON)
.content(mockData))
.andExpect(status().isOk())
.andExpect(jsonPath("$.msg").value("Push failed"));
.andExpect(status().isOk());
}
}
@@ -1,77 +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.push.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.hertzbeat.common.entity.push.PushMetrics;
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.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* test case for {@link PushMetricsDao}
*/
@ExtendWith(MockitoExtension.class)
public class PushMetricsDaoTest {
@Mock
private PushMetricsDao pushMetricsDao;
@InjectMocks
private PushMetricsDaoTest pushMetricsDaoTest;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void shallFindFirstByMonitorIdOrderByTimeDesc() {
PushMetrics expectedMetrics = new PushMetrics();
expectedMetrics.setMonitorId(1L);
expectedMetrics.setTime(System.currentTimeMillis());
when(pushMetricsDao.findFirstByMonitorIdOrderByTimeDesc(1L)).thenReturn(expectedMetrics);
PushMetrics actualMetrics = pushMetricsDao.findFirstByMonitorIdOrderByTimeDesc(1L);
assertEquals(expectedMetrics, actualMetrics);
verify(pushMetricsDao, times(1)).findFirstByMonitorIdOrderByTimeDesc(1L);
}
@Test
void shallDeleteAllByTimeBefore() {
doNothing().when(pushMetricsDao).deleteAllByTimeBefore(anyLong());
pushMetricsDao.deleteAllByTimeBefore(1000L);
verify(pushMetricsDao, times(1)).deleteAllByTimeBefore(1000L);
}
}
@@ -1,132 +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.push.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.push.PushMetrics;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.apache.hertzbeat.push.dao.PushMetricsDao;
import org.apache.hertzbeat.push.dao.PushMonitorDao;
import org.apache.hertzbeat.push.service.impl.PushServiceImpl;
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.test.util.ReflectionTestUtils;
/**
* test case for {@link PushServiceImpl}
*/
@ExtendWith(MockitoExtension.class)
class PushServiceTest {
@Mock
private PushMonitorDao monitorDao;
@Mock
private PushMetricsDao metricsDao;
@InjectMocks
private PushServiceImpl pushService;
@BeforeEach
void setUp() {
pushService = new PushServiceImpl();
ReflectionTestUtils.setField(pushService, "monitorDao", monitorDao);
ReflectionTestUtils.setField(pushService, "metricsDao", metricsDao);
}
@Test
void testPushMetricsData() {
PushMetricsDto pushMetricsDto = new PushMetricsDto();
List<PushMetricsDto.Metrics> metricsList = new ArrayList<>();
PushMetricsDto.Metrics metrics = new PushMetricsDto.Metrics();
metrics.setMonitorId(1L);
metricsList.add(metrics);
pushMetricsDto.setMetricsList(metricsList);
when(monitorDao.findById(anyLong())).thenReturn(Optional.of(new Monitor()));
pushService.pushMetricsData(pushMetricsDto);
verify(metricsDao, times(1)).saveAll(any());
}
@Test
void testGetPushMetricData() {
Long monitorId = 1L;
Long time = System.currentTimeMillis();
PushMetrics pushMetrics = PushMetrics.builder()
.monitorId(monitorId)
.time(time)
.metrics("[{\"key\":\"value\"}]")
.build();
when(metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId)).thenReturn(pushMetrics);
PushMetricsDto result = pushService.getPushMetricData(monitorId, time);
assertEquals(1, result.getMetricsList().size());
assertEquals(monitorId, result.getMetricsList().get(0).getMonitorId());
}
@Test
void testGetPushMetricDataTimeInvalid() {
Long monitorId = 1L;
Long time = System.currentTimeMillis() + 10000;
PushMetrics pushMetrics = PushMetrics.builder()
.monitorId(monitorId)
.time(System.currentTimeMillis())
.metrics("[{\"key\":\"value\"}]")
.build();
when(metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId)).thenReturn(pushMetrics);
PushMetricsDto result = pushService.getPushMetricData(monitorId, time);
assertTrue(result.getMetricsList().isEmpty());
}
@Test
void testDeletePeriodically() {
pushService.deletePeriodically();
verify(metricsDao, times(1)).deleteAllByTimeBefore(anyLong());
}
}
@@ -56,4 +56,8 @@ public interface WarehouseConstants {
String MEMORY = "memory";
}
String PROMQL = "promql";
String SQL = "sql";
}
@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.warehouse.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.hertzbeat.common.entity.dto.Message;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import org.apache.hertzbeat.warehouse.service.MetricsDataQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* Metrics Data Query API
*/
@RestController
@RequestMapping(produces = {APPLICATION_JSON_VALUE})
@Tag(name = "Metrics Data Query API")
public class MetricsDataQueryController {
@Autowired
private MetricsDataQueryService queryService;
@GetMapping("/api/warehouse/query")
@Operation(summary = "Query Real Time Metrics Data")
public ResponseEntity<Message<List<MetricQueryData>>> queryMetricsData(
@Parameter(description = "Query PromQL expr list", example = "cpu")
@RequestParam List<String> queries,
@Parameter(description = "Query type", example = "promql")
@RequestParam String type,
@Parameter(description = "Query timestamp", example = "1725854804451")
@RequestParam Long time) {
return ResponseEntity.ok(Message.success(queryService.query(queries, type, time)));
}
@GetMapping("/api/warehouse/query/range")
@Operation(summary = "Query Range Metrics Data")
public ResponseEntity<Message<List<MetricQueryData>>> queryMetricsDataRange(
@Parameter(description = "Query PromQL expr list", example = "cpu")
@RequestParam List<String> queries,
@Parameter(description = "Query type", example = "promql")
@RequestParam String type,
@Parameter(description = "Query start timestamp", example = "1725854804451")
@RequestParam Long start,
@Parameter(description = "Query end timestamp", example = "1733630804452")
@RequestParam Long end,
@Parameter(description = "Query step", example = "4m")
@RequestParam String step
) {
return ResponseEntity.ok(Message.success(queryService.queryRange(queries, type, start, end, step)));
}
}
@@ -18,29 +18,12 @@
package org.apache.hertzbeat.warehouse.db;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.NetworkConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* query executor for victor metrics
@@ -48,71 +31,15 @@ import org.springframework.web.util.UriComponentsBuilder;
@Component
@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true")
@Slf4j
public class GreptimePromqlQueryExecutor implements QueryExecutor {
public class GreptimePromqlQueryExecutor extends PromqlQueryExecutor {
private static final String QUERY_PATH = "/v1/prometheus/api/v1/query";
private final GreptimeProperties greptimeProperties;
private final RestTemplate restTemplate;
public GreptimePromqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) {
super(restTemplate, new HttpPromqlProperties(greptimeProperties.httpEndpoint() + QUERY_PATH,
greptimeProperties.username(), greptimeProperties.password()));
this.greptimeProperties = greptimeProperties;
this.restTemplate = restTemplate;
}
@Override
public List<Map<String, Object>> execute(String query) {
// http run the promql query
List<Map<String, Object>> results = new LinkedList<>();
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(greptimeProperties.username())
&& StringUtils.hasText(greptimeProperties.password())) {
String authStr = greptimeProperties.username() + ":" + greptimeProperties.password();
String encodedAuth = Base64Util.encode(authStr);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
URI uri = UriComponentsBuilder.fromHttpUrl(greptimeProperties.httpEndpoint() + QUERY_PATH)
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
.build(true).toUri();
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
&& responseEntity.getBody().getData().getResult() != null) {
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
for (PromQlQueryContent.ContentData.Content content : contents) {
Map<String, String> labels = content.getMetric();
Map<String, Object> queryResult = new HashMap<>(8);
queryResult.putAll(labels);
if (content.getValue() != null && content.getValue().length == 2) {
queryResult.put("__timestamp__", content.getValue()[0]);
queryResult.put("__value__", content.getValue()[1]);
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
List<Object> values = new LinkedList<>();
for (Object[] valueArr : content.getValues()) {
values.add(valueArr[1]);
}
queryResult.put("__value__", values);
}
results.add(queryResult);
}
}
} else {
log.error("query metrics data from greptime failed. {}", responseEntity);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return results;
}
@Override
public boolean support(String datasource) {
return "promql".equals(datasource);
}
}
@@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.db;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.NetworkConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.apache.hertzbeat.warehouse.constants.WarehouseConstants.PROMQL;
/**
* abstract class for promql query executor
*/
@Slf4j
public abstract class PromqlQueryExecutor implements QueryExecutor {
private static final String supportQueryLanguage = PROMQL;
protected static final String HTTP_QUERY_PARAM = "query";
protected static final String HTTP_TIME_PARAM = "time";
protected static final String HTTP_START_PARAM = "start";
protected static final String HTTP_END_PARAM = "end";
protected static final String HTTP_STEP_PARAM = "step";
private final RestTemplate restTemplate;
private final HttpPromqlProperties httpPromqlProperties;
PromqlQueryExecutor(RestTemplate restTemplate, HttpPromqlProperties httpPromqlProperties) {
this.restTemplate = restTemplate;
this.httpPromqlProperties = httpPromqlProperties;
}
/**
* record class for promql http connection
*/
protected record HttpPromqlProperties (
String url,
String username,
String password
){}
protected List<Map<String, Object>> http_promql(Map<String, Object> params) {
// http run the promql query
List<Map<String, Object>> results = new LinkedList<>();
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(httpPromqlProperties.username())
&& StringUtils.hasText(httpPromqlProperties.password())) {
String authStr = httpPromqlProperties.username() + ":" + httpPromqlProperties.password();
String encodedAuth = Base64Util.encode(authStr);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpPromqlProperties.url);
for (Map.Entry<String, Object> entry : params.entrySet()) {
uriComponentsBuilder.queryParam(entry.getKey(), entry.getValue());
}
URI uri = uriComponentsBuilder.build(true).toUri();
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
&& responseEntity.getBody().getData().getResult() != null) {
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
for (PromQlQueryContent.ContentData.Content content : contents) {
Map<String, String> labels = content.getMetric();
Map<String, Object> queryResult = new HashMap<>(8);
queryResult.putAll(labels);
if (content.getValue() != null && content.getValue().length == 2) {
queryResult.put("__timestamp__", content.getValue()[0]);
queryResult.put("__value__", content.getValue()[1]);
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
List<Object> values = new LinkedList<>();
for (Object[] valueArr : content.getValues()) {
values.add(valueArr[1]);
}
queryResult.put("__value__", values);
}
results.add(queryResult);
}
}
} else {
log.error("query metrics data from greptime failed. {}", responseEntity);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return results;
}
public MetricQueryData convertToMetricQueryData(Object object) {
MetricQueryData metricQueryData = new MetricQueryData();
try {
List<Map<String, Object>> metrics = (List<Map<String, Object>>) object;
// todo
} catch (Exception e) {
log.error("converting to metric query data failed.");
}
return metricQueryData;
}
public List<Map<String, Object>> execute(String queryString) {
Map<String, Object> params = new HashMap<>();
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
return http_promql(params);
}
public List<Map<String, Object>> query(String queryString, Long time) {
Map<String, Object> params = new HashMap<>();
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
params.put(HTTP_TIME_PARAM, time);
return http_promql(params);
}
public List<Map<String, Object>> query_range(String queryString, Long start, Long end, String step) {
Map<String, Object> params = new HashMap<>();
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
params.put(HTTP_START_PARAM, start);
params.put(HTTP_END_PARAM, end);
params.put(HTTP_STEP_PARAM, step);
return http_promql(params);
}
public boolean support(String datasource) {
return supportQueryLanguage.equals(datasource);
}
}
@@ -17,6 +17,8 @@
package org.apache.hertzbeat.warehouse.db;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import java.util.List;
import java.util.Map;
@@ -24,8 +26,14 @@ import java.util.Map;
* query executor interface
*/
public interface QueryExecutor {
MetricQueryData convertToMetricQueryData(Object object);
List<Map<String, Object>> execute(String query);
List<Map<String, Object>> query(String query, Long time);
List<Map<String, Object>> query_range(String query, Long start, Long end, String step);
boolean support(String datasource);
}
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.db;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import java.util.List;
import java.util.Map;
import static org.apache.hertzbeat.warehouse.constants.WarehouseConstants.SQL;
/**
* abstract class for sql query executor
*/
@Slf4j
public abstract class SqlQueryExecutor implements QueryExecutor {
private static final String supportQueryLanguage = SQL;
/**
* record class for sql connection
*/
protected record ConnectorSqlProperties () {}
protected abstract List<Map<String, Object>> do_sql(Map<String, Object> params);
public MetricQueryData convertToMetricQueryData(Object object) {
MetricQueryData metricQueryData = new MetricQueryData();
try {
List<Map<String, Object>> metrics = (List<Map<String, Object>>) object;
// todo
} catch (Exception e) {
log.error("converting to metric query data failed.");
}
return metricQueryData;
}
public abstract List<Map<String, Object>> execute(String query);
public abstract List<Map<String, Object>> query(String query, Long time);
public abstract List<Map<String, Object>> query_range(String query, Long start, Long end, String step);
public boolean support(String datasource) {
return supportQueryLanguage.equals(datasource);
}
}
@@ -18,29 +18,11 @@
package org.apache.hertzbeat.warehouse.db;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.constants.NetworkConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
import org.apache.hertzbeat.common.util.Base64Util;
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
import org.apache.hertzbeat.warehouse.store.history.vm.VictoriaMetricsProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
* query executor for victor metrics
@@ -48,71 +30,16 @@ import org.springframework.web.util.UriComponentsBuilder;
@Component
@ConditionalOnProperty(prefix = "warehouse.store.victoria-metrics", name = "enabled", havingValue = "true")
@Slf4j
public class VictoriaMetricsQueryExecutor implements QueryExecutor {
public class VictoriaMetricsQueryExecutor extends PromqlQueryExecutor {
private static final String QUERY_PATH = "/api/v1/query";
private final VictoriaMetricsProperties victoriaMetricsProp;
private final RestTemplate restTemplate;
public VictoriaMetricsQueryExecutor(VictoriaMetricsProperties victoriaMetricsProp, RestTemplate restTemplate) {
super(restTemplate, new HttpPromqlProperties(victoriaMetricsProp.url() + QUERY_PATH,
victoriaMetricsProp.username(), victoriaMetricsProp.password()));
this.victoriaMetricsProp = victoriaMetricsProp;
this.restTemplate = restTemplate;
}
@Override
public List<Map<String, Object>> execute(String query) {
// http run the promql query
List<Map<String, Object>> results = new LinkedList<>();
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
if (StringUtils.hasText(victoriaMetricsProp.username())
&& StringUtils.hasText(victoriaMetricsProp.password())) {
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
String encodedAuth = Base64Util.encode(authStr);
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
}
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
URI uri = UriComponentsBuilder.fromHttpUrl(victoriaMetricsProp.url() + QUERY_PATH)
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
.build(true).toUri();
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
&& responseEntity.getBody().getData().getResult() != null) {
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
for (PromQlQueryContent.ContentData.Content content : contents) {
Map<String, String> labels = content.getMetric();
Map<String, Object> queryResult = new HashMap<>(8);
queryResult.putAll(labels);
if (content.getValue() != null && content.getValue().length == 2) {
queryResult.put("__timestamp__", content.getValue()[0]);
queryResult.put("__value__", content.getValue()[1]);
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
List<Object> values = new LinkedList<>();
for (Object[] valueArr : content.getValues()) {
values.add(valueArr[1]);
}
queryResult.put("__value__", values);
}
results.add(queryResult);
}
}
} else {
log.error("query metrics data from victor-metrics failed. {}", responseEntity);
}
} catch (Exception e) {
log.error(e.toString(), e);
}
return results;
}
@Override
public boolean support(String datasource) {
return "promql".equals(datasource);
}
}
@@ -15,17 +15,32 @@
* limitations under the License.
*/
package org.apache.hertzbeat.push.service;
package org.apache.hertzbeat.warehouse.service;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.springframework.stereotype.Service;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import java.util.List;
/**
* push metrics
* metrics data query service
*/
@Service
public interface PushService {
void pushMetricsData(PushMetricsDto pushMetricsData);
public interface MetricsDataQueryService {
/**
* Query metrics data
* @param queries query expr
* @param time time
* @return data
*/
List<MetricQueryData> query(List<String> queries, String queryType, Long time);
PushMetricsDto getPushMetricData(Long monitorId, Long time);
/**
* Query metrics data range
* @param queries query expr
* @param start start
* @param end end
* @param step step
* @return data
*/
List<MetricQueryData> queryRange(List<String> queries, String queryType, Long start, Long end, String step);
}
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.service.impl;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
import org.apache.hertzbeat.warehouse.service.MetricsDataQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MetricsDataQueryServiceImpl implements MetricsDataQueryService {
@Autowired(required = false)
List<QueryExecutor> executors;
@Override
public List<MetricQueryData> query(List<String> queries, String queryType, Long time) {
if (queries == null || executors.isEmpty()) {
throw new IllegalArgumentException("No query executor found");
}
QueryExecutor executor = executors.stream().filter(e -> e.support(queryType)).findFirst().orElse(null);
if (executor == null) {
throw new IllegalArgumentException("Unsupported datasource: " + queryType);
}
List<MetricQueryData> metricQueryDataList = new ArrayList<>();
for (String query : queries) {
metricQueryDataList.add(executor.convertToMetricQueryData(executor.query(query, time)));
}
return metricQueryDataList;
}
@Override
public List<MetricQueryData> queryRange(List<String> queries, String queryType, Long start, Long end, String step) {
if (queries == null || executors.isEmpty()) {
throw new IllegalArgumentException("No query executor found");
}
QueryExecutor executor = executors.stream().filter(e -> e.support(queryType)).findFirst().orElse(null);
if (executor == null) {
throw new IllegalArgumentException("Unsupported datasource: " + queryType);
}
List<MetricQueryData> metricQueryDataList = new ArrayList<>();
for (String query : queries) {
metricQueryDataList.add(executor.convertToMetricQueryData(executor.query_range(query, start, end, step)));
}
return metricQueryDataList;
}
}
@@ -0,0 +1,106 @@
---
title: "Behind the Scenes of HertzBeat: How Metric Collection Works"
author: JuJinPark
author_title: JuJin
author_url: https://github.com/JuJinPark
tags: [opensource, practice]
keywords: [open source monitoring system]
---
## Behind the Scenes of HertzBeat: How Metric Collection Works
HertzBeat is an open-source, real-time monitoring system designed for flexibility and ease of use. But how exactly does it collect, process, and store metrics from various systems?
In this post, well walk through the internal architecture behind **HertzBeats metric collection pipeline** — from job distribution to alerting and storage — with the help of a high-level system diagram.
---
### HertzBeats Metric Collection Architecture
![HertzBeat Architecture](/img/docs/hertzbeat-metrics-collection-arch.png)
> **Figure:** High-level architecture of HertzBeat's metric collection system. The Manager handles job scheduling, alerting, and storage, while Collectors (external or internal) perform the actual metric collection. Communication between the Manager and Collectors uses a custom Netty TCP protocol.
---
### 1. Job Distribution: Assigning What to Monitor
When the **Manager** component starts, it loads monitoring targets from the database. These targets define the host, collection interval, and other parameters.
To distribute the workload, the Manager sends jobs to **external Collectors** over a custom **Netty-based TCP protocol**. The `CollectJobScheduling` module handles this logic using **consistent hashing**, ensuring jobs are evenly distributed across collectors.
> 💡 HertzBeat also includes a built-in **main collector** (identified as `MAIN_COLLECTOR_NODE`) that runs directly inside the Manager. This allows HertzBeat to operate in **standalone mode** without requiring any external collectors.
---
### 2. Task Scheduling: When to Monitor
Once a Collector receives a job, it registers it with the **`TimerDispatch`** system.
- For **external collectors**, the Manager sends the task via the TCP connection.
- For the **main collector**, the Manager directly invokes `CollectJobService` within the same process.
Each Collector runs a **`Timer`** in a background thread, which schedules tasks according to their configured intervals. When the time is up, the timer triggers a `TimerTask` to begin metric collection.
---
### 3. Task Execution: How Metrics Are Collected
When a `TimerTask` is triggered, it creates a `MetricsCollect` task and passes it to `MetricsTaskDispatch`, which places it in the **`MetricsCollectorQueue`**.
- A dedicated thread (`CommonDispatcher`) continuously polls this queue.
- Tasks are executed by a **worker thread pool**, allowing multiple metric collections to run concurrently.
- Each task uses a specific **collector strategy** (e.g., HTTP, JDBC, SSH) to fetch metrics from the target system.
---
### 4. Result Processing: What Happens to Collected Data
Once metrics are collected, the results are processed by the **`CollectDataDispatch`** module.
- If the task is recurring, it is rescheduled via `TimerDispatch`.
- Results are added to a **`CommonDataQueue`** for further handling.
For external collectors, results are sent **back to the Manager** via the Netty TCP connection. For the main collector, results are forwarded **directly** to the next processing stage without network overhead.
---
### 5. Alerting & Storage: Making Metrics Useful
The Manager receives metric data and pushes it into the `MetricsDataToAlertQueue`, where it is processed through two main pipelines:
#### 🔔 Alerting
- The `RealTimeAlertCalculator` consumes metrics from the alert queue.
- It checks each metric against user-defined alert rules and triggers alerts if conditions are met.
#### 🧠 Storage
- After alert evaluation, metrics are added to the `MetricsDataToStorageQueue`.
- A background thread (`DataStorageDispatch`) processes this queue and stores the metrics in a database for long-term analysis and dashboard visualization.
---
### Standalone Mode: No External Collectors Required
Thanks to the built-in **main collector**, HertzBeat can operate entirely in standalone mode. This is especially useful for testing, small deployments, or quick setup. All core components — job scheduling, collection, alerting, and storage — run within a single process.
---
### 🧠 Conclusion
HertzBeats metric collection system is designed for **performance, scalability, and flexibility**. With its:
- **Queue-based, multi-threaded architecture**
- **Persistent TCP connections** for reliable job/result flow
- **Built-in main collector** for standalone operation
it handles large-scale monitoring workloads with minimal overhead and high efficiency.
---
### 🙌 Whats Next?
If you're curious to explore more:
- ⭐️ [Star the project on GitHub](https://github.com/apache/hertzbeat)
- 🤝 [Contribute or open an issue](https://github.com/apache/hertzbeat/issues)
+258
View File
@@ -0,0 +1,258 @@
---
id: extend-http-xmlpath
title: HTTP Protocol XmlPath Parsing Method
sidebar_label: XmlPath Parsing Method
---
> After calling the HTTP API to obtain the response data, use the XmlPath script parsing method to parse the response data.
Note⚠️ The response data must be in XML format.
**Use XPath scripts to parse the response data into data that conforms to the data structure rules specified by HertzBeat.**
### XmlPath Parsing Logic
The XmlPath parsing method in HertzBeat uses a two-step XPath process:
1. **Main XPath Expression (`parseScript`)**: This XPath expression is defined in the `http` configuration section under `parseScript`. It is used to select one or more main XML nodes from the response. Each selected node will correspond to one row of metric data in HertzBeat.
2. **Relative Field XPath Expressions (`xpath`)**: For each metric field defined in the `fields` list, you can specify a relative `xpath`. This XPath expression is evaluated *relative to each main node* selected by the `parseScript` in step 1. It extracts the specific value for that metric field from the current main node.
This allows you to easily parse structured XML data where multiple records or items are present.
**Special Metrics**:
* `responseTime`: This built-in metric represents the HTTP request's response time and is automatically collected. It does not require an `xpath`.
* `keyword`: This built-in metric counts the occurrences of a specified keyword (configured in `http.keyword`) in the raw response body. It does not require an `xpath`.
### Example
Assume the HTTP API returns the following XML data:
```xml
<DeviceStatus xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
<CPUList>
<CPU>
<cpuUtilization>36.400002</cpuUtilization>
</CPU>
</CPUList>
<MemoryList>
<Memory>
<memoryUsage>399640</memoryUsage>
<memoryAvailable>98792</memoryAvailable>
<cacheSize>228492</cacheSize>
</Memory>
</MemoryList>
<NetPortStatusList>
<NetPortStatus>
<id>1</id>
<workSpeed>1000</workSpeed>
</NetPortStatus>
<NetPortStatus>
<id>2</id>
<workSpeed>0</workSpeed>
</NetPortStatus>
</NetPortStatusList>
<bootTime>2025-01-06 10:27:48</bootTime>
<deviceUpTime>87天0时55分59秒</deviceUpTime>
<lastCalibrationTime>2025-04-03 11:09:18</lastCalibrationTime>
<lastCalibrationTimeDiff>1</lastCalibrationTimeDiff>
<uploadTimeConsumingList>
<avgTime>16</avgTime>
<maxTime>23</maxTime>
<minTime>12</minTime>
</uploadTimeConsumingList>
<lastCalibrationTimeMode>NTP</lastCalibrationTimeMode>
<lastCalibrationTimeAddress>34.191.45.101</lastCalibrationTimeAddress>
</DeviceStatus>
```
We want to monitor the device status and extract various metrics.
Here's how you would configure the monitoring template YML:
```yaml
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: status
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: cpuUtilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
xpath: CPUList/CPU/cpuUtilization
- field: memoryUsage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
xpath: MemoryList/Memory/memoryUsage
- field: memoryAvailable
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
xpath: MemoryList/Memory/memoryAvailable
- field: cacheSize
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
xpath: MemoryList/Memory/cacheSize
- field: netPort1Speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
- field: netPort2Speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
- field: bootTime
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
xpath: bootTime
- field: deviceUpTime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
xpath: deviceUpTime
- field: lastCalibrationTime
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
xpath: lastCalibrationTime
- field: lastCalibrationTimeDiff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
xpath: lastCalibrationTimeDiff
- field: avgUploadTime
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/avgTime
- field: maxUploadTime
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/maxTime
- field: minUploadTime
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/minTime
- field: lastCalibrationMode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
xpath: lastCalibrationTimeMode
- field: lastCalibrationAddress
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
xpath: lastCalibrationTimeAddress
- field: responseTime
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
units:
- memoryUsage=KB->MB
- memoryAvailable=KB->MB
- cacheSize=KB->MB
+2 -2
View File
@@ -62,8 +62,8 @@ The HertzBeat install package will at `dist/hertzbeat-{version}.tar.gz`
2. Execute under the project root directory: `mvn clean install`
3. Cd to the `collector` directory: `cd collector`
3. Cd to the `hertzbeat-collector` directory: `cd hertzbeat-collector`
4. Execute under `collector` directory: `mvn clean package -Pcluster`
4. Execute under `hertzbeat-collector` directory: `mvn clean package -Pcluster`
The HertzBeat collector package will at `dist/hertzbeat-collector-{version}.tar.gz`
+7 -1
View File
@@ -215,6 +215,7 @@ The release package are here:
- `dist/apache-hertzbeat-{version}-incubating-bin.tar.gz`
- `dist/apache-hertzbeat-collector-{version}-incubating-bin.tar.gz`
- `dist/apache-hertzbeat-{version}-incubating-docker-compose.tar.gz`
#### 3.4 Package the source code
@@ -257,6 +258,9 @@ apache-hertzbeat-1.6.0-incubating-src.tar.gz.sha512
apache-hertzbeat-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-1.6.0-incubating-bin.tar.gz.asc
apache-hertzbeat-1.6.0-incubating-bin.tar.gz.sha512
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz.asc
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz.sha512
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.asc
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.sha512
@@ -290,6 +294,8 @@ apache-hertzbeat-1.6.0-incubating-src.tar.gz
apache-hertzbeat-1.6.0-incubating-src.tar.gz: OK
apache-hertzbeat-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-1.6.0-incubating-bin.tar.gz: OK
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz: OK
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz: OK
```
@@ -335,7 +341,7 @@ svn commit -m "release for HertzBeat 1.6.0"
- Check Apache SVN Commit Results
> Visit the address <https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1/> in the browser, check if existed the new material package
> Visit the address <https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/> in the browser, check if existed the new material package
## 4. Enter the community voting stage
+4 -3
View File
@@ -20,9 +20,10 @@ sidebar_label: Download
Previous releases of HertzBeat may be affected by security issues, please use the latest one.
:::
| Version | Date | Download | Release Notes |
|---------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| v1.6.1 | 2024.10.29 | [apache-hertzbeat-1.6.1-incubating-bin.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz) (HertzBeat) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz) (HertzBeat Collector) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz) (HertzBeat Source) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz) (docker-compose) ( [signature](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.asc) , [sha512](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.1)|
| Version | Date | Download | Release Notes |
| ------- | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| v1.7.0 | 2025.04.02 | [apache-hertzbeat-1.7.0-incubating-bin.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz) (HertzBeat) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz) (HertzBeat Collector) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.7.0-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz) (HertzBeat Source) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz) (docker-compose) ( [signature](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz.asc) , [sha512](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.7.0) |
| v1.6.1 | 2024.10.29 | [apache-hertzbeat-1.6.1-incubating-bin.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz) (HertzBeat) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz) (HertzBeat Collector) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz) (HertzBeat Source) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz) (docker-compose) ( [signature](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.asc) , [sha512](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.1) |
| v1.6.0 | 2024.06.10 | [apache-hertzbeat-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz) (HertzBeat) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz) (HertzBeat Collector) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.0-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz) (HertzBeat Source) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.0) |
## Release Docker Image
+57
View File
@@ -0,0 +1,57 @@
---
id: alarm_center
title: Alarm Center
sidebar_label: Alarm Center
keywords:
[open-source monitoring system, alarm center, alarm management, alarm display]
---
> The Alarm Center serves as a comprehensive visualization platform that displays all alarms after undergoing grouping, consolidation, suppression, and silencing processes. It encompasses both internally triggered threshold-based alarms and integrated third-party notifications.
## Alarm Sources
The HertzBeat Alarm Center manages notifications from two primary sources:
1. Internal Threshold-Triggered Alarms
- Generated when monitoring metrics exceed predefined thresholds
- Directly correlated with monitoring tasks and threshold rules configured within the system
- Manageable through adjustment of monitoring parameters and threshold configurations
2. Third-Party Integrated Alarms
- Received through API interfaces from external systems
- Compatible with various monitoring systems and alarm platforms
- Processed through identical workflow as internal alarms
## Alarm Processing Mechanism
Before appearing in the Alarm Center, all notifications undergo several processing stages:
1. Grouping
- Categorizes related alarms based on source, type, severity, and other attributes (labels)
- Facilitates efficient management of high-volume alarms
- Supports customizable grouping rules for diverse scenarios
2. Consolidation
- Mitigates notification fatigue from multiple similar alarms within short intervals
- Presents consolidated alarms in a streamlined format, eliminating redundancy
3. Suppression
- Manages alarm dependencies
- Suppresses secondary alarms when primary alarms are triggered
- Supports configurable suppression rules based on alarm dependencies
4. Silencing
- Temporarily mutes specific alarms during designated periods
- Ideal for system maintenance windows and known issue handling
- Enables time-based silence rule configuration
## Alarm Center Interface
![alarm_center](/img/docs/help/alarm-center-1.png)
The Alarm Center provides a comprehensive view of all system alarms:
1. Alarm Display
- Lists all alarms with crucial information including status, source, labels, and timestamps
- Offers detailed view functionality for comprehensive alarm information and context
2. Search Functionality
- Enables rapid alarm identification
- Supports multiple search criteria (labels, annotations, alarm status)
3. Alarm Management
- Alarm Deletion: Removes alarms no longer requiring attention
+24
View File
@@ -0,0 +1,24 @@
---
id: alarm_group
title: Alarm Grouping
sidebar_label: Alarm Grouping
keywords: [Open source monitoring system, alarm reduce, alarm grouping]
---
> Group convergence supports grouping and convergence of alarms for specified packet labels, deduplication and convergence of the same repeated alarms for the time period. When the threshold rule triggers the alarm or external alarm reporting, it will enter the packet convergence to alarm grouping to deduplicate the alarm to avoid a large number of alarm messages causing alarm storms.
## Grouping Policy Parameter Configuration
- Strategy Name: The name that uniquely identifies the grouping policy
- Group Labels: Alarm grouping tag, support up to 10 tags
> Tag source: monitoring, threshold rules, tags carried by external alarms
- Wait Time: Waiting time after a new alarm is generated. The same alarms received during this time will be grouped, with a default of 30 seconds.
> When a new (unable to join an existing group) alarm is generated, the group convergence will wait according to the `wait time`, during which time, the same alarm or the alarm that meets the grouping conditions will be grouped. The alarm after the grouping is sent to the alarm suppression module for subsequent processing until the time interval between the current time and the first alarm generation in the packet exceeds the `wait time`.
- Interval time: The minimum time interval for sending group alarm notifications to avoid excessive alarm notifications, default 5 minutes
- Repeat interval: The minimum notification interval for repeated alarms. For continuously triggered alarms, avoid repeated notifications, default 4 hours
**Note**: Only grouped alarms can be suppressed using suppression rules.
+45
View File
@@ -0,0 +1,45 @@
---
id: alarm_inhibit
title: Alarm Inhibition
sidebar_label: Alarm Inhibition
keywords: [ Open Source Monitoring System, Alarm Convergence, Alarm Inhibition ]
---
> Alarm inhibition is used to configure the inhibition relationship between alarms. When an alarm occurs, other alarms can be suppressed. It can be understood as "important" alarms suppressing "
> unimportant" alarms. For example, the alarm generated by a server crash suppresses the alarms generated by other services on this server.
## Prerequisites
- Correctly configure the alarm grouping rule
## Inhibit rule configuration
- Inhibit Rule Name: The name that uniquely identifies the suppression rule
- Source Labels: When the alarm contains these tags, the target alarm will be suppressed. Multiple tags can be added.
> Identify the tag of the "important" alarm. The alarm tag needs to contain all source tags to suppress the alarm marked by the target tag.
- Target Labels: Alarms matching these tags will be suppressed.
> Identify the label of "unimportant" alarms. Alarm labels need to contain all target labels to be suppressed.
- Equal Labels: Labels for determining alarm correlation. Supports up to 10 labels.
- Enabled: Enable or disable this inhibit rule
## Example
Scenario: Use Hertzbeat to monitor two Centos servers 192.168.1.1, 192.168.1.2, and Redis services Redis-1 and Redis-2 deployed on the two servers.
And configure the following threshold rules:
- Monitor Centos Linux / Monitor availability. Bind label `server-status:down`
- Monitor Redis database / Monitor availability. Bind label `redis-status:down`
If you need to achieve that when the Centos downtime alarm is generated, the Redis alarm will no longer be generated, you can configure the following alarm suppression rules:
- Source label: `server-status:down`
- Target label: `redis-status:down`
- Equal label: `instancehost`
When the Centos 192.168.1.1 downtime alarm is generated, the Redis-1 unavailable alarm will no longer be generated. And at the same time, when Centos 192.168.1.2 is running normally and Redis-2 is
unavailable, the alarm notifying Redis-2 unavailable will be generated normally.
+6 -6
View File
@@ -1,15 +1,15 @@
---
id: api
title: Monitoring HTTP API
sidebar_label: HTTP API
keywords: [open source monitoring tool, monitoring http api]
id: api
title: Monitoring HTTP API
sidebar_label: HTTP API
keywords: [ open source monitoring tool, monitoring http api ]
---
> Call HTTP API interface, check whether the interface is available, and monitor its response time and other Metrics.
### Configuration parameter
| Parameter name | Parameter help description |
| Parameter name | Parameter help description |
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Monitoring Host | Monitored IPV4, IPV6 or domain name. Note⚠️Without protocol header (eg: https://, http://) |
| Monitoring name | Identify the name of this monitoring. The name needs to be unique |
@@ -22,7 +22,7 @@ keywords: [open source monitoring tool, monitoring http api]
| Headers | HTTP request headers |
| Params | HTTP query params, support [time expression](time_expression) |
| Content-Type | Set the resource type when carrying the BODY request body data request |
| Request BODY | Set the carry BODY request body data, which is valid when PUT or POST request method is used |
| Request BODY | Set the carry BODY request body data, which is valid when PUT or POST request method is used, support [time expression](time_expression) |
| Collection interval | Interval time of monitor periodic data collection, unit: second, and the minimum interval that can be set is 30 seconds |
| Whether to detect | Whether to detect and check the availability of monitoring before adding monitoring. Adding and modifying operations will continue only after the detection is successful |
| Description remarks | For more information about identifying and describing this monitoring, users can note information here |
+1 -1
View File
@@ -9,7 +9,7 @@ keywords: [bulletin, custom]
### Add Bulletin Item
1. Click `Add New Bulletin Item`, enter the `Bulletin Name`, scroll down to select the `Monitoring Type`, select the associated `Monitoring Task Name`, and then select the `Monitoring Metrics` you want to show in the shuttle box.
1. Click `Add New Bulletin Item`, enter the `Bulletin Name`, scroll down to select the `Monitoring Type`, select the associated `Monitoring Task Name`, You can filter `Monitor Task Name` by `Label`, and then select the `Monitoring Metrics` you want to show in the shuttle box.
2. Click `OK` button to finish creating the customized bulletin.
+131
View File
@@ -0,0 +1,131 @@
---
id: collector
title: HertzBeat Collector
sidebar_label: Collector
keywords: [monitoring, observability, collector, metrics]
---
> HertzBeat Collector is a lightweight data collection module that enables metrics collection, high availability deployments, and cloud-edge collaboration in Apache HertzBeat (incubating).
## Introduction
HertzBeat Collector is a versatile and lightweight metrics collection module within the Apache HertzBeat monitoring system. It's designed to gather monitoring data from various targets and send the collected metrics to the main HertzBeat server for processing, alerting, and visualization.
With the collector module, you can implement:
- **High Availability**: Deploy multiple collectors to ensure continuous monitoring even if some collector instances fail
- **Load Balancing**: Distribute monitoring tasks across multiple collectors to improve performance
- **Cloud-Edge Collaboration**: Monitor resources in isolated networks while managing everything from a central HertzBeat server
## Collector Architecture
The collector module is built with a modular design to make it easily extensible for various monitoring scenarios. The architecture consists of:
1. **Collector Entry Point**: The main entry point for running the collector module, from which collection tasks are executed after startup.
2. **collector-basic**: Contains implementations for common protocols like HTTP, JDBC, SSH, SNMP, etc. These collectors typically don't require additional proprietary dependencies and can handle most basic monitoring needs.
3. **collector-common**: Provides general utility classes and methods, such as connection pools and caching mechanisms that other modules can reuse.
4. **collector-xxx**: Extension modules for specific services or protocols (MongoDB, RocketMQ, Kafka, NebulaGraph, etc.). These modules often require specific dependencies for their respective services.
## Supported Protocols
HertzBeat Collector supports an extensive list of monitoring protocols:
| Protocol Category | Protocols |
| ----------------- | ------------------------------------------------------------------------------------- |
| Web/API | `http`, `ssl_cert`, `websocket` |
| Databases | `jdbc`, `redis`, `mongodb`, `memcached` |
| Operating Systems | `ssh`, `ipmi` |
| Network | `icmp` (ping), `telnet`, `snmp`, `modbus` |
| Messaging | `mqtt`, `rocketmq`, `kafka` |
| Email | `pop3`, `smtp`, `imap` |
| Cloud Services | `prometheus`, `nebulagraph`, `ngql` |
| Others | `jmx`, `dns`, `ftp`, `ntp`, `udp`, `nginx`, `redfish`, `script`, `registry`, `httpsd` |
## Deployment Options
You can deploy HertzBeat Collector in several ways depending on your environment and needs, once you log in to the HertzBeat web interface and go to the collector, you can see the deployment options.
Parameters explanation:
- `-e IDENTITY=custom-collector-name`: (Optional) Set a unique identifier for this collector. Must be unique across all collectors.
- `-e MODE=public`: Set the running mode (public or private), for public cluster or private cloud-edge mode.
- `-e MANAGER_HOST=192.168.1.100`: Important! Set the IP address of the main HertzBeat server. Replace with your actual server IP.
- `-e MANAGER_PORT=1158`: (Optional) Set the port of the main HertzBeat server, default is 1158.
- `-v $(pwd)/logs:/opt/hertzbeat-collector/logs`: (Optional) Mount the log files to the local host.
## Operating Modes
HertzBeat Collector supports two operating modes:
### Public Mode (Cluster Mode)
In public mode, collectors form a cluster with the main HertzBeat server. Tasks are automatically distributed among collectors, providing high availability and load balancing.
- Set `MODE=public` when deploying the collector
- All collectors must have connectivity to the main HertzBeat server
- Great for horizontal scaling to handle large numbers of monitoring tasks
### Private Mode (Cloud-Edge Mode)
In private mode, collectors operate in isolated networks while still reporting to a central HertzBeat server. This allows monitoring of resources in multiple separate networks.
- Set `MODE=private` when deploying the collector
- Collectors need outbound connectivity to the HertzBeat server, but inbound connectivity is not required
- Ideal for monitoring resources across different data centers, cloud providers, or network segments
## Configuration Parameters
| Parameter | Description | Default |
| -------------- | ----------------------------------- | ------------------------- |
| `identity` | Unique identifier for the collector | Auto-generated if not set |
| `mode` | Operating mode (public/private) | public |
| `manager-host` | IP address of the HertzBeat server | IP |
| `manager-port` | Port of the HertzBeat server | 1158 |
## Collector Management
You can manage collectors through the HertzBeat web interface:
1. Navigate to the Overview page to see all registered collectors
2. Monitor collector status (online/offline), metrics tasks, and system information
3. Enable or disable collectors as needed
## High Availability Setup
To achieve high availability with HertzBeat collectors:
1. Deploy multiple collector instances across different servers or containers
2. Ensure all collectors have the same `mode` setting
3. Connect all collectors to the same HertzBeat server
4. HertzBeat will automatically distribute monitoring tasks and handle failover
If a collector goes offline, its tasks will be reassigned to other available collectors. When the collector comes back online, it will receive new tasks based on the current load distribution.
## Cloud-Edge Collaboration
For monitoring across isolated networks:
1. Deploy HertzBeat Server in your central management network
2. Deploy collectors in each isolated network you need to monitor
3. Configure collectors with:
- `MODE=private`
- `MANAGER_HOST=` pointing to your central HertzBeat server
4. Ensure outbound connectivity from each isolated network to the central server
5. Manage all monitoring tasks from the central HertzBeat dashboard
## Advanced Features
### Custom Protocol Support
HertzBeat's architecture allows for extending the collector with custom protocols. Developers can create new collector modules following the project's modular design.
### Task Scheduling
The collector automatically handles task scheduling based on task priority, available resources, and current system load. Tasks are processed with intelligent prioritization to ensure critical monitoring is performed first.
### Resource Utilization
Collectors are designed to be lightweight and efficient with system resources, making them suitable for deployment on various hardware, from small edge devices to powerful servers.
+9
View File
@@ -107,6 +107,8 @@ sidebar_label: Help Center
> The triggered alarm information center provides query and filtering of alarm deletion, alarm processing, mark unprocessed, alarm level status, etc.
More details see&emsp;&#x1F449;&emsp;[Alarm center](alarm_center)
### Alarm configuration
> The Metric threshold configuration provides the Metric threshold configuration in the form of expression, which can set the alarm level, trigger times, alarm notification template and whether it is enabled, correlation monitoring and other functions.
@@ -114,6 +116,13 @@ sidebar_label: Help Center
More details see&emsp;&#x1F449;&emsp;[Threshold alarm](alert_threshold) <br />
&emsp;&emsp;&emsp;&#x1F449;&emsp;[Threshold expression](alert_threshold_expr)
### Alarm reduce
> Combine related alarms through alarm grouping, alarm suppression and other functions to reduce the alarm storm caused by one event, reduce alarm noise and improve alarm response efficiency.
More details see&emsp;&#x1F449;&emsp;[Alarm grouping](alarm_group) <br />
&emsp;&emsp;&emsp;&#x1F449;&emsp;[Alarm inhibit](alarm_inhibit)
### Alarm notification
> After triggering the alarm information, in addition to being displayed in the alarm center list, it can also be notified to the designated recipient in a specified way (e-mail, wechat and FeiShu etc.)
+60
View File
@@ -0,0 +1,60 @@
---
id: hikvision_isapi
title: Monitor Hikvision ISAPI
sidebar_label: Hikvision ISAPI
keywords: [ monitor, hikvision_isapi ]
---
> Monitor Hikvision devices through ISAPI interface to collect health data.
## Monitor Configuration
| Parameter | Description |
| ----------- | ----------- |
| Host | The IP or domain name of the monitored device. Note⚠️ Do not include protocol prefix (eg: https://, http://). |
| Name | The unique name that identifies this monitor. |
| Port | Network request port, default is 80. |
| Timeout | Timeout period, in milliseconds, default is 5000ms. |
| Username | Login username for Hikvision device. |
| Password | Login password for Hikvision device. |
| SSL | Whether to enable HTTPS, disabled by default. |
| Collection Interval | The interval time for periodic data collection, in seconds. The minimum interval is 30 seconds. |
## Metrics
### System Info
- Device Name
- Device ID
- Firmware Version
- Device Model
- Mac Address
### Status
- CPU Utilization (%)
- Memory Usage (MB)
- Memory Available (MB)
- Cache Size (MB)
- Net Port 1 Speed (Mbps)
- Net Port 2 Speed (Mbps)
- Boot Time
- Device Uptime
- Last Calibration Time
- Last Calibration Time Diff (s)
- Avg Upload Time (ms)
- Max Upload Time (ms)
- Min Upload Time (ms)
- Last Calibration Mode
- Last Calibration Address
- Response Time (ms)
## Implementation Principle
The monitoring is implemented by accessing the Hikvision device's ISAPI interface:
1. Collect system information through: `/ISAPI/System/deviceInfo`
2. Collect device status through: `/ISAPI/System/status`
It uses HTTP protocol with Digest Authentication to access the interfaces and parses XML response data to extract monitoring metrics.
+1 -1
View File
@@ -73,4 +73,4 @@ Use advanced settings when adding `SqlServer` monitoring, customize JDBC URL, ad
Example: ```jdbc:sqlserver://127.0.0.1:1433;DatabaseName=demo;encrypt=true;trustServerCertificate=true;```
Reference document: [microsoft pkix-path-building-failed-unable-to-find-valid-certification](<https://techcommunity.microsoft.com/t5/azure-database-support-blog/pkix-path-building-> failed-unable-to-find-valid-certification/ba-p/2591304)
Reference document: [microsoft pkix-path-building-failed-unable-to-find-valid-certification](https://techcommunity.microsoft.com/t5/azure-database-support-blog/pkix-path-building-failed-unable-to-find-valid-certification/ba-p/2591304)
+17 -17
View File
@@ -16,23 +16,23 @@ sidebar_label: Template Marketplace
1. **No filter: displayed in order of upload**
![search.png](template-marketplace-img/search.png)
![search.png](../../static/img/template-marketplace/search.png)
2. **Filtering by category: currently divided into six categories**
> **📋Todo:** develop tag function, subdivided within the category, such as database monitoring template can be divided into MySQL, Oracle, etc.
![search-category.png](template-marketplace-img/search-category.png)
![search-category.png](../../static/img/template-marketplace/search-category.png)
3. **Fuzzy search by Title**
![search-name.png](template-marketplace-img/search-name.png)
![search-name.png](../../static/img/template-marketplace/search-name.png)
4. **Hover window function: download the latest version, view details, favorite/un-favorite**
> Show if the user has favorites after logging in
![img.png](template-marketplace-img/hover-window-guest.png)![img_1.png](template-marketplace-img/hover-window-user.png)
![img.png](../../static/img/template-marketplace/hover-window-guest.png)![img_1.png](../../static/img/template-marketplace/hover-window-user.png)
5. **Sort: Eight Sorting Methods**
@@ -48,19 +48,19 @@ sidebar_label: Template Marketplace
> **📋Todo:** Upgrade to MarkDown format
![img.png](template-marketplace-img/detail-info.png)
![img.png](../../static/img/template-marketplace/detail-info.png)
2. **Version: Historical version download, sharing and basic information display**
> **📋Todo:** Set up a view function for each historical version to display information such as the version description.
![img.png](template-marketplace-img/detail-version.png)
![img.png](../../static/img/template-marketplace/detail-version.png)
3. **FAQ**
> **📋Todo:** Discussion or issue Q&A section
![img.png](template-marketplace-img/detail-faq.png)
![img.png](../../static/img/template-marketplace/detail-faq.png)
4. **Download**
@@ -68,7 +68,7 @@ sidebar_label: Template Marketplace
> The latest version can also be downloaded directly from the template detail page.
> The historical version can be downloaded from the version page.
![img.png](template-marketplace-img/download.png)
![img.png](../../static/img/template-marketplace/download.png)
5. **Share**
@@ -78,7 +78,7 @@ sidebar_label: Template Marketplace
>
> **📋Todo:** Shared template detail page is accessed through the URL of the shared template, and the shared person is free to choose whether to download or not.
![img.png](template-marketplace-img/share.png)
![img.png](../../static/img/template-marketplace/share.png)
### User Center
@@ -94,21 +94,21 @@ sidebar_label: Template Marketplace
>
> **📋Todo:** Function to update template information
![img_1.png](template-marketplace-img/user-center-asset.png)
![img_1.png](../../static/img/template-marketplace/user-center-asset.png)
![img.png](template-marketplace-img/asset-detail.png)
![img.png](../../static/img/template-marketplace/asset-detail.png)
2. **Version Upgrade**
> The user defines the new version number under this template family, updates the version information, and uploads the latest version of the file
![img.png](template-marketplace-img/asset-upgrade.png)
![img.png](../../static/img/template-marketplace/asset-upgrade.png)
3. **Star**
![img.png](template-marketplace-img/user-center-star.png)
![img.png](../../static/img/template-marketplace/user-center-star.png)
![img.png](template-marketplace-img/user-center-star-detail.png)
![img.png](../../static/img/template-marketplace/user-center-star-detail.png)
4. **Upload**
@@ -116,7 +116,7 @@ sidebar_label: Template Marketplace
>
> Fill in the template name, select the template category, fill in the description information and version information, and upload files
![img.png](template-marketplace-img/user-center-upload.png)
![img.png](../../static/img/template-marketplace/user-center-upload.png)
### Sign Up & Login
@@ -128,13 +128,13 @@ sidebar_label: Template Marketplace
>
> **📋Todo:** Captcha function, email verification function
![img.png](template-marketplace-img/sign-up.png)
![img.png](../../static/img/template-marketplace/sign-up.png)
2. **Login**
> **📋Todo:** Captcha function and forgot password function
![img.png](template-marketplace-img/email-login.png)
![img.png](../../static/img/template-marketplace/email-login.png)
## Development Steps
+1
View File
@@ -53,6 +53,7 @@ ${FORMATTER [{ + | - }<DURATION> <TIME_UNIT>]}
#### Where to Use
- Request parameters for HTTP protocol monitoring types
- Request Body for HTTP protocol monitoring types
#### Usage Examples
@@ -1,6 +1,6 @@
{
"version.label": {
"message": "v1.6.x",
"message": "v1.7.x",
"description": "The label for version current"
},
"sidebar.docs.category.quickstart": {
@@ -51,6 +51,10 @@
"message": "Threshold Alarm Setting",
"description": "The label for category threshold in sidebar docs"
},
"sidebar.docs.category.reduce": {
"message": "Alarm Reduce Setting",
"description": "The label for category reduce in sidebar docs"
},
"sidebar.docs.category.notice": {
"message": "Alarm Notice Setting",
"description": "The label for category notice in sidebar docs"
@@ -1,122 +0,0 @@
{
"version.label": {
"message": "v1.5.x",
"description": "The label for version v1.5.x"
},
"sidebar.docs.category.quickstart": {
"message": "quickstart",
"description": "The label for category quickstart in sidebar docs"
},
"sidebar.docs.category.custom": {
"message": "custom",
"description": "The label for category custom in sidebar docs"
},
"sidebar.docs.category.http": {
"message": "http",
"description": "The label for category http in sidebar docs"
},
"sidebar.docs.category.jdbc": {
"message": "jdbc",
"description": "The label for category jdbc in sidebar docs"
},
"sidebar.docs.category.ssh": {
"message": "ssh",
"description": "The label for category ssh in sidebar docs"
},
"sidebar.docs.category.jmx": {
"message": "jmx",
"description": "The label for category jmx in sidebar docs"
},
"sidebar.docs.category.snmp": {
"message": "snmp",
"description": "The label for category snmp in sidebar docs"
},
"sidebar.docs.category.push": {
"message": "push",
"description": "The label for category push in sidebar docs"
},
"sidebar.docs.category.NGQL": {
"message": "NGQL",
"description": "The label for category NGQL in sidebar docs"
},
"sidebar.docs.category.Community": {
"message": "Community",
"description": "The label for category Community in sidebar docs"
},
"sidebar.docs.category.contribution": {
"message": "contribution",
"description": "The label for category contribution in sidebar docs"
},
"sidebar.docs.category.submit": {
"message": "submit",
"description": "The label for category submit in sidebar docs"
},
"sidebar.docs.category.release": {
"message": "release",
"description": "The label for category release in sidebar docs"
},
"sidebar.docs.category.help": {
"message": "help",
"description": "The label for category help in sidebar docs"
},
"sidebar.docs.category.service": {
"message": "service",
"description": "The label for category service in sidebar docs"
},
"sidebar.docs.category.program": {
"message": "program",
"description": "The label for category program in sidebar docs"
},
"sidebar.docs.category.database": {
"message": "database",
"description": "The label for category database in sidebar docs"
},
"sidebar.docs.category.cache": {
"message": "cache",
"description": "The label for category cache in sidebar docs"
},
"sidebar.docs.category.os": {
"message": "os",
"description": "The label for category os in sidebar docs"
},
"sidebar.docs.category.mid": {
"message": "mid",
"description": "The label for category mid in sidebar docs"
},
"sidebar.docs.category.bigdata": {
"message": "bigdata",
"description": "The label for category bigdata in sidebar docs"
},
"sidebar.docs.category.webserver": {
"message": "webserver",
"description": "The label for category webserver in sidebar docs"
},
"sidebar.docs.category.cloud-native": {
"message": "cloud-native",
"description": "The label for category cloud-native in sidebar docs"
},
"sidebar.docs.category.llm": {
"message": "llm",
"description": "The label for category llm in sidebar docs"
},
"sidebar.docs.category.network": {
"message": "network",
"description": "The label for category network in sidebar docs"
},
"sidebar.docs.category.threshold": {
"message": "threshold",
"description": "The label for category threshold in sidebar docs"
},
"sidebar.docs.category.notice": {
"message": "notice",
"description": "The label for category notice in sidebar docs"
},
"sidebar.docs.category.Others": {
"message": "Others",
"description": "The label for category Others in sidebar docs"
},
"sidebar.docs.link.Install via Helm": {
"message": "Install via Helm",
"description": "The label for link Install via Helm in sidebar docs, linking to https://artifacthub.io/packages/helm/hertzbeat/hertzbeat"
}
}
@@ -48,7 +48,7 @@
"description": "The label of footer link with label=contact linking to /docs/community/contact"
},
"copyright": {
"message": "\n <div style=\"text-align: left;margin-top:30px\">\n <div style=\"align-items: center; display: flex\">\n <div style=\"width: 1200px; background-color: #282c77; padding: 10px; border-radius: 6px\">\n <a href=\"https://incubator.apache.org/\">\n <img src=\"/img/icons/apache-incubator.svg\" alt=\"Apache Incubator logo\">\n </a>\n </div>\n <div style=\"margin-left: 40px\">\n <p style=\"font-size: 14px;line-height: 25px;\">\n Apache HertzBeat is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.\n </p>\n </div>\n </div>\n\n <div style=\"border-top: 1px solid #525252;min-height: 60px;line-height: 25px;text-align: left;font-size: 14px;display: flex;align-items: center;\">\n <span>\n Copyright © 2024 The Apache Software Foundation. Apache HertzBeat, HertzBeat, and its feather logo are trademarks of The Apache Software Foundation.\n </span>\n </div>\n </div>",
"message": "\n <div style=\"text-align: left;margin-top:30px\">\n <div style=\"align-items: center; display: flex\">\n <div style=\"width: 1200px; background-color: #282c77; padding: 10px; border-radius: 6px\">\n <a href=\"https://incubator.apache.org/\">\n <img src=\"/img/icons/apache-incubator.svg\" alt=\"Apache Incubator logo\">\n </a>\n </div>\n <div style=\"margin-left: 40px\">\n <p style=\"font-size: 14px;line-height: 25px;\">\n Apache HertzBeat is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.\n </p>\n </div>\n </div>\n\n <div style=\"border-top: 1px solid #525252;min-height: 60px;line-height: 25px;text-align: left;font-size: 14px;display: flex;align-items: center;\">\n <span>\n Copyright © 2024-2025 The Apache Software Foundation. Apache HertzBeat, HertzBeat, and its feather logo are trademarks of The Apache Software Foundation.\n </span>\n </div>\n </div>",
"description": "The footer copyright"
}
}
@@ -1,6 +1,6 @@
{
"version.label": {
"message": "v1.6.x",
"message": "v1.7.x",
"description": "The label for version current"
},
"sidebar.docs.category.quickstart": {
@@ -55,6 +55,10 @@
"message": "阈值告警配置",
"description": "The label for category threshold in sidebar docs"
},
"sidebar.docs.category.reduce": {
"message": "告警收敛配置",
"description": "The label for category reduce in sidebar docs"
},
"sidebar.docs.category.notice": {
"message": "告警通知配置",
"description": "The label for category notice in sidebar docs"
@@ -128,11 +132,11 @@
"description": "The label for category NGQL in sidebar docs"
},
"sidebar.docs.category.llm": {
"message": "Ai大模型监控",
"message": "AI大模型监控",
"description": "The label for category llm in sidebar docs"
},
"sidebar.docs.category.aiConfig": {
"message": "Ai大模型配置",
"message": "AI大模型配置",
"description": "The label for category aiConfig in sidebar docs"
},
"sidebar.docs.category.install": {
@@ -0,0 +1,258 @@
---
id: extend-http-xmlpath
title: HTTP 协议 XmlPath 解析方法
sidebar_label: XmlPath 解析方法
---
> 调用 HTTP API 获取响应数据后,使用 XmlPath 脚本解析方法解析响应数据。
注意⚠️ 响应数据必须是 XML 格式。
**使用 XPath 脚本将响应数据解析为符合 HertzBeat 指定的数据结构规则的数据。**
### XmlPath 解析逻辑
HertzBeat 中的 XmlPath 解析方法使用两步 XPath 处理:
1. **主 XPath 表达式 (`parseScript`)**: 此 XPath 表达式在 `http` 配置部分的 `parseScript` 中定义。它用于从响应中选择一个或多个主要的 XML 节点。每个选中的节点将对应 HertzBeat 中的一行指标数据。
2. **相对字段 XPath 表达式 (`xpath`)**: 对于在 `fields` 列表中定义的每个指标字段,您可以指定一个相对的 `xpath`。此 XPath 表达式是*相对于*步骤 1 中 `parseScript` 选择的每个主节点进行评估的。它从当前主节点中提取该指标字段的具体值。
这使您可以轻松地解析包含多个记录或项目的结构化 XML 数据。
**特殊指标**:
* `responseTime`: 这个内置指标代表 HTTP 请求的响应时间,是自动收集的。它不需要 `xpath`
* `keyword`: 这个内置指标计算原始响应体中指定关键字(在 `http.keyword` 中配置)的出现次数。它不需要 `xpath`
### 示例
假设 HTTP API 返回以下 XML 数据:
```xml
<DeviceStatus xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
<CPUList>
<CPU>
<cpuUtilization>36.400002</cpuUtilization>
</CPU>
</CPUList>
<MemoryList>
<Memory>
<memoryUsage>399640</memoryUsage>
<memoryAvailable>98792</memoryAvailable>
<cacheSize>228492</cacheSize>
</Memory>
</MemoryList>
<NetPortStatusList>
<NetPortStatus>
<id>1</id>
<workSpeed>1000</workSpeed>
</NetPortStatus>
<NetPortStatus>
<id>2</id>
<workSpeed>0</workSpeed>
</NetPortStatus>
</NetPortStatusList>
<bootTime>2025-01-06 10:27:48</bootTime>
<deviceUpTime>87天0时55分59秒</deviceUpTime>
<lastCalibrationTime>2025-04-03 11:09:18</lastCalibrationTime>
<lastCalibrationTimeDiff>1</lastCalibrationTimeDiff>
<uploadTimeConsumingList>
<avgTime>16</avgTime>
<maxTime>23</maxTime>
<minTime>12</minTime>
</uploadTimeConsumingList>
<lastCalibrationTimeMode>NTP</lastCalibrationTimeMode>
<lastCalibrationTimeAddress>34.191.45.101</lastCalibrationTimeAddress>
</DeviceStatus>
```
我们想要监控设备状态并提取各种指标。
以下是您将如何配置监控模板 YML
```yaml
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: status
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: cpuUtilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
xpath: CPUList/CPU/cpuUtilization
- field: memoryUsage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
xpath: MemoryList/Memory/memoryUsage
- field: memoryAvailable
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
xpath: MemoryList/Memory/memoryAvailable
- field: cacheSize
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
xpath: MemoryList/Memory/cacheSize
- field: netPort1Speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
- field: netPort2Speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
- field: bootTime
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
xpath: bootTime
- field: deviceUpTime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
xpath: deviceUpTime
- field: lastCalibrationTime
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
xpath: lastCalibrationTime
- field: lastCalibrationTimeDiff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
xpath: lastCalibrationTimeDiff
- field: avgUploadTime
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/avgTime
- field: maxUploadTime
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/maxTime
- field: minUploadTime
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
xpath: uploadTimeConsumingList/minTime
- field: lastCalibrationMode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
xpath: lastCalibrationTimeMode
- field: lastCalibrationAddress
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
xpath: lastCalibrationTimeAddress
- field: responseTime
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
units:
- memoryUsage=KB->MB
- memoryAvailable=KB->MB
- cacheSize=KB->MB
@@ -65,8 +65,8 @@ HertzBeat 包将生成为 `dist/hertzbeat-{version}.tar.gz`
2. 在项目根目录运行: `mvn clean install`
3. 切换到 `collector` 目录: `cd collector`
3. 切换到 `hertzbeat-collector` 目录: `cd hertzbeat-collector`
4.`collector` 目录下执行: `mvn clean package -Pcluster`
4.`hertzbeat-collector` 目录下执行: `mvn clean package -Pcluster`
HertzBeat 采样器包将生成为 `dist/hertzbeat-collector-{version}.tar.gz`
@@ -215,6 +215,7 @@ mvn clean package -Pcluster
- `dist/apache-hertzbeat-{version}-incubating-bin.tar.gz`
- `dist/apache-hertzbeat-collector-{version}-incubating-bin.tar.gz`
- `dist/apache-hertzbeat-{version}-incubating-docker-compose.tar.gz`
#### 3.4 打包项目源代码
@@ -259,6 +260,9 @@ apache-hertzbeat-1.6.0-incubating-src.tar.gz.sha512
apache-hertzbeat-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-1.6.0-incubating-bin.tar.gz.asc
apache-hertzbeat-1.6.0-incubating-bin.tar.gz.sha512
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz.asc
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz.sha512
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.asc
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.sha512
@@ -292,6 +296,8 @@ apache-hertzbeat-1.6.0-incubating-src.tar.gz
apache-hertzbeat-1.6.0-incubating-src.tar.gz: OK
apache-hertzbeat-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-1.6.0-incubating-bin.tar.gz: OK
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz
apache-hertzbeat-1.6.0-incubating-docker-compose.tar.gz: OK
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz
apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz: OK
```
@@ -22,6 +22,7 @@ sidebar_label: Download
| 版本 | 日期 | 下载 | Release Notes |
|--------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| v1.7.0 | 2025.04.02 | [apache-hertzbeat-1.7.0-incubating-bin.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz) (HertzBeat 主程序) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz) (HertzBeat 采集器) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-collector-1.7.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.7.0-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz) (HertzBeat 源代码) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-src.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz) (docker-compose) ( [signature](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz.asc) , [sha512](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.7.0/apache-hertzbeat-1.7.0-incubating-docker-compose.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.7.0) |
| v1.6.1 | 2024.10.29 | [apache-hertzbeat-1.6.1-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz) (HertzBeat 主程序) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz) (HertzBeat 采集器) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-collector-1.6.1-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz) (HertzBeat 源代码包) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-src.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz) (docker-compose) ( [signature](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.asc) , [sha512](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.1/apache-hertzbeat-1.6.1-incubating-docker-compose.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.1) |
| v1.6.0 | 2024.06.10 | [apache-hertzbeat-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz) (HertzBeat 主程序) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz) (HertzBeat 采集器) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.0-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz) (HertzBeat 源代码包) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.0) |
@@ -0,0 +1,56 @@
---
id: alarm_center
title: 告警中心
sidebar_label: 告警中心
keywords: [开源监控系统, 告警中心, 告警管理,告警显示]
---
> 告警中心是一个展示平台,用于显示所有经过分组、收敛、抑制、静默等处理的告警,包括由内部系统阈值触发的告警和第三方接入告警。
## 告警来源
HertzBeat 的告警中心管理来自两个主要来源的告警:
1. 系统内部阈值触发的告警
- 当监控指标超过预定义阈值时生成
- 与系统中配置的监控任务和阈值规则直接相关
- 可以通过调整监控任务和阈值设置进行控制
2. 第三方接入告警
- 通过 API 接口从外部系统接收
- 支持与其他监控系统或告警平台集成
- 与内部告警一样经过相同的处理流程
## 告警处理机制
在显示到告警中心之前,所有告警都会经过几个处理步骤:
1. 分组
- 基于来源、类型、严重程度和其他属性(标签)对相关告警进行分类
- 帮助高效管理大量告警
- 支持针对不同场景的自定义分组规则
2. 收敛
- 减少短时间内发生的多个类似告警产生的干扰
- 以更简洁的方式呈现收敛后的告警,避免信息冗余
3. 抑制
- 处理告警之间的依赖关系
- 当关键告警触发时,可以抑制相关的次要告警
- 支持配置定义告警依赖关系的抑制规则
4. 静默
- 在特定时间段内暂时屏蔽某些告警
- 适用于系统维护、已知问题处理等场景
- 可以基于时间设置静默规则
## 告警中心界面
![alarm_center](/img/docs/help/alarm-center-1.png)
告警中心提供了系统所有告警的全面视图:
1. 告警显示
- 列出所有告警,包含告警状态、来源、标签和时间等关键信息
- 提供详细视图功能,显示完整的告警信息和上下文
2. 搜索功能
- 帮助快速定位特定告警
- 支持多种搜索(标签、注解、告警状态等)
3. 告警管理
- 告警删除:移除不再需要关注的告警
@@ -0,0 +1,24 @@
---
id: alarm_group
title: 分组收敛
sidebar_label: 分组收敛
keywords: [ 开源监控系统, 告警收敛, 告警分组 ]
---
> 分组收敛支持对指定分组标签的告警进行分组合并,对时间段的相同重复告警去重收敛。 当阈值规则触发告警或外部告警上报后,会进入到分组收敛进行告警分组,告警去重,以避免大量告警消息导致告警风暴。
## 分组策略参数配置
- 策略名称:唯一标识分组策略的名称
- 分组标签:告警分组标签,最多支持添加10个标签
> 标签来源:监控,阈值规则,外部告警携带的标签
- 等待时间:新告警产生后等待时间,在此时间内收到的相同告警将被分组,默认30秒
> 当一条新(无法加入已有分组)的告警产生,分组收敛将按照 `等待时间` 等待,在此期间,相同告警或满足分组条件的告警将被分组。直到当前时间与该分组第一条告警产生时间间隔超过 `等待时间`,分组后的告警才被发送到告警抑制模块进行后续处理。
- 间隔时间:发送分组告警通知的最小时间间隔,避免告警通知过于频繁,默认5分钟
- 重复间隔:重复告警的最小通知间隔,对于持续触发的告警,避免重复发送通知,默认4小时
**注意**:只有分组后的告警才能使用抑制规则进行告警抑制。
@@ -0,0 +1,38 @@
---
id: alarm_inhibit
title: 告警抑制
sidebar_label: 告警抑制
keywords: [ 开源监控系统, 告警收敛, 告警抑制 ]
---
> 告警抑制用于配置告警之间的抑制关系。当某个告警发生时,可以抑制其他告警的产生,可以理解为“重要”告警抑制“不重要”告警的产生,例如一台服务器宕机产生的告警抑制这台服务器上其他服务产生的告警。
## 前置条件
- 正确配置分组收敛规则
## 抑制规则配置
- 抑制规则名称: 唯一标识抑制规则的名称;
- 源标签: 当告警包含这些标签时,将会抑制目标告警,支持添加多个标签;
> 识别“重要”告警的标签,告警标签需要包含全部源标签才会抑制被目标标签标记的告警。
- 目标标签: 匹配这些标签的告警将被抑制;
> 识别“不重要”告警的标签,告警标签需要包含全部目标标签才会被抑制。
- 相等标签: 判断告警相关性的标签。支持最多10个标签;
- 启用状态: 启用或禁用该抑制规则。
## 示例
场景: 使用 Hertzbeat 监控 两个 Centos 服务器 192.168.1.1 和 192.168.1.2,和部署在两个服务器上的 Redis 服务 Redis-1 和 Redis-2。
并配置如下阈值规则:
- 监控 Centos Linux /监控可用性。绑定标签 `server-status:down`
- 监控 Redis数据库 /监控可用性。绑定标签 `redis-status:down`
如果需要实现当Centos 宕机告警产生后,Redis 告警不再产生,则可以配置如下告警抑制规则:
- 源标签: `server-status:down`
- 目标标签: `redis-status:down`
- 相等标签: `instancehost`
当 Centos 192.168.1.1 宕机告警产生时,通知Redis-1 不可用的告警将不再产生。且同时 Centos 192.168.1.2 运行正常且 Redis-2 不可用时,通知 Redis-2 不可用的告警将正常产生。
@@ -1,15 +1,15 @@
---
id: api
title: 监控:HTTP API
sidebar_label: HTTP API
keywords: [开源监控系统, 开源网站监控, HTTP API监控]
id: api
title: 监控:HTTP API
sidebar_label: HTTP API
keywords: [ 开源监控系统, 开源网站监控, HTTP API监控 ]
---
> 调用HTTP API接口,查看接口是否可用,对其响应时间等指标进行监测
### 配置参数
| 参数名称 | 参数帮助描述 |
| 参数名称 | 参数帮助描述 |
|--------------|-------------------------------------------------------------------|
| 监控Host | 被监控的对端IPV4,IPV6或域名。注意⚠️不带协议头(eg: https://, http://)。 |
| 任务名称 | 标识此监控的名称,名称需要保证唯一性。 |
@@ -22,7 +22,7 @@ keywords: [开源监控系统, 开源网站监控, HTTP API监控]
| 请求Headers | HTTP 请求头 |
| 查询Params | HTTP查询参数,支持[时间表达式](time_expression) |
| Content-Type | 设置携带BODY请求体数据请求时的资源类型 |
| 请求BODY | 设置携带BODY请求体数据,PUT POST请求方式时有效 |
| 请求BODY | 设置携带BODY请求体数据,PUT POST请求方式时有效,支持[时间表达式](time_expression) |
| 采集间隔 | 监控周期性采集数据间隔时间,单位秒,可设置的最小间隔为30秒 |
| 是否探测 | 新增监控前是否先探测检查监控可用性,探测成功才会继续新增修改操作 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
@@ -31,6 +31,6 @@ keywords: [开源监控系统, 开源网站监控, HTTP API监控]
#### 指标集合:summary
| 指标名称 | 指标单位 | 指标帮助描述 |
| 指标名称 | 指标单位 | 指标帮助描述 |
|--------------|------|--------|
| responseTime | ms毫秒 | 网站响应时间 |
@@ -9,7 +9,7 @@ keywords: [看板, 自定义]
### 新增看板项
1. 点击新增看板项, 输入`看板名称`, 下拉选择`监控类型`, 再选择关联的`监控任务名称`, 最后在穿梭框中选择需要展示的`监控指标`
1. 点击新增看板项, 输入`看板名称`, 下拉选择`监控类型`, 可根据`标签``监控任务名称`进行筛选,再选择关联的`监控任务名称`, 最后在穿梭框中选择需要展示的`监控指标`
2. 点击`确定`按钮, 即可完成自定义看板的创建。
@@ -88,7 +88,7 @@ sidebar_label: 帮助入门
&emsp;&#x1F449;&emsp;[ElasticSearch](elasticsearch) <br />
&emsp;&#x1F449;&emsp;[Flink](flink) <br />
### Ai大模型监控
### AI大模型监控
&emsp;&#x1F449;&emsp;[OpenAi](openai) <br />
@@ -107,6 +107,8 @@ sidebar_label: 帮助入门
> 已触发的告警信息中心,提供告警删除,告警处理,标记未处理,告警级别状态等查询过滤。
详见&emsp;&#x1F449;&emsp;[告警中心](alarm_center)
### 告警配置
> 指标阈值配置,提供表达式形式的指标阈值配置,可设置告警级别,触发次数,告警通知模版和是否启用,关联监控等功能。
@@ -114,6 +116,13 @@ sidebar_label: 帮助入门
详见&emsp;&#x1F449;&emsp;[阈值告警](alert_threshold) <br />
&emsp;&emsp;&emsp;&#x1F449;&emsp;[阈值表达式](alert_threshold_expr)
### 告警收敛
> 通过分组收敛、告警抑制等功能合并相关告警,减少由一个事件引发的告警风暴,降低告警噪声,提升告警响应效率。
详见&emsp;&#x1F449;&emsp;[分组收敛](alarm_group) <br />
&emsp;&emsp;&emsp;&#x1F449;&emsp;[告警抑制](alarm_inhibit)
### 告警通知
> 触发告警信息后,除了显示在告警中心列表外,还可以用指定方式(邮件钉钉微信飞书等)通知给指定接收人。
@@ -0,0 +1,60 @@
---
id: hikvision_isapi
title: 监控 海康ISAPI
sidebar_label: 海康ISAPI
keywords: [ monitor, hikvision_isapi, 海康 ]
---
> 通过ISAPI接口监控海康威视设备状态,获取设备健康数据。
## 监控配置参数
| 参数名称 | 参数帮助描述 |
| ----------- | ----------- |
| 监控Host | 被监控的对端IP或域名。注意⚠️不带协议头(eg: https://, http://)。 |
| 监控名称 | 标识此监控的名称,名称需要保证唯一性。 |
| 端口 | 网络请求端口,默认为80。 |
| 超时时间 | 设置超时时间,单位ms毫秒,默认5000毫秒。 |
| 用户名 | 海康设备登录用户名。 |
| 密码 | 海康设备登录密码。 |
| 启用HTTPS | 是否启用HTTPS,默认未启用。 |
| 采集间隔 | 监控周期性采集数据间隔时间,单位秒,可设置的最小间隔为30秒 |
## 采集指标
### 系统信息
- 设备名称
- 设备ID
- 固件版本
- 设备型号
- mac地址
### 设备状态
- CPU 利用率(%)
- 内存使用量(MB)
- 可用内存(MB)
- 缓存大小(MB)
- 网口1速度(Mbps)
- 网口2速度(Mbps)
- 启动时间
- 运行时长
- 上次校时时间
- 上次校时时间差(s)
- 平均上传耗时(ms)
- 最大上传耗时(ms)
- 最小上传耗时(ms)
- 上次校时模式
- 上次校时地址
- 响应时间(ms)
## 监控实现原理
通过海康威视设备的ISAPI接口获取设备信息和状态:
1. 采集系统信息:`/ISAPI/System/deviceInfo`
2. 采集设备状态:`/ISAPI/System/status`
采用HTTP协议Digest认证方式访问接口,解析XML格式响应数据获取监控指标。
@@ -53,6 +53,7 @@ ${FORMATTER [{ + | - }<DURATION> <TIME_UNIT>]}
#### 在哪里可以使用
- HTTP协议监控类型的请求参数
- HTTP协议监控类型的请求体
#### 使用示例
@@ -41,7 +41,7 @@ Docker 工具自身的下载请参考 [Docker官网文档](https://docs.docker.c
### 创建数据库实例
> [TDengine CLI 小技巧](https://docs.taosdata.com/develop/model/)
> [TDengine CLI 小技巧](https://docs.taosdata.com/develop/)
1. 进入数据库Docker容器
@@ -1,122 +0,0 @@
{
"version.label": {
"message": "v1.5.x",
"description": "The label for version v1.5.x"
},
"sidebar.docs.category.quickstart": {
"message": "quickstart",
"description": "The label for category quickstart in sidebar docs"
},
"sidebar.docs.category.custom": {
"message": "custom",
"description": "The label for category custom in sidebar docs"
},
"sidebar.docs.category.http": {
"message": "http",
"description": "The label for category http in sidebar docs"
},
"sidebar.docs.category.jdbc": {
"message": "jdbc",
"description": "The label for category jdbc in sidebar docs"
},
"sidebar.docs.category.ssh": {
"message": "ssh",
"description": "The label for category ssh in sidebar docs"
},
"sidebar.docs.category.jmx": {
"message": "jmx",
"description": "The label for category jmx in sidebar docs"
},
"sidebar.docs.category.snmp": {
"message": "snmp",
"description": "The label for category snmp in sidebar docs"
},
"sidebar.docs.category.push": {
"message": "push",
"description": "The label for category push in sidebar docs"
},
"sidebar.docs.category.NGQL": {
"message": "NGQL",
"description": "The label for category NGQL in sidebar docs"
},
"sidebar.docs.category.Community": {
"message": "Community",
"description": "The label for category Community in sidebar docs"
},
"sidebar.docs.category.contribution": {
"message": "contribution",
"description": "The label for category contribution in sidebar docs"
},
"sidebar.docs.category.submit": {
"message": "submit",
"description": "The label for category submit in sidebar docs"
},
"sidebar.docs.category.release": {
"message": "release",
"description": "The label for category release in sidebar docs"
},
"sidebar.docs.category.help": {
"message": "help",
"description": "The label for category help in sidebar docs"
},
"sidebar.docs.category.service": {
"message": "service",
"description": "The label for category service in sidebar docs"
},
"sidebar.docs.category.program": {
"message": "program",
"description": "The label for category program in sidebar docs"
},
"sidebar.docs.category.database": {
"message": "database",
"description": "The label for category database in sidebar docs"
},
"sidebar.docs.category.cache": {
"message": "cache",
"description": "The label for category cache in sidebar docs"
},
"sidebar.docs.category.os": {
"message": "os",
"description": "The label for category os in sidebar docs"
},
"sidebar.docs.category.mid": {
"message": "mid",
"description": "The label for category mid in sidebar docs"
},
"sidebar.docs.category.bigdata": {
"message": "bigdata",
"description": "The label for category bigdata in sidebar docs"
},
"sidebar.docs.category.webserver": {
"message": "webserver",
"description": "The label for category webserver in sidebar docs"
},
"sidebar.docs.category.cloud-native": {
"message": "cloud-native",
"description": "The label for category cloud-native in sidebar docs"
},
"sidebar.docs.category.llm": {
"message": "llm",
"description": "The label for category llm in sidebar docs"
},
"sidebar.docs.category.network": {
"message": "network",
"description": "The label for category network in sidebar docs"
},
"sidebar.docs.category.threshold": {
"message": "threshold",
"description": "The label for category threshold in sidebar docs"
},
"sidebar.docs.category.notice": {
"message": "notice",
"description": "The label for category notice in sidebar docs"
},
"sidebar.docs.category.Others": {
"message": "Others",
"description": "The label for category Others in sidebar docs"
},
"sidebar.docs.link.Install via Helm": {
"message": "Install via Helm",
"description": "The label for link Install via Helm in sidebar docs, linking to https://artifacthub.io/packages/helm/hertzbeat/hertzbeat"
}
}
@@ -1,159 +0,0 @@
---
id: extend-http-default
title: HTTP协议系统默认解析方式
sidebar_label: 系统默认解析方式
---
> HTTP接口调用获取响应数据后,用 Apache HertzBeat (incubating) 默认的解析方式去解析响应数据。
**此需接口响应数据结构符合HertzBeat指定的数据结构规则**
### HertzBeat数据格式规范
注意⚠️ 响应数据为JSON
单层格式:key-value
```json
{
"metricName1": "metricValue",
"metricName2": "metricValue",
"metricName3": "metricValue",
"metricName4": "metricValue"
}
```
多层格式:数组里面套key-value
```json
[
{
"metricName1": "metricValue",
"metricName2": "metricValue",
"metricName3": "metricValue",
"metricName4": "metricValue"
},
{
"metricName1": "metricValue",
"metricName2": "metricValue",
"metricName3": "metricValue",
"metricName4": "metricValue"
}
]
```
样例:
查询自定义系统的CPU信息,其暴露接口为 `/metrics/cpu`,我们需要其中的`hostname,core,useage`指标
若只有一台虚拟机,其单层格式为:
```json
{
"hostname": "linux-1",
"core": 1,
"usage": 78.0,
"allTime": 200,
"runningTime": 100
}
```
若有多台虚拟机,其多层格式为:
```json
[
{
"hostname": "linux-1",
"core": 1,
"usage": 78.0,
"allTime": 200,
"runningTime": 100
},
{
"hostname": "linux-2",
"core": 3,
"usage": 78.0,
"allTime": 566,
"runningTime": 34
},
{
"hostname": "linux-3",
"core": 4,
"usage": 38.0,
"allTime": 500,
"runningTime": 20
}
]
```
**对应的监控模版YML可以配置为如下**
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: example
name:
zh-CN: 模拟应用类型
en-US: EXAMPLE APP
# 监控参数定义. field 这些为输入参数变量,即可以用^_^host^_^的形式写到后面的配置中,系统自动变量值替换
# 强制固定必须参数 - host
params:
# field-字段名称标识符
- field: host
# name-参数字段显示名称
name:
zh-CN: 主机Host
en-US: Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
type: number
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
# 端口默认值
defaultValue: 80
# 参数输入框提示信息
placeholder: '请输入端口'
# collect metrics config list
# 采集指标配置列表
metrics:
# First monitoring metric group cpu
# Note: The built-in monitoring metrics include (responseTime - response time)
- name: cpu
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 0
# 具体监控指标列表
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: hostname
type: 1
label: true
- field: usage
type: 0
unit: '%'
- field: core
type: 0
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# 当protocol为http协议时具体的采集配置
http:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
# url请求接口路径
url: /metrics/cpu
# 请求方式 GET POST PUT DELETE PATCH
method: GET
# 是否启用ssl/tls,即是http还是https,默认false
ssl: false
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
# 这里使用HertzBeat默认解析
parseType: default
```
@@ -1,206 +0,0 @@
---
id: extend-http
title: HTTP协议自定义监控
sidebar_label: HTTP协议自定义监控
---
> 从[自定义监控](extend-point)了解熟悉了怎么自定义类型,指标,协议等,这里我们来详细介绍下用HTTP协议自定义指标监控。
### HTTP协议采集流程
【**HTTP接口调用**】->【**响应校验**】->【**响应数据解析**】->【**默认方式解析|JsonPath脚本解析 | XmlPath解析(todo) | Prometheus解析**】->【**指标数据提取**】
由流程可见,我们自定义一个HTTP协议的监控类型,需要配置HTTP请求参数,配置获取哪些指标,对响应数据配置解析方式和解析脚本。
HTTP协议支持我们自定义HTTP请求路径,请求header,请求参数,请求方式,请求体等。
**系统默认解析方式**http接口返回hertzbeat规定的json数据结构,即可用默认解析方式解析数据提取对应的指标数据,详细介绍见 [**系统默认解析**](extend-http-default)
**JsonPath脚本解析方式**:用JsonPath脚本对响应的json数据进行解析,返回系统指定的数据结构,然后提供对应的指标数据,详细介绍见 [**JsonPath脚本解析**](extend-http-jsonpath)
### 自定义步骤
**HertzBeat页面** -> **监控模版菜单** -> **新增监控类型** -> **配置自定义监控模版YML** -> **点击保存应用** -> **使用新监控类型添加监控**
![HertzBeat](/img/docs/advanced/extend-point-1.png)
-------
下面详细介绍下监控模版YML的配置用法,请注意看使用注释。
### 监控模版YML
> 监控模版YML用于定义 *监控类型的名称(国际化), 请求参数结构定义(前端页面根据配置自动渲染UI), 采集指标信息, 采集协议配置* 等。
> 即我们通过自定义这个监控模版,配置定义什么监控类型,前端页面需要输入什么参数,采集哪些性能指标,通过什么协议去采集。
样例:自定义一个名称为example_http的自定义监控类型,其使用HTTP协议采集指标数据。
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: example_http
name:
zh-CN: 模拟应用类型
en-US: EXAMPLE APP
# 监控参数定义. field 这些为输入参数变量,即可以用^_^host^_^的形式写到后面的配置中,系统自动变量值替换
# 强制固定必须参数 - host
params:
# field-字段名称标识符
- field: host
# name-参数字段显示名称
name:
zh-CN: 主机Host
en-US: Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
type: number
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
# 端口默认值
defaultValue: 80
# 参数输入框提示信息
placeholder: '请输入端口'
- field: username
name:
zh-CN: 用户名
en-US: Username
type: text
# 当type为text时,用limit表示字符串限制大小
limit: 50
required: false
- field: password
name:
zh-CN: 密码
en-US: Password
type: password
required: false
- field: ssl
name:
zh-CN: 启动SSL
en-US: Enable SSL
# 当type为boolean时,前端用switch展示开关
type: boolean
required: false
- field: method
name:
zh-CN: 请求方式
en-US: Method
type: radio
required: true
# 当type为radio单选框,checkbox复选框时,option表示可选项值列表 {name1:value1,name2:value2}
options:
- label: GET请求
value: GET
- label: POST请求
value: POST
- label: PUT请求
value: PUT
- label: DELETE请求
value: DELETE
# 采集指标配置列表
metrics:
# 第一个监控指标 cpu
# 注意:内置监控指标有 (responseTime - 响应时间)
- name: cpu
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 0
# 具体监控指标列表
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: hostname
type: 1
label: true
- field: usage
type: 0
unit: '%'
- field: cores
type: 0
- field: waitTime
type: 0
unit: s
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- hostname
- core1
- core2
- usage
- allTime
- runningTime
# (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- hostname=hostname
- cores=core1+core2
- usage=usage
- waitTime=allTime-runningTime
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# 当protocol为http协议时具体的采集配置
http:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
# url请求接口路径
url: /metrics/cpu
# 请求方式 GET POST PUT DELETE PATCH
method: GET
# 是否启用ssl/tls,即是http还是https,默认false
ssl: false
# 请求头内容
headers:
apiVersion: v1
# 请求参数内容
params:
param1: param1
param2: param2
# 认证
authorization:
# 认证方式: Basic Auth, Digest Auth, Bearer Token
type: Basic Auth
basicAuthUsername: ^_^username^_^
basicAuthPassword: ^_^password^_^
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
# todo xmlPath-xmlPath脚本,prometheus-Prometheus数据规则
parseType: jsonPath
parseScript: '$'
- name: memory
priority: 1
fields:
- field: hostname
type: 1
label: true
- field: total
type: 0
unit: kb
- field: usage
type: 0
unit: '%'
- field: speed
type: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
url: /metrics/memory
method: GET
headers:
apiVersion: v1
# 查询参数,支持使用时间表达式
params:
param1: param1
param2: param2
authorization:
type: Basic Auth
basicAuthUsername: ^_^username^_^
basicAuthPassword: ^_^password^_^
parseType: default
```
@@ -1,244 +0,0 @@
---
id: 'new_committer_process'
title: '提名新Committer流程'
sidebar_position: 4
---
<!--
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
https://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.
-->
[官方指南](https://community.apache.org/newcommitter.html#new-committer-process)
## 提名新Committer的流程
- 在邮件`private@hertzbeat.apache.org`中发起投票
参见 **Committer投票模板**
- 关闭投票
参见 **关闭投票模板**
- 如果结果是赞成,邀请新的Committer
参见 **Committer邀请模板**
- 如果同意,那么:接受Committer
参见 **Committer接受模板**
- 新Committer签署CLA,等待CLA的接收记录
- 请求创建Committer账户
参见 **Committer账户创建模板**
- 等待root告诉我们已经完成
- PMC主席开启svn和其他访问权限
- 在JIRA和CWiki中将Committer添加到适当的组中
- 通知Committer完成
参见 **Committer完成模板**
## 模板
请注意,模板中有三个占位符在使用之前应该替换:
- NEW_COMMITTER_NAME
- NEW_COMMITTER_EMAIL
- NEW_COMMITTER_APACHE_NAME
### Committer投票模板
```text
To: private@hertzbeat.apache.org
Subject: [VOTE] New committer: ${NEW_COMMITTER_NAME}
```
```text
Hi HertzBeat PPMC,
This is a formal vote about inviting ${NEW_COMMITTER_NAME} as our new committer.
${Work list}[1]
[1] https://github.com/apache/hertzbeat/commits?author=${NEW_COMMITTER_NAME}
```
注意,投票将在今天一周后结束,即
[midnight UTC on YYYY-MM-DD](https://www.timeanddate.com/counters/customcounter.html?year=YYYY&month=MM&day=DD)
[Apache投票指南](https://community.apache.org/newcommitter.html)
### 关闭投票模板
```text
To: private@hertzbeat.apache.org
Subject: [RESULT] [VOTE] New committer: ${NEW_COMMITTER_NAME}
```
```text
Hi HertzBeat PPMC,
The vote has now closed. The results are:
Binding Votes:
+1 [TOTAL BINDING +1 VOTES]
0 [TOTAL BINDING +0/-0 VOTES]
-1 [TOTAL BINDING -1 VOTES]
The vote is ***successful/not successful***
```
### Committer邀请模板
```text
To: ${NEW_COMMITTER_EMAIL}
Cc: private@hertzbeat.apache.org
Subject: Invitation to become HertzBeat committer: ${NEW_COMMITTER_NAME}
```
```text
Hello ${NEW_COMMITTER_NAME},
The HertzBeat Project Management Committee (PMC)
hereby offers you committer privileges to the project.
These privileges are offered on the understanding that
you'll use them reasonably and with common sense.
We like to work on trust rather than unnecessary constraints.
Being a committer enables you to more easily make
changes without needing to go through the patch
submission process.
Being a committer does not require you to
participate any more than you already do. It does
tend to make one even more committed. You will
probably find that you spend more time here.
Of course, you can decline and instead remain as a
contributor, participating as you do now.
A. This personal invitation is a chance for you to
accept or decline in private. Either way, please
let us know in reply to the private@hertzbeat.apache.org
address only.
B. If you accept, the next step is to register an iCLA:
1. Details of the iCLA and the forms are found
through this link: https://www.apache.org/licenses/#clas
2. Instructions for its completion and return to
the Secretary of the ASF are found at
https://www.apache.org/licenses/#submitting
3. When you transmit the completed iCLA, request
to notify the Apache HertzBeat and choose a
unique Apache ID. Look to see if your preferred
ID is already taken at
https://people.apache.org/committer-index.html
This will allow the Secretary to notify the PMC
when your iCLA has been recorded.
When recording of your iCLA is noted, you will
receive a follow-up message with the next steps for
establishing you as a committer.
```
### Committer接受模板
```text
To: ${NEW_COMMITTER_EMAIL}
Cc: private@hertzbeat.apache.org
Subject: Re: invitation to become HertzBeat committer
```
```text
Welcome. Here are the next steps in becoming a project committer. After that
we will make an announcement to the dev@hertzbeat.apache.org list.
You need to send a Contributor License Agreement to the ASF.
Normally you would send an Individual CLA. If you also make
contributions done in work time or using work resources,
see the Corporate CLA. Ask us if you have any issues.
https://www.apache.org/licenses/#clas.
You need to choose a preferred ASF user name and alternatives.
In order to ensure it is available you can view a list of taken IDs at
https://people.apache.org/committer-index.html
Please notify us when you have submitted the CLA and by what means
you did so. This will enable us to monitor its progress.
We will arrange for your Apache user account when the CLA has
been recorded.
After that is done, please make followup replies to the dev@hertzbeat.apache.org list.
We generally discuss everything there and keep the
private@hertzbeat.apache.org list for occasional matters which must be private.
The developer section of the website describes roles within the ASF and provides other
resources:
https://www.apache.org/foundation/how-it-works.html
https://www.apache.org/dev/
The incubator also has some useful information for new committers
in incubating projects:
https://incubator.apache.org/guides/committer.html
https://incubator.apache.org/guides/ppmc.html
Just as before you became a committer, participation in any ASF community
requires adherence to the ASF Code of Conduct:
https://www.apache.org/foundation/policies/conduct.html
Yours,
The Apache HertzBeat PPMC
```
### Committer完成模板
```text
To: private@hertzbeat.apache.org, ${NEW_COMMITTER_EMAIL}
Subject: account request: ${NEW_COMMITTER_NAME}
```
```text
${NEW_COMMITTER_NAME}, as you know, the ASF Infrastructure has set up your
committer account with the username '${NEW_COMMITTER_APACHE_NAME}'.
Please follow the instructions to set up your SSH,
svn password, svn configuration, email forwarding, etc.
https://www.apache.org/dev/#committers
You have commit access to specific sections of the
ASF repository, as follows:
The general "committers" at:
https://svn.apache.org/repos/private/committers
If you have any questions during this phase, then please
see the following resources:
Apache developer's pages: https://www.apache.org/dev/
Incubator committer guide: https://incubator.apache.org/guides/committer.html
Naturally, if you don't understand anything be sure to ask us on the dev@hertzbeat.apache.org mailing list.
Documentation is maintained by volunteers and hence can be out-of-date and incomplete - of course
you can now help fix that.
A PPMC member will announce your election to the dev list soon.
```
@@ -1,36 +0,0 @@
---
id: download
title: 下载 Apache HertzBeat (incubating)
sidebar_label: Download
---
> **这里是 Apache HertzBeat (incubating) 官方下载页面。**
> **请再下方表中选择版本下载,推荐使用最新版本。**
:::tip
- 验证下载版本,请使用相应的哈希(sha512)、签名和[项目发布KEYS](https://downloads.apache.org/incubator/hertzbeat/KEYS)。
- 检查哈希和签名的方法参考 [如何验证](https://www.apache.org/dyn/closer.cgi#verify)。
:::
## 最新版本
:::tip
以前版本的 HertzBeat 可能会受到安全问题的影响,请考虑使用最新版本。
:::
| 版本 | 日期 | 下载 | Release Notes |
|--------|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| v1.6.0 | 2024.06.10 | [apache-hertzbeat-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz.sha512) ) <br/> [apache-hertzbeat-1.6.0-incubating-src.tar.gz](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz) ( [signature](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.asc) , [sha512](https://downloads.apache.org/incubator/hertzbeat/1.6.0/apache-hertzbeat-1.6.0-incubating-src.tar.gz.sha512) ) | [release note](https://github.com/apache/hertzbeat/releases/tag/v1.6.0) |
## 归档版本
在这里查看所有归档版本:[archive](https://archive.apache.org/dist/incubator/hertzbeat/).
## Docker 镜像版本
> Apache HertzBeat 为每个版本制作了 Docker 镜像. 您可以从 [Docker Hub](https://hub.docker.com/r/apache/hertzbeat) 拉取使用.
- HertzBeat <https://hub.docker.com/r/apache/hertzbeat>
- HertzBeat Collector <https://hub.docker.com/r/apache/hertzbeat-collector>
@@ -1,27 +0,0 @@
---
id: plugin
title: 自定义插件
sidebar_label: 自定义插件
---
## 自定义插件
### 简介
当前`Hertzbeat`在使用时,主要依赖`alert`模块对用户进行通知,然后用户采取一些措施如发送请求、执行`sql`、执行`shell`脚本等。
但目前只能通过手动或者`webhook`接收告警信息进行自动化处理。基于此,`HertzBeat`新增了`plugin`模块,该模块有一个通用接口`Plugin`,用户可以自己实现这个接口的`alert`方法,接收`Alert`类作为参数进行自定义操作。
用户添加自定义代码后,只需要对`plugin`模块进行打包,拷贝到安装目录下`/ext-lib`文件夹中,重启`HertzBeat`主程序,即可实现告警后执行自定义功能,无需自己重新打包部署整个程序。
目前,`HertzBeat`只在告警后设置了触发`alert`方法,如需在采集、启动程序等时机设置触发方法,请在`https://github.com/apache/hertzbeat/issues/new/choose``Task`
### 具体使用
1. 拉取主分支代码 `git clone https://github.com/apache/hertzbeat.git` ,定位到`plugin`模块的
`Plugin`接口。
![plugin-1.png](/img/docs/help/plugin-1.png)
2.`org.apache.hertzbeat.plugin.impl`目录下, 新建一个接口实现类,如`org.apache.hertzbeat.plugin.impl.DemoPluginImpl`,在实现类中接收`Alert`类作为参数,实现`alert`方法,逻辑由用户自定义,这里我们简单打印一下对象。
![plugin-2.png](/img/docs/help/plugin-2.png)
3. 打包`hertzbeat-plugin`模块。
![plugin-3.png](/img/docs/help/plugin-3.png)
4. 将打包后的`jar`包,拷贝到安装目录下的`ext-lib`目录下(若为`docker`安装则先将`ext-lib`目录挂载出来,再拷贝到该目录下)
![plugin-4.png](/img/docs/help/plugin-4.png)
5. 然后重启`HertzBeat`,即可实现自定义告警后处理策略。
@@ -1,43 +0,0 @@
---
id: windows
title: 监控:Windows操作系统监控
sidebar_label: Windows操作系统
keywords: [开源监控系统, 开源操作系统监控, Windows操作系统监控]
---
> 通过SNMP协议对Windows操作系统的通用性能指标进行采集监控。
> 注意⚠️ Windows服务器需开启SNMP服务
参考资料:
[什么是SNMP协议1](https://www.cnblogs.com/xdp-gacl/p/3978825.html)
[什么是SNMP协议2](https://www.auvik.com/franklyit/blog/network-basics-what-is-snmp/)
[Win配置SNMP英文](https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-snmp-service)
[Win配置SNMP中文](https://docs.microsoft.com/zh-cn/troubleshoot/windows-server/networking/configure-snmp-service)
### 配置参数
| 参数名称 | 参数帮助描述 |
|----------|----------------------------------------------------------------------------|
| 监控Host | 被监控的对端IPV4,IPV6或域名。注意⚠️不带协议头(eg: https://, http://)。 |
| 任务名称 | 标识此监控的名称,名称需要保证唯一性。 |
| 端口 | Windows SNMP服务对外提供的端口,默认为 161。 |
| SNMP 版本 | SNMP协议版本 V1 V2c V3 |
| SNMP 团体字 | SNMP 协议团体名(Community Name),用于实现SNMP网络管理员访问SNMP管理代理时的身份验证。类似于密码,默认值为 public |
| 超时时间 | 协议连接超时时间 |
| 采集间隔 | 监控周期性采集数据间隔时间,单位秒,可设置的最小间隔为30秒 |
| 是否探测 | 新增监控前是否先探测检查监控可用性,探测成功才会继续新增修改操作 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
### 采集指标
#### 指标集合:system
| 指标名称 | 指标单位 | 指标帮助描述 |
|--------------|------|--------|
| name | 无 | 主机名称 |
| descr | 无 | 操作系统描述 |
| uptime | 无 | 系统运行时间 |
| numUsers | 个数 | 当前用户数 |
| services | 个数 | 当前服务数量 |
| processes | 个数 | 当前进程数量 |
| responseTime | ms | 采集响应时间 |
@@ -1,130 +0,0 @@
---
id: account-modify
title: 配置修改账户密码和加密密钥
sidebar_label: 更新账户和密钥
---
## 更新账户
Apache HertzBeat (incubating) 默认内置三个用户账户,分别为 admin/hertzbeat tom/hertzbeat guest/hertzbeat
若需要新增删除修改账户或密码,可以通过配置 `sureness.yml` 实现,若无此需求可忽略此步骤
修改位于安装目录下的 `/hertzbeat/config/sureness.yml` 的配置文件,docker环境目录为`opt/hertzbeat/config/sureness.yml`,建议提前挂载映射
配置文件内容参考 项目仓库[/script/sureness.yml](https://github.com/apache/hertzbeat/blob/master/script/sureness.yml)
```yaml
resourceRole:
- /api/account/auth/refresh===post===[admin,user,guest]
- /api/apps/**===get===[admin,user,guest]
- /api/monitor/**===get===[admin,user,guest]
- /api/monitor/**===post===[admin,user]
- /api/monitor/**===put===[admin,user]
- /api/monitor/**===delete==[admin]
- /api/monitors/**===get===[admin,user,guest]
- /api/monitors/**===post===[admin,user]
- /api/monitors/**===put===[admin,user]
- /api/monitors/**===delete===[admin]
- /api/alert/**===get===[admin,user,guest]
- /api/alert/**===post===[admin,user]
- /api/alert/**===put===[admin,user]
- /api/alert/**===delete===[admin]
- /api/alerts/**===get===[admin,user,guest]
- /api/alerts/**===post===[admin,user]
- /api/alerts/**===put===[admin,user]
- /api/alerts/**===delete===[admin]
- /api/notice/**===get===[admin,user,guest]
- /api/notice/**===post===[admin,user]
- /api/notice/**===put===[admin,user]
- /api/notice/**===delete===[admin]
- /api/tag/**===get===[admin,user,guest]
- /api/tag/**===post===[admin,user]
- /api/tag/**===put===[admin,user]
- /api/tag/**===delete===[admin]
- /api/summary/**===get===[admin,user,guest]
- /api/summary/**===post===[admin,user]
- /api/summary/**===put===[admin,user]
- /api/summary/**===delete===[admin]
# 需要被过滤保护的资源,不认证鉴权直接访问
# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权
excludedResource:
- /api/account/auth/**===*
- /api/i18n/**===get
- /api/apps/hierarchy===get
# web ui 前端静态资源
- /===get
- /dashboard/**===get
- /monitors/**===get
- /alert/**===get
- /account/**===get
- /setting/**===get
- /passport/**===get
- /**/*.html===get
- /**/*.js===get
- /**/*.css===get
- /**/*.ico===get
- /**/*.ttf===get
- /**/*.png===get
- /**/*.gif===get
- /**/*.jpg===get
- /**/*.svg===get
- /**/*.json===get
# swagger ui 资源
- /swagger-resources/**===get
- /v2/api-docs===get
- /v3/api-docs===get
# 用户账户信息
# 下面有 admin tom lili 三个账户
# eg: admin 拥有[admin,user]角色,密码为hertzbeat
# eg: tom 拥有[user],密码为hertzbeat
# eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289
account:
- appId: admin
credential: hertzbeat
role: [admin,user]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
```
修改`sureness.yml`的如下**部分参数**:**[注意⚠️sureness配置的其它默认参数需保留]**
```yaml
# 用户账户信息
# 下面有 admin tom lili 三个账户
# eg: admin 拥有[admin,user]角色,密码为hertzbeat
# eg: tom 拥有[user],密码为hertzbeat
# eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289
account:
- appId: admin
credential: hertzbeat
role: [admin,user]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
```
## 更新安全密钥
> 此密钥为账户安全加密管理的密钥,需要更新为相同长度的你自定义密钥串。
更新 `config` 目录下的 `application.yml` 文件,修改 `sureness.jwt.secret` 参数为你自定义的相同长度的密钥串。
```yaml
sureness:
jwt:
secret: 'CyaFv0bwq2Eik0jdrKUtsA6bx3sDJeFV643R
LnfKefTjsIfJLBa2YkhEqEGtcHDTNe4CU6+9
8tVt4bisXQ13rbN0oxhUZR73M6EByXIO+SV5
dKhaX0csgOCTlCxq20yhmUea6H6JIpSE2Rwp'
```
**重启 HertzBeat 浏览器访问 <http://ip:1157/> 即可探索使用 HertzBeat**

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