-
Notifications
You must be signed in to change notification settings - Fork 994
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add a online prometheus parser and a prometheus-like push style. (#1644)
- Loading branch information
Showing
6 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
common/src/main/java/org/dromara/hertzbeat/common/util/prometheus/Label.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.dromara.hertzbeat.common.util.prometheus; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
/** | ||
* prometheus label entity | ||
* | ||
* | ||
*/ | ||
@Data | ||
@Builder | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class Label { | ||
private String name; | ||
private String value; | ||
} |
24 changes: 24 additions & 0 deletions
24
common/src/main/java/org/dromara/hertzbeat/common/util/prometheus/Metric.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.dromara.hertzbeat.common.util.prometheus; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* prometheus metric line entity | ||
* | ||
* | ||
*/ | ||
@Data | ||
@Builder | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class Metric { | ||
private String metricName; | ||
private List<Label> labelList; | ||
private Double value; | ||
private Long timestamp; | ||
} |
233 changes: 233 additions & 0 deletions
233
common/src/main/java/org/dromara/hertzbeat/common/util/prometheus/PrometheusUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package org.dromara.hertzbeat.common.util.prometheus; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.math.BigDecimal; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* prometheus metric sparser | ||
* | ||
* | ||
*/ | ||
public class PrometheusUtil { | ||
|
||
private static final int ERROR_FORMAT = -1; //解析过程中出现了未知格式数据,因为无法继续解析或已经到达输入流的末尾 | ||
|
||
private static final int NORMAL_END = -2; //输入流正常结束 | ||
|
||
private static final int COMMENT_LINE = -3; | ||
|
||
|
||
private static int parseMetricName(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException { | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
int i; | ||
i = inputStream.read(); | ||
if (i == -1) { | ||
return NORMAL_END; | ||
} | ||
else if (i == '#') { | ||
return COMMENT_LINE; | ||
} | ||
|
||
while (i != -1) { | ||
if (i == ' ' || i == '{') { | ||
metricBuilder.metricName(stringBuilder.toString()); | ||
return i; | ||
} | ||
stringBuilder.append((char) i); | ||
i = inputStream.read(); | ||
} | ||
|
||
return ERROR_FORMAT; | ||
} | ||
|
||
private static int parseLabel(InputStream inputStream, List<Label> labelList) throws IOException { | ||
Label.LabelBuilder labelBuilder = new Label.LabelBuilder(); | ||
int i; | ||
|
||
StringBuilder labelName = new StringBuilder(); | ||
i = inputStream.read(); | ||
while (i != -1 && i != '=') { | ||
labelName.append((char) i); | ||
i = inputStream.read(); | ||
} | ||
if (i == -1) { | ||
return ERROR_FORMAT; | ||
} | ||
labelBuilder.name(labelName.toString()); | ||
|
||
if (inputStream.read() != '\"') { | ||
return ERROR_FORMAT; | ||
} | ||
|
||
StringBuilder labelValue = new StringBuilder(); | ||
i = inputStream.read(); | ||
while (i != -1 && i != ',' && i != '}') { | ||
labelValue.append((char) i); | ||
i = inputStream.read(); | ||
} | ||
if (i == -1 || labelValue.charAt(labelValue.length() - 1) != '\"') { | ||
return ERROR_FORMAT; | ||
} | ||
|
||
// skip space only in this condition | ||
if (i == '}' && inputStream.read() != ' ') { | ||
return ERROR_FORMAT; | ||
} | ||
|
||
labelValue.deleteCharAt(labelValue.length() - 1); | ||
labelBuilder.value(labelValue.toString()); | ||
|
||
labelList.add(labelBuilder.build()); | ||
return i; | ||
} | ||
|
||
private static int parseLabelList(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException { | ||
List<Label> labelList = new ArrayList<>(); | ||
int i; | ||
|
||
i = parseLabel(inputStream, labelList); | ||
while (i == ',') { | ||
i = parseLabel(inputStream, labelList); | ||
} | ||
if (i == -1) { | ||
return ERROR_FORMAT; | ||
} | ||
|
||
metricBuilder.labelList(labelList); | ||
return i; | ||
} | ||
|
||
private static int parseValue(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException { | ||
int i; | ||
|
||
StringBuilder stringBuilder = new StringBuilder(); | ||
i = inputStream.read(); | ||
while (i != -1 && i != ' ' && i != '\n') { | ||
stringBuilder.append((char) i); | ||
i = inputStream.read(); | ||
} | ||
|
||
String string = stringBuilder.toString(); | ||
|
||
switch (string) { | ||
case "NaN": | ||
metricBuilder.value(Double.NaN); | ||
break; | ||
case "+Inf": | ||
metricBuilder.value(Double.POSITIVE_INFINITY); | ||
break; | ||
case "-Inf": | ||
metricBuilder.value(Double.NEGATIVE_INFINITY); | ||
break; | ||
default: | ||
try { | ||
BigDecimal bigDecimal = new BigDecimal(string); | ||
metricBuilder.value(bigDecimal.doubleValue()); | ||
} catch (NumberFormatException e) { | ||
return ERROR_FORMAT; | ||
} | ||
break; | ||
} | ||
|
||
if (i == -1) { | ||
return NORMAL_END; | ||
} | ||
else { | ||
return i; // ' ' or \n' | ||
} | ||
} | ||
|
||
private static int parseTimestamp(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException { | ||
int i; | ||
|
||
StringBuilder stringBuilder = new StringBuilder(); | ||
i = inputStream.read(); | ||
while (i != -1 && i != '\n') { | ||
stringBuilder.append((char) i); | ||
i = inputStream.read(); | ||
} | ||
|
||
String string = stringBuilder.toString(); | ||
try { | ||
metricBuilder.timestamp(Long.parseLong(string)); | ||
} catch (NumberFormatException e) { | ||
return ERROR_FORMAT; | ||
} | ||
|
||
if (i == -1) { | ||
return NORMAL_END; | ||
} | ||
else { | ||
return i; // '\n' | ||
} | ||
} | ||
|
||
// return value: | ||
// -1: error format | ||
// -2: normal end | ||
// '\n': more lines | ||
private static int parseMetric(InputStream inputStream, List<Metric> metrics) throws IOException { | ||
Metric.MetricBuilder metricBuilder = new Metric.MetricBuilder(); | ||
|
||
int i = parseMetricName(inputStream, metricBuilder); // RET: -1, -2, -3, '{', ' ' | ||
if (i == ERROR_FORMAT || i == NORMAL_END || i == COMMENT_LINE) { | ||
return i; | ||
} | ||
|
||
if (i == '{') { | ||
i = parseLabelList(inputStream, metricBuilder); // RET: -1, '}' | ||
if (i == ERROR_FORMAT) { | ||
return i; | ||
} | ||
} | ||
|
||
|
||
i = parseValue(inputStream, metricBuilder); // RET: -1, -2, '\n', ' ' | ||
if (i != ' ') { | ||
metrics.add(metricBuilder.build()); | ||
return i; | ||
} | ||
|
||
i = parseTimestamp(inputStream, metricBuilder); // RET: -1, -2, '\n' | ||
|
||
metrics.add(metricBuilder.build()); | ||
return i; | ||
|
||
} | ||
|
||
private static int skipCommentLine(InputStream inputStream) throws IOException { | ||
int i = inputStream.read(); | ||
while (i != -1 && i != '\n') { | ||
i = inputStream.read(); | ||
} | ||
if (i == -1) { | ||
return NORMAL_END; | ||
} | ||
return i; | ||
} | ||
|
||
public static List<Metric> parseMetrics(InputStream inputStream) throws IOException { | ||
List<Metric> metricList= new ArrayList<>(); | ||
int i = parseMetric(inputStream, metricList); | ||
while (i == '\n' || i == COMMENT_LINE) { | ||
if (i == COMMENT_LINE) { | ||
if (skipCommentLine(inputStream) == NORMAL_END) { | ||
return metricList; | ||
} | ||
|
||
} | ||
i = parseMetric(inputStream, metricList); | ||
} | ||
if (i == NORMAL_END) { | ||
return metricList; | ||
} | ||
else { | ||
return null; | ||
} | ||
} | ||
|
||
|
||
} |
43 changes: 43 additions & 0 deletions
43
push/src/main/java/org/dromara/hertzbeat/push/controller/PushGatewayController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.dromara.hertzbeat.push.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import org.dromara.hertzbeat.common.entity.dto.Message; | ||
import org.dromara.hertzbeat.push.service.PushGatewayService; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
|
||
/** | ||
* push gateway controller | ||
* | ||
* | ||
*/ | ||
@Tag(name = "Metrics Push Gateway API | 监控数据推送网关API") | ||
@RestController | ||
@RequestMapping(value = "/api/push/pushgateway") | ||
public class PushGatewayController { | ||
|
||
@Autowired | ||
private PushGatewayService pushGatewayService; | ||
|
||
@PostMapping() | ||
@Operation(summary = "Push metric data to hertzbeat pushgateway", description = "推送监控数据到hertzbeat推送网关") | ||
public ResponseEntity<Message<Void>> pushMetrics(HttpServletRequest request) throws IOException { | ||
InputStream inputStream = request.getInputStream(); | ||
boolean result = pushGatewayService.pushMetricsData(inputStream); | ||
if (result) { | ||
return ResponseEntity.ok(Message.success("Push success")); | ||
} | ||
else { | ||
return ResponseEntity.ok(Message.success("Push failed")); | ||
} | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
push/src/main/java/org/dromara/hertzbeat/push/service/PushGatewayService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.dromara.hertzbeat.push.service; | ||
|
||
import org.springframework.stereotype.Service; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
|
||
/** | ||
* push gateway metrics | ||
* | ||
* | ||
*/ | ||
|
||
@Service | ||
public interface PushGatewayService { | ||
|
||
boolean pushMetricsData(InputStream inputStream) throws IOException; | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
push/src/main/java/org/dromara/hertzbeat/push/service/impl/PushGatewayServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.dromara.hertzbeat.push.service.impl; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.dromara.hertzbeat.common.util.prometheus.Metric; | ||
import org.dromara.hertzbeat.common.util.prometheus.PrometheusUtil; | ||
import org.dromara.hertzbeat.push.service.PushGatewayService; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.List; | ||
|
||
/** | ||
* push gateway service impl | ||
* | ||
* | ||
*/ | ||
|
||
@Slf4j | ||
@Service | ||
public class PushGatewayServiceImpl implements PushGatewayService { | ||
|
||
@Override | ||
public boolean pushMetricsData(InputStream inputStream) throws IOException { | ||
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream); | ||
return metrics != null; | ||
} | ||
} |