mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cd50eb958 | ||
|
|
5d6266acb1 | ||
|
|
93eaade19d | ||
|
|
676d8d738b | ||
|
|
f854d3d869 | ||
|
|
32f8014df1 | ||
|
|
0385892674 | ||
|
|
26230985fd | ||
|
|
cf1c1ebdc2 | ||
|
|
a011d9b52f | ||
|
|
3af8adf37f | ||
|
|
944d128129 | ||
|
|
a1965963d1 | ||
|
|
a2ad01248b |
+1
-1
@@ -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}
|
||||
|
||||
+133
-15
@@ -91,8 +91,12 @@ import org.xml.sax.InputSource;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics.Field;
|
||||
import java.util.function.Function;
|
||||
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
|
||||
@@ -161,6 +165,8 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
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);
|
||||
}
|
||||
@@ -382,9 +388,6 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Field> fieldMap = metrics.getFields().stream()
|
||||
.collect(Collectors.toMap(Field::getField, Function.identity(), (field1, field2) -> field1));
|
||||
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
@@ -395,19 +398,11 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumn(Integer.toString(keywordNum));
|
||||
} else {
|
||||
Field field = fieldMap.get(alias);
|
||||
if (field == null || !StringUtils.hasText(field.getXpath())) {
|
||||
log.warn("No field definition or xpath found for alias '{}' in XML path parsing.", alias);
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
String relativeXpath = field.getXpath();
|
||||
try {
|
||||
String value = (String) xpath.evaluate(relativeXpath, node, XPathConstants.STRING);
|
||||
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 relative XPath '{}' (from field definition) for node [{}]: {}", relativeXpath, node.getNodeName(), e.getMessage());
|
||||
log.warn("Failed to evaluate XPath '{}' for node [{}]: {}", alias, node.getNodeName(), e.getMessage());
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
@@ -422,6 +417,129 @@ public class HttpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
List<Object> results = JsonPathParser.parseContentWithJsonPath(resp, http.getParseScript());
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import java.util.stream.Stream;
|
||||
import javax.net.ssl.SSLException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.common.http.CommonHttpClient;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.parser.MetricFamily;
|
||||
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.parser.TextParser;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.ParseException;
|
||||
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
|
||||
import org.apache.hertzbeat.common.util.StrBuffer;
|
||||
|
||||
/**
|
||||
|
||||
+14
-14
@@ -99,26 +99,26 @@ class HttpCollectImplTest {
|
||||
</server>
|
||||
</root>
|
||||
""";
|
||||
|
||||
|
||||
// Set up HttpProtocol with XML path parsing
|
||||
HttpProtocol http = HttpProtocol.builder()
|
||||
.parseType(DispatchConstants.PARSE_XML_PATH)
|
||||
.parseScript("//server") // XPath to select all server nodes
|
||||
.build();
|
||||
|
||||
|
||||
// Set up Metrics with fields that have XPath expressions
|
||||
List<Metrics.Field> fields = new ArrayList<>();
|
||||
fields.add(Metrics.Field.builder().field("name").xpath("name").build());
|
||||
fields.add(Metrics.Field.builder().field("status").xpath("status").build());
|
||||
fields.add(Metrics.Field.builder().field("cpu").xpath("metrics/cpu").build());
|
||||
fields.add(Metrics.Field.builder().field("memory").xpath("metrics/memory").build());
|
||||
|
||||
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", "cpu", "memory"))
|
||||
.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() {
|
||||
@@ -128,7 +128,7 @@ class HttpCollectImplTest {
|
||||
return super.addValueRow(valueRow);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Use reflection to access the private parseResponseByXmlPath method
|
||||
Method parseMethod = HttpCollectImpl.class.getDeclaredMethod(
|
||||
"parseResponseByXmlPath",
|
||||
@@ -137,13 +137,13 @@ class HttpCollectImplTest {
|
||||
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");
|
||||
@@ -151,7 +151,7 @@ class HttpCollectImplTest {
|
||||
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");
|
||||
|
||||
+2
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.prometheus.parser;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
|
||||
import org.apache.hertzbeat.common.util.OnlineParser;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
+1
-1
@@ -302,7 +302,7 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
|
||||
value = String.valueOf(objValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("[calculates execute warning] {}.", e.getMessage());
|
||||
log.warn("[calculates execute warning, use original value.] {}", e.getMessage());
|
||||
value = Optional.ofNullable(fieldValueMap.get(expression.getSourceText()))
|
||||
.map(String::valueOf)
|
||||
.orElse(null);
|
||||
|
||||
+4
@@ -202,6 +202,10 @@ 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
|
||||
*/
|
||||
|
||||
+15
@@ -455,4 +455,19 @@ public interface CommonConstants {
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte STATUS_PAGE_INCIDENT_STATE_RESOLVED = 3;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_NORMAL = 0;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_PUSH_AUTO_CREATE = 1;
|
||||
|
||||
/**
|
||||
* status page incident state resolved
|
||||
*/
|
||||
byte MONITOR_TYPE_DISCOVERY_AUTO_CREATE = 2;
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.prometheus.parser;
|
||||
package org.apache.hertzbeat.common.entity.dto;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.dto.query;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Metric History Range Query Data
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Metric Query Data")
|
||||
public class MetricQueryData {
|
||||
|
||||
@Schema(title = "Metric Schema")
|
||||
private MetricSchema schema;
|
||||
|
||||
@Schema(title = "metrics row values, first is the timestamp-ts", example = "[[29,32,44],[32,34,true]]")
|
||||
private List<List<Object>> values;
|
||||
|
||||
/**
|
||||
* Metric Schema
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static final class MetricSchema {
|
||||
|
||||
@Schema(title = "Metrics Field")
|
||||
private List<MetricField> fields;
|
||||
|
||||
@Schema(title = "Meta Information")
|
||||
private Map<String, String> meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metric Field
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public static final class MetricField {
|
||||
|
||||
@Schema(title = "Metric Field Name")
|
||||
private String name;
|
||||
|
||||
@Schema(title = "Field Type: number, string, time, bool")
|
||||
private String type;
|
||||
|
||||
@Schema(title = "Field Unit: %, Mb, Kbps etc.")
|
||||
private String unit;
|
||||
|
||||
@Schema(title = "Whether is a label")
|
||||
private Boolean label;
|
||||
}
|
||||
}
|
||||
@@ -377,10 +377,5 @@ public class Metrics {
|
||||
* Metric unit
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* when parse type is xmlParse, use it, like NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
*/
|
||||
private String xpath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ public class Monitor {
|
||||
@Max(4)
|
||||
private byte status;
|
||||
|
||||
@Schema(title = "Task type 0: Normal, 1: push auto create, 2: discovery auto create")
|
||||
private byte type;
|
||||
|
||||
@Schema(title = "task label", example = "{env:test}", accessMode = READ_WRITE)
|
||||
@Convert(converter = JsonMapAttributeConverter.class)
|
||||
@Column(length = 4096)
|
||||
|
||||
+65
-36
@@ -15,18 +15,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.prometheus.parser;
|
||||
package org.apache.hertzbeat.common.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
@@ -36,9 +38,23 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@Slf4j
|
||||
public class OnlineParser {
|
||||
|
||||
private static final Map<Integer, Integer> escapeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
escapeMap.put((int) 'n', (int) '\n');
|
||||
escapeMap.put((int) 'b', (int) '\b');
|
||||
escapeMap.put((int) 't', (int) '\t');
|
||||
escapeMap.put((int) 'r', (int) '\r');
|
||||
escapeMap.put((int) 'f', (int) '\f');
|
||||
escapeMap.put((int) '\'', (int) '\'');
|
||||
escapeMap.put((int) '\"', (int) '\"');
|
||||
escapeMap.put((int) '\\', (int) '\\');
|
||||
}
|
||||
|
||||
private static class FormatException extends Exception {
|
||||
|
||||
public FormatException() {}
|
||||
public FormatException() {
|
||||
}
|
||||
|
||||
public FormatException(String message) {
|
||||
super(message);
|
||||
@@ -123,51 +139,65 @@ public class OnlineParser {
|
||||
|
||||
}
|
||||
|
||||
private static CharChecker parseOneChar(InputStream inputStream) throws IOException {
|
||||
private static int getChar(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = inputStream.read();
|
||||
if (i == '\\') {
|
||||
i = inputStream.read();
|
||||
if (escapeMap.containsKey(i)) {
|
||||
return escapeMap.get(i);
|
||||
} else {
|
||||
throw new FormatException("Escape character failed.");
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
private static CharChecker parseOneChar(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker parseOneDouble(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || i == '-' || i == '+' || i == 'e' || i == '.') {
|
||||
stringBuilder.append((char) i);
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipOneLong(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker skipOneLong(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while (i >= '0' && i <= '9') {
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker parseMetricName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == ':') {
|
||||
stringBuilder.append((char) i);
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker parseLabelName(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_') {
|
||||
stringBuilder.append((char) i);
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker parseLabelValue(InputStream inputStream, StringBuilder stringBuilder) throws IOException, FormatException {
|
||||
int i = inputStream.read();
|
||||
int i = getChar(inputStream);
|
||||
while (i != '"' && i != -1) {
|
||||
if (i == '\\') {
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
switch (i) {
|
||||
case 'n':
|
||||
stringBuilder.append('\n');
|
||||
@@ -181,27 +211,26 @@ public class OnlineParser {
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
stringBuilder.append((char) i);
|
||||
}
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipSpaces(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker skipSpaces(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while (i == ' ') {
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
|
||||
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException {
|
||||
int i = inputStream.read();
|
||||
private static CharChecker skipToLineEnd(InputStream inputStream) throws IOException, FormatException {
|
||||
int i = getChar(inputStream);
|
||||
while (i != '\n' && i != -1) {
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
return new CharChecker(i);
|
||||
}
|
||||
@@ -267,18 +296,18 @@ public class OnlineParser {
|
||||
metricFamily.setMetricList(new ArrayList<>());
|
||||
metricFamily.setName(metricName);
|
||||
metricFamilyMap.put(metricName, metricFamily);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
metricFamily = metricFamilyMap.get(metricName);
|
||||
}
|
||||
|
||||
if (i == ' ') {
|
||||
i = skipSpaces(inputStream).getInt();
|
||||
}
|
||||
|
||||
List<MetricFamily.Label> labelList = new LinkedList<>();
|
||||
metric.setLabels(labelList);
|
||||
if (i == '{') {
|
||||
List<MetricFamily.Label> labelList = new LinkedList<>();
|
||||
parseLabels(inputStream, stringBuilder, labelList);
|
||||
metric.setLabels(labelList);
|
||||
i = skipSpaces(inputStream).getInt();
|
||||
}
|
||||
|
||||
@@ -307,8 +336,8 @@ public class OnlineParser {
|
||||
|
||||
public static Map<String, MetricFamily> parseMetrics(InputStream inputStream) throws IOException {
|
||||
Map<String, MetricFamily> metricFamilyMap = new ConcurrentHashMap<>(10);
|
||||
int i = inputStream.read();
|
||||
try {
|
||||
int i = getChar(inputStream);
|
||||
while (i != -1) {
|
||||
if (i == '#' || i == '\n') {
|
||||
skipToLineEnd(inputStream).maybeEol().maybeEof().noElse();
|
||||
@@ -317,7 +346,7 @@ public class OnlineParser {
|
||||
stringBuilder.append((char) i);
|
||||
parseMetric(inputStream, metricFamilyMap, stringBuilder);
|
||||
}
|
||||
i = inputStream.read();
|
||||
i = getChar(inputStream);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
log.error("prometheus parser failed because of wrong input format. {}", e.getMessage());
|
||||
@@ -28,6 +28,7 @@ import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* start up class.
|
||||
@@ -41,6 +42,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
|
||||
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class Manager {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Manager.class, args);
|
||||
|
||||
+1
-1
@@ -531,7 +531,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
MonitorDto monitorDto = new MonitorDto();
|
||||
List<Param> params = paramDao.findParamsByMonitorId(id);
|
||||
monitorDto.setParams(params);
|
||||
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp())) {
|
||||
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(monitor.getApp()) || monitor.getType() == CommonConstants.MONITOR_TYPE_PUSH_AUTO_CREATE) {
|
||||
List<CollectRep.MetricsData> metricsDataList = warehouseService.queryMonitorMetricsData(id);
|
||||
List<String> metrics = metricsDataList.stream().map(CollectRep.MetricsData::getMetrics).collect(Collectors.toList());
|
||||
monitorDto.setMetrics(metrics);
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: dahua
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: 大华
|
||||
en-US: Dahua
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: 通过http接口监控大华设备状态,获取设备健康数据。
|
||||
en-US: Monitor Dahua devices through http interface to collect health data.
|
||||
|
||||
# Input params define for monitoring(render web ui by the definition)
|
||||
params:
|
||||
- field: host
|
||||
name:
|
||||
zh-CN: 主机Host
|
||||
en-US: Host
|
||||
type: host
|
||||
required: true
|
||||
- field: port
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
type: number
|
||||
range: '[0,65535]'
|
||||
required: true
|
||||
defaultValue: 80
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
type: number
|
||||
range: '[1000,60000]'
|
||||
required: true
|
||||
defaultValue: 5000
|
||||
- field: username
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
type: text
|
||||
required: true
|
||||
- field: password
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
type: password
|
||||
required: true
|
||||
- field: ssl
|
||||
name:
|
||||
zh-CN: 启用HTTPS
|
||||
en-US: SSL
|
||||
type: boolean
|
||||
required: false
|
||||
defaultValue: false
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
- name: network_info # 指标集合名称
|
||||
i18n:
|
||||
zh-CN: 网络信息
|
||||
en-US: Network Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/configManager.cgi?action=getConfig&name=Network
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
fields:
|
||||
- field: default_interface
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 默认网卡
|
||||
en-US: Default Interface
|
||||
- field: domain_name
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 域名
|
||||
en-US: Domain Name
|
||||
- field: hostname
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 主机名
|
||||
en-US: Hostname
|
||||
- field: eth0_ip_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 IP地址
|
||||
en-US: Interface eth0 IP Address
|
||||
- field: eth0_gateway
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 默认网关
|
||||
en-US: Interface eth0 Default Gateway
|
||||
- field: eth0_mac_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 物理地址
|
||||
en-US: Interface eth0 Physical Address
|
||||
- field: eth0_subnet_mask
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 子网掩码
|
||||
en-US: Interface eth0 Subnet Mask
|
||||
- field: eth0_mtu
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 MTU
|
||||
en-US: Interface eth0 MTU
|
||||
- field: eth0_dns1
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 DNS服务器1
|
||||
en-US: Interface eth0 DNS Server 1
|
||||
- field: eth0_dns2
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 网卡 eth0 DNS服务器2
|
||||
en-US: Interface eth0 DNS Server 2
|
||||
aliasFields:
|
||||
- table.Network.DefaultInterface
|
||||
- table.Network.Domain
|
||||
- table.Network.Hostname
|
||||
- table.Network.eth0.IPAddress
|
||||
- table.Network.eth0.DefaultGateway
|
||||
- table.Network.eth0.PhysicalAddress
|
||||
- table.Network.eth0.SubnetMask
|
||||
- table.Network.eth0.MTU
|
||||
- table.Network.eth0.DnsServers[0]
|
||||
- table.Network.eth0.DnsServers[1]
|
||||
calculates:
|
||||
- default_interface=table.Network.DefaultInterface
|
||||
- domain_name=table.Network.Domain
|
||||
- hostname=table.Network.Hostname
|
||||
- eth0_ip_address=table.Network.eth0.IPAddress
|
||||
- eth0_gateway=table.Network.eth0.DefaultGateway
|
||||
- eth0_mac_address=table.Network.eth0.PhysicalAddress
|
||||
- eth0_subnet_mask=table.Network.eth0.SubnetMask
|
||||
- eth0_mtu=table.Network.eth0.MTU
|
||||
- eth0_dns1=table.Network.eth0.DnsServers[0]
|
||||
- eth0_dns2=table.Network.eth0.DnsServers[1]
|
||||
- name: user_info
|
||||
i18n:
|
||||
zh-CN: 用户信息
|
||||
en-US: User Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/userManager.cgi?action=getActiveUserInfoAll
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
parseScript: users
|
||||
fields:
|
||||
- field: ClientAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端地址
|
||||
en-US: ClientAddress
|
||||
- field: Name
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端用户
|
||||
en-US: Name
|
||||
- field: ClientType
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端登录类型
|
||||
en-US: ClientType
|
||||
- field: LoginTime
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 客户端登录时间
|
||||
en-US: LoginTime
|
||||
- name: ntp_info
|
||||
i18n:
|
||||
zh-CN: 校时信息
|
||||
en-US: Ntp Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /cgi-bin/configManager.cgi?action=getConfig&name=NTP
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: config
|
||||
fields:
|
||||
- field: ntp_address
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时服务器
|
||||
en-US: Ntp Address
|
||||
- field: ntp_port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时端口
|
||||
en-US: Ntp Port
|
||||
- field: ntp_update_period
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 校时间隔
|
||||
en-US: Ntp Update Period
|
||||
aliasFields:
|
||||
- table.NTP.Address
|
||||
- table.NTP.Port
|
||||
- table.NTP.UpdatePeriod
|
||||
calculates:
|
||||
- ntp_address=table.NTP.Address
|
||||
- ntp_port=table.NTP.Port
|
||||
- ntp_update_period=table.NTP.UpdatePeriod
|
||||
@@ -90,39 +90,38 @@ metrics:
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: 'DeviceInfo'
|
||||
parseScript: //DeviceInfo
|
||||
fields:
|
||||
- field: deviceName
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备名称
|
||||
en-US: Device Name
|
||||
xpath: deviceName
|
||||
- field: deviceID
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备ID
|
||||
en-US: Device ID
|
||||
xpath: deviceID
|
||||
- field: firmwareVersion
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 固件版本
|
||||
en-US: Firmware Version
|
||||
xpath: firmwareVersion
|
||||
- field: model
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备型号
|
||||
en-US: Device Model
|
||||
xpath: model
|
||||
- field: macAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: mac地址
|
||||
en-US: Mac Address
|
||||
xpath: macAddress
|
||||
- name: status
|
||||
i18n:
|
||||
zh-CN: 设备状态
|
||||
en-US: Status
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
@@ -136,115 +135,134 @@ metrics:
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: 'DeviceStatus'
|
||||
parseScript: //DeviceStatus
|
||||
fields:
|
||||
- field: cpuUtilization
|
||||
- field: CPU_utilization
|
||||
i18n:
|
||||
zh-CN: CPU 利用率
|
||||
en-US: CPU Utilization
|
||||
type: 0
|
||||
unit: '%'
|
||||
xpath: CPUList/CPU/cpuUtilization
|
||||
- field: memoryUsage
|
||||
- field: memory_usage
|
||||
i18n:
|
||||
zh-CN: 内存使用量
|
||||
en-US: Memory Usage
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryUsage
|
||||
- field: memoryAvailable
|
||||
- field: memory_available
|
||||
i18n:
|
||||
zh-CN: 可用内存
|
||||
en-US: Memory Available
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryAvailable
|
||||
- field: cacheSize
|
||||
- field: cache_size
|
||||
i18n:
|
||||
zh-CN: 缓存大小
|
||||
en-US: Cache Size
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/cacheSize
|
||||
- field: netPort1Speed
|
||||
- field: net_port_1_speed
|
||||
i18n:
|
||||
zh-CN: 网口1速度
|
||||
en-US: Net Port 1 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
- field: netPort2Speed
|
||||
- field: net_port_2_speed
|
||||
i18n:
|
||||
zh-CN: 网口2速度
|
||||
en-US: Net Port 2 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
|
||||
- field: bootTime
|
||||
- field: boot_time
|
||||
i18n:
|
||||
zh-CN: 启动时间
|
||||
en-US: Boot Time
|
||||
type: 1
|
||||
xpath: bootTime
|
||||
- field: deviceUpTime
|
||||
- field: device_uptime
|
||||
i18n:
|
||||
zh-CN: 运行时长
|
||||
en-US: Device Uptime
|
||||
type: 1
|
||||
xpath: deviceUpTime
|
||||
- field: lastCalibrationTime
|
||||
- field: last_calibration_time
|
||||
i18n:
|
||||
zh-CN: 上次校时时间
|
||||
en-US: Last Calibration Time
|
||||
type: 1
|
||||
xpath: lastCalibrationTime
|
||||
- field: lastCalibrationTimeDiff
|
||||
- field: last_calibration_time_diff
|
||||
i18n:
|
||||
zh-CN: 上次校时时间差
|
||||
en-US: Last Calibration Time Diff
|
||||
type: 0
|
||||
unit: s
|
||||
xpath: lastCalibrationTimeDiff
|
||||
- field: avgUploadTime
|
||||
- field: avg_upload_time
|
||||
i18n:
|
||||
zh-CN: 平均上传耗时
|
||||
en-US: Avg Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/avgTime
|
||||
- field: maxUploadTime
|
||||
- field: max_upload_time
|
||||
i18n:
|
||||
zh-CN: 最大上传耗时
|
||||
en-US: Max Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/maxTime
|
||||
- field: minUploadTime
|
||||
- field: min_upload_time
|
||||
i18n:
|
||||
zh-CN: 最小上传耗时
|
||||
en-US: Min Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/minTime
|
||||
- field: lastCalibrationMode
|
||||
- field: last_calibration_mode
|
||||
i18n:
|
||||
zh-CN: 上次校时模式
|
||||
en-US: Last Calibration Mode
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeMode
|
||||
- field: lastCalibrationAddress
|
||||
- field: last_calibration_address
|
||||
i18n:
|
||||
zh-CN: 上次校时地址
|
||||
en-US: Last Calibration Address
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeAddress
|
||||
- field: responseTime
|
||||
- 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:
|
||||
- memoryUsage=KB->MB
|
||||
- memoryAvailable=KB->MB
|
||||
- cacheSize=KB->MB
|
||||
- memory_usage=KB->MB
|
||||
- memory_available=KB->MB
|
||||
- cache_size=KB->MB
|
||||
@@ -0,0 +1,154 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: uniview
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: 宇视
|
||||
en-US: Uniview
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: 通过HTTP接口监控宇视设备状态,获取设备健康数据。
|
||||
en-US: Monitor Uniview devices through HTTP interface to collect health data.
|
||||
|
||||
# Input params define for monitoring(render web ui by the definition)
|
||||
params:
|
||||
- field: host
|
||||
name:
|
||||
zh-CN: 主机Host
|
||||
en-US: Host
|
||||
type: host
|
||||
required: true
|
||||
- field: port
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
type: number
|
||||
range: '[0,65535]'
|
||||
required: true
|
||||
defaultValue: 80
|
||||
- field: timeout
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
type: number
|
||||
range: '[1000,60000]'
|
||||
required: true
|
||||
defaultValue: 5000
|
||||
- field: username
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
type: text
|
||||
required: true
|
||||
- field: password
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
type: password
|
||||
required: true
|
||||
- field: ssl
|
||||
name:
|
||||
zh-CN: 启用HTTPS
|
||||
en-US: SSL
|
||||
type: boolean
|
||||
required: false
|
||||
defaultValue: false
|
||||
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
- name: system_info
|
||||
i18n:
|
||||
zh-CN: 系统信息
|
||||
en-US: System Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /LAPI/V1.0/System/DeviceInfo
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: jsonPath
|
||||
parseScript: '$.Response.Data'
|
||||
fields:
|
||||
- field: DeviceName
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备名称
|
||||
en-US: Device Name
|
||||
- field: SerialNumber
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 序列号
|
||||
en-US: SerialNumber
|
||||
- field: FirmwareVersion
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 固件版本
|
||||
en-US: Firmware Version
|
||||
- field: DeviceModel
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 设备型号
|
||||
en-US: Device Model
|
||||
- name: ntp_info
|
||||
i18n:
|
||||
zh-CN: 校时信息
|
||||
en-US: NTP Info
|
||||
priority: 0
|
||||
protocol: http
|
||||
http:
|
||||
host: ^_^host^_^
|
||||
port: ^_^port^_^
|
||||
ssl: ^_^ssl^_^
|
||||
url: /LAPI/V1.0/System/Time/NTP
|
||||
method: GET
|
||||
timeout: ^_^timeout^_^
|
||||
authorization:
|
||||
type: Digest Auth
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: jsonPath
|
||||
parseScript: '$.Response.Data.NTPServerInfos[0]'
|
||||
fields:
|
||||
- field: IPAddress
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: IP地址
|
||||
en-US: IPAddress
|
||||
- field: Port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 端口号
|
||||
en-US: Port
|
||||
- field: SynchronizeInterval
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 同步间隔
|
||||
en-US: SynchronizeInterval
|
||||
- field: Enabled
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 是否启用
|
||||
en-US: Enabled
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.config;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* push error request wrapper
|
||||
*/
|
||||
@Getter
|
||||
public class PushErrorRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
|
||||
private final String job;
|
||||
|
||||
private final String instance;
|
||||
|
||||
public PushErrorRequestWrapper(HttpServletRequest request, String job, String instance) {
|
||||
super(request);
|
||||
this.job = job;
|
||||
this.instance = instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.config;
|
||||
|
||||
import org.apache.hertzbeat.push.service.PushGatewayService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class PushFilterConfig {
|
||||
|
||||
@Autowired
|
||||
private PushGatewayService pushGatewayService;
|
||||
|
||||
private static final String URI_PREFIX = "/api/push/prometheus/*";
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<PushPrometheusStreamReadingFilter> contentTypeFilter() {
|
||||
FilterRegistrationBean<PushPrometheusStreamReadingFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new PushPrometheusStreamReadingFilter(pushGatewayService));
|
||||
registrationBean.addUrlPatterns(URI_PREFIX);
|
||||
registrationBean.setOrder(Integer.MIN_VALUE);
|
||||
return registrationBean;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.config;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.hertzbeat.push.service.PushGatewayService;
|
||||
|
||||
|
||||
/**
|
||||
* todo
|
||||
*/
|
||||
public class PushPrometheusStreamReadingFilter implements Filter {
|
||||
|
||||
private final PushGatewayService pushGatewayService;
|
||||
|
||||
private final Pattern pathPattern = Pattern.compile("^/api/push/prometheus/job/([a-zA-Z0-9_]*)(?:/instance/([a-zA-Z0-9_]*))?$");
|
||||
|
||||
public PushPrometheusStreamReadingFilter(PushGatewayService pushGatewayService) {
|
||||
this.pushGatewayService = pushGatewayService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (request instanceof HttpServletRequest httpRequest) {
|
||||
String uri = httpRequest.getRequestURI();
|
||||
Matcher matcher = pathPattern.matcher(uri);
|
||||
String job = null;
|
||||
String instance = null;
|
||||
if (matcher.matches()) {
|
||||
job = matcher.group(1);
|
||||
instance = matcher.group(2);
|
||||
boolean flag = pushGatewayService.pushPrometheusMetrics(request.getInputStream(), job, instance);
|
||||
if (flag) {
|
||||
PushSuccessRequestWrapper successRequestWrapper = new PushSuccessRequestWrapper(httpRequest, job, instance);
|
||||
chain.doFilter(successRequestWrapper, response);
|
||||
} else {
|
||||
PushErrorRequestWrapper errorRequestWrapper = new PushErrorRequestWrapper(httpRequest, job, instance);
|
||||
chain.doFilter(errorRequestWrapper, response);
|
||||
}
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.config;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* push success request wrapper
|
||||
*/
|
||||
@Getter
|
||||
public class PushSuccessRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final String job;
|
||||
|
||||
private final String instance;
|
||||
|
||||
public PushSuccessRequestWrapper(HttpServletRequest request, String job, String instance) {
|
||||
super(request);
|
||||
this.job = job;
|
||||
this.instance = instance;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
|
||||
import org.apache.hertzbeat.push.service.PushService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* push controller
|
||||
*/
|
||||
@Tag(name = "Metrics Push API")
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/push", produces = {APPLICATION_JSON_VALUE})
|
||||
public class PushController {
|
||||
|
||||
@Autowired
|
||||
private PushService pushService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Push metric data to hertzbeat", description = "Push metric data to hertzbeat")
|
||||
public ResponseEntity<Message<Void>> pushMetrics(@RequestBody PushMetricsDto pushMetricsDto) {
|
||||
pushService.pushMetricsData(pushMetricsDto);
|
||||
return ResponseEntity.ok(Message.success("Push success"));
|
||||
}
|
||||
|
||||
@GetMapping()
|
||||
@Operation(summary = "Get metric data for hertzbeat", description = "Get metric data for hertzbeat")
|
||||
public ResponseEntity<Message<PushMetricsDto>> getMetrics(
|
||||
@Parameter(description = "Monitor ID", example = "6565463543") @RequestParam("id") final Long id,
|
||||
@Parameter(description = "Last pull time", example = "6565463543") @RequestParam("time") final Long time) {
|
||||
PushMetricsDto pushMetricsDto = pushService.getPushMetricData(id, time);
|
||||
return ResponseEntity.ok(Message.success(pushMetricsDto));
|
||||
}
|
||||
}
|
||||
+15
-16
@@ -22,11 +22,9 @@ package org.apache.hertzbeat.push.controller;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.push.service.PushGatewayService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.apache.hertzbeat.push.config.PushErrorRequestWrapper;
|
||||
import org.apache.hertzbeat.push.config.PushSuccessRequestWrapper;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -37,22 +35,23 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@Tag(name = "Metrics Push Gateway API")
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/push/pushgateway")
|
||||
public class PushGatewayController {
|
||||
|
||||
@Autowired
|
||||
private PushGatewayService pushGatewayService;
|
||||
@RequestMapping(value = "/api/push/prometheus/**")
|
||||
public class PushPrometheusController {
|
||||
|
||||
@PostMapping()
|
||||
@Operation(summary = "Push metric data to hertzbeat pushgateway", description = "Push metric data to hertzbeat pushgateway")
|
||||
public ResponseEntity<Message<Void>> pushMetrics(HttpServletRequest request) throws IOException {
|
||||
InputStream inputStream = request.getInputStream();
|
||||
boolean result = pushGatewayService.pushMetricsData(inputStream);
|
||||
if (result) {
|
||||
return ResponseEntity.ok(Message.success("Push success"));
|
||||
@Operation(summary = "Prometheus push gateway", description = "Push prometheus metric data to hertzbeat")
|
||||
public ResponseEntity<Message<Void>> pushMetrics(HttpServletRequest request) {
|
||||
if (request instanceof PushErrorRequestWrapper error) {
|
||||
return ResponseEntity.badRequest().body(Message.success(String.format("Push failed, job: %s, instance: %s",
|
||||
error.getJob(), error.getInstance())));
|
||||
}
|
||||
else if (request instanceof PushSuccessRequestWrapper success) {
|
||||
return ResponseEntity.ok(Message.success(String.format("Push success, job: %s, instance: %s",
|
||||
success.getJob(), success.getInstance())));
|
||||
}
|
||||
else {
|
||||
return ResponseEntity.ok(Message.success("Push failed"));
|
||||
return ResponseEntity.badRequest()
|
||||
.body(Message.success(String.format("Request %s not matched.", request.getRequestURI())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.push.dao;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
@@ -24,4 +25,11 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
* push monitor dao
|
||||
*/
|
||||
public interface PushMonitorDao extends JpaRepository<Monitor, Long> {
|
||||
|
||||
/**
|
||||
* Find all monitoring entities by type
|
||||
* @param type Monitoring type
|
||||
* @return Monitoring entity list
|
||||
*/
|
||||
List<Monitor> findMonitorsByType(byte type);
|
||||
}
|
||||
|
||||
+10
-3
@@ -19,17 +19,24 @@
|
||||
|
||||
package org.apache.hertzbeat.push.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* push gateway metrics
|
||||
* push gateway service
|
||||
*/
|
||||
|
||||
@Service
|
||||
public interface PushGatewayService {
|
||||
|
||||
boolean pushMetricsData(InputStream inputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* push prometheus metrics data
|
||||
* @param inputStream input stream
|
||||
* @param job job name, maybe null
|
||||
* @param instance instance name, maybe null
|
||||
* @return push success or not
|
||||
*/
|
||||
boolean pushPrometheusMetrics(InputStream inputStream, String job, String instance);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.service;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* push metrics
|
||||
*/
|
||||
@Service
|
||||
public interface PushService {
|
||||
void pushMetricsData(PushMetricsDto pushMetricsData);
|
||||
|
||||
PushMetricsDto getPushMetricData(Long monitorId, Long time);
|
||||
}
|
||||
+96
-3
@@ -19,9 +19,23 @@
|
||||
|
||||
package org.apache.hertzbeat.push.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.dto.MetricFamily;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.common.util.OnlineParser;
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.apache.hertzbeat.push.dao.PushMonitorDao;
|
||||
import org.apache.hertzbeat.push.service.PushGatewayService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -32,9 +46,88 @@ import org.springframework.stereotype.Service;
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PushGatewayServiceImpl implements PushGatewayService {
|
||||
|
||||
private final CommonDataQueue commonDataQueue;
|
||||
|
||||
private final PushMonitorDao pushMonitorDao;
|
||||
|
||||
private final Map<String, Long> jobInstanceMap;
|
||||
|
||||
public PushGatewayServiceImpl(CommonDataQueue commonDataQueue, PushMonitorDao pushMonitorDao) {
|
||||
this.commonDataQueue = commonDataQueue;
|
||||
this.pushMonitorDao = pushMonitorDao;
|
||||
jobInstanceMap = new ConcurrentHashMap<>();
|
||||
pushMonitorDao.findMonitorsByType((byte) 1).forEach(monitor ->
|
||||
jobInstanceMap.put(monitor.getApp() + "_" + monitor.getName(), monitor.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pushMetricsData(InputStream inputStream) throws IOException {
|
||||
return true;
|
||||
public boolean pushPrometheusMetrics(InputStream inputStream, String job, String instance) {
|
||||
try {
|
||||
long curTime = Instant.now().toEpochMilli();
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
if (metricFamilyMap == null) {
|
||||
log.error("parse prometheus metrics is null, job: {}, instance: {}", job, instance);
|
||||
return false;
|
||||
}
|
||||
long id = 0L;
|
||||
if (job != null && instance != null) {
|
||||
// auto create monitor when job and instance not null
|
||||
// job is app, instance is the name
|
||||
id = jobInstanceMap.computeIfAbsent(job + "_" + instance, key -> {
|
||||
log.info("auto create monitor by prometheus push, job: {}, instance: {}", job, instance);
|
||||
long monitorId = SnowFlakeIdGenerator.generateId();
|
||||
Monitor monitor = Monitor.builder()
|
||||
.id(monitorId)
|
||||
.app(job)
|
||||
.name(instance)
|
||||
.host(instance)
|
||||
.type((byte) 1)
|
||||
.status(CommonConstants.MONITOR_UP_CODE)
|
||||
.build();
|
||||
this.pushMonitorDao.save(monitor);
|
||||
return monitorId;
|
||||
});
|
||||
}
|
||||
for (Map.Entry<String, MetricFamily> entry : metricFamilyMap.entrySet()) {
|
||||
CollectRep.MetricsData.Builder builder = CollectRep.MetricsData.newBuilder();
|
||||
builder.setId(id);
|
||||
builder.setApp(job);
|
||||
builder.setTime(curTime);
|
||||
String metricsName = entry.getKey();
|
||||
builder.setMetrics(metricsName);
|
||||
MetricFamily metricFamily = entry.getValue();
|
||||
if (!metricFamily.getMetricList().isEmpty()) {
|
||||
List<String> metricsFields = new LinkedList<>();
|
||||
for (int index = 0; index < metricFamily.getMetricList().size(); index++) {
|
||||
MetricFamily.Metric metric = metricFamily.getMetricList().get(index);
|
||||
if (index == 0) {
|
||||
metric.getLabels().forEach(label -> {
|
||||
metricsFields.add(label.getName());
|
||||
builder.addField(CollectRep.Field.newBuilder().setName(label.getName())
|
||||
.setType(CommonConstants.TYPE_STRING).setLabel(true).build());
|
||||
});
|
||||
builder.addField(CollectRep.Field.newBuilder().setName("value")
|
||||
.setType(CommonConstants.TYPE_NUMBER).setLabel(false).build());
|
||||
}
|
||||
Map<String, String> labelMap = metric.getLabels()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String field : metricsFields) {
|
||||
String fieldValue = labelMap.get(field);
|
||||
valueRowBuilder.addColumn(fieldValue == null ? CommonConstants.NULL_VALUE : fieldValue);
|
||||
}
|
||||
valueRowBuilder.addColumn(String.valueOf(metric.getValue()));
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
commonDataQueue.sendMetricsData(builder.build());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("push prometheus metrics error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.service.impl;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetrics;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.push.dao.PushMetricsDao;
|
||||
import org.apache.hertzbeat.push.dao.PushMonitorDao;
|
||||
import org.apache.hertzbeat.push.service.PushService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* push service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PushServiceImpl implements PushService {
|
||||
|
||||
@Autowired
|
||||
private PushMonitorDao monitorDao;
|
||||
|
||||
@Autowired
|
||||
private PushMetricsDao metricsDao;
|
||||
|
||||
private final Map<Long, Long> monitorIdCache; // key: monitorId, value: time stamp of last query
|
||||
|
||||
private static final long cacheTimeout = 5000L; // ms
|
||||
|
||||
private final Map<Long, PushMetricsDto.Metrics> lastPushMetrics;
|
||||
|
||||
private static final long deleteMetricsPeriod = 1000 * 60 * 60 * 12L;
|
||||
|
||||
private static final long deleteBeforeTime = deleteMetricsPeriod / 2;
|
||||
|
||||
public PushServiceImpl(){
|
||||
monitorIdCache = new HashMap<>();
|
||||
lastPushMetrics = new HashMap<>();
|
||||
|
||||
new Timer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
deletePeriodically();
|
||||
} catch (Exception e) {
|
||||
log.error("periodical deletion failed. {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}, 1000, deleteMetricsPeriod);
|
||||
}
|
||||
|
||||
public void deletePeriodically(){
|
||||
metricsDao.deleteAllByTimeBefore(System.currentTimeMillis() - deleteBeforeTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushMetricsData(PushMetricsDto pushMetricsDto) throws RuntimeException {
|
||||
List<PushMetrics> pushMetricsList = new ArrayList<>();
|
||||
long curTime = System.currentTimeMillis();
|
||||
for (PushMetricsDto.Metrics metrics : pushMetricsDto.getMetricsList()) {
|
||||
long monitorId = metrics.getMonitorId();
|
||||
metrics.setTime(curTime);
|
||||
|
||||
if (!monitorIdCache.containsKey(monitorId) || (monitorIdCache.containsKey(monitorId) && curTime > monitorIdCache.get(monitorId) + cacheTimeout)) {
|
||||
Optional<Monitor> queryOption = monitorDao.findById(monitorId);
|
||||
if (queryOption.isEmpty()) {
|
||||
monitorIdCache.remove(monitorId);
|
||||
continue;
|
||||
}
|
||||
monitorIdCache.put(monitorId, curTime);
|
||||
}
|
||||
|
||||
PushMetrics pushMetrics = PushMetrics.builder()
|
||||
.monitorId(metrics.getMonitorId())
|
||||
.time(curTime)
|
||||
.metrics(JsonUtil.toJson(metrics.getMetrics())).build();
|
||||
lastPushMetrics.put(monitorId, metrics);
|
||||
pushMetricsList.add(pushMetrics);
|
||||
}
|
||||
|
||||
metricsDao.saveAll(pushMetricsList);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushMetricsDto getPushMetricData(final Long monitorId, final Long time) {
|
||||
PushMetricsDto.Metrics metrics;
|
||||
PushMetricsDto pushMetricsDto = new PushMetricsDto();
|
||||
if (lastPushMetrics.containsKey(monitorId)) {
|
||||
metrics = lastPushMetrics.get(monitorId);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
PushMetrics pushMetrics = metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId);
|
||||
if (pushMetrics == null || pushMetrics.getMetrics() == null) {
|
||||
return pushMetricsDto;
|
||||
}
|
||||
List<Map<String, String>> jsonMap = JsonUtil.fromJson(pushMetrics.getMetrics(), new TypeReference<>() {
|
||||
});
|
||||
metrics = PushMetricsDto.Metrics.builder().monitorId(monitorId).metrics(jsonMap).time(pushMetrics.getTime()).build();
|
||||
lastPushMetrics.put(monitorId, metrics);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("no metrics found, monitor id: {}, {}", monitorId, e.getMessage(), e);
|
||||
return pushMetricsDto;
|
||||
}
|
||||
}
|
||||
if (time > metrics.getTime()) {
|
||||
// return void because time param is invalid
|
||||
return pushMetricsDto;
|
||||
}
|
||||
pushMetricsDto.getMetricsList().add(metrics);
|
||||
return pushMetricsDto;
|
||||
}
|
||||
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.controller;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.push.service.PushService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
/**
|
||||
* test case for {@link PushController}
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PushControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private PushService pushService;
|
||||
|
||||
@InjectMocks
|
||||
private PushController pushController;
|
||||
|
||||
private PushMetricsDto mockPushMetricsDto;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
this.mockMvc = standaloneSetup(this.pushController).build();
|
||||
|
||||
mockPushMetricsDto = PushMetricsDto.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPushMetrics() throws Exception {
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.post("/api/push")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(mockPushMetricsDto)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetMetrics() throws Exception {
|
||||
|
||||
Long id = 6565463543L;
|
||||
Long time = 6565463543L;
|
||||
|
||||
when(pushService.getPushMetricData(id, time)).thenReturn(mockPushMetricsDto);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/push")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("id", id.toString())
|
||||
.param("time", time.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
}
|
||||
+9
-12
@@ -22,12 +22,11 @@ package org.apache.hertzbeat.push.controller;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import java.io.InputStream;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.push.service.PushGatewayService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -38,11 +37,12 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* test case for {@link PushGatewayController}
|
||||
* test case for {@link PushPrometheusController}
|
||||
*/
|
||||
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PushGatewayControllerTest {
|
||||
class PushPrometheusControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@@ -50,7 +50,7 @@ class PushGatewayControllerTest {
|
||||
private PushGatewayService pushGatewayService;
|
||||
|
||||
@InjectMocks
|
||||
private PushGatewayController gatewayController;
|
||||
private PushPrometheusController gatewayController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -63,14 +63,12 @@ class PushGatewayControllerTest {
|
||||
|
||||
String mockData = "some metric data";
|
||||
|
||||
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(true);
|
||||
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(true);
|
||||
|
||||
mockMvc.perform(post("/api/push/pushgateway")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(mockData))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Push success"));
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,13 +76,12 @@ class PushGatewayControllerTest {
|
||||
|
||||
String mockData = "some metric data";
|
||||
|
||||
when(pushGatewayService.pushMetricsData(any(InputStream.class))).thenReturn(false);
|
||||
when(pushGatewayService.pushPrometheusMetrics(any(InputStream.class), any(), any())).thenReturn(false);
|
||||
|
||||
mockMvc.perform(post("/api/push/pushgateway")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(mockData))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.msg").value("Push failed"));
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.dao;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetrics;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* test case for {@link PushMetricsDao}
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PushMetricsDaoTest {
|
||||
@Mock
|
||||
private PushMetricsDao pushMetricsDao;
|
||||
|
||||
@InjectMocks
|
||||
private PushMetricsDaoTest pushMetricsDaoTest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shallFindFirstByMonitorIdOrderByTimeDesc() {
|
||||
|
||||
PushMetrics expectedMetrics = new PushMetrics();
|
||||
expectedMetrics.setMonitorId(1L);
|
||||
expectedMetrics.setTime(System.currentTimeMillis());
|
||||
|
||||
when(pushMetricsDao.findFirstByMonitorIdOrderByTimeDesc(1L)).thenReturn(expectedMetrics);
|
||||
|
||||
PushMetrics actualMetrics = pushMetricsDao.findFirstByMonitorIdOrderByTimeDesc(1L);
|
||||
|
||||
assertEquals(expectedMetrics, actualMetrics);
|
||||
verify(pushMetricsDao, times(1)).findFirstByMonitorIdOrderByTimeDesc(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shallDeleteAllByTimeBefore() {
|
||||
|
||||
doNothing().when(pushMetricsDao).deleteAllByTimeBefore(anyLong());
|
||||
|
||||
pushMetricsDao.deleteAllByTimeBefore(1000L);
|
||||
|
||||
verify(pushMetricsDao, times(1)).deleteAllByTimeBefore(1000L);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetrics;
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetricsDto;
|
||||
import org.apache.hertzbeat.push.dao.PushMetricsDao;
|
||||
import org.apache.hertzbeat.push.dao.PushMonitorDao;
|
||||
import org.apache.hertzbeat.push.service.impl.PushServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* test case for {@link PushServiceImpl}
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PushServiceTest {
|
||||
|
||||
@Mock
|
||||
private PushMonitorDao monitorDao;
|
||||
|
||||
@Mock
|
||||
private PushMetricsDao metricsDao;
|
||||
|
||||
@InjectMocks
|
||||
private PushServiceImpl pushService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
pushService = new PushServiceImpl();
|
||||
|
||||
ReflectionTestUtils.setField(pushService, "monitorDao", monitorDao);
|
||||
ReflectionTestUtils.setField(pushService, "metricsDao", metricsDao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPushMetricsData() {
|
||||
|
||||
PushMetricsDto pushMetricsDto = new PushMetricsDto();
|
||||
List<PushMetricsDto.Metrics> metricsList = new ArrayList<>();
|
||||
PushMetricsDto.Metrics metrics = new PushMetricsDto.Metrics();
|
||||
metrics.setMonitorId(1L);
|
||||
metricsList.add(metrics);
|
||||
pushMetricsDto.setMetricsList(metricsList);
|
||||
|
||||
when(monitorDao.findById(anyLong())).thenReturn(Optional.of(new Monitor()));
|
||||
|
||||
pushService.pushMetricsData(pushMetricsDto);
|
||||
|
||||
verify(metricsDao, times(1)).saveAll(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPushMetricData() {
|
||||
|
||||
Long monitorId = 1L;
|
||||
Long time = System.currentTimeMillis();
|
||||
PushMetrics pushMetrics = PushMetrics.builder()
|
||||
.monitorId(monitorId)
|
||||
.time(time)
|
||||
.metrics("[{\"key\":\"value\"}]")
|
||||
.build();
|
||||
|
||||
when(metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId)).thenReturn(pushMetrics);
|
||||
|
||||
PushMetricsDto result = pushService.getPushMetricData(monitorId, time);
|
||||
|
||||
assertEquals(1, result.getMetricsList().size());
|
||||
assertEquals(monitorId, result.getMetricsList().get(0).getMonitorId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPushMetricDataTimeInvalid() {
|
||||
|
||||
Long monitorId = 1L;
|
||||
Long time = System.currentTimeMillis() + 10000;
|
||||
PushMetrics pushMetrics = PushMetrics.builder()
|
||||
.monitorId(monitorId)
|
||||
.time(System.currentTimeMillis())
|
||||
.metrics("[{\"key\":\"value\"}]")
|
||||
.build();
|
||||
|
||||
when(metricsDao.findFirstByMonitorIdOrderByTimeDesc(monitorId)).thenReturn(pushMetrics);
|
||||
|
||||
PushMetricsDto result = pushService.getPushMetricData(monitorId, time);
|
||||
|
||||
assertTrue(result.getMetricsList().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletePeriodically() {
|
||||
|
||||
pushService.deletePeriodically();
|
||||
verify(metricsDao, times(1)).deleteAllByTimeBefore(anyLong());
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -56,4 +56,8 @@ public interface WarehouseConstants {
|
||||
String MEMORY = "memory";
|
||||
}
|
||||
|
||||
String PROMQL = "promql";
|
||||
|
||||
String SQL = "sql";
|
||||
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.warehouse.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
import org.apache.hertzbeat.warehouse.service.MetricsDataQueryService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
/**
|
||||
* Metrics Data Query API
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(produces = {APPLICATION_JSON_VALUE})
|
||||
@Tag(name = "Metrics Data Query API")
|
||||
public class MetricsDataQueryController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private MetricsDataQueryService queryService;
|
||||
|
||||
|
||||
@GetMapping("/api/warehouse/query")
|
||||
@Operation(summary = "Query Real Time Metrics Data")
|
||||
public ResponseEntity<Message<List<MetricQueryData>>> queryMetricsData(
|
||||
@Parameter(description = "Query PromQL expr list", example = "cpu")
|
||||
@RequestParam List<String> queries,
|
||||
@Parameter(description = "Query type", example = "promql")
|
||||
@RequestParam String type,
|
||||
@Parameter(description = "Query timestamp", example = "1725854804451")
|
||||
@RequestParam long time) {
|
||||
return ResponseEntity.ok(Message.success(queryService.query(queries, type, time)));
|
||||
}
|
||||
|
||||
@GetMapping("/api/warehouse/query/range")
|
||||
@Operation(summary = "Query Range Metrics Data")
|
||||
public ResponseEntity<Message<List<MetricQueryData>>> queryMetricsDataRange(
|
||||
@Parameter(description = "Query PromQL expr list", example = "cpu")
|
||||
@RequestParam List<String> queries,
|
||||
@Parameter(description = "Query type", example = "promql")
|
||||
@RequestParam String type,
|
||||
@Parameter(description = "Query start timestamp", example = "1725854804451")
|
||||
@RequestParam long start,
|
||||
@Parameter(description = "Query end timestamp", example = "1733630804452")
|
||||
@RequestParam long end,
|
||||
@Parameter(description = "Query step", example = "4m")
|
||||
@RequestParam String step
|
||||
) {
|
||||
return ResponseEntity.ok(Message.success(queryService.queryRange(queries, type, start, end, step)));
|
||||
}
|
||||
}
|
||||
+4
-77
@@ -18,29 +18,12 @@
|
||||
package org.apache.hertzbeat.warehouse.db;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.NetworkConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* query executor for victor metrics
|
||||
@@ -48,71 +31,15 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
public class GreptimePromqlQueryExecutor implements QueryExecutor {
|
||||
public class GreptimePromqlQueryExecutor extends PromqlQueryExecutor {
|
||||
|
||||
private static final String QUERY_PATH = "/v1/prometheus/api/v1/query";
|
||||
|
||||
private final GreptimeProperties greptimeProperties;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public GreptimePromqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) {
|
||||
super(restTemplate, new HttpPromqlProperties(greptimeProperties.httpEndpoint() + QUERY_PATH,
|
||||
greptimeProperties.username(), greptimeProperties.password()));
|
||||
this.greptimeProperties = greptimeProperties;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> execute(String query) {
|
||||
// http run the promql query
|
||||
List<Map<String, Object>> results = new LinkedList<>();
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
if (StringUtils.hasText(greptimeProperties.username())
|
||||
&& StringUtils.hasText(greptimeProperties.password())) {
|
||||
String authStr = greptimeProperties.username() + ":" + greptimeProperties.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(greptimeProperties.httpEndpoint() + QUERY_PATH)
|
||||
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
|
||||
.build(true).toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
|
||||
&& responseEntity.getBody().getData().getResult() != null) {
|
||||
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
|
||||
for (PromQlQueryContent.ContentData.Content content : contents) {
|
||||
Map<String, String> labels = content.getMetric();
|
||||
Map<String, Object> queryResult = new HashMap<>(8);
|
||||
queryResult.putAll(labels);
|
||||
if (content.getValue() != null && content.getValue().length == 2) {
|
||||
queryResult.put("__timestamp__", content.getValue()[0]);
|
||||
queryResult.put("__value__", content.getValue()[1]);
|
||||
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
|
||||
List<Object> values = new LinkedList<>();
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
values.add(valueArr[1]);
|
||||
}
|
||||
queryResult.put("__value__", values);
|
||||
}
|
||||
results.add(queryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("query metrics data from greptime failed. {}", responseEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean support(String datasource) {
|
||||
return "promql".equals(datasource);
|
||||
}
|
||||
}
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.warehouse.db;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.NetworkConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.hertzbeat.warehouse.constants.WarehouseConstants.PROMQL;
|
||||
|
||||
/**
|
||||
* abstract class for promql query executor
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class PromqlQueryExecutor implements QueryExecutor {
|
||||
|
||||
private static final String supportQueryLanguage = PROMQL;
|
||||
protected static final String HTTP_QUERY_PARAM = "query";
|
||||
protected static final String HTTP_TIME_PARAM = "time";
|
||||
protected static final String HTTP_START_PARAM = "start";
|
||||
protected static final String HTTP_END_PARAM = "end";
|
||||
protected static final String HTTP_STEP_PARAM = "step";
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private final HttpPromqlProperties httpPromqlProperties;
|
||||
|
||||
PromqlQueryExecutor(RestTemplate restTemplate, HttpPromqlProperties httpPromqlProperties) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.httpPromqlProperties = httpPromqlProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* record class for promql http connection
|
||||
*/
|
||||
protected record HttpPromqlProperties (
|
||||
String url,
|
||||
String username,
|
||||
String password
|
||||
){}
|
||||
|
||||
protected List<Map<String, Object>> http_promql(Map<String, Object> params) {
|
||||
// http run the promql query
|
||||
List<Map<String, Object>> results = new LinkedList<>();
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
if (StringUtils.hasText(httpPromqlProperties.username())
|
||||
&& StringUtils.hasText(httpPromqlProperties.password())) {
|
||||
String authStr = httpPromqlProperties.username() + ":" + httpPromqlProperties.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(httpPromqlProperties.url);
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
uriComponentsBuilder.queryParam(entry.getKey(), entry.getValue());
|
||||
}
|
||||
URI uri = uriComponentsBuilder.build(true).toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
|
||||
&& responseEntity.getBody().getData().getResult() != null) {
|
||||
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
|
||||
for (PromQlQueryContent.ContentData.Content content : contents) {
|
||||
Map<String, String> labels = content.getMetric();
|
||||
Map<String, Object> queryResult = new HashMap<>(8);
|
||||
queryResult.putAll(labels);
|
||||
if (content.getValue() != null && content.getValue().length == 2) {
|
||||
queryResult.put("__timestamp__", content.getValue()[0]);
|
||||
queryResult.put("__value__", content.getValue()[1]);
|
||||
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
|
||||
List<Object> values = new LinkedList<>();
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
values.add(valueArr[1]);
|
||||
}
|
||||
queryResult.put("__value__", values);
|
||||
}
|
||||
results.add(queryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("query metrics data from greptime failed. {}", responseEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public MetricQueryData convertToMetricQueryData(Object object) {
|
||||
MetricQueryData metricQueryData = new MetricQueryData();
|
||||
try {
|
||||
List<Map<String, Object>> metrics = (List<Map<String, Object>>) object;
|
||||
// todo
|
||||
} catch (Exception e) {
|
||||
log.error("converting to metric query data failed.");
|
||||
}
|
||||
return metricQueryData;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> execute(String queryString) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
|
||||
return http_promql(params);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> query(String queryString, long time) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
|
||||
params.put(HTTP_TIME_PARAM, time);
|
||||
return http_promql(params);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> query_range(String queryString, long start, long end, String step) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(HTTP_QUERY_PARAM, URLEncoder.encode(queryString, StandardCharsets.UTF_8));
|
||||
params.put(HTTP_START_PARAM, start);
|
||||
params.put(HTTP_END_PARAM, end);
|
||||
params.put(HTTP_STEP_PARAM, step);
|
||||
return http_promql(params);
|
||||
}
|
||||
|
||||
public boolean support(String datasource) {
|
||||
return supportQueryLanguage.equals(datasource);
|
||||
}
|
||||
|
||||
}
|
||||
+9
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.apache.hertzbeat.warehouse.db;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -24,8 +26,14 @@ import java.util.Map;
|
||||
* query executor interface
|
||||
*/
|
||||
public interface QueryExecutor {
|
||||
|
||||
|
||||
MetricQueryData convertToMetricQueryData(Object object);
|
||||
|
||||
List<Map<String, Object>> execute(String query);
|
||||
|
||||
List<Map<String, Object>> query(String query, long time);
|
||||
|
||||
List<Map<String, Object>> query_range(String query, long start, long end, String step);
|
||||
|
||||
boolean support(String datasource);
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.warehouse.db;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.hertzbeat.warehouse.constants.WarehouseConstants.SQL;
|
||||
|
||||
/**
|
||||
* abstract class for sql query executor
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class SqlQueryExecutor implements QueryExecutor {
|
||||
|
||||
private static final String supportQueryLanguage = SQL;
|
||||
|
||||
/**
|
||||
* record class for sql connection
|
||||
*/
|
||||
protected record ConnectorSqlProperties () {}
|
||||
|
||||
protected abstract List<Map<String, Object>> do_sql(Map<String, Object> params);
|
||||
|
||||
public MetricQueryData convertToMetricQueryData(Object object) {
|
||||
MetricQueryData metricQueryData = new MetricQueryData();
|
||||
try {
|
||||
List<Map<String, Object>> metrics = (List<Map<String, Object>>) object;
|
||||
// todo
|
||||
} catch (Exception e) {
|
||||
log.error("converting to metric query data failed.");
|
||||
}
|
||||
return metricQueryData;
|
||||
}
|
||||
|
||||
public abstract List<Map<String, Object>> execute(String query);
|
||||
|
||||
public abstract List<Map<String, Object>> query(String query, long time);
|
||||
|
||||
public abstract List<Map<String, Object>> query_range(String query, long start, long end, String step);
|
||||
|
||||
|
||||
public boolean support(String datasource) {
|
||||
return supportQueryLanguage.equals(datasource);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-76
@@ -18,29 +18,11 @@
|
||||
package org.apache.hertzbeat.warehouse.db;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.NetworkConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.VictoriaMetricsProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* query executor for victor metrics
|
||||
@@ -48,71 +30,16 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "warehouse.store.victoria-metrics", name = "enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
public class VictoriaMetricsQueryExecutor implements QueryExecutor {
|
||||
public class VictoriaMetricsQueryExecutor extends PromqlQueryExecutor {
|
||||
|
||||
private static final String QUERY_PATH = "/api/v1/query";
|
||||
|
||||
private final VictoriaMetricsProperties victoriaMetricsProp;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public VictoriaMetricsQueryExecutor(VictoriaMetricsProperties victoriaMetricsProp, RestTemplate restTemplate) {
|
||||
super(restTemplate, new HttpPromqlProperties(victoriaMetricsProp.url() + QUERY_PATH,
|
||||
victoriaMetricsProp.username(), victoriaMetricsProp.password()));
|
||||
this.victoriaMetricsProp = victoriaMetricsProp;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> execute(String query) {
|
||||
// http run the promql query
|
||||
List<Map<String, Object>> results = new LinkedList<>();
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
if (StringUtils.hasText(victoriaMetricsProp.username())
|
||||
&& StringUtils.hasText(victoriaMetricsProp.password())) {
|
||||
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(victoriaMetricsProp.url() + QUERY_PATH)
|
||||
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
|
||||
.build(true).toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
|
||||
&& responseEntity.getBody().getData().getResult() != null) {
|
||||
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
|
||||
for (PromQlQueryContent.ContentData.Content content : contents) {
|
||||
Map<String, String> labels = content.getMetric();
|
||||
Map<String, Object> queryResult = new HashMap<>(8);
|
||||
queryResult.putAll(labels);
|
||||
if (content.getValue() != null && content.getValue().length == 2) {
|
||||
queryResult.put("__timestamp__", content.getValue()[0]);
|
||||
queryResult.put("__value__", content.getValue()[1]);
|
||||
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
|
||||
List<Object> values = new LinkedList<>();
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
values.add(valueArr[1]);
|
||||
}
|
||||
queryResult.put("__value__", values);
|
||||
}
|
||||
results.add(queryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("query metrics data from victor-metrics failed. {}", responseEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean support(String datasource) {
|
||||
return "promql".equals(datasource);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-9
@@ -15,19 +15,32 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.push.dao;
|
||||
package org.apache.hertzbeat.warehouse.service;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.push.PushMetrics;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* push metrics dao
|
||||
* metrics data query service
|
||||
*/
|
||||
public interface PushMetricsDao extends JpaRepository<PushMetrics, Long> {
|
||||
public interface MetricsDataQueryService {
|
||||
|
||||
PushMetrics findFirstByMonitorIdOrderByTimeDesc(Long monitorId);
|
||||
/**
|
||||
* Query metrics data
|
||||
* @param queries query expr
|
||||
* @param time time
|
||||
* @return data
|
||||
*/
|
||||
List<MetricQueryData> query(List<String> queries, String queryType, long time);
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
void deleteAllByTimeBefore(Long time);
|
||||
/**
|
||||
* Query metrics data range
|
||||
* @param queries query expr
|
||||
* @param start start
|
||||
* @param end end
|
||||
* @param step step
|
||||
* @return data
|
||||
*/
|
||||
List<MetricQueryData> queryRange(List<String> queries, String queryType, long start, long end, String step);
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.warehouse.service.impl;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.dto.query.MetricQueryData;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
import org.apache.hertzbeat.warehouse.service.MetricsDataQueryService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class MetricsDataQueryServiceImpl implements MetricsDataQueryService {
|
||||
@Autowired(required = false)
|
||||
List<QueryExecutor> executors;
|
||||
|
||||
@Override
|
||||
public List<MetricQueryData> query(List<String> queries, String queryType, long time) {
|
||||
if (queries == null || executors.isEmpty()) {
|
||||
throw new IllegalArgumentException("No query executor found");
|
||||
}
|
||||
QueryExecutor executor = executors.stream().filter(e -> e.support(queryType)).findFirst().orElse(null);
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Unsupported datasource: " + queryType);
|
||||
}
|
||||
List<MetricQueryData> metricQueryDataList = new ArrayList<>();
|
||||
for (String query : queries) {
|
||||
metricQueryDataList.add(executor.convertToMetricQueryData(executor.query(query, time)));
|
||||
}
|
||||
return metricQueryDataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricQueryData> queryRange(List<String> queries, String queryType, long start, long end, String step) {
|
||||
if (queries == null || executors.isEmpty()) {
|
||||
throw new IllegalArgumentException("No query executor found");
|
||||
}
|
||||
QueryExecutor executor = executors.stream().filter(e -> e.support(queryType)).findFirst().orElse(null);
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Unsupported datasource: " + queryType);
|
||||
}
|
||||
List<MetricQueryData> metricQueryDataList = new ArrayList<>();
|
||||
for (String query : queries) {
|
||||
metricQueryDataList.add(executor.convertToMetricQueryData(executor.query_range(query, start, end, step)));
|
||||
}
|
||||
return metricQueryDataList;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,56 @@ params:
|
||||
|
||||
# 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^_^
|
||||
@@ -144,115 +193,134 @@ metrics:
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: 'DeviceStatus'
|
||||
parseScript: //DeviceStatus
|
||||
fields:
|
||||
- field: cpuUtilization
|
||||
- field: CPU_utilization
|
||||
i18n:
|
||||
zh-CN: CPU 利用率
|
||||
en-US: CPU Utilization
|
||||
type: 0
|
||||
unit: '%'
|
||||
xpath: CPUList/CPU/cpuUtilization
|
||||
- field: memoryUsage
|
||||
- field: memory_usage
|
||||
i18n:
|
||||
zh-CN: 内存使用量
|
||||
en-US: Memory Usage
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryUsage
|
||||
- field: memoryAvailable
|
||||
- field: memory_available
|
||||
i18n:
|
||||
zh-CN: 可用内存
|
||||
en-US: Memory Available
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryAvailable
|
||||
- field: cacheSize
|
||||
- field: cache_size
|
||||
i18n:
|
||||
zh-CN: 缓存大小
|
||||
en-US: Cache Size
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/cacheSize
|
||||
- field: netPort1Speed
|
||||
- field: net_port_1_speed
|
||||
i18n:
|
||||
zh-CN: 网口1速度
|
||||
en-US: Net Port 1 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
- field: netPort2Speed
|
||||
- field: net_port_2_speed
|
||||
i18n:
|
||||
zh-CN: 网口2速度
|
||||
en-US: Net Port 2 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
|
||||
- field: bootTime
|
||||
- field: boot_time
|
||||
i18n:
|
||||
zh-CN: 启动时间
|
||||
en-US: Boot Time
|
||||
type: 1
|
||||
xpath: bootTime
|
||||
- field: deviceUpTime
|
||||
- field: device_uptime
|
||||
i18n:
|
||||
zh-CN: 运行时长
|
||||
en-US: Device Uptime
|
||||
type: 1
|
||||
xpath: deviceUpTime
|
||||
- field: lastCalibrationTime
|
||||
- field: last_calibration_time
|
||||
i18n:
|
||||
zh-CN: 上次校时时间
|
||||
en-US: Last Calibration Time
|
||||
type: 1
|
||||
xpath: lastCalibrationTime
|
||||
- field: lastCalibrationTimeDiff
|
||||
- field: last_calibration_time_diff
|
||||
i18n:
|
||||
zh-CN: 上次校时时间差
|
||||
en-US: Last Calibration Time Diff
|
||||
type: 0
|
||||
unit: s
|
||||
xpath: lastCalibrationTimeDiff
|
||||
- field: avgUploadTime
|
||||
- field: avg_upload_time
|
||||
i18n:
|
||||
zh-CN: 平均上传耗时
|
||||
en-US: Avg Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/avgTime
|
||||
- field: maxUploadTime
|
||||
- field: max_upload_time
|
||||
i18n:
|
||||
zh-CN: 最大上传耗时
|
||||
en-US: Max Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/maxTime
|
||||
- field: minUploadTime
|
||||
- field: min_upload_time
|
||||
i18n:
|
||||
zh-CN: 最小上传耗时
|
||||
en-US: Min Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/minTime
|
||||
- field: lastCalibrationMode
|
||||
- field: last_calibration_mode
|
||||
i18n:
|
||||
zh-CN: 上次校时模式
|
||||
en-US: Last Calibration Mode
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeMode
|
||||
- field: lastCalibrationAddress
|
||||
- field: last_calibration_address
|
||||
i18n:
|
||||
zh-CN: 上次校时地址
|
||||
en-US: Last Calibration Address
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeAddress
|
||||
- field: responseTime
|
||||
- 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:
|
||||
- memoryUsage=KB->MB
|
||||
- memoryAvailable=KB->MB
|
||||
- cacheSize=KB->MB
|
||||
- memory_usage=KB->MB
|
||||
- memory_available=KB->MB
|
||||
- cache_size=KB->MB
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
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.
|
||||
+103
-35
@@ -130,7 +130,56 @@ params:
|
||||
|
||||
# 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^_^
|
||||
@@ -144,115 +193,134 @@ metrics:
|
||||
digestAuthUsername: ^_^username^_^
|
||||
digestAuthPassword: ^_^password^_^
|
||||
parseType: xmlPath
|
||||
parseScript: 'DeviceStatus'
|
||||
parseScript: //DeviceStatus
|
||||
fields:
|
||||
- field: cpuUtilization
|
||||
- field: CPU_utilization
|
||||
i18n:
|
||||
zh-CN: CPU 利用率
|
||||
en-US: CPU Utilization
|
||||
type: 0
|
||||
unit: '%'
|
||||
xpath: CPUList/CPU/cpuUtilization
|
||||
- field: memoryUsage
|
||||
- field: memory_usage
|
||||
i18n:
|
||||
zh-CN: 内存使用量
|
||||
en-US: Memory Usage
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryUsage
|
||||
- field: memoryAvailable
|
||||
- field: memory_available
|
||||
i18n:
|
||||
zh-CN: 可用内存
|
||||
en-US: Memory Available
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/memoryAvailable
|
||||
- field: cacheSize
|
||||
- field: cache_size
|
||||
i18n:
|
||||
zh-CN: 缓存大小
|
||||
en-US: Cache Size
|
||||
type: 0
|
||||
unit: MB
|
||||
xpath: MemoryList/Memory/cacheSize
|
||||
- field: netPort1Speed
|
||||
- field: net_port_1_speed
|
||||
i18n:
|
||||
zh-CN: 网口1速度
|
||||
en-US: Net Port 1 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='1']/workSpeed
|
||||
- field: netPort2Speed
|
||||
- field: net_port_2_speed
|
||||
i18n:
|
||||
zh-CN: 网口2速度
|
||||
en-US: Net Port 2 Speed
|
||||
type: 0
|
||||
unit: Mbps
|
||||
xpath: NetPortStatusList/NetPortStatus[id='2']/workSpeed
|
||||
- field: bootTime
|
||||
- field: boot_time
|
||||
i18n:
|
||||
zh-CN: 启动时间
|
||||
en-US: Boot Time
|
||||
type: 1
|
||||
xpath: bootTime
|
||||
- field: deviceUpTime
|
||||
- field: device_uptime
|
||||
i18n:
|
||||
zh-CN: 运行时长
|
||||
en-US: Device Uptime
|
||||
type: 1
|
||||
xpath: deviceUpTime
|
||||
- field: lastCalibrationTime
|
||||
- field: last_calibration_time
|
||||
i18n:
|
||||
zh-CN: 上次校时时间
|
||||
en-US: Last Calibration Time
|
||||
type: 1
|
||||
xpath: lastCalibrationTime
|
||||
- field: lastCalibrationTimeDiff
|
||||
- field: last_calibration_time_diff
|
||||
i18n:
|
||||
zh-CN: 上次校时时间差
|
||||
en-US: Last Calibration Time Diff
|
||||
type: 0
|
||||
unit: s
|
||||
xpath: lastCalibrationTimeDiff
|
||||
- field: avgUploadTime
|
||||
- field: avg_upload_time
|
||||
i18n:
|
||||
zh-CN: 平均上传耗时
|
||||
en-US: Avg Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/avgTime
|
||||
- field: maxUploadTime
|
||||
- field: max_upload_time
|
||||
i18n:
|
||||
zh-CN: 最大上传耗时
|
||||
en-US: Max Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/maxTime
|
||||
- field: minUploadTime
|
||||
- field: min_upload_time
|
||||
i18n:
|
||||
zh-CN: 最小上传耗时
|
||||
en-US: Min Upload Time
|
||||
type: 0
|
||||
unit: ms
|
||||
xpath: uploadTimeConsumingList/minTime
|
||||
- field: lastCalibrationMode
|
||||
- field: last_calibration_mode
|
||||
i18n:
|
||||
zh-CN: 上次校时模式
|
||||
en-US: Last Calibration Mode
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeMode
|
||||
- field: lastCalibrationAddress
|
||||
- field: last_calibration_address
|
||||
i18n:
|
||||
zh-CN: 上次校时地址
|
||||
en-US: Last Calibration Address
|
||||
type: 1
|
||||
xpath: lastCalibrationTimeAddress
|
||||
- field: responseTime
|
||||
- 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:
|
||||
- memoryUsage=KB->MB
|
||||
- memoryAvailable=KB->MB
|
||||
- cacheSize=KB->MB
|
||||
- memory_usage=KB->MB
|
||||
- memory_available=KB->MB
|
||||
- cache_size=KB->MB
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
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认证方式,解析设备返回的配置数据格式。
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
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格式响应数据。
|
||||
+3
-1
@@ -147,7 +147,9 @@
|
||||
"label": "server",
|
||||
"items": [
|
||||
"help/ipmi",
|
||||
"help/hikvision_isapi"
|
||||
"help/hikvision_isapi",
|
||||
"help/dahua",
|
||||
"help/uniview"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@ export class Monitor {
|
||||
intervals: number = 60;
|
||||
// Monitoring status 0: Paused, 1: Up, 2: Down
|
||||
status!: number;
|
||||
// Task type 0: Normal, 1: push auto create, 2: discovery auto create
|
||||
type!: number;
|
||||
description!: string;
|
||||
labels!: Record<string, string>;
|
||||
annotations!: Record<string, string>;
|
||||
|
||||
Reference in New Issue
Block a user