Compare commits

..
4 Commits
Author SHA1 Message Date
aias00 4a4fbc57aa Merge branch 'master' into new-ppmc 2025-03-31 14:33:55 +08:00
tomsun28 f44e19e537 Merge branch 'master' into new-ppmc 2025-03-29 10:41:03 +08:00
tomsun28 49ad608ed9 [doc] update new hertzbeat ppmc 2025-03-29 10:40:33 +08:00
tomsun28 22a7ae55e3 [doc] update new hertzbeat ppmc 2025-03-29 08:45:12 +08:00
485 changed files with 4898 additions and 16296 deletions
@@ -1,133 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.controller;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.apache.hertzbeat.alert.service.AlertInhibitService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.HashMap;
/**
* test case for {@link AlertInhibitControllerTest}
*/
@ExtendWith(MockitoExtension.class)
public class AlertInhibitControllerTest {
private MockMvc mockMvc;
@Mock
private AlertInhibitService alertInhibitService;
@InjectMocks
private AlertInhibitController alertInhibitController;
private AlertInhibit alertInhibit;
@BeforeEach
void setUp() {
this.mockMvc = standaloneSetup(alertInhibitController).build();
HashMap<String, String> sourceLabels = new HashMap<>();
HashMap<String, String> targetLabels = new HashMap<>();
alertInhibit = AlertInhibit.builder()
.id(1L)
.name("test")
.sourceLabels(sourceLabels)
.targetLabels(targetLabels)
.creator("test")
.build();
}
@Test
void testAddNewAlertInhibit() throws Exception {
doNothing().when(alertInhibitService).validate(any(AlertInhibit.class), eq(false));
doNothing().when(alertInhibitService).addAlertInhibit(any(AlertInhibit.class));
mockMvc.perform(post("/api/alert/inhibit")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(alertInhibit)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Add success"));
}
@Test
void testModifyAlertInhibit() throws Exception {
doNothing().when(alertInhibitService).validate(any(AlertInhibit.class), eq(true));
doNothing().when(alertInhibitService).modifyAlertInhibit(any(AlertInhibit.class));
mockMvc.perform(put("/api/alert/inhibit")
.contentType(MediaType.APPLICATION_JSON)
.content(JsonUtil.toJson(alertInhibit)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Modify success"));
}
@Test
void testGetAlertInhibitExists() throws Exception {
when(alertInhibitService.getAlertInhibit(1L)).thenReturn(alertInhibit);
mockMvc.perform(get("/api/alert/inhibit/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id").value(alertInhibit.getId()));
}
@Test
void testGetAlertInhibitNotExists() throws Exception {
when(alertInhibitService.getAlertInhibit(1L)).thenReturn(null);
mockMvc.perform(get("/api/alert/inhibit/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
.andExpect(jsonPath("$.msg").value("AlertInhibit not exist."));
}
}
@@ -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,11 +155,6 @@
<artifactId>plc4j-driver-modbus</artifactId>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-driver-s7</artifactId>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId>
@@ -40,10 +40,6 @@ 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;
@@ -67,9 +63,11 @@ import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
@@ -79,6 +77,8 @@ import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
@@ -91,12 +91,6 @@ import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Collections;
/**
* http https collect
@@ -127,7 +121,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)) {
@@ -158,15 +152,13 @@ public class HttpCollectImpl extends AbstractCollect {
case DispatchConstants.PARSE_PROMETHEUS ->
parseResponseByPrometheusExporter(resp, metrics.getAliasFields(), builder);
case DispatchConstants.PARSE_XML_PATH ->
parseResponseByXmlPath(resp, metrics, builder, responseTime);
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
case DispatchConstants.PARSE_WEBSITE ->
parseResponseByWebsite(resp, metrics, metrics.getHttp(), builder, responseTime);
case DispatchConstants.PARSE_SITE_MAP ->
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
case DispatchConstants.PARSE_HEADER ->
parseResponseByHeader(builder, metrics.getAliasFields(), response);
case DispatchConstants.PARSE_CONFIG ->
parseResponseByConfig(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
default ->
parseResponseByDefault(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
}
@@ -337,207 +329,8 @@ public class HttpCollectImpl extends AbstractCollect {
}
}
private void parseResponseByXmlPath(String resp, Metrics metrics,
CollectRep.MetricsData.Builder builder, Long responseTime) {
HttpProtocol http = metrics.getHttp();
List<String> aliasFields = metrics.getAliasFields();
String xpathExpression = http.getParseScript();
if (!StringUtils.hasText(xpathExpression)) {
log.warn("Http collect parse type is xmlPath, but the xpath expression is empty.");
builder.setCode(CollectRep.Code.FAIL);
builder.setMsg("XPath expression is empty");
return;
}
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(new InputSource(new StringReader(resp)));
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
NodeList nodeList = (NodeList) xpath.evaluate(xpathExpression, document, XPathConstants.NODESET);
if (nodeList == null || nodeList.getLength() == 0) {
log.debug("XPath expression '{}' returned no nodes.", xpathExpression);
boolean requestedSummaryFields = aliasFields.stream()
.anyMatch(alias -> NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)
|| CollectorConstants.KEYWORD.equalsIgnoreCase(alias));
if (requestedSummaryFields) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String alias : aliasFields) {
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(Integer.toString(keywordNum));
} else {
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
}
}
builder.addValueRow(valueRowBuilder.build());
}
return;
}
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String alias : aliasFields) {
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(Integer.toString(keywordNum));
} else {
try {
String value = (String) xpath.evaluate(alias, node, XPathConstants.STRING);
valueRowBuilder.addColumn(StringUtils.hasText(value) ? value : CommonConstants.NULL_VALUE);
} catch (XPathExpressionException e) {
log.warn("Failed to evaluate XPath '{}' for node [{}]: {}", alias, node.getNodeName(), e.getMessage());
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
}
}
}
builder.addValueRow(valueRowBuilder.build());
}
} catch (Exception e) {
log.warn("Failed to parse XML response with XPath '{}': {}", xpathExpression, e.getMessage(), e);
builder.setCode(CollectRep.Code.FAIL);
builder.setMsg("Failed to parse XML response: " + e.getMessage());
}
}
/**
* Parses the response body in Properties/Config format.
* Two modes are supported:
* 1. single-object mode: if http.parseScript is null, aliasFields are treated as indicator names.
* - If there is a locator in the indicator definition, use the locator as the key of the Properties.
* - Otherwise, use aliasField (metric name) as the key for Properties.
* Generate a single row of data.
* 2. array mode: if http.parseScript is not empty (e.g. “users”), treat it as an array base path.
* Treat aliasFields as the attribute name of an array element, and generate a single row of data for each array index. locator is invalid in this mode.
*
* @param resp Response body string
* @param aliasFields List of metrics aliases (i.e., the list of fields in metrics.fields).
* @param http http protocol configuration
* @param builder The metrics data builder.
* @param responseTime response time
*/
private void parseResponseByConfig(String resp, List<String> aliasFields, HttpProtocol http,
CollectRep.MetricsData.Builder builder, Long responseTime) {
if (!StringUtils.hasText(resp)) {
log.warn("Http collect parse type is config, but response body is empty.");
builder.setCode(CollectRep.Code.FAIL);
builder.setMsg("Response body is empty");
return;
}
Properties properties = new Properties();
try (StringReader reader = new StringReader(resp)) {
properties.load(reader);
} catch (IOException e) {
log.warn("Failed to parse config response: {}", e.getMessage(), e);
builder.setCode(CollectRep.Code.FAIL);
builder.setMsg("Failed to parse config response: " + e.getMessage());
return;
}
String arrayBasePath = http.getParseScript();
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
if (!StringUtils.hasText(arrayBasePath)) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String alias : aliasFields) {
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(Integer.toString(keywordNum));
} else {
String value = properties.getProperty(alias);
valueRowBuilder.addColumn(value != null ? value : CommonConstants.NULL_VALUE);
}
}
CollectRep.ValueRow valueRow = valueRowBuilder.build();
if (hasMeaningfulDataInRow(valueRow, aliasFields)) {
builder.addValueRow(valueRow);
} else {
log.warn("No meaningful data found in single config object response for aliasFields: {}", aliasFields);
}
} else {
Pattern pattern = Pattern.compile("^" + Pattern.quote(arrayBasePath) + "\\[(\\d+)]\\.");
Set<Integer> existingIndices = new HashSet<>();
for (String key : properties.stringPropertyNames()) {
Matcher matcher = pattern.matcher(key);
if (matcher.find()) {
try {
int index = Integer.parseInt(matcher.group(1));
existingIndices.add(index);
} catch (NumberFormatException e) {
log.error("Could not parse index from key: {}", key);
}
}
}
if (existingIndices.isEmpty()) {
log.warn("Could not find any array elements for base path '{}' in config response.", arrayBasePath);
return;
}
List<Integer> sortedIndices = new ArrayList<>(existingIndices);
Collections.sort(sortedIndices);
for (int i : sortedIndices) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String alias : aliasFields) {
if (NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumn(Integer.toString(keywordNum));
} else {
String currentKey = arrayBasePath + "[" + i + "]." + alias;
String value = properties.getProperty(currentKey);
valueRowBuilder.addColumn(value != null ? value : CommonConstants.NULL_VALUE);
}
}
CollectRep.ValueRow valueRow = valueRowBuilder.build();
if (hasMeaningfulDataInRow(valueRow, aliasFields)) {
builder.addValueRow(valueRowBuilder.build());
}
}
}
}
private boolean hasMeaningfulDataInRow(CollectRep.ValueRow valueRow, List<String> aliasFields) {
if (valueRow.getColumnsCount() == 0) {
return false;
}
if (valueRow.getColumnsCount() != aliasFields.size()) {
log.error("Column count ({}) mismatch with aliasFields size ({}) when checking meaningful data.",
valueRow.getColumnsCount(), aliasFields.size());
return false;
}
boolean hasMeaningfulData = false;
for (int i = 0; i < valueRow.getColumnsCount(); i++) {
String columnValue = valueRow.getColumns(i);
String alias = aliasFields.get(i);
if (!CommonConstants.NULL_VALUE.equals(columnValue) && (!NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias) && !CollectorConstants.KEYWORD.equalsIgnoreCase(alias))) {
hasMeaningfulData = true;
break;
}
if ((NetworkConstants.RESPONSE_TIME.equalsIgnoreCase(alias) || CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) && !CommonConstants.NULL_VALUE.equals(columnValue)) {
hasMeaningfulData = true;
}
}
return hasMeaningfulData;
private void parseResponseByXmlPath(String resp, List<String> aliasFields, HttpProtocol http,
CollectRep.MetricsData.Builder builder) {
}
private void parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
@@ -680,10 +473,11 @@ public class HttpCollectImpl extends AbstractCollect {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getDigestAuthUsername(),
auth.getDigestAuthPassword());
AuthScope authScope = new AuthScope(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort()));
provider.setCredentials(authScope, credentials);
provider.setCredentials(AuthScope.ANY, credentials);
AuthCache authCache = new BasicAuthCache();
authCache.put(new HttpHost(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort())), new DigestScheme());
clientContext.setCredentialsProvider(provider);
clientContext.setAuthCache(authCache);
return clientContext;
}
}
@@ -718,7 +512,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();
@@ -728,7 +522,7 @@ public class HttpCollectImpl extends AbstractCollect {
continue;
}
if (!queryParams.isEmpty()) {
if (queryParams.length() > 0) {
queryParams.append("&");
}
@@ -746,7 +540,7 @@ public class HttpCollectImpl extends AbstractCollect {
}
}
}
// The default request header can be overridden if customized
// keep-alive
requestBuilder.addHeader(HttpHeaders.CONNECTION, NetworkConstants.KEEP_ALIVE);
@@ -787,7 +581,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(TimeExpressionUtil.calculate(httpProtocol.getPayload()), StandardCharsets.UTF_8));
requestBuilder.setEntity(new StringEntity(httpProtocol.getPayload(), StandardCharsets.UTF_8));
}
// uri encode
@@ -806,10 +600,10 @@ public class HttpCollectImpl extends AbstractCollect {
}
// append query params
if (!queryParams.isEmpty()) {
uri += (uri.contains("?") ? "&" : "?") + queryParams;
if (queryParams.length() > 0) {
uri += (uri.contains("?") ? "&" : "?") + queryParams.toString();
}
String finalUri;
if (IpDomainUtil.isHasSchema(httpProtocol.getHost())) {
finalUri = httpProtocol.getHost() + ":" + httpProtocol.getPort() + uri;
@@ -825,7 +619,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.common.entity.dto.MetricFamily;
import org.apache.hertzbeat.collector.collect.prometheus.parser.MetricFamily;
import org.apache.hertzbeat.collector.collect.prometheus.parser.TextParser;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.collector.util.CollectUtil;
@@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.dto;
package org.apache.hertzbeat.collector.collect.prometheus.parser;
import java.util.List;
import lombok.Data;
@@ -15,20 +15,18 @@
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
package org.apache.hertzbeat.collector.collect.prometheus.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import 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;
@@ -38,23 +36,9 @@ 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);
@@ -139,65 +123,51 @@ public class OnlineParser {
}
private static int getChar(InputStream inputStream) throws IOException, FormatException {
private static CharChecker parseOneChar(InputStream inputStream) throws IOException {
int i = inputStream.read();
if (i == '\\') {
i = inputStream.read();
if (escapeMap.containsKey(i)) {
return escapeMap.get(i);
} else {
throw new FormatException("Escape character failed.");
}
} else {
return i;
}
}
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, FormatException {
int i = getChar(inputStream);
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
while ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || i == '-' || i == '+' || i == 'e' || i == '.') {
stringBuilder.append((char) i);
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker skipOneLong(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
private static CharChecker skipOneLong(InputStream inputStream) throws IOException {
int i = inputStream.read();
while (i >= '0' && i <= '9') {
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = getChar(inputStream);
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == ':') {
stringBuilder.append((char) i);
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = getChar(inputStream);
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
int i = inputStream.read();
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_') {
stringBuilder.append((char) i);
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker parseLabelValue(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
int i = getChar(inputStream);
int i = inputStream.read();
while (i != '"' && i != -1) {
if (i == '\\') {
i = getChar(inputStream);
i = inputStream.read();
switch (i) {
case 'n':
stringBuilder.append('\n');
@@ -211,26 +181,27 @@ public class OnlineParser {
default:
throw new FormatException();
}
} else {
}
else {
stringBuilder.append((char) i);
}
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker skipSpaces(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
private static CharChecker skipSpaces(InputStream inputStream) throws IOException {
int i = inputStream.read();
while (i == ' ') {
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException, FormatException {
int i = getChar(inputStream);
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException {
int i = inputStream.read();
while (i != '\n' && i != -1) {
i = getChar(inputStream);
i = inputStream.read();
}
return new CharChecker(i);
}
@@ -296,18 +267,18 @@ public class OnlineParser {
metricFamily.setMetricList(new ArrayList<>());
metricFamily.setName(metricName);
metricFamilyMap.put(metricName, metricFamily);
} else {
}
else {
metricFamily = metricFamilyMap.get(metricName);
}
if (i == ' ') {
i = skipSpaces(inputStream).getInt();
}
List<MetricFamily.Label> labelList = new LinkedList<>();
metric.setLabels(labelList);
if (i == '{') {
List<MetricFamily.Label> labelList = new LinkedList<>();
parseLabels(inputStream, stringBuilder, labelList);
metric.setLabels(labelList);
i = skipSpaces(inputStream).getInt();
}
@@ -336,8 +307,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();
@@ -346,7 +317,7 @@ public class OnlineParser {
stringBuilder.append((char) i);
parseMetric(inputStream, metricFamilyMap, stringBuilder);
}
i = getChar(inputStream);
i = inputStream.read();
}
} catch (FormatException e) {
log.error("prometheus parser failed because of wrong input format. {}", e.getMessage());
@@ -24,7 +24,6 @@ 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;
/**
@@ -1,95 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.s7;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.collector.collect.plc.AbstractPlcCollectImpl;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.PlcProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.S7Protocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* plc collect
*/
@Slf4j
public class S7CollectImpl extends AbstractPlcCollectImpl {
@Override
public void preCheck(Metrics metrics) throws IllegalArgumentException {
S7Protocol s7 = metrics.getS7();
List<String> registerAddressList = s7.getRegisterAddresses();
// check RackId
if (!StringUtils.hasText(s7.getRackId())) {
s7.setRackId("0");
}
// check SlotId
if (!StringUtils.hasText(s7.getSlotId())) {
s7.setSlotId("0");
}
// check controllerType
if (!StringUtils.hasText(s7.getControllerType())) {
s7.setControllerType("S7_1500");
}
if (!StringUtils.hasText(s7.getTimeout())) {
s7.setTimeout("5000");
}
PlcProtocol plc = metrics.getPlc() == null ? new PlcProtocol() : metrics.getPlc();
plc.setRegisterAddresses(registerAddressList);
BeanUtils.copyProperties(s7, plc);
metrics.setPlc(plc);
super.preCheck(metrics);
}
@Override
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
super.collect(builder, metrics);
}
@Override
public String supportProtocol() {
return DispatchConstants.PROTOCOL_S7;
}
@Override
protected String getConnectionString(Metrics metrics) {
S7Protocol s7Protocol = metrics.getS7();
return "s7://" + s7Protocol.getHost() + ":" + s7Protocol.getPort() + "?remote-rack:" + s7Protocol.getRackId()
+ "&remote-slot:" + s7Protocol.getSlotId() + "&controller-type=" + s7Protocol.getControllerType();
}
@Override
protected PlcReadRequest buildRequest(Metrics metrics, PlcConnection connection) {
S7Protocol s7Protocol = metrics.getS7();
List<String> registerAddressList = s7Protocol.getRegisterAddresses();
// Create a new read request:
PlcReadRequest.Builder requestBuilder = connection.readRequestBuilder();
for (int i = 0; i < registerAddressList.size(); i++) {
String s1 = registerAddressList.get(i);
requestBuilder.addTagAddress(metrics.getS7().getAddressSyntax() + ":" + i, s1);
}
return requestBuilder.build();
}
}
@@ -47,28 +47,31 @@ public class DnsCollectImplTest {
.address("www.google.com")
.timeout("3000")
.port("53")
.tcp("tcp")
.build();
}
@Test
public void testPreCheck() {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
//metrics is null
dnsCollect.collect(builder, null);
assertEquals(CollectRep.Code.FAIL, builder.getCode());
assertThrows(IllegalArgumentException.class, () -> {
Metrics metrics = new Metrics();
metrics.setName("question");
metrics.setDns(dnsProtocol);
dnsCollect.preCheck(metrics);
});
//invalid DnsProtocol
Metrics metrics = new Metrics();
//query class is blank
assertThrows(IllegalArgumentException.class, () -> {
DnsProtocol dns = DnsProtocol.builder().build();
Metrics metrics = new Metrics();
metrics.setDns(dns);
dnsCollect.preCheck(metrics);
});
//validated DnsProtocol
// no exception throws
assertDoesNotThrow(() -> {
metrics.setName("question");
dnsProtocol.setTcp("tcp");
Metrics metrics = new Metrics();
metrics.setDns(dnsProtocol);
dnsCollect.preCheck(metrics);
});
@@ -77,23 +80,28 @@ public class DnsCollectImplTest {
@Test
public void testCollect() {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
Metrics metrics0 = Metrics.builder()
.name("question")
.dns(dnsProtocol)
.aliasFields(Collections.singletonList("section"))
.build();
dnsCollect.collect(builder, metrics0);
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
long monitorId = 666;
String app = "testDNS";
Metrics metrics = new Metrics();
metrics.setName("question");
metrics.setDns(dnsProtocol);
metrics.setAliasFields(Collections.singletonList("section"));
dnsCollect.collect(builder, metrics);
assertNotNull(builder.getValues(0).getColumns(0));
// dns is null, no exception throws
assertDoesNotThrow(() -> {
dnsCollect.collect(builder, null);
});
// metric name is header
Metrics metrics1 = Metrics.builder()
.name("header")
.dns(dnsProtocol)
.aliasFields(Collections.singletonList("section"))
.build();
dnsCollect.collect(builder, metrics1);
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
assertDoesNotThrow(() -> {
Metrics metrics1 = new Metrics();
metrics1.setName("header");
metrics1.setDns(dnsProtocol);
metrics1.setAliasFields(Collections.singletonList("section"));
dnsCollect.collect(builder, metrics1);
});
}
@Test
@@ -18,14 +18,7 @@
package org.apache.hertzbeat.collector.collect.http;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
@@ -48,7 +41,7 @@ class HttpCollectImplTest {
assertThrows(IllegalArgumentException.class, () -> {
httpCollectImpl.preCheck(null);
});
assertThrows(IllegalArgumentException.class, () -> {
Metrics metrics = Metrics.builder().build();
httpCollectImpl.preCheck(metrics);
@@ -72,92 +65,4 @@ class HttpCollectImplTest {
String protocol = httpCollectImpl.supportProtocol();
assert "http".equals(protocol);
}
@Test
void parseResponseByXmlPath() throws Exception {
// Create a sample XML response
String xmlResponse = """
<?xml version="1.0" encoding="UTF-8"?>
<root>
<server>
<name>Server1</name>
<status>Running</status>
<metrics>
<cpu>75.5</cpu>
<memory>1024</memory>
<disk>500</disk>
</metrics>
</server>
<server>
<name>Server2</name>
<status>Stopped</status>
<metrics>
<cpu>0.0</cpu>
<memory>0</memory>
<disk>500</disk>
</metrics>
</server>
</root>
""";
// Set up HttpProtocol with XML path parsing
HttpProtocol http = HttpProtocol.builder()
.parseType(DispatchConstants.PARSE_XML_PATH)
.parseScript("//server") // XPath to select all server nodes
.build();
// Set up Metrics with fields that have XPath expressions
List<Metrics.Field> fields = new ArrayList<>();
fields.add(Metrics.Field.builder().field("name").build());
fields.add(Metrics.Field.builder().field("status").build());
fields.add(Metrics.Field.builder().field("metrics/cpu").build());
fields.add(Metrics.Field.builder().field("metrics/memory").build());
Metrics metrics = Metrics.builder()
.http(http)
.fields(fields)
.aliasFields(Arrays.asList("name", "status", "metrics/cpu", "metrics/memory"))
.build();
// Create a custom builder that captures added rows
List<CollectRep.ValueRow> capturedRows = new ArrayList<>();
CollectRep.MetricsData.Builder builder = new CollectRep.MetricsData.Builder() {
@Override
public CollectRep.MetricsData.Builder addValueRow(CollectRep.ValueRow valueRow) {
capturedRows.add(valueRow);
return super.addValueRow(valueRow);
}
};
// Use reflection to access the private parseResponseByXmlPath method
Method parseMethod = HttpCollectImpl.class.getDeclaredMethod(
"parseResponseByXmlPath",
String.class,
Metrics.class,
CollectRep.MetricsData.Builder.class,
Long.class);
parseMethod.setAccessible(true);
// Call the method
parseMethod.invoke(httpCollectImpl, xmlResponse, metrics, builder, 100L);
// Verify the results
assertEquals(2, capturedRows.size(), "Should have parsed 2 server nodes");
// Check first server
CollectRep.ValueRow firstRow = capturedRows.get(0);
assertEquals(4, firstRow.getColumnsCount(), "First row should have 4 columns");
assertEquals("Server1", firstRow.getColumns(0), "First server name should be Server1");
assertEquals("Running", firstRow.getColumns(1), "First server status should be Running");
assertEquals("75.5", firstRow.getColumns(2), "First server CPU should be 75.5");
assertEquals("1024", firstRow.getColumns(3), "First server memory should be 1024");
// Check second server
CollectRep.ValueRow secondRow = capturedRows.get(1);
assertEquals(4, secondRow.getColumnsCount(), "Second row should have 4 columns");
assertEquals("Server2", secondRow.getColumns(0), "Second server name should be Server2");
assertEquals("Stopped", secondRow.getColumns(1), "Second server status should be Stopped");
assertEquals("0.0", secondRow.getColumns(2), "Second server CPU should be 0.0");
assertEquals("0", secondRow.getColumns(3), "Second server memory should be 0");
}
}
@@ -17,8 +17,6 @@
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;
@@ -302,7 +302,7 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
value = String.valueOf(objValue);
}
} catch (Exception e) {
log.warn("[calculates execute warning, use original value.] {}", e.getMessage());
log.info("[calculates execute warning] {}.", e.getMessage());
value = Optional.ofNullable(fieldValueMap.get(expression.getSourceText()))
.map(String::valueOf)
.orElse(null);
@@ -29,5 +29,4 @@ org.apache.hertzbeat.collector.collect.mqtt.MqttCollectImpl
org.apache.hertzbeat.collector.collect.ipmi2.IpmiCollectImpl
org.apache.hertzbeat.collector.collect.kafka.KafkaCollectImpl
org.apache.hertzbeat.collector.collect.sd.HttpSdCollectImpl
org.apache.hertzbeat.collector.collect.modbus.ModbusCollectImpl
org.apache.hertzbeat.collector.collect.s7.S7CollectImpl
org.apache.hertzbeat.collector.collect.modbus.ModbusCollectImpl
@@ -202,10 +202,6 @@ public interface DispatchConstants {
* Parsing method prometheus exporter data
*/
String PARSE_PROMETHEUS = "prometheus";
/**
* Parse response body as config/properties format
*/
String PARSE_CONFIG = "config";
/**
* prometheus accept header
*/
@@ -231,9 +227,4 @@ public interface DispatchConstants {
* protocol modbus
*/
String PROTOCOL_MODBUS = "modbus";
/**
* protocol modbus
*/
String PROTOCOL_S7 = "s7";
}
@@ -242,7 +242,6 @@ public class KafkaCollectImpl extends AbstractCollect {
boolean isKafkaCommand = SupportedCommand.isKafkaCommand(command);
if (!isKafkaCommand) {
log.error("Unsupported command: {}", command);
builder.setCode(CollectRep.Code.FAIL);
return;
}
@@ -274,7 +273,7 @@ public class KafkaCollectImpl extends AbstractCollect {
}
}
protected AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
private AdminClient getAdminClient(KafkaProtocol kafkaProtocol) {
CacheIdentifier kafkaAdminClientIdentifier = CacheIdentifier.builder()
.ip(kafkaProtocol.getHost()).port(kafkaProtocol.getPort())
.build();
@@ -17,24 +17,12 @@
package org.apache.hertzbeat.collector.collect.kafka;
import org.apache.hertzbeat.collector.collect.kafka.constants.SupportedCommand;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.KafkaProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.ListTopicsOptions;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.common.KafkaFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.HashSet;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,25 +31,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Test case for {@link KafkaCollectImpl}
*/
@ExtendWith(MockitoExtension.class)
public class KafkaCollectTest {
private static final String HOST = "127.0.0.1";
private static final String PORT = "9092";
private KafkaCollectImpl collect;
@Mock
private AdminClient adminClient;
@BeforeEach
public void setUp() throws Exception {
collect = new KafkaCollectImpl(){
@Override
protected AdminClient getAdminClient(KafkaProtocol protocol) {
return adminClient;
}
};
collect = new KafkaCollectImpl();
}
@Test
@@ -84,51 +59,38 @@ public class KafkaCollectTest {
});
// kafka port is null
assertThrows(IllegalArgumentException.class, () -> {
kafka.setHost(HOST);
kafka.setHost("127.0.0.1");
collect.preCheck(metric);
});
// no exception throw
assertDoesNotThrow(() -> {
kafka.setPort(PORT);
kafka.setPort("9092");
collect.preCheck(metric);
});
}
@Test
void testCollect() throws Exception {
Set<String> topicSet = new HashSet<>();
topicSet.add("test-topic");
ListTopicsResult listTopicsResult = Mockito.mock(ListTopicsResult.class);
KafkaFuture<Set<String>> future = Mockito.mock(KafkaFuture.class);
Mockito.when(adminClient.listTopics(Mockito.any(ListTopicsOptions.class))).thenReturn(listTopicsResult);
Mockito.when(listTopicsResult.names()).thenReturn(future);
Mockito.when(future.get()).thenReturn(topicSet);
KafkaProtocol kafka = KafkaProtocol.builder()
.host(HOST)
.port(PORT)
.build();
Metrics metrics = Metrics.builder()
.kclient(kafka)
.build();
// test if not kafka command
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
collect.collect(builder, metrics);
assertEquals(CollectRep.Code.FAIL, builder.getCode());
void collect() {
// metrics is null
assertThrows(NullPointerException.class, () -> {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
collect.collect(builder, null);
});
KafkaProtocol kafka = KafkaProtocol.builder().host("127.0.0.1").port("9092").build();
Metrics metrics = Metrics.builder().kclient(kafka).build();
//test if not kafka command
assertDoesNotThrow(() -> {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
collect.collect(builder, metrics);
});
//test kafka command
kafka.setCommand(SupportedCommand.TOPIC_LIST.getCommand());
builder.setCode(CollectRep.Code.forNumber(6));
collect.collect(builder, metrics);
assertDoesNotThrow(() -> {
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
metrics.getKclient().setCommand("topic-list");
collect.collect(builder, metrics);
});
assertEquals(CollectRep.Code.SUCCESS, builder.getCode());
assertEquals(1, builder.getValuesList().size());
assertEquals("test-topic", builder.getValues(0).getColumns(0));
Mockito.verify(adminClient).listTopics(Mockito.any(ListTopicsOptions.class));
}
@Test
@@ -455,19 +455,4 @@ public interface CommonConstants {
* status page incident state resolved
*/
byte STATUS_PAGE_INCIDENT_STATE_RESOLVED = 3;
/**
* status page incident state resolved
*/
byte MONITOR_TYPE_NORMAL = 0;
/**
* status page incident state resolved
*/
byte MONITOR_TYPE_PUSH_AUTO_CREATE = 1;
/**
* status page incident state resolved
*/
byte MONITOR_TYPE_DISCOVERY_AUTO_CREATE = 2;
}
@@ -1,82 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.dto.query;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
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,7 +54,6 @@ import org.apache.hertzbeat.common.entity.job.protocol.PushProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.RedfishProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.RedisProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.RocketmqProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.S7Protocol;
import org.apache.hertzbeat.common.entity.job.protocol.ScriptProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.SmtpProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.SnmpProtocol;
@@ -271,10 +270,6 @@ public class Metrics {
* Monitoring configuration information using the public modBus protocol
*/
private ModbusProtocol modbus;
/**
* Monitoring configuration information using the public s7 protocol
*/
private S7Protocol s7;
/**
* collector use - Temporarily store subTask metrics response data
*/
@@ -91,9 +91,6 @@ public class Monitor {
@Max(4)
private byte status;
@Schema(title = "Task type 0: Normal, 1: push auto create, 2: discovery auto create")
private byte type;
@Schema(title = "task label", example = "{env:test}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@Column(length = 4096)
@@ -28,7 +28,6 @@ import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* start up class.
@@ -42,7 +41,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
@EnableAsync
@EnableScheduling
public class Manager {
public static void main(String[] args) {
SpringApplication.run(Manager.class, args);
@@ -531,7 +531,7 @@ public class MonitorServiceImpl implements MonitorService {
MonitorDto monitorDto = new MonitorDto();
List<Param> params = paramDao.findParamsByMonitorId(id);
monitorDto.setParams(params);
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp()) || monitor.getType() == CommonConstants.MONITOR_TYPE_PUSH_AUTO_CREATE) {
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp())) {
List<CollectRep.MetricsData> metricsDataList = warehouseService.queryMonitorMetricsData(id);
List<String> metrics = metricsDataList.stream().map(CollectRep.MetricsData::getMetrics).collect(Collectors.toList());
monitorDto.setMetrics(metrics);
@@ -1,247 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The monitoring type 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: dahua
# The monitoring i18n name
name:
zh-CN: 大华
en-US: Dahua
# The description and help of this monitoring type
help:
zh-CN: 通过http接口监控大华设备状态,获取设备健康数据。
en-US: Monitor Dahua devices through http interface to collect health data.
# Input params define for monitoring(render web ui by the definition)
params:
- field: host
name:
zh-CN: 主机Host
en-US: Host
type: host
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
type: number
range: '[0,65535]'
required: true
defaultValue: 80
- field: timeout
name:
zh-CN: 超时时间(ms)
en-US: Timeout(ms)
type: number
range: '[1000,60000]'
required: true
defaultValue: 5000
- field: username
name:
zh-CN: 用户名
en-US: Username
type: text
required: true
- field: password
name:
zh-CN: 密码
en-US: Password
type: password
required: true
- field: ssl
name:
zh-CN: 启用HTTPS
en-US: SSL
type: boolean
required: false
defaultValue: false
# collect metrics config list
metrics:
- name: network_info # 指标集合名称
i18n:
zh-CN: 网络信息
en-US: Network Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /cgi-bin/configManager.cgi?action=getConfig&name=Network
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: config
fields:
- field: default_interface
type: 1
i18n:
zh-CN: 默认网卡
en-US: Default Interface
- field: domain_name
type: 1
i18n:
zh-CN: 域名
en-US: Domain Name
- field: hostname
type: 1
i18n:
zh-CN: 主机名
en-US: Hostname
- field: eth0_ip_address
type: 1
i18n:
zh-CN: 网卡 eth0 IP地址
en-US: Interface eth0 IP Address
- field: eth0_gateway
type: 1
i18n:
zh-CN: 网卡 eth0 默认网关
en-US: Interface eth0 Default Gateway
- field: eth0_mac_address
type: 1
i18n:
zh-CN: 网卡 eth0 物理地址
en-US: Interface eth0 Physical Address
- field: eth0_subnet_mask
type: 1
i18n:
zh-CN: 网卡 eth0 子网掩码
en-US: Interface eth0 Subnet Mask
- field: eth0_mtu
type: 0
i18n:
zh-CN: 网卡 eth0 MTU
en-US: Interface eth0 MTU
- field: eth0_dns1
type: 1
i18n:
zh-CN: 网卡 eth0 DNS服务器1
en-US: Interface eth0 DNS Server 1
- field: eth0_dns2
type: 1
i18n:
zh-CN: 网卡 eth0 DNS服务器2
en-US: Interface eth0 DNS Server 2
aliasFields:
- table.Network.DefaultInterface
- table.Network.Domain
- table.Network.Hostname
- table.Network.eth0.IPAddress
- table.Network.eth0.DefaultGateway
- table.Network.eth0.PhysicalAddress
- table.Network.eth0.SubnetMask
- table.Network.eth0.MTU
- table.Network.eth0.DnsServers[0]
- table.Network.eth0.DnsServers[1]
calculates:
- default_interface=table.Network.DefaultInterface
- domain_name=table.Network.Domain
- hostname=table.Network.Hostname
- eth0_ip_address=table.Network.eth0.IPAddress
- eth0_gateway=table.Network.eth0.DefaultGateway
- eth0_mac_address=table.Network.eth0.PhysicalAddress
- eth0_subnet_mask=table.Network.eth0.SubnetMask
- eth0_mtu=table.Network.eth0.MTU
- eth0_dns1=table.Network.eth0.DnsServers[0]
- eth0_dns2=table.Network.eth0.DnsServers[1]
- name: user_info
i18n:
zh-CN: 用户信息
en-US: User Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /cgi-bin/userManager.cgi?action=getActiveUserInfoAll
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: config
parseScript: users
fields:
- field: ClientAddress
type: 1
i18n:
zh-CN: 客户端地址
en-US: ClientAddress
- field: Name
type: 1
i18n:
zh-CN: 客户端用户
en-US: Name
- field: ClientType
type: 1
i18n:
zh-CN: 客户端登录类型
en-US: ClientType
- field: LoginTime
type: 1
i18n:
zh-CN: 客户端登录时间
en-US: LoginTime
- name: ntp_info
i18n:
zh-CN: 校时信息
en-US: Ntp Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /cgi-bin/configManager.cgi?action=getConfig&name=NTP
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: config
fields:
- field: ntp_address
type: 1
i18n:
zh-CN: 校时服务器
en-US: Ntp Address
- field: ntp_port
type: 1
i18n:
zh-CN: 校时端口
en-US: Ntp Port
- field: ntp_update_period
type: 1
i18n:
zh-CN: 校时间隔
en-US: Ntp Update Period
aliasFields:
- table.NTP.Address
- table.NTP.Port
- table.NTP.UpdatePeriod
calculates:
- ntp_address=table.NTP.Address
- ntp_port=table.NTP.Port
- ntp_update_period=table.NTP.UpdatePeriod
@@ -1,268 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The monitoring type 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
- field: deviceID
type: 1
i18n:
zh-CN: 设备ID
en-US: Device ID
- field: firmwareVersion
type: 1
i18n:
zh-CN: 固件版本
en-US: Firmware Version
- field: model
type: 1
i18n:
zh-CN: 设备型号
en-US: Device Model
- field: macAddress
type: 1
i18n:
zh-CN: mac地址
en-US: Mac Address
- name: status
i18n:
zh-CN: 设备状态
en-US: Status
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /ISAPI/System/status
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: xmlPath
parseScript: //DeviceStatus
fields:
- field: CPU_utilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
- field: memory_usage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
- field: memory_available
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
- field: cache_size
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
- field: net_port_1_speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
- field: net_port_2_speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
- field: boot_time
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
- field: device_uptime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
- field: last_calibration_time
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
- field: last_calibration_time_diff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
- field: avg_upload_time
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
- field: max_upload_time
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
- field: min_upload_time
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
- field: last_calibration_mode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
- field: last_calibration_address
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
- field: response_time
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
aliasFields:
- CPUList/CPU/cpuUtilization
- MemoryList/Memory/memoryUsage
- MemoryList/Memory/memoryAvailable
- MemoryList/Memory/cacheSize
- NetPortStatusList/NetPortStatus[id='1']/workSpeed
- NetPortStatusList/NetPortStatus[id='2']/workSpeed
- bootTime
- deviceUpTime
- lastCalibrationTime
- lastCalibrationTimeDiff
- uploadTimeConsumingList/avgTime
- uploadTimeConsumingList/maxTime
- uploadTimeConsumingList/minTime
- lastCalibrationTimeMode
- lastCalibrationTimeAddress
- responseTime
calculates:
- CPU_utilization=CPUList/CPU/cpuUtilization
- memory_usage=MemoryList/Memory/memoryUsage
- memory_available=MemoryList/Memory/memoryAvailable
- cache_size=MemoryList/Memory/cacheSize
- net_port_1_speed=NetPortStatusList/NetPortStatus[id='1']/workSpeed
- net_port_2_speed=NetPortStatusList/NetPortStatus[id='2']/workSpeed
- boot_time=bootTime
- device_uptime=deviceUpTime
- last_calibration_time=lastCalibrationTime
- last_calibration_time_diff=lastCalibrationTimeDiff
- avg_upload_time=uploadTimeConsumingList/avgTime
- max_upload_time=uploadTimeConsumingList/maxTime
- min_upload_time=uploadTimeConsumingList/minTime
- last_calibration_mode=lastCalibrationTimeMode
- last_calibration_address=lastCalibrationTimeAddress
- response_time=responseTime
units:
- memory_usage=KB->MB
- memory_available=KB->MB
- cache_size=KB->MB
@@ -1,263 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The monitoring type 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^_^ ]
@@ -1,154 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The monitoring type 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: uniview
# The monitoring i18n name
name:
zh-CN: 宇视
en-US: Uniview
# The description and help of this monitoring type
help:
zh-CN: 通过HTTP接口监控宇视设备状态,获取设备健康数据。
en-US: Monitor Uniview devices through HTTP interface to collect health data.
# Input params define for monitoring(render web ui by the definition)
params:
- field: host
name:
zh-CN: 主机Host
en-US: Host
type: host
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
type: number
range: '[0,65535]'
required: true
defaultValue: 80
- field: timeout
name:
zh-CN: 超时时间(ms)
en-US: Timeout(ms)
type: number
range: '[1000,60000]'
required: true
defaultValue: 5000
- field: username
name:
zh-CN: 用户名
en-US: Username
type: text
required: true
- field: password
name:
zh-CN: 密码
en-US: Password
type: password
required: true
- field: ssl
name:
zh-CN: 启用HTTPS
en-US: SSL
type: boolean
required: false
defaultValue: false
# collect metrics config list
metrics:
- name: system_info
i18n:
zh-CN: 系统信息
en-US: System Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /LAPI/V1.0/System/DeviceInfo
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: jsonPath
parseScript: '$.Response.Data'
fields:
- field: DeviceName
type: 1
i18n:
zh-CN: 设备名称
en-US: Device Name
- field: SerialNumber
type: 1
i18n:
zh-CN: 序列号
en-US: SerialNumber
- field: FirmwareVersion
type: 1
i18n:
zh-CN: 固件版本
en-US: Firmware Version
- field: DeviceModel
type: 1
i18n:
zh-CN: 设备型号
en-US: Device Model
- name: ntp_info
i18n:
zh-CN: 校时信息
en-US: NTP Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /LAPI/V1.0/System/Time/NTP
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: jsonPath
parseScript: '$.Response.Data.NTPServerInfos[0]'
fields:
- field: IPAddress
type: 1
i18n:
zh-CN: IP地址
en-US: IPAddress
- field: Port
type: 1
i18n:
zh-CN: 端口号
en-US: Port
- field: SynchronizeInterval
type: 1
i18n:
zh-CN: 同步间隔
en-US: SynchronizeInterval
- field: Enabled
type: 1
i18n:
zh-CN: 是否启用
en-US: Enabled
@@ -1,42 +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.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;
}
}
@@ -1,47 +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.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;
}
}
@@ -1,82 +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.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() {}
}
@@ -1,41 +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.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;
}
}
@@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.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,9 +22,11 @@ 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.config.PushErrorRequestWrapper;
import org.apache.hertzbeat.push.config.PushSuccessRequestWrapper;
import org.apache.hertzbeat.push.service.PushGatewayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -35,23 +37,22 @@ import org.springframework.web.bind.annotation.RestController;
*/
@Tag(name = "Metrics Push Gateway API")
@RestController
@RequestMapping(value = "/api/push/prometheus/**")
public class PushPrometheusController {
@RequestMapping(value = "/api/push/pushgateway")
public class PushGatewayController {
@Autowired
private PushGatewayService pushGatewayService;
@PostMapping()
@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())));
@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"));
}
else {
return ResponseEntity.badRequest()
.body(Message.success(String.format("Request %s not matched.", request.getRequestURI())));
return ResponseEntity.ok(Message.success("Push failed"));
}
}
@@ -15,45 +15,19 @@
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.job.protocol;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
package org.apache.hertzbeat.push.dao;
import org.apache.hertzbeat.common.entity.push.PushMetrics;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
/**
* Modbus Protocol
* push metrics dao
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class S7Protocol implements Protocol {
public interface PushMetricsDao extends JpaRepository<PushMetrics, Long> {
PushMetrics findFirstByMonitorIdOrderByTimeDesc(Long monitorId);
/**
* 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;
@Transactional(rollbackFor = Exception.class)
void deleteAllByTimeBefore(Long time);
}
@@ -17,7 +17,6 @@
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;
@@ -25,11 +24,4 @@ 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,24 +19,17 @@
package org.apache.hertzbeat.push.service;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.stereotype.Service;
/**
* push gateway service
* push gateway metrics
*/
@Service
public interface PushGatewayService {
/**
* 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);
boolean pushMetricsData(InputStream inputStream) throws IOException;
}
@@ -15,32 +15,17 @@
* limitations under the License.
*/
package org.apache.hertzbeat.warehouse.service;
package org.apache.hertzbeat.push.service;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import java.util.List;
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
import org.springframework.stereotype.Service;
/**
* metrics data query service
* push metrics
*/
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);
@Service
public interface PushService {
void pushMetricsData(PushMetricsDto pushMetricsData);
/**
* 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);
PushMetricsDto getPushMetricData(Long monitorId, Long time);
}
@@ -19,23 +19,9 @@
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;
@@ -46,88 +32,9 @@ 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 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;
}
public boolean pushMetricsData(InputStream inputStream) throws IOException {
return true;
}
}
@@ -0,0 +1,143 @@
/*
* 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;
}
}
@@ -0,0 +1,93 @@
/*
* 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,11 +22,12 @@ 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;
@@ -37,12 +38,11 @@ import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
/**
* test case for {@link PushPrometheusController}
* test case for {@link PushGatewayController}
*/
@Disabled
@ExtendWith(MockitoExtension.class)
class PushPrometheusControllerTest {
class PushGatewayControllerTest {
private MockMvc mockMvc;
@@ -50,7 +50,7 @@ class PushPrometheusControllerTest {
private PushGatewayService pushGatewayService;
@InjectMocks
private PushPrometheusController gatewayController;
private PushGatewayController gatewayController;
@BeforeEach
void setUp() {
@@ -63,12 +63,14 @@ class PushPrometheusControllerTest {
String mockData = "some metric data";
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(true);
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(true);
mockMvc.perform(post("/api/push/pushgateway")
.contentType(MediaType.APPLICATION_JSON)
.content(mockData))
.andExpect(status().isOk());
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
.andExpect(jsonPath("$.msg").value("Push success"));
}
@Test
@@ -76,12 +78,13 @@ class PushPrometheusControllerTest {
String mockData = "some metric data";
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(false);
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(false);
mockMvc.perform(post("/api/push/pushgateway")
.contentType(MediaType.APPLICATION_JSON)
.content(mockData))
.andExpect(status().isOk());
.andExpect(status().isOk())
.andExpect(jsonPath("$.msg").value("Push failed"));
}
}
@@ -0,0 +1,77 @@
/*
* 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);
}
}
@@ -0,0 +1,132 @@
/*
* 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,8 +56,4 @@ public interface WarehouseConstants {
String MEMORY = "memory";
}
String PROMQL = "promql";
String SQL = "sql";
}
@@ -1,78 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.warehouse.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,12 +18,29 @@
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
@@ -31,15 +48,71 @@ import org.springframework.web.client.RestTemplate;
@Component
@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true")
@Slf4j
public class GreptimePromqlQueryExecutor extends PromqlQueryExecutor {
public class GreptimePromqlQueryExecutor implements QueryExecutor {
private static final String QUERY_PATH = "/v1/prometheus/api/v1/query";
private final GreptimeProperties greptimeProperties;
private final RestTemplate restTemplate;
public GreptimePromqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) {
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);
}
}
@@ -1,166 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.db;
import 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,8 +17,6 @@
package org.apache.hertzbeat.warehouse.db;
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
import java.util.List;
import java.util.Map;
@@ -26,14 +24,8 @@ 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);
}
@@ -1,67 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.db;
import 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,11 +18,29 @@
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
@@ -30,16 +48,71 @@ import org.springframework.web.client.RestTemplate;
@Component
@ConditionalOnProperty(prefix = "warehouse.store.victoria-metrics", name = "enabled", havingValue = "true")
@Slf4j
public class VictoriaMetricsQueryExecutor extends PromqlQueryExecutor {
public class VictoriaMetricsQueryExecutor implements QueryExecutor {
private static final String QUERY_PATH = "/api/v1/query";
private final VictoriaMetricsProperties victoriaMetricsProp;
private final RestTemplate restTemplate;
public VictoriaMetricsQueryExecutor(VictoriaMetricsProperties victoriaMetricsProp, RestTemplate restTemplate) {
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);
}
}
@@ -1,67 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.hertzbeat.warehouse.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;
}
}
-326
View File
@@ -1,326 +0,0 @@
---
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: system_info
i18n:
zh-CN: 系统信息
en-US: System Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /ISAPI/System/deviceInfo
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: xmlPath
parseScript: //DeviceInfo
fields:
- field: deviceName
type: 1
i18n:
zh-CN: 设备名称
en-US: Device Name
- field: deviceID
type: 1
i18n:
zh-CN: 设备ID
en-US: Device ID
- field: firmwareVersion
type: 1
i18n:
zh-CN: 固件版本
en-US: Firmware Version
- field: model
type: 1
i18n:
zh-CN: 设备型号
en-US: Device Model
- field: macAddress
type: 1
i18n:
zh-CN: mac地址
en-US: Mac Address
- name: status
i18n:
zh-CN: 设备状态
en-US: Status
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /ISAPI/System/status
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: xmlPath
parseScript: //DeviceStatus
fields:
- field: CPU_utilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
- field: memory_usage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
- field: memory_available
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
- field: cache_size
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
- field: net_port_1_speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
- field: net_port_2_speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
- field: boot_time
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
- field: device_uptime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
- field: last_calibration_time
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
- field: last_calibration_time_diff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
- field: avg_upload_time
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
- field: max_upload_time
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
- field: min_upload_time
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
- field: last_calibration_mode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
- field: last_calibration_address
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
- field: response_time
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
aliasFields:
- CPUList/CPU/cpuUtilization
- MemoryList/Memory/memoryUsage
- MemoryList/Memory/memoryAvailable
- MemoryList/Memory/cacheSize
- NetPortStatusList/NetPortStatus[id='1']/workSpeed
- NetPortStatusList/NetPortStatus[id='2']/workSpeed
- bootTime
- deviceUpTime
- lastCalibrationTime
- lastCalibrationTimeDiff
- uploadTimeConsumingList/avgTime
- uploadTimeConsumingList/maxTime
- uploadTimeConsumingList/minTime
- lastCalibrationTimeMode
- lastCalibrationTimeAddress
- responseTime
calculates:
- CPU_utilization=CPUList/CPU/cpuUtilization
- memory_usage=MemoryList/Memory/memoryUsage
- memory_available=MemoryList/Memory/memoryAvailable
- cache_size=MemoryList/Memory/cacheSize
- net_port_1_speed=NetPortStatusList/NetPortStatus[id='1']/workSpeed
- net_port_2_speed=NetPortStatusList/NetPortStatus[id='2']/workSpeed
- boot_time=bootTime
- device_uptime=deviceUpTime
- last_calibration_time=lastCalibrationTime
- last_calibration_time_diff=lastCalibrationTimeDiff
- avg_upload_time=uploadTimeConsumingList/avgTime
- max_upload_time=uploadTimeConsumingList/maxTime
- min_upload_time=uploadTimeConsumingList/minTime
- last_calibration_mode=lastCalibrationTimeMode
- last_calibration_address=lastCalibrationTimeAddress
- response_time=responseTime
units:
- memory_usage=KB->MB
- memory_available=KB->MB
- cache_size=KB->MB
+1 -1
View File
@@ -341,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/> in the browser, check if existed the new material package
> 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
## 4. Enter the community voting stage
+3 -4
View File
@@ -20,10 +20,9 @@ sidebar_label: Download
Previous releases of HertzBeat may be affected by security issues, please use the latest one.
:::
| 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) |
| 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)|
| 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
-24
View File
@@ -1,24 +0,0 @@
---
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
@@ -1,45 +0,0 @@
---
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, support [time expression](time_expression) |
| Request BODY | Set the carry BODY request body data, which is valid when PUT or POST request method is used |
| 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 |
-60
View File
@@ -1,60 +0,0 @@
---
id: dahua
title: Monitor Dahua Devices
sidebar_label: Dahua
keywords: [ monitor, dahua ]
---
> Monitor Dahua devices through HTTP interface to collect health data.
## Configuration
| Parameter | Description |
| ----------- | ----------- |
| Host | Target IP/Domain |
| Name | Unique monitor name |
| Port | Network port (default 80) |
| Timeout | Request timeout in ms |
| Username | Device username |
| Password | Device password |
| SSL | Enable HTTPS |
| Interval | Collection interval (≥30s) |
## Metrics
### Network Info
- Default Interface
- Domain Name
- Hostname
- eth0 IP Address
- eth0 Gateway
- eth0 MAC
- eth0 Subnet Mask
- eth0 MTU
- DNS Servers
### User Info
- Client Address
- Username
- Login Type
- Login Time
### NTP Info
- NTP Server
- NTP Port
- Sync Interval
## Implementation
Access device APIs via:
1. Network: `/cgi-bin/configManager.cgi?action=getConfig&name=Network`
2. Users: `/cgi-bin/userManager.cgi?action=getActiveUserInfoAll`
3. NTP: `/cgi-bin/configManager.cgi?action=getConfig&name=NTP`
Using Digest Auth and parsing config format responses.
-7
View File
@@ -116,13 +116,6 @@ More details see&emsp;&#x1F449;&emsp;[Alarm center](alarm_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
@@ -1,60 +0,0 @@
---
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)

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

+17 -17
View File
@@ -16,23 +16,23 @@ sidebar_label: Template Marketplace
1. **No filter: displayed in order of upload**
![search.png](../../static/img/template-marketplace/search.png)
![search.png](template-marketplace-img/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](../../static/img/template-marketplace/search-category.png)
![search-category.png](template-marketplace-img/search-category.png)
3. **Fuzzy search by Title**
![search-name.png](../../static/img/template-marketplace/search-name.png)
![search-name.png](template-marketplace-img/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](../../static/img/template-marketplace/hover-window-guest.png)![img_1.png](../../static/img/template-marketplace/hover-window-user.png)
![img.png](template-marketplace-img/hover-window-guest.png)![img_1.png](template-marketplace-img/hover-window-user.png)
5. **Sort: Eight Sorting Methods**
@@ -48,19 +48,19 @@ sidebar_label: Template Marketplace
> **📋Todo:** Upgrade to MarkDown format
![img.png](../../static/img/template-marketplace/detail-info.png)
![img.png](template-marketplace-img/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](../../static/img/template-marketplace/detail-version.png)
![img.png](template-marketplace-img/detail-version.png)
3. **FAQ**
> **📋Todo:** Discussion or issue Q&A section
![img.png](../../static/img/template-marketplace/detail-faq.png)
![img.png](template-marketplace-img/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](../../static/img/template-marketplace/download.png)
![img.png](template-marketplace-img/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](../../static/img/template-marketplace/share.png)
![img.png](template-marketplace-img/share.png)
### User Center
@@ -94,21 +94,21 @@ sidebar_label: Template Marketplace
>
> **📋Todo:** Function to update template information
![img_1.png](../../static/img/template-marketplace/user-center-asset.png)
![img_1.png](template-marketplace-img/user-center-asset.png)
![img.png](../../static/img/template-marketplace/asset-detail.png)
![img.png](template-marketplace-img/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](../../static/img/template-marketplace/asset-upgrade.png)
![img.png](template-marketplace-img/asset-upgrade.png)
3. **Star**
![img.png](../../static/img/template-marketplace/user-center-star.png)
![img.png](template-marketplace-img/user-center-star.png)
![img.png](../../static/img/template-marketplace/user-center-star-detail.png)
![img.png](template-marketplace-img/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](../../static/img/template-marketplace/user-center-upload.png)
![img.png](template-marketplace-img/user-center-upload.png)
### Sign Up & Login
@@ -128,13 +128,13 @@ sidebar_label: Template Marketplace
>
> **📋Todo:** Captcha function, email verification function
![img.png](../../static/img/template-marketplace/sign-up.png)
![img.png](template-marketplace-img/sign-up.png)
2. **Login**
> **📋Todo:** Captcha function and forgot password function
![img.png](../../static/img/template-marketplace/email-login.png)
![img.png](template-marketplace-img/email-login.png)
## Development Steps
-1
View File
@@ -53,7 +53,6 @@ ${FORMATTER [{ + | - }<DURATION> <TIME_UNIT>]}
#### Where to Use
- Request parameters for HTTP protocol monitoring types
- Request Body for HTTP protocol monitoring types
#### Usage Examples
-47
View File
@@ -1,47 +0,0 @@
---
id: uniview
title: Monitor Uniview Devices
sidebar_label: Uniview
keywords: [ monitor, uniview ]
---
> Monitor Uniview devices through HTTP interface.
## Configuration
| Parameter | Description |
| ----------- | ----------- |
| Host | Device IP/Domain |
| Name | Unique identifier |
| Port | Default 80 |
| Timeout | Milliseconds |
| Username | Auth username |
| Password | Auth password |
| SSL | HTTPS Enable |
| Interval | ≥30 seconds |
## Metrics
### System Info
- Device Name
- Serial Number
- Firmware Version
- Device Model
### NTP Info
- NTP Server IP
- NTP Port
- Sync Interval
- NTP Status
## Implementation
Access device APIs:
1. System: `/LAPI/V1.0/System/DeviceInfo`
2. NTP: `/LAPI/V1.0/System/Time/NTP`
Using Digest Authentication and parsing JSON responses.
@@ -1,6 +1,6 @@
{
"version.label": {
"message": "v1.7.x",
"message": "v1.6.x",
"description": "The label for version current"
},
"sidebar.docs.category.quickstart": {
@@ -51,10 +51,6 @@
"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"
@@ -0,0 +1,122 @@
{
"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,6 +1,6 @@
{
"version.label": {
"message": "v1.7.x",
"message": "v1.6.x",
"description": "The label for version current"
},
"sidebar.docs.category.quickstart": {
@@ -55,10 +55,6 @@
"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"
@@ -1,326 +0,0 @@
---
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: system_info
i18n:
zh-CN: 系统信息
en-US: System Info
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /ISAPI/System/deviceInfo
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: xmlPath
parseScript: //DeviceInfo
fields:
- field: deviceName
type: 1
i18n:
zh-CN: 设备名称
en-US: Device Name
- field: deviceID
type: 1
i18n:
zh-CN: 设备ID
en-US: Device ID
- field: firmwareVersion
type: 1
i18n:
zh-CN: 固件版本
en-US: Firmware Version
- field: model
type: 1
i18n:
zh-CN: 设备型号
en-US: Device Model
- field: macAddress
type: 1
i18n:
zh-CN: mac地址
en-US: Mac Address
- name: status
i18n:
zh-CN: 设备状态
en-US: Status
priority: 0
protocol: http
http:
host: ^_^host^_^
port: ^_^port^_^
ssl: ^_^ssl^_^
url: /ISAPI/System/status
method: GET
timeout: ^_^timeout^_^
authorization:
type: Digest Auth
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
parseType: xmlPath
parseScript: //DeviceStatus
fields:
- field: CPU_utilization
i18n:
zh-CN: CPU 利用率
en-US: CPU Utilization
type: 0
unit: '%'
- field: memory_usage
i18n:
zh-CN: 内存使用量
en-US: Memory Usage
type: 0
unit: MB
- field: memory_available
i18n:
zh-CN: 可用内存
en-US: Memory Available
type: 0
unit: MB
- field: cache_size
i18n:
zh-CN: 缓存大小
en-US: Cache Size
type: 0
unit: MB
- field: net_port_1_speed
i18n:
zh-CN: 网口1速度
en-US: Net Port 1 Speed
type: 0
unit: Mbps
- field: net_port_2_speed
i18n:
zh-CN: 网口2速度
en-US: Net Port 2 Speed
type: 0
unit: Mbps
- field: boot_time
i18n:
zh-CN: 启动时间
en-US: Boot Time
type: 1
- field: device_uptime
i18n:
zh-CN: 运行时长
en-US: Device Uptime
type: 1
- field: last_calibration_time
i18n:
zh-CN: 上次校时时间
en-US: Last Calibration Time
type: 1
- field: last_calibration_time_diff
i18n:
zh-CN: 上次校时时间差
en-US: Last Calibration Time Diff
type: 0
unit: s
- field: avg_upload_time
i18n:
zh-CN: 平均上传耗时
en-US: Avg Upload Time
type: 0
unit: ms
- field: max_upload_time
i18n:
zh-CN: 最大上传耗时
en-US: Max Upload Time
type: 0
unit: ms
- field: min_upload_time
i18n:
zh-CN: 最小上传耗时
en-US: Min Upload Time
type: 0
unit: ms
- field: last_calibration_mode
i18n:
zh-CN: 上次校时模式
en-US: Last Calibration Mode
type: 1
- field: last_calibration_address
i18n:
zh-CN: 上次校时地址
en-US: Last Calibration Address
type: 1
- field: response_time
i18n:
zh-CN: 响应时间
en-US: Response Time
type: 0
unit: ms
aliasFields:
- CPUList/CPU/cpuUtilization
- MemoryList/Memory/memoryUsage
- MemoryList/Memory/memoryAvailable
- MemoryList/Memory/cacheSize
- NetPortStatusList/NetPortStatus[id='1']/workSpeed
- NetPortStatusList/NetPortStatus[id='2']/workSpeed
- bootTime
- deviceUpTime
- lastCalibrationTime
- lastCalibrationTimeDiff
- uploadTimeConsumingList/avgTime
- uploadTimeConsumingList/maxTime
- uploadTimeConsumingList/minTime
- lastCalibrationTimeMode
- lastCalibrationTimeAddress
- responseTime
calculates:
- CPU_utilization=CPUList/CPU/cpuUtilization
- memory_usage=MemoryList/Memory/memoryUsage
- memory_available=MemoryList/Memory/memoryAvailable
- cache_size=MemoryList/Memory/cacheSize
- net_port_1_speed=NetPortStatusList/NetPortStatus[id='1']/workSpeed
- net_port_2_speed=NetPortStatusList/NetPortStatus[id='2']/workSpeed
- boot_time=bootTime
- device_uptime=deviceUpTime
- last_calibration_time=lastCalibrationTime
- last_calibration_time_diff=lastCalibrationTimeDiff
- avg_upload_time=uploadTimeConsumingList/avgTime
- max_upload_time=uploadTimeConsumingList/maxTime
- min_upload_time=uploadTimeConsumingList/minTime
- last_calibration_mode=lastCalibrationTimeMode
- last_calibration_address=lastCalibrationTimeAddress
- response_time=responseTime
units:
- memory_usage=KB->MB
- memory_available=KB->MB
- cache_size=KB->MB
@@ -22,7 +22,6 @@ 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) |
@@ -1,24 +0,0 @@
---
id: alarm_group
title: 分组收敛
sidebar_label: 分组收敛
keywords: [ 开源监控系统, 告警收敛, 告警分组 ]
---
> 分组收敛支持对指定分组标签的告警进行分组合并,对时间段的相同重复告警去重收敛。 当阈值规则触发告警或外部告警上报后,会进入到分组收敛进行告警分组,告警去重,以避免大量告警消息导致告警风暴。
## 分组策略参数配置
- 策略名称:唯一标识分组策略的名称
- 分组标签:告警分组标签,最多支持添加10个标签
> 标签来源:监控,阈值规则,外部告警携带的标签
- 等待时间:新告警产生后等待时间,在此时间内收到的相同告警将被分组,默认30秒
> 当一条新(无法加入已有分组)的告警产生,分组收敛将按照 `等待时间` 等待,在此期间,相同告警或满足分组条件的告警将被分组。直到当前时间与该分组第一条告警产生时间间隔超过 `等待时间`,分组后的告警才被发送到告警抑制模块进行后续处理。
- 间隔时间:发送分组告警通知的最小时间间隔,避免告警通知过于频繁,默认5分钟
- 重复间隔:重复告警的最小通知间隔,对于持续触发的告警,避免重复发送通知,默认4小时
**注意**:只有分组后的告警才能使用抑制规则进行告警抑制。
@@ -1,38 +0,0 @@
---
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请求方式时有效,支持[时间表达式](time_expression) |
| 请求BODY | 设置携带BODY请求体数据,PUT POST请求方式时有效 |
| 采集间隔 | 监控周期性采集数据间隔时间,单位秒,可设置的最小间隔为30秒 |
| 是否探测 | 新增监控前是否先探测检查监控可用性,探测成功才会继续新增修改操作 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
@@ -31,6 +31,6 @@ keywords: [ 开源监控系统, 开源网站监控, HTTP API监控 ]
#### 指标集合:summary
| 指标名称 | 指标单位 | 指标帮助描述 |
| 指标名称 | 指标单位 | 指标帮助描述 |
|--------------|------|--------|
| responseTime | ms毫秒 | 网站响应时间 |
@@ -1,60 +0,0 @@
---
id: dahua
title: 监控 大华设备
sidebar_label: 大华
keywords: [ monitor, dahua, 大华 ]
---
> 通过HTTP接口监控大华设备状态,获取设备健康数据。
## 监控配置参数
| 参数名称 | 参数帮助描述 |
| ----------- | ----------- |
| 监控Host | 被监控的对端IP或域名 |
| 监控名称 | 标识此监控的唯一名称 |
| 端口 | 网络请求端口,默认80 |
| 超时时间 | 请求超时时间,单位毫秒 |
| 用户名 | 设备登录用户名 |
| 密码 | 设备登录密码 |
| 启用HTTPS | 是否启用HTTPS协议 |
| 采集间隔 | 数据采集周期(≥30秒) |
## 采集指标
### 网络信息
- 默认网卡
- 域名
- 主机名
- 网卡 eth0 IP地址
- 网卡 eth0 默认网关
- 网卡 eth0 物理地址
- 网卡 eth0 子网掩码
- 网卡 eth0 MTU
- DNS服务器1/2
### 用户信息
- 客户端地址
- 客户端用户
- 客户端登录类型
- 客户端登录时间
### 校时信息
- 校时服务器
- 校时端口
- 校时间隔
## 实现原理
通过大华设备HTTP接口获取数据:
1. 网络信息:`/cgi-bin/configManager.cgi?action=getConfig&name=Network`
2. 用户信息:`/cgi-bin/userManager.cgi?action=getActiveUserInfoAll`
3. 校时信息:`/cgi-bin/configManager.cgi?action=getConfig&name=NTP`
使用Digest认证方式,解析设备返回的配置数据格式。
@@ -116,13 +116,6 @@ 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)
### 告警通知
> 触发告警信息后,除了显示在告警中心列表外,还可以用指定方式(邮件钉钉微信飞书等)通知给指定接收人。
@@ -1,60 +0,0 @@
---
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,7 +53,6 @@ ${FORMATTER [{ + | - }<DURATION> <TIME_UNIT>]}
#### 在哪里可以使用
- HTTP协议监控类型的请求参数
- HTTP协议监控类型的请求体
#### 使用示例
@@ -1,47 +0,0 @@
---
id: uniview
title: 监控 宇视设备
sidebar_label: 宇视
keywords: [ monitor, uniview, 宇视 ]
---
> 通过HTTP接口监控宇视设备状态,获取设备健康数据。
## 监控配置参数
| 参数名称 | 参数帮助描述 |
| ----------- | ----------- |
| 监控Host | 设备IP/域名 |
| 监控名称 | 唯一标识名称 |
| 端口 | 默认80端口 |
| 超时时间 | 毫秒级超时设置 |
| 用户名 | 认证用户名 |
| 密码 | 认证密码 |
| 启用HTTPS | HTTPS开关 |
| 采集间隔 | ≥30秒采集周期 |
## 采集指标
### 系统信息
- 设备名称
- 序列号
- 固件版本
- 设备型号
### 校时信息
- NTP服务器IP
- 校时端口
- 同步间隔
- 校时启用状态
## 实现原理
通过宇视HTTP API接口:
1. 系统信息:`/LAPI/V1.0/System/DeviceInfo`
2. 校时信息:`/LAPI/V1.0/System/Time/NTP`
使用Digest认证,解析JSON格式响应数据。
@@ -41,7 +41,7 @@ Docker 工具自身的下载请参考 [Docker官网文档](https://docs.docker.c
### 创建数据库实例
> [TDengine CLI 小技巧](https://docs.taosdata.com/develop/)
> [TDengine CLI 小技巧](https://docs.taosdata.com/develop/model/)
1. 进入数据库Docker容器
@@ -0,0 +1,122 @@
{
"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"
}
}
@@ -0,0 +1,159 @@
---
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
```
@@ -67,25 +67,18 @@ sidebar_label: 教程一:适配一款HTTP协议监控
样例:自定义一个名称为`hertzbeat`的自定义监控类型,其使用HTTP协议采集指标数据。
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# The monitoring type eg: linux windows tomcat mysql aws...
# 监控应用类型名称(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: hertzbeat
name:
zh-CN: HertzBeat
en-US: HertzBeat
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 Hertzbeat 监控系统的通用指标进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控系统</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor through general performance metric. You could click the "<i>New HertzBeat Monitor</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對Hertzbeat監控系統的通用名額進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控系統</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat
zh-CN: HertzBeat监控系统
en-US: HertzBeat Monitor
params:
- field: host
name:
zh-CN: 目标Host
en-US: Target Host
zh-CN: 主机Host
en-US: Host
type: host
required: true
- field: port
@@ -136,16 +129,21 @@ params:
type: password
required: false
hide: true
# collect metrics config list
# 采集指标配置列表
metrics:
# the first metrics summary
# attention: Built-in monitoring metrics contains (responseTime - Response time)
# metrics - summary
# 监控指标 - summary
- name: summary
# 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
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 0
# collect metrics content
# 具体监控指标列表
fields:
# metrics content contains field-metric name, type-metric type:0-number,1-string, instance-if is metrics, unit-metric unit('%','ms','MB')
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: responseTime
type: 0
unit: ms
- field: app
type: 1
label: true
@@ -156,33 +154,33 @@ metrics:
- field: size
type: 0
- field: availableSize
type: 0
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk, we use HTTP protocol here
type: 0
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk, 我们这里使用HTTP协议
protocol: http
# the config content when protocol is http
# 当protocol为http协议时具体的采集配置
http:
# host: ipv4 ipv6 domain
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# http port
# 端口
port: ^_^port^_^
# http url, we don't need to enter a parameter here, just set the fixed value to /api/summary
# url请求接口路径,我们这里不需要输入传参,写死为 /api/summary
url: /api/summary
timeout: ^_^timeout^_^
# http method: GET POST PUT DELETE PATCH, default fixed value is GET
# 请求方式 GET POST PUT DELETE PATCH,写死为
method: GET
# if enabled https, default value is false
# 是否启用ssl/tls,即是http还是https,默认false
ssl: ^_^ssl^_^
# http auth
# 认证
authorization:
# http auth type: Basic Auth, Digest Auth, Bearer Token
# 认证方式: Basic Auth, Digest Auth, Bearer Token
type: ^_^authType^_^
basicAuthUsername: ^_^username^_^
basicAuthPassword: ^_^password^_^
digestAuthUsername: ^_^username^_^
digestAuthPassword: ^_^password^_^
# http response data parse type: default-hertzbeat rule, jsonpath-jsonpath script, website-for website monitoring, we use jsonpath to parse response data here
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控,我们这里使用jsonpath来解析响应数据
parseType: jsonPath
parseScript: '$.data.apps.*'
parseScript: '$.data.apps.*'
```
@@ -44,49 +44,41 @@ sidebar_label: 教程二:获取TOKEN后续认证使用
我们直接复用 app-hertzbeat.yml 里面的定义内容,修改为我们当前的监控类型`hertzbeat_auth`配置参数, 比如 `app, category等`
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# The monitoring type eg: linux windows tomcat mysql aws...
# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: hertzbeat_token
# The monitoring i18n name
name:
zh-CN: HertzBeat(Token)
en-US: HertzBeat(Token)
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 HertzBeat监控(Token)进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控(Token)</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor(Token). You could click the "<i>New HertzBeat Monitor(Token)</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對HertzBeat監控(Token)進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控(Token</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat_token
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat_token
# Input params define for monitoring(render web ui by the definition)
zh-CN: HertzBeat监控(Token)
en-US: HertzBeat Monitor(Token)
params:
# field-param field key
# field-字段名称标识符
- field: host
# name-param field display i18n name
# name-参数字段显示名称
name:
zh-CN: 目标Host
en-US: Target Host
# type-param field type(most mapping the html input type)
zh-CN: 主机Host
en-US: Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# required-true or false
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
# 端口默认值
defaultValue: 1157
placeholder: 'Please input port'
# 参数输入框提示信息
placeholder: '请输入端口'
- field: ssl
name:
zh-CN: 启动SSL
en-US: SSL
# type-param field type(boolean mapping the html switch tag)
# typeboolean时,前端用switch展示开关
type: boolean
required: false
- field: contentType
@@ -103,6 +95,9 @@ params:
type: textarea
placeholder: 'Available When POST PUT'
required: false
# 采集指标配置列表 todo 下方配置
metrics: ......
```
### 定义监控指标`auth`登录请求获取`token`
@@ -111,49 +106,41 @@ params:
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# The monitoring type eg: linux windows tomcat mysql aws...
# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: hertzbeat_token
# The monitoring i18n name
name:
zh-CN: HertzBeat(Token)
en-US: HertzBeat(Token)
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 HertzBeat监控(Token)进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控(Token)</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor(Token). You could click the "<i>New HertzBeat Monitor(Token)</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對HertzBeat監控(Token)進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控(Token</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat_token
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat_token
# Input params define for monitoring(render web ui by the definition)
zh-CN: HertzBeat监控(Token)
en-US: HertzBeat Monitor(Token)
params:
# field-param field key
# field-字段名称标识符
- field: host
# name-param field display i18n name
# name-参数字段显示名称
name:
zh-CN: 目标Host
en-US: Target Host
# type-param field type(most mapping the html input type)
zh-CN: 主机Host
en-US: Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# required-true or false
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
# 端口默认值
defaultValue: 1157
placeholder: 'Please input port'
# 参数输入框提示信息
placeholder: '请输入端口'
- field: ssl
name:
zh-CN: 启动SSL
en-US: SSL
# type-param field type(boolean mapping the html switch tag)
# typeboolean时,前端用switch展示开关
type: boolean
required: false
- field: contentType
@@ -170,43 +157,40 @@ params:
type: textarea
placeholder: 'Available When POST PUT'
required: false
# collect metrics config list
# 采集指标配置列表
metrics:
# metrics - auth
# 第一个监控指标 auth
# 注意:内置监控指标有 (responseTime - 响应时间)
- name: auth
# 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
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 0
# collect metrics content
# 具体监控指标列表
fields:
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: token
type: 1
- field: refreshToken
type: 1
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# the config content when protocol is http
# 当protocol为http协议时具体的采集配置
http:
# http host: ipv4 ipv6 domain
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# http port
# 端口
port: ^_^port^_^
# http url
# url请求接口路径
url: /api/account/auth/form
# http method: GET POST PUT DELETE PATCH
# 请求方式 GET POST PUT DELETE PATCH
method: POST
# if enabled https
# 是否启用ssl/tls,即是http还是https,默认false
ssl: ^_^ssl^_^
payload: ^_^payload^_^
# http request header content
# 请求头内容
headers:
content-type: ^_^contentType^_^
^_^headers^_^: ^_^headers^_^
# http request params
params:
^_^params^_^: ^_^params^_^
# http response data parse type: default-hertzbeat rule, jsonpath-jsonpath script, website-for website monitoring, prometheus-prometheus exporter rule
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
parseType: jsonPath
parseScript: '$.data'
@@ -249,49 +233,41 @@ metrics:
```yaml
# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等
category: custom
# The monitoring type eg: linux windows tomcat mysql aws...
# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: hertzbeat_token
# The monitoring i18n name
name:
zh-CN: HertzBeat(Token)
en-US: HertzBeat(Token)
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 对 HertzBeat监控(Token)进行测量监控。<br>您可以点击 “<i>新建 HertzBeat监控(Token)</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: Hertzbeat monitors HertzBeat Monitor(Token). You could click the "<i>New HertzBeat Monitor(Token)</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat對HertzBeat監控(Token)進行量測監控。<br>您可以點擊“<i>新建HertzBeat監控(Token</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hertzbeat_token
en-US: https://hertzbeat.apache.org/docs/help/hertzbeat_token
# Input params define for monitoring(render web ui by the definition)
zh-CN: HertzBeat监控(Token)
en-US: HertzBeat Monitor(Token)
params:
# field-param field key
# field-字段名称标识符
- field: host
# name-param field display i18n name
# name-参数字段显示名称
name:
zh-CN: 目标Host
en-US: Target Host
# type-param field type(most mapping the html input type)
zh-CN: 主机Host
en-US: Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# required-true or false
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name:
zh-CN: 端口
en-US: Port
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
# 端口默认值
defaultValue: 1157
placeholder: 'Please input port'
# 参数输入框提示信息
placeholder: '请输入端口'
- field: ssl
name:
zh-CN: 启动SSL
en-US: SSL
# type-param field type(boolean mapping the html switch tag)
# typeboolean时,前端用switch展示开关
type: boolean
required: false
- field: contentType
@@ -308,72 +284,83 @@ params:
type: textarea
placeholder: 'Available When POST PUT'
required: false
# collect metrics config list
# 采集指标配置列表
metrics:
# metrics - auth
# 第一个监控指标 cpu
# 注意:内置监控指标有 (responseTime - 响应时间)
- name: auth
# 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
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 0
# collect metrics content
# 具体监控指标列表
fields:
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: token
type: 1
- field: refreshToken
type: 1
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# the config content when protocol is http
# 当protocol为http协议时具体的采集配置
http:
# http host: ipv4 ipv6 domain
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# http port
# 端口
port: ^_^port^_^
# http url
# url请求接口路径
url: /api/account/auth/form
# http method: GET POST PUT DELETE PATCH
# 请求方式 GET POST PUT DELETE PATCH
method: POST
# if enabled https
# 是否启用ssl/tls,即是http还是https,默认false
ssl: ^_^ssl^_^
payload: ^_^payload^_^
# http request header content
# 请求头内容
headers:
content-type: ^_^contentType^_^
^_^headers^_^: ^_^headers^_^
# http request params
# 请求参数内容
params:
^_^params^_^: ^_^params^_^
# http response data parse type: default-hertzbeat rule, jsonpath-jsonpath script, website-for website monitoring, prometheus-prometheus exporter rule
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
parseType: jsonPath
parseScript: '$.data'
- name: summary
# 指标调度优先级(0-127)越小优先级越高,优先级低的指标会等优先级高的指标采集完成后才会被调度,相同优先级的指标会并行调度采集
# 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度
priority: 1
# 具体监控指标列表
fields:
- field: app
type: 1
label: true
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label是否为标签 unit:指标单位
- field: category
type: 1
- field: status
type: 0
- field: app
type: 1
- field: size
type: 0
- field: availableSize
- field: status
type: 0
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# 当protocol为http协议时具体的采集配置
http:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
# url请求接口路径
url: /api/summary
# 请求方式 GET POST PUT DELETE PATCH
method: GET
# 是否启用ssl/tls,即是http还是https,默认false
ssl: ^_^ssl^_^
# 认证
authorization:
# 认证方式: Basic Auth, Digest Auth, Bearer Token
type: Bearer Token
# ^o^xxx^o^ ^o^ substitution represents the value of the acquisition metric xxx of the previous priority
bearerTokenToken: ^o^token^o^
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
parseType: jsonPath
parseScript: '$.data.apps.*'

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