mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 09:40:58 +00:00
Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b65d57b492 | ||
|
|
f31c835181 | ||
|
|
33d9764631 | ||
|
|
5d1b1eade7 | ||
|
|
26f9e66143 | ||
|
|
dca55b6d99 | ||
|
|
48b1e52585 | ||
|
|
2a20150d7c | ||
|
|
552554b96d | ||
|
|
4adcfd2a4e | ||
|
|
e630beecb4 | ||
|
|
6c2b38e180 | ||
|
|
84986dd1bf | ||
|
|
af868e083a | ||
|
|
be3e2f3558 | ||
|
|
d32818e8ef | ||
|
|
10b283522c | ||
|
|
31ab2da41e | ||
|
|
a9436049e3 | ||
|
|
41c010511c | ||
|
|
b5281c1274 | ||
|
|
62cf3d1b2b | ||
|
|
dc8ae844c1 | ||
|
|
91a7593b87 | ||
|
|
4442a52adf | ||
|
|
60e3437e82 | ||
|
|
21d6ec2f1b | ||
|
|
3ed50f39cb | ||
|
|
dfba21f224 |
@@ -2485,6 +2485,25 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Duansg",
|
||||
"name": "Duansg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/112607719?v=4",
|
||||
"profile": "https://github.com/Duansg",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xiaomizhou2",
|
||||
"name": "zhangyaxi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/47807926?v=4",
|
||||
"profile": "https://github.com/xiaomizhou2",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -523,6 +523,10 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Duansg"><img src="https://avatars.githubusercontent.com/u/112607719?v=4?s=100" width="100px;" alt="Duansg"/><br /><sub><b>Duansg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Duansg" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaomizhou2"><img src="https://avatars.githubusercontent.com/u/47807926?v=4?s=100" width="100px;" alt="zhangyaxi"/><br /><sub><b>zhangyaxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -544,7 +548,7 @@ WeChat Group : Add friend `ahertzbeat` and invite to the group.
|
||||
|
||||
WeChat Public : Search ID `usthecom`.
|
||||
|
||||
[QQ Group](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : Group num `630061200`
|
||||
[QQ Group](https://qm.qq.com/q/xxqecSC2cw) : Group num `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
|
||||
+9
-5
@@ -34,7 +34,7 @@
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
@@ -59,8 +59,8 @@
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为 `yml` 监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为 `yml` 监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
|
||||
@@ -98,7 +98,7 @@
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
## 🐕 快速开始
|
||||
@@ -521,6 +521,10 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Duansg"><img src="https://avatars.githubusercontent.com/u/112607719?v=4?s=100" width="100px;" alt="Duansg"/><br /><sub><b>Duansg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Duansg" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaomizhou2"><img src="https://avatars.githubusercontent.com/u/47807926?v=4?s=100" width="100px;" alt="zhangyaxi"/><br /><sub><b>zhangyaxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -542,7 +546,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
|
||||
微信公众号 : 搜索 ID `usthecom`.
|
||||
|
||||
[QQ交流群](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : 群号 `630061200`
|
||||
[QQ交流群](https://qm.qq.com/q/xxqecSC2cw) : 群号 `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
|
||||
+1
-1
@@ -546,7 +546,7 @@ WeChatグループ : `ahertzbeat` を検索.
|
||||
|
||||
WeChat公式アカウント : `usthecom`を検索.
|
||||
|
||||
[QQグループ](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : グループ番号 `630061200`
|
||||
[QQグループ](https://qm.qq.com/q/xxqecSC2cw) : グループ番号 `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
|
||||
+12
-1
@@ -31,6 +31,8 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Send alarm information through Server
|
||||
*/
|
||||
@@ -54,7 +56,16 @@ public class ServerChanAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<ServerChanAlertNotifyHandlerImpl.ServerChanWebHookDto> httpEntity = new HttpEntity<>(serverChanWebHookDto, headers);
|
||||
String webHookUrl = String.format(alerterProperties.getServerChanWebhookUrl(), receiver.getServerChanToken());
|
||||
String sanitizedToken = receiver.getServerChanToken().replaceAll("[^a-zA-Z0-9_-]", "");
|
||||
String webHookUrl = String.format(alerterProperties.getServerChanWebhookUrl(), sanitizedToken);
|
||||
|
||||
// Validate the constructed URL against a whitelist
|
||||
List<String> allowedBaseUrls = List.of("https://api.serverchan.com", "https://serverchan.example.com");
|
||||
boolean isValidUrl = allowedBaseUrls.stream().anyMatch(webHookUrl::startsWith);
|
||||
if (!isValidUrl) {
|
||||
throw new AlertNoticeException("Invalid webhook URL: " + webHookUrl);
|
||||
}
|
||||
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity = restTemplate.postForEntity(webHookUrl,
|
||||
httpEntity, CommonRobotNotifyResp.class);
|
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||
|
||||
+24
-1
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -52,7 +53,12 @@ final class SlackAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<SlackNotifyDTO> slackNotifyEntity = new HttpEntity<>(slackNotify, headers);
|
||||
var entity = restTemplate.postForEntity(receiver.getSlackWebHookUrl(), slackNotifyEntity, String.class);
|
||||
String slackWebHookUrl = receiver.getSlackWebHookUrl();
|
||||
if (!isValidSlackWebHookUrl(slackWebHookUrl)) {
|
||||
log.warn("Invalid Slack Webhook URL: {}", slackWebHookUrl);
|
||||
throw new AlertNoticeException("Invalid Slack Webhook URL");
|
||||
}
|
||||
var entity = restTemplate.postForEntity(slackWebHookUrl, slackNotifyEntity, String.class);
|
||||
if (entity.getStatusCode() == HttpStatus.OK && entity.getBody() != null) {
|
||||
var body = entity.getBody();
|
||||
if (Objects.equals(SUCCESS, body)) {
|
||||
@@ -81,4 +87,21 @@ final class SlackAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
private String text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate if the Slack Webhook URL belongs to an allowed domain.
|
||||
*
|
||||
* @param url the Slack Webhook URL to validate
|
||||
* @return true if the URL is valid, false otherwise
|
||||
*/
|
||||
private boolean isValidSlackWebHookUrl(String url) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
String host = uri.getHost();
|
||||
return "hooks.slack.com".equals(host);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error validating Slack Webhook URL: {}", url, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -42,9 +42,14 @@ import org.springframework.stereotype.Component;
|
||||
final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert)
|
||||
throws AlertNoticeException {
|
||||
try {
|
||||
String url = String.format(alerterProperties.getTelegramWebhookUrl(), receiver.getTgBotToken());
|
||||
String token = receiver.getTgBotToken();
|
||||
if (!isValidTelegramToken(token)) {
|
||||
throw new AlertNoticeException("Invalid Telegram Bot Token");
|
||||
}
|
||||
String url = String.format(alerterProperties.getTelegramWebhookUrl(), token);
|
||||
TelegramBotNotifyDTO notifyBody = TelegramBotNotifyDTO.builder()
|
||||
.chatId(receiver.getTgUserId())
|
||||
.text(renderContent(noticeTemplate, alert))
|
||||
@@ -54,7 +59,8 @@ final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<TelegramBotNotifyDTO> telegramEntity = new HttpEntity<>(notifyBody, headers);
|
||||
ResponseEntity<TelegramBotNotifyResponse> entity = restTemplate.postForEntity(url, telegramEntity, TelegramBotNotifyResponse.class);
|
||||
ResponseEntity<TelegramBotNotifyResponse> entity = restTemplate.postForEntity(url, telegramEntity,
|
||||
TelegramBotNotifyResponse.class);
|
||||
if (entity.getStatusCode() == HttpStatus.OK && entity.getBody() != null) {
|
||||
TelegramBotNotifyResponse body = entity.getBody();
|
||||
if (body.ok) {
|
||||
@@ -99,4 +105,10 @@ final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
private String description;
|
||||
}
|
||||
|
||||
private boolean isValidTelegramToken(String token) {
|
||||
// Adjusted pattern to match real Telegram Bot tokens like
|
||||
// 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw
|
||||
String tokenPattern = "^[0-9]+:[a-zA-Z0-9_-]+$";
|
||||
return token != null && token.matches(tokenPattern);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -57,7 +57,12 @@ final class WeComRobotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerI
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<WeWorkWebHookDto> httpEntity = new HttpEntity<>(weWorkWebHookDTO, headers);
|
||||
String webHookUrl = alerterProperties.getWeWorkWebhookUrl() + receiver.getWechatId();
|
||||
String wechatId = receiver.getWechatId();
|
||||
if (!isValidWechatId(wechatId)) {
|
||||
log.warn("Invalid WeChat ID: {}", wechatId);
|
||||
throw new AlertNoticeException("Invalid WeChat ID provided.");
|
||||
}
|
||||
String webHookUrl = alerterProperties.getWeWorkWebhookUrl() + wechatId;
|
||||
ResponseEntity<CommonRobotNotifyResp> entity = restTemplate.postForEntity(webHookUrl, httpEntity, CommonRobotNotifyResp.class);
|
||||
if (entity.getStatusCode() == HttpStatus.OK) {
|
||||
assert entity.getBody() != null;
|
||||
@@ -170,4 +175,15 @@ final class WeComRobotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerI
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the WeChat ID to ensure it meets the expected format.
|
||||
*
|
||||
* @param wechatId the WeChat ID to validate
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
private boolean isValidWechatId(String wechatId) {
|
||||
// Example validation: ensure the ID is alphanumeric and non-empty
|
||||
return StringUtils.isNotBlank(wechatId) && wechatId.matches("^[a-zA-Z0-9_-]+$");
|
||||
}
|
||||
}
|
||||
|
||||
+16
-26
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.alert.notice.impl;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.hertzbeat.alert.AlerterProperties;
|
||||
@@ -48,7 +49,7 @@ import java.util.ResourceBundle;
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ServerChanAlertNotifyHandlerImplTest {
|
||||
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@@ -57,20 +58,21 @@ class ServerChanAlertNotifyHandlerImplTest {
|
||||
|
||||
@Mock
|
||||
private ResourceBundle bundle;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private ServerChanAlertNotifyHandlerImpl serverChanAlertNotifyHandler;
|
||||
|
||||
|
||||
private NoticeReceiver receiver;
|
||||
private GroupAlert groupAlert;
|
||||
private NoticeTemplate template;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
receiver = new NoticeReceiver();
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
receiver.setAccessToken("test-token");
|
||||
receiver.setServerChanToken("SCT193569TSNm6xIabdjqeZPtOGOWcvU1e");
|
||||
|
||||
groupAlert = new GroupAlert();
|
||||
SingleAlert singleAlert = new SingleAlert();
|
||||
@@ -87,43 +89,31 @@ class ServerChanAlertNotifyHandlerImplTest {
|
||||
template.setName("test-template");
|
||||
template.setContent("test content");
|
||||
|
||||
when(alerterProperties.getServerChanWebhookUrl()).thenReturn("http://test.url/");
|
||||
when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
|
||||
lenient().when(alerterProperties.getServerChanWebhookUrl())
|
||||
.thenReturn("https://api.serverchan.com/send/%s");
|
||||
lenient().when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertSuccess() {
|
||||
CommonRobotNotifyResp successResp = new CommonRobotNotifyResp();
|
||||
successResp.setErrCode(0);
|
||||
successResp.setMsg("success");
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity =
|
||||
new ResponseEntity<>(successResp, HttpStatus.OK);
|
||||
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity = new ResponseEntity<>(successResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(
|
||||
any(String.class),
|
||||
any(),
|
||||
eq(CommonRobotNotifyResp.class)
|
||||
)).thenReturn(responseEntity);
|
||||
eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
|
||||
|
||||
serverChanAlertNotifyHandler.send(receiver, template, groupAlert);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertFailure() {
|
||||
CommonRobotNotifyResp failResp = new CommonRobotNotifyResp();
|
||||
failResp.setCode(1);
|
||||
failResp.setErrMsg("Test Error");
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity =
|
||||
new ResponseEntity<>(failResp, HttpStatus.BAD_REQUEST);
|
||||
|
||||
when(restTemplate.postForEntity(
|
||||
any(String.class),
|
||||
any(),
|
||||
eq(CommonRobotNotifyResp.class)
|
||||
)).thenReturn(responseEntity);
|
||||
public void testNotifyAlertWithInvalidUrl() {
|
||||
when(alerterProperties.getServerChanWebhookUrl()).thenReturn("http://invalid-url.com/%s");
|
||||
|
||||
assertThrows(AlertNoticeException.class,
|
||||
assertThrows(AlertNoticeException.class,
|
||||
() -> serverChanAlertNotifyHandler.send(receiver, template, groupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
+11
-15
@@ -51,7 +51,7 @@ class SlackAlertNotifyHandlerImplTest {
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
|
||||
@Mock
|
||||
private ResourceBundle bundle;
|
||||
|
||||
@@ -68,18 +68,18 @@ class SlackAlertNotifyHandlerImplTest {
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
receiver.setAccessToken("test-token");
|
||||
receiver.setSlackWebHookUrl("http://localhost:8080");
|
||||
|
||||
receiver.setSlackWebHookUrl("https://hooks.slack.com/services/ABCDEF/GHIJKL/mnopqrstuvwxyz");
|
||||
|
||||
groupAlert = new GroupAlert();
|
||||
SingleAlert singleAlert = new SingleAlert();
|
||||
singleAlert.setLabels(new HashMap<>());
|
||||
singleAlert.getLabels().put("severity", "critical");
|
||||
singleAlert.getLabels().put("alertname", "Test Alert");
|
||||
|
||||
|
||||
List<SingleAlert> alerts = new ArrayList<>();
|
||||
alerts.add(singleAlert);
|
||||
groupAlert.setAlerts(alerts);
|
||||
|
||||
|
||||
template = new NoticeTemplate();
|
||||
template.setId(1L);
|
||||
template.setName("test-template");
|
||||
@@ -90,22 +90,18 @@ class SlackAlertNotifyHandlerImplTest {
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertSuccess() {
|
||||
ResponseEntity<String> responseEntity =
|
||||
new ResponseEntity<>("ok", HttpStatus.OK);
|
||||
|
||||
ResponseEntity<String> responseEntity = new ResponseEntity<>("ok", HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(any(String.class), any(), eq(String.class))).thenReturn(responseEntity);
|
||||
|
||||
|
||||
slackAlertNotifyHandler.send(receiver, template, groupAlert);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertFailure() {
|
||||
ResponseEntity<String> responseEntity =
|
||||
new ResponseEntity<>("invalid_payload", HttpStatus.BAD_REQUEST);
|
||||
public void testNotifyAlertWithInvalidUrl() {
|
||||
receiver.setSlackWebHookUrl("http://localhost:8080");
|
||||
|
||||
when(restTemplate.postForEntity(any(String.class), any(), eq(String.class))).thenReturn(responseEntity);
|
||||
|
||||
assertThrows(AlertNoticeException.class,
|
||||
assertThrows(AlertNoticeException.class,
|
||||
() -> slackAlertNotifyHandler.send(receiver, template, groupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.alert.notice.impl;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.hertzbeat.alert.AlerterProperties;
|
||||
@@ -51,10 +52,10 @@ class TelegramBotAlertNotifyHandlerImplTest {
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
|
||||
@Mock
|
||||
private AlerterProperties alerterProperties;
|
||||
|
||||
|
||||
@Mock
|
||||
private ResourceBundle bundle;
|
||||
|
||||
@@ -70,58 +71,57 @@ class TelegramBotAlertNotifyHandlerImplTest {
|
||||
receiver = new NoticeReceiver();
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
receiver.setAccessToken("test-token");
|
||||
receiver.setTgBotToken("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11");
|
||||
receiver.setTgUserId("123456789"); // Telegram specific - chat ID
|
||||
|
||||
|
||||
groupAlert = new GroupAlert();
|
||||
SingleAlert singleAlert = new SingleAlert();
|
||||
singleAlert.setLabels(new HashMap<>());
|
||||
singleAlert.getLabels().put("severity", "critical");
|
||||
singleAlert.getLabels().put("alertname", "Test Alert");
|
||||
|
||||
|
||||
List<SingleAlert> alerts = new ArrayList<>();
|
||||
alerts.add(singleAlert);
|
||||
groupAlert.setAlerts(alerts);
|
||||
|
||||
|
||||
template = new NoticeTemplate();
|
||||
template.setId(1L);
|
||||
template.setName("test-template");
|
||||
template.setContent("test content");
|
||||
|
||||
when(alerterProperties.getTelegramWebhookUrl()).thenReturn("https://api.telegram.org/bot%s/sendMessage");
|
||||
when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
|
||||
|
||||
lenient().when(alerterProperties.getTelegramWebhookUrl())
|
||||
.thenReturn("https://api.telegram.org/bot%s/sendMessage");
|
||||
lenient().when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertSuccess() {
|
||||
TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse successResp =
|
||||
new TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse();
|
||||
TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse successResp = new TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse();
|
||||
successResp.setOk(true);
|
||||
successResp.setDescription("Test Success");
|
||||
|
||||
ResponseEntity<TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse> responseEntity =
|
||||
new ResponseEntity<>(successResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(any(String.class), any(),
|
||||
|
||||
ResponseEntity<TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse> responseEntity = new ResponseEntity<>(
|
||||
successResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(any(String.class), any(),
|
||||
eq(TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse.class))).thenReturn(responseEntity);
|
||||
|
||||
|
||||
telegramBotAlertNotifyHandler.send(receiver, template, groupAlert);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertFailure() {
|
||||
TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse successResp =
|
||||
new TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse();
|
||||
successResp.setOk(false);
|
||||
successResp.setDescription("Test failed");
|
||||
TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse failureResp = new TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse();
|
||||
failureResp.setOk(false);
|
||||
failureResp.setDescription("Test failed");
|
||||
|
||||
ResponseEntity<TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse> responseEntity =
|
||||
new ResponseEntity<>(successResp, HttpStatus.BAD_REQUEST);
|
||||
ResponseEntity<TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse> responseEntity = new ResponseEntity<>(
|
||||
failureResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(any(String.class), any(),
|
||||
eq(TelegramBotAlertNotifyHandlerImpl.TelegramBotNotifyResponse.class))).thenReturn(responseEntity);
|
||||
|
||||
assertThrows(AlertNoticeException.class,
|
||||
|
||||
assertThrows(AlertNoticeException.class,
|
||||
() -> telegramBotAlertNotifyHandler.send(receiver, template, groupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
+13
-16
@@ -48,7 +48,7 @@ import java.util.ResourceBundle;
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WeComRobotAlertNotifyHandlerImplTest {
|
||||
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@@ -57,20 +57,21 @@ class WeComRobotAlertNotifyHandlerImplTest {
|
||||
|
||||
@Mock
|
||||
private ResourceBundle bundle;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private WeComRobotAlertNotifyHandlerImpl weComRobotAlertNotifyHandler;
|
||||
|
||||
|
||||
private NoticeReceiver receiver;
|
||||
private GroupAlert groupAlert;
|
||||
private NoticeTemplate template;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
receiver = new NoticeReceiver();
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
receiver.setAccessToken("test-token");
|
||||
receiver.setWechatId("test-wechat-id");
|
||||
|
||||
groupAlert = new GroupAlert();
|
||||
SingleAlert singleAlert = new SingleAlert();
|
||||
@@ -95,33 +96,29 @@ class WeComRobotAlertNotifyHandlerImplTest {
|
||||
public void testNotifyAlertSuccess() {
|
||||
CommonRobotNotifyResp successResp = new CommonRobotNotifyResp();
|
||||
successResp.setErrCode(0);
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity =
|
||||
new ResponseEntity<>(successResp, HttpStatus.OK);
|
||||
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity = new ResponseEntity<>(successResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(
|
||||
any(String.class),
|
||||
any(),
|
||||
eq(CommonRobotNotifyResp.class)
|
||||
)).thenReturn(responseEntity);
|
||||
eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
|
||||
|
||||
weComRobotAlertNotifyHandler.send(receiver, template, groupAlert);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotifyAlertFailure() {
|
||||
CommonRobotNotifyResp failResp = new CommonRobotNotifyResp();
|
||||
failResp.setCode(1);
|
||||
failResp.setErrMsg("Test Error");
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity =
|
||||
new ResponseEntity<>(failResp, HttpStatus.OK);
|
||||
|
||||
ResponseEntity<CommonRobotNotifyResp> responseEntity = new ResponseEntity<>(failResp, HttpStatus.OK);
|
||||
|
||||
when(restTemplate.postForEntity(
|
||||
any(String.class),
|
||||
any(),
|
||||
eq(CommonRobotNotifyResp.class)
|
||||
)).thenReturn(responseEntity);
|
||||
eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
|
||||
|
||||
assertThrows(AlertNoticeException.class,
|
||||
assertThrows(AlertNoticeException.class,
|
||||
() -> weComRobotAlertNotifyHandler.send(receiver, template, groupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- clickhouse -->
|
||||
@@ -164,5 +164,11 @@
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
</dependency>
|
||||
<!-- Zookeeper -->
|
||||
<dependency>
|
||||
<groupId>org.apache.zookeeper</groupId>
|
||||
<artifactId>zookeeper</artifactId>
|
||||
<version>${zookeeper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+97
-12
@@ -66,7 +66,7 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
private static final String JMX_URL_PREFIX = "service:jmx:rmi:///jndi/rmi://";
|
||||
|
||||
private static final String JMX_URL_SUFFIX = "/jmxrmi";
|
||||
|
||||
|
||||
private static final String IGNORED_STUB = "/stub/";
|
||||
|
||||
private static final String SUB_ATTRIBUTE = "->";
|
||||
@@ -75,7 +75,6 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
|
||||
private final ClassLoader jmxClassLoader;
|
||||
|
||||
|
||||
public JmxCollectImpl() {
|
||||
jmxClassLoader = new JmxClassLoader(ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
@@ -83,13 +82,66 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
Assert.isTrue(metrics != null && metrics.getJmx() != null, "JMX collect must have JMX params");
|
||||
JmxProtocol jmxProtocol = metrics.getJmx();
|
||||
|
||||
String url = metrics.getJmx().getUrl();
|
||||
// Validate JMX URL if provided
|
||||
String url = jmxProtocol.getUrl();
|
||||
if (StringUtils.hasText(url)) {
|
||||
Assert.doesNotContain(url, IGNORED_STUB, "JMX url prohibit contains stub, please check");
|
||||
|
||||
// Prevent JNDI injection by validating URL format
|
||||
validateJmxUrl(url);
|
||||
} else {
|
||||
// Validate host and port inputs
|
||||
String host = jmxProtocol.getHost();
|
||||
int port = Integer.parseInt(jmxProtocol.getPort());
|
||||
|
||||
// Validate host format (only allow valid hostnames or IP addresses)
|
||||
Assert.isTrue(isValidHostname(host), "Invalid hostname format");
|
||||
Assert.isTrue(port > 0 && port <= 65535, "Port must be between 1 and 65535");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate JMX URL
|
||||
*
|
||||
* @param url JMX URL to validate
|
||||
* @throws IllegalArgumentException if URL is potentially malicious
|
||||
*/
|
||||
private void validateJmxUrl(String url) throws IllegalArgumentException {
|
||||
// Only allow service:jmx:rmi protocol
|
||||
Assert.isTrue(url.startsWith("service:jmx:rmi:"), "Only service:jmx:rmi protocol is supported");
|
||||
|
||||
String[] disallowedPatterns = { "ldap:", "rmi:", "iiop:", "nis:", "dns:", "corbaname:", "http:", "https:" };
|
||||
for (String pattern : disallowedPatterns) {
|
||||
if (url.contains(pattern) && !pattern.equals("rmi:///jndi/rmi:")) {
|
||||
throw new IllegalArgumentException("Potentially unsafe JNDI protocol detected in URL: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for suspicious patterns
|
||||
if (url.contains("${") || url.contains("$[") || url.contains(":#") || url.contains(":/")) {
|
||||
throw new IllegalArgumentException("Potentially malicious pattern detected in JMX URL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate hostname format
|
||||
*
|
||||
* @param hostname Hostname to validate
|
||||
* @return true if hostname is valid
|
||||
*/
|
||||
private boolean isValidHostname(String hostname) {
|
||||
if (hostname == null || hostname.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simplified hostname/IP validation regex
|
||||
// This regex accepts valid hostnames, IPv4 and IPv6 addresses
|
||||
String hostnameRegex = "^([a-zA-Z0-9][-a-zA-Z0-9]*\\.)+[a-zA-Z0-9][-a-zA-Z0-9]*$|^(\\d{1,3}\\.){3}\\d{1,3}$|^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$";
|
||||
return hostname.matches(hostnameRegex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
@@ -172,16 +224,18 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
log.info("attribute {} value is null.", attribute.getName());
|
||||
continue;
|
||||
}
|
||||
if (value instanceof Number || value instanceof String || value instanceof ObjectName
|
||||
if (value instanceof Number || value instanceof String || value instanceof ObjectName
|
||||
|| value instanceof Boolean || value instanceof Date || value instanceof TimeUnit) {
|
||||
attributeValueMap.put(attribute.getName(), value.toString());
|
||||
} else if (value instanceof CompositeData compositeData) {
|
||||
} else if (value instanceof CompositeData) {
|
||||
CompositeData compositeData = (CompositeData) value;
|
||||
CompositeType compositeType = compositeData.getCompositeType();
|
||||
for (String typeKey : compositeType.keySet()) {
|
||||
Object fieldValue = compositeData.get(typeKey);
|
||||
attributeValueMap.put(attribute.getName() + SUB_ATTRIBUTE + typeKey, fieldValue.toString());
|
||||
}
|
||||
} else if (value instanceof String[] values) {
|
||||
} else if (value instanceof String[]) {
|
||||
String[] values = (String[]) value;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < values.length; index++) {
|
||||
builder.append(values[index]);
|
||||
@@ -219,12 +273,33 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
String url;
|
||||
if (jmxProtocol.getUrl() != null) {
|
||||
url = jmxProtocol.getUrl();
|
||||
// Double check URL format for security
|
||||
if (!url.startsWith("service:jmx:rmi:")) {
|
||||
throw new IOException("Unsupported JMX URL protocol. Only service:jmx:rmi: is allowed.");
|
||||
}
|
||||
} else {
|
||||
url = JMX_URL_PREFIX + jmxProtocol.getHost() + ":" + jmxProtocol.getPort() + JMX_URL_SUFFIX;
|
||||
// More strict formatting with proper escaping
|
||||
String host = jmxProtocol.getHost();
|
||||
int port = Integer.parseInt(jmxProtocol.getPort());
|
||||
|
||||
// Additional validation at connection time
|
||||
if (!isValidHostname(host)) {
|
||||
throw new IOException("Invalid hostname format for JMX connection: " + host);
|
||||
}
|
||||
if (port <= 0 || port > 65535) {
|
||||
throw new IOException("Invalid port for JMX connection: " + port);
|
||||
}
|
||||
|
||||
url = JMX_URL_PREFIX + host + ":" + port + JMX_URL_SUFFIX;
|
||||
}
|
||||
|
||||
// Set security properties to prevent remote class loading
|
||||
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "false");
|
||||
System.setProperty("com.sun.jndi.cosnaming.object.trustURLCodebase", "false");
|
||||
|
||||
Map<String, Object> environment = new HashMap<>(4);
|
||||
if (StringUtils.hasText(jmxProtocol.getUsername()) && StringUtils.hasText(jmxProtocol.getPassword())) {
|
||||
String[] credential = new String[] {jmxProtocol.getUsername(), jmxProtocol.getPassword()};
|
||||
String[] credential = new String[] { jmxProtocol.getUsername(), jmxProtocol.getPassword() };
|
||||
environment.put(javax.management.remote.JMXConnector.CREDENTIALS, credential);
|
||||
}
|
||||
if (Boolean.TRUE.toString().equals(jmxProtocol.getSsl())) {
|
||||
@@ -233,10 +308,20 @@ public class JmxCollectImpl extends AbstractCollect {
|
||||
environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
|
||||
environment.put("com.sun.jndi.rmi.factory.socket", clientSocketFactory);
|
||||
}
|
||||
JMXServiceURL jmxServiceUrl = new JMXServiceURL(url);
|
||||
conn = JMXConnectorFactory.connect(jmxServiceUrl, environment);
|
||||
connectionCommonCache.addCache(identifier, new JmxConnect(conn));
|
||||
return conn;
|
||||
|
||||
// Limit JMX connection timeout
|
||||
environment.put("jmx.remote.x.client.connection.timeout", 10000);
|
||||
environment.put("jmx.remote.x.server.connection.timeout", 10000);
|
||||
|
||||
try {
|
||||
JMXServiceURL jmxServiceUrl = new JMXServiceURL(url);
|
||||
conn = JMXConnectorFactory.connect(jmxServiceUrl, environment);
|
||||
connectionCommonCache.addCache(identifier, new JmxConnect(conn));
|
||||
return conn;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to connect to JMX server: {}", e.getMessage());
|
||||
throw new IOException("Failed to connect to JMX server: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ public class PrometheusAutoCollectImpl {
|
||||
builder.addField(CollectRep.Field.newBuilder().setName(label.getName())
|
||||
.setType(CommonConstants.TYPE_STRING).setLabel(true).build());
|
||||
});
|
||||
builder.addField(CollectRep.Field.newBuilder().setName("value")
|
||||
builder.addField(CollectRep.Field.newBuilder().setName("metric_value")
|
||||
.setType(CommonConstants.TYPE_NUMBER).setLabel(false).build());
|
||||
}
|
||||
Map<String, String> labelMap = metric.getLabels()
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.sd;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.registry.constant.DiscoveryClientInstance;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.DiscoveryClient;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.DiscoveryClientManagement;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.entity.ServiceInstance;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Nacos Service Discovery Collector
|
||||
* This collector integrates with Nacos to discover registered services
|
||||
*/
|
||||
@Slf4j
|
||||
public class NacosSdCollectImpl extends AbstractCollect {
|
||||
|
||||
/**
|
||||
* Client management to interact with discovery services
|
||||
*/
|
||||
private final DiscoveryClientManagement discoveryClientManagement = new DiscoveryClientManagement();
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
// Validate the required configuration is present
|
||||
if (metrics == null || metrics.getNacos_sd() == null) {
|
||||
throw new IllegalArgumentException("Nacos service discovery monitoring, the config is null");
|
||||
}
|
||||
if (metrics.getNacos_sd().isInvalid()) {
|
||||
throw new IllegalArgumentException("Nacos service discovery monitoring, the config is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
// Create Registry protocol from NacosSd protocol
|
||||
RegistryProtocol registryProtocol = RegistryProtocol.builder()
|
||||
.host(metrics.getNacos_sd().getHost())
|
||||
.port(metrics.getNacos_sd().getPort())
|
||||
.discoveryClientTypeName(DiscoveryClientInstance.NACOS.name())
|
||||
.build();
|
||||
|
||||
DiscoveryClient discoveryClient = null;
|
||||
try {
|
||||
// Use the existing NacosDiscoveryClient through DiscoveryClientManagement
|
||||
discoveryClient = discoveryClientManagement.getClient(registryProtocol);
|
||||
if (discoveryClient == null) {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Failed to get Nacos discovery client");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all services registered in Nacos
|
||||
List<ServiceInstance> services = discoveryClient.getServices();
|
||||
if (CollectionUtils.isEmpty(services)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the response data with service information
|
||||
services.forEach(service -> {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
valueRowBuilder.addColumn(service.getAddress());
|
||||
valueRowBuilder.addColumn(String.valueOf(service.getPort()));
|
||||
valueRowBuilder.addColumn(service.getServiceName());
|
||||
valueRowBuilder.addColumn(service.getHealthStatus());
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
});
|
||||
} catch (Exception e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.warn("Failed to fetch services from Nacos: {}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} finally {
|
||||
// Close the discovery client to release resources
|
||||
if (discoveryClient != null) {
|
||||
try {
|
||||
discoveryClient.close();
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to close Nacos discovery client: {}", CommonUtil.getMessageFromThrowable(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_NACOS_SD;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.sd;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ZookeeperSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.sd.ConnectionConfig;
|
||||
import org.apache.zookeeper.KeeperException;
|
||||
import org.apache.zookeeper.ZooKeeper;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* zookeeper sd collector
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZookeeperSdCollectImpl extends AbstractCollect {
|
||||
|
||||
private static int TIMEOUT = 30000;
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
ZookeeperSdProtocol zookeeperSdProtocol = metrics.getZookeeper_sd();
|
||||
String url = zookeeperSdProtocol.getUrl();
|
||||
String pathPrefix = zookeeperSdProtocol.getPathPrefix();
|
||||
try (ZooKeeper zk = new ZooKeeper(url, TIMEOUT, event -> {})){
|
||||
|
||||
List<String> children = zk.getChildren(pathPrefix, false);
|
||||
List<ConnectionConfig> connectionConfigs = children.stream().map(node -> {
|
||||
String[] split = node.split(":");
|
||||
if (split.length != 2) log.warn("Node format is incorrect: {}, expected format is 'host:port'", node);
|
||||
return new ConnectionConfig(split[0], split[1]);
|
||||
}).toList();
|
||||
connectionConfigs.forEach(config -> {
|
||||
CollectRep.ValueRow valueRow = CollectRep.ValueRow.newBuilder()
|
||||
.addColumn(config.getHost())
|
||||
.addColumn(config.getPort())
|
||||
.build();
|
||||
builder.addValueRow(valueRow);
|
||||
});
|
||||
} catch (IOException e){
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error("Failed to connect to Zookeeper: {}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (InterruptedException e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error("Zookeeper connection interrupted: {}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (KeeperException e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error("Zookeeper operation failed: {}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
if (metrics.getZookeeper_sd() == null) {
|
||||
throw new IllegalArgumentException("Zookeeper SD configuration cannot be null");
|
||||
}
|
||||
if (metrics.getZookeeper_sd().getUrl() == null || metrics.getZookeeper_sd().getUrl().isEmpty()) {
|
||||
throw new IllegalArgumentException("Zookeeper URL cannot be null or empty");
|
||||
}
|
||||
if (metrics.getZookeeper_sd().getPathPrefix() == null || metrics.getZookeeper_sd().getPathPrefix().isEmpty()) {
|
||||
throw new IllegalArgumentException("Zookeeper path prefix cannot be null or empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return "zookeeper_sd";
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.sd;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.hertzbeat.collector.collect.common.http.CommonHttpClient;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryResponseEntity;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
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.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* Test case for {@link HttpSdCollectImpl}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HttpSdCollectImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private HttpSdCollectImpl httpSdCollectImpl;
|
||||
|
||||
@Mock
|
||||
private CloseableHttpClient httpClient;
|
||||
|
||||
@Mock
|
||||
private CloseableHttpResponse httpResponse;
|
||||
|
||||
private Metrics metrics;
|
||||
private HttpProtocol httpProtocol;
|
||||
private CollectRep.MetricsData.Builder builder;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
httpProtocol = HttpProtocol.builder()
|
||||
.url("http://localhost:8080/services")
|
||||
.build();
|
||||
|
||||
metrics = Metrics.builder()
|
||||
.http_sd(httpProtocol)
|
||||
.build();
|
||||
|
||||
builder = CollectRep.MetricsData.newBuilder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectSuccess() throws IOException {
|
||||
List<ServiceDiscoveryResponseEntity> responseEntities = new ArrayList<>();
|
||||
|
||||
ServiceDiscoveryResponseEntity entity1 = new ServiceDiscoveryResponseEntity();
|
||||
entity1.setTarget(Arrays.asList("192.168.1.10:8080", "192.168.1.11:8081"));
|
||||
|
||||
ServiceDiscoveryResponseEntity entity2 = new ServiceDiscoveryResponseEntity();
|
||||
entity2.setTarget(Arrays.asList("192.168.1.12:8082"));
|
||||
|
||||
responseEntities.add(entity1);
|
||||
responseEntities.add(entity2);
|
||||
|
||||
String jsonResponse = JsonUtil.toJson(responseEntities);
|
||||
|
||||
try (MockedStatic<CommonHttpClient> mockedHttpClient = Mockito.mockStatic(CommonHttpClient.class)) {
|
||||
mockedHttpClient.when(CommonHttpClient::getHttpClient).thenReturn(httpClient);
|
||||
|
||||
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(httpResponse);
|
||||
|
||||
StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
|
||||
when(httpResponse.getStatusLine()).thenReturn(statusLine);
|
||||
|
||||
HttpEntity entity = new StringEntity(jsonResponse, ContentType.APPLICATION_JSON);
|
||||
when(httpResponse.getEntity()).thenReturn(entity);
|
||||
|
||||
httpSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(3, builder.getValuesCount());
|
||||
|
||||
CollectRep.ValueRow firstRow = builder.getValues(0);
|
||||
assertEquals("192.168.1.10", firstRow.getColumns(0));
|
||||
assertEquals("8080", firstRow.getColumns(1));
|
||||
|
||||
CollectRep.ValueRow secondRow = builder.getValues(1);
|
||||
assertEquals("192.168.1.11", secondRow.getColumns(0));
|
||||
assertEquals("8081", secondRow.getColumns(1));
|
||||
|
||||
CollectRep.ValueRow thirdRow = builder.getValues(2);
|
||||
assertEquals("192.168.1.12", thirdRow.getColumns(0));
|
||||
assertEquals("8082", thirdRow.getColumns(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectWithNon200Status() throws IOException {
|
||||
try (MockedStatic<CommonHttpClient> mockedHttpClient = Mockito.mockStatic(CommonHttpClient.class)) {
|
||||
mockedHttpClient.when(CommonHttpClient::getHttpClient).thenReturn(httpClient);
|
||||
|
||||
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(httpResponse);
|
||||
|
||||
StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 404, "Not Found");
|
||||
when(httpResponse.getStatusLine()).thenReturn(statusLine);
|
||||
|
||||
httpSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
assertEquals("StatusCode 404", builder.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void testCollectWithException() {
|
||||
String exceptionMessage = "Connection refused";
|
||||
IOException testException = new IOException(exceptionMessage);
|
||||
|
||||
try (MockedStatic<CommonHttpClient> mockedHttpClient = Mockito.mockStatic(CommonHttpClient.class);
|
||||
MockedStatic<CommonUtil> mockedCommonUtil = Mockito.mockStatic(CommonUtil.class)) {
|
||||
|
||||
mockedHttpClient.when(CommonHttpClient::getHttpClient).thenReturn(httpClient);
|
||||
|
||||
when(httpClient.execute(any(HttpUriRequest.class))).thenThrow(testException);
|
||||
mockedCommonUtil.when(() -> CommonUtil.getMessageFromThrowable(any(Throwable.class)))
|
||||
.thenReturn(exceptionMessage);
|
||||
|
||||
httpSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
assertEquals(exceptionMessage, builder.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectWithEmptyServiceList() throws IOException {
|
||||
List<ServiceDiscoveryResponseEntity> emptyList = Collections.emptyList();
|
||||
String jsonResponse = JsonUtil.toJson(emptyList);
|
||||
|
||||
try (MockedStatic<CommonHttpClient> mockedHttpClient = Mockito.mockStatic(CommonHttpClient.class)) {
|
||||
mockedHttpClient.when(CommonHttpClient::getHttpClient).thenReturn(httpClient);
|
||||
|
||||
when(httpClient.execute(any(HttpUriRequest.class))).thenReturn(httpResponse);
|
||||
|
||||
StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
|
||||
when(httpResponse.getStatusLine()).thenReturn(statusLine);
|
||||
|
||||
HttpEntity entity = new StringEntity(jsonResponse, ContentType.APPLICATION_JSON);
|
||||
when(httpResponse.getEntity()).thenReturn(entity);
|
||||
|
||||
httpSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(0, builder.getValuesCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupportProtocol() {
|
||||
assertEquals(DispatchConstants.PROTOCOL_HTTP_SD, httpSdCollectImpl.supportProtocol());
|
||||
}
|
||||
}
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.sd;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.DiscoveryClient;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.DiscoveryClientManagement;
|
||||
import org.apache.hertzbeat.collector.collect.registry.discovery.entity.ServiceInstance;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.NacosSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
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.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Test case for {@link NacosSdCollectImpl}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NacosSdCollectImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private NacosSdCollectImpl nacosSdCollectImpl;
|
||||
|
||||
@Mock
|
||||
private DiscoveryClientManagement discoveryClientManagement;
|
||||
|
||||
@Mock
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
private Metrics metrics;
|
||||
private NacosSdProtocol nacosSdProtocol;
|
||||
private CollectRep.MetricsData.Builder builder;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
nacosSdProtocol = NacosSdProtocol.builder()
|
||||
.host("localhost")
|
||||
.port("8848")
|
||||
.username("nacos")
|
||||
.password("nacos")
|
||||
.build();
|
||||
|
||||
metrics = Metrics.builder()
|
||||
.nacos_sd(nacosSdProtocol)
|
||||
.build();
|
||||
|
||||
builder = CollectRep.MetricsData.newBuilder();
|
||||
|
||||
ReflectionTestUtils.setField(nacosSdCollectImpl, "discoveryClientManagement", discoveryClientManagement);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithNullMetrics() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> nacosSdCollectImpl.preCheck(null));
|
||||
|
||||
assertEquals("Nacos service discovery monitoring, the config is null", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithNullNacosSdConfig() {
|
||||
Metrics metricsWithoutNacosSd = Metrics.builder().build();
|
||||
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> nacosSdCollectImpl.preCheck(metricsWithoutNacosSd));
|
||||
|
||||
assertEquals("Nacos service discovery monitoring, the config is null", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithInvalidNacosSdConfig() {
|
||||
NacosSdProtocol invalidNacosSdProtocol = NacosSdProtocol.builder()
|
||||
.host("")
|
||||
.port("8848")
|
||||
.build();
|
||||
|
||||
Metrics metricsWithInvalidNacosSd = Metrics.builder()
|
||||
.nacos_sd(invalidNacosSdProtocol)
|
||||
.build();
|
||||
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> nacosSdCollectImpl.preCheck(metricsWithInvalidNacosSd));
|
||||
|
||||
assertEquals("Nacos service discovery monitoring, the config is invalid", exception.getMessage());
|
||||
|
||||
NacosSdProtocol portInvalidNacosSdProtocol = NacosSdProtocol.builder()
|
||||
.host("localhost")
|
||||
.port("")
|
||||
.build();
|
||||
|
||||
Metrics portMetricsWithInvalidNacosSd = Metrics.builder()
|
||||
.nacos_sd(portInvalidNacosSdProtocol)
|
||||
.build();
|
||||
|
||||
exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> nacosSdCollectImpl.preCheck(portMetricsWithInvalidNacosSd));
|
||||
|
||||
assertEquals("Nacos service discovery monitoring, the config is invalid", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithValidConfig() {
|
||||
assertDoesNotThrow(() -> nacosSdCollectImpl.preCheck(metrics));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectSuccess() throws Exception {
|
||||
List<ServiceInstance> services = new ArrayList<>();
|
||||
services.add(ServiceInstance.builder()
|
||||
.serviceId("service-1")
|
||||
.serviceName("user-service")
|
||||
.address("192.168.1.10")
|
||||
.port(8080)
|
||||
.healthStatus("UP")
|
||||
.build());
|
||||
services.add(ServiceInstance.builder()
|
||||
.serviceId("service-2")
|
||||
.serviceName("order-service")
|
||||
.address("192.168.1.11")
|
||||
.port(8081)
|
||||
.healthStatus("UP")
|
||||
.build());
|
||||
|
||||
when(discoveryClientManagement.getClient(any(RegistryProtocol.class))).thenReturn(discoveryClient);
|
||||
|
||||
when(discoveryClient.getServices()).thenReturn(services);
|
||||
|
||||
nacosSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
|
||||
verify(discoveryClient, times(1)).close();
|
||||
|
||||
assertEquals(2, builder.getValuesCount());
|
||||
|
||||
CollectRep.ValueRow firstRow = builder.getValues(0);
|
||||
assertEquals("192.168.1.10", firstRow.getColumns(0));
|
||||
assertEquals("8080", firstRow.getColumns(1));
|
||||
assertEquals("user-service", firstRow.getColumns(2));
|
||||
assertEquals("UP", firstRow.getColumns(3));
|
||||
|
||||
CollectRep.ValueRow secondRow = builder.getValues(1);
|
||||
assertEquals("192.168.1.11", secondRow.getColumns(0));
|
||||
assertEquals("8081", secondRow.getColumns(1));
|
||||
assertEquals("order-service", secondRow.getColumns(2));
|
||||
assertEquals("UP", secondRow.getColumns(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectClientNull() {
|
||||
when(discoveryClientManagement.getClient(any(RegistryProtocol.class))).thenReturn(null);
|
||||
|
||||
nacosSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
assertEquals("Failed to get Nacos discovery client", builder.getMsg());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void testCollectWithException() {
|
||||
String exceptionMessage = "Connection refused";
|
||||
Exception testException = new RuntimeException(exceptionMessage);
|
||||
|
||||
when(discoveryClientManagement.getClient(any(RegistryProtocol.class))).thenReturn(discoveryClient);
|
||||
when(discoveryClient.getServices()).thenThrow(testException);
|
||||
|
||||
try (MockedStatic<CommonUtil> mockedCommonUtil = Mockito.mockStatic(CommonUtil.class)) {
|
||||
mockedCommonUtil.when(() -> CommonUtil.getMessageFromThrowable(any(Throwable.class)))
|
||||
.thenReturn(exceptionMessage);
|
||||
|
||||
nacosSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(CollectRep.Code.FAIL, builder.getCode());
|
||||
assertEquals(exceptionMessage, builder.getMsg());
|
||||
|
||||
verify(discoveryClient, times(1)).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectWithEmptyServiceList() throws Exception {
|
||||
when(discoveryClientManagement.getClient(any(RegistryProtocol.class))).thenReturn(discoveryClient);
|
||||
when(discoveryClient.getServices()).thenReturn(Collections.emptyList());
|
||||
|
||||
nacosSdCollectImpl.collect(builder, metrics);
|
||||
|
||||
assertEquals(0, builder.getValuesCount());
|
||||
|
||||
verify(discoveryClient, times(1)).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupportProtocol() {
|
||||
assertEquals(DispatchConstants.PROTOCOL_NACOS_SD, nacosSdCollectImpl.supportProtocol());
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.sd;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ZookeeperSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.zookeeper.ZooKeeper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ZookeeperSdCollectImplTest {
|
||||
|
||||
private ZookeeperSdCollectImpl zookeeperSdCollect;
|
||||
|
||||
private Metrics metrics;
|
||||
private ZookeeperSdProtocol protocol;
|
||||
private CollectRep.MetricsData.Builder builder;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
zookeeperSdCollect = new ZookeeperSdCollectImpl();
|
||||
|
||||
protocol = ZookeeperSdProtocol.builder()
|
||||
.url("localhost:2181")
|
||||
.pathPrefix("/services")
|
||||
.build();
|
||||
|
||||
metrics = Metrics.builder()
|
||||
.zookeeper_sd(protocol)
|
||||
.build();
|
||||
|
||||
builder = CollectRep.MetricsData.newBuilder();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithNullConfig() {
|
||||
Metrics invalid = Metrics.builder().build();
|
||||
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
|
||||
() -> zookeeperSdCollect.preCheck(invalid));
|
||||
assertEquals("Zookeeper SD configuration cannot be null", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithEmptyUrl() {
|
||||
ZookeeperSdProtocol badProtocol = ZookeeperSdProtocol.builder().url("").pathPrefix("/path").build();
|
||||
Metrics m = Metrics.builder().zookeeper_sd(badProtocol).build();
|
||||
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
|
||||
() -> zookeeperSdCollect.preCheck(m));
|
||||
assertEquals("Zookeeper URL cannot be null or empty", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckWithEmptyPath() {
|
||||
ZookeeperSdProtocol badProtocol = ZookeeperSdProtocol.builder().url("host").pathPrefix("").build();
|
||||
Metrics m = Metrics.builder().zookeeper_sd(badProtocol).build();
|
||||
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
|
||||
() -> zookeeperSdCollect.preCheck(m));
|
||||
assertEquals("Zookeeper path prefix cannot be null or empty", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPreCheckValid() {
|
||||
assertDoesNotThrow(() -> zookeeperSdCollect.preCheck(metrics));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupportProtocol() {
|
||||
assertEquals("zookeeper_sd", zookeeperSdCollect.supportProtocol());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectSuccess() throws Exception {
|
||||
try (MockedConstruction<ZooKeeper> mocked = Mockito.mockConstruction(ZooKeeper.class,
|
||||
(mock, context) -> Mockito.when(mock.getChildren("/services", false)).thenReturn(List.of("host1:8080", "host2:9090")))) {
|
||||
|
||||
zookeeperSdCollect.collect(builder, metrics);
|
||||
|
||||
assertEquals(2, builder.getValuesCount());
|
||||
assertEquals("host1", builder.getValues(0).getColumns(0));
|
||||
assertEquals("8080", builder.getValues(0).getColumns(1));
|
||||
assertEquals("host2", builder.getValues(1).getColumns(0));
|
||||
assertEquals("9090", builder.getValues(1).getColumns(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import org.apache.hertzbeat.collector.dispatch.CollectorInfoProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.CommonThreadPool;
|
||||
import org.apache.hertzbeat.remoting.RemotingClient;
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.WorkerPool;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.CollectServer;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
|
||||
+3
-3
@@ -23,9 +23,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.UnitConvert;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
|
||||
+2
-2
@@ -23,8 +23,8 @@ import org.apache.commons.jexl3.JexlExpression;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.PrometheusAutoCollectImpl;
|
||||
import org.apache.hertzbeat.collector.collect.strategy.CollectStrategyFactory;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.UnitConvert;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ org.apache.hertzbeat.collector.collect.mqtt.MqttCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.ipmi2.IpmiCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.kafka.KafkaCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.HttpSdCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.NacosSdCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.DnsSdCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.EurekaSdCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.sd.ConsulSdCollectImpl
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch;
|
||||
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
|
||||
|
||||
+4
@@ -127,6 +127,10 @@ public interface DispatchConstants {
|
||||
* protocol http sd
|
||||
*/
|
||||
String PROTOCOL_HTTP_SD = "http_sd";
|
||||
/**
|
||||
* protocol nacos sd
|
||||
*/
|
||||
String PROTOCOL_NACOS_SD = "nacos_sd";
|
||||
/**
|
||||
* protocol dns sd
|
||||
*/
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch;
|
||||
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
|
||||
/**
|
||||
* Metrics collection task scheduler interface
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import org.apache.hertzbeat.collector.dispatch.entrance.processor.GoCloseProcess
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.processor.GoOfflineProcessor;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.processor.GoOnlineProcessor;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.processor.HeartbeatProcessor;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.entity.dto.CollectorInfo;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.CommonThreadPool;
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.WorkerPool;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.CollectServer;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package org.apache.hertzbeat.collector.dispatch.entrance.processor;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.CollectServer;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.SpringContextHolder;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package org.apache.hertzbeat.collector.dispatch.entrance.processor;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.SpringContextHolder;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package org.apache.hertzbeat.collector.dispatch.entrance.processor;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.SpringContextHolder;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.collector.timer;
|
||||
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectResponseEventListener;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
+4
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.collector.timer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,6 +28,9 @@ import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectResponse
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.timer.HashedWheelTimer;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.Timer;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
+3
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.collector.timer;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -28,6 +28,8 @@ import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.support.SpringContextHolder;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.TimerTask;
|
||||
import org.apache.hertzbeat.common.util.AesUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -36,6 +36,7 @@ import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.EurekaSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ConsulSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ModbusProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.NacosSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.PlcProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.IcmpProtocol;
|
||||
@@ -66,11 +67,12 @@ import org.apache.hertzbeat.common.entity.job.protocol.TelnetProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.UdpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.WebsocketProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ZookeeperSdProtocol;
|
||||
/**
|
||||
* Details of the monitoring metrics collected
|
||||
* eg: cpu | memory | health
|
||||
*/
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@@ -278,6 +280,14 @@ public class Metrics {
|
||||
* consul sd protocol
|
||||
*/
|
||||
private ConsulSdProtocol consul_sd;
|
||||
/**
|
||||
* nacos sd protocol
|
||||
*/
|
||||
private NacosSdProtocol nacos_sd;
|
||||
/*
|
||||
* zookeeper sd protocol
|
||||
*/
|
||||
private ZookeeperSdProtocol zookeeper_sd;
|
||||
/**
|
||||
* Monitoring configuration information using the public plc protocol
|
||||
*/
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.job.protocol;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Nacos Service Discovery Protocol
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class NacosSdProtocol implements Protocol {
|
||||
/**
|
||||
* Nacos server host
|
||||
*/
|
||||
private String host;
|
||||
/**
|
||||
* Nacos server port
|
||||
*/
|
||||
private String port;
|
||||
/**
|
||||
* Nacos namespace
|
||||
*/
|
||||
private String namespace;
|
||||
/**
|
||||
* Nacos username for authentication
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* Nacos password for authentication
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Check if the essential protocol parameters are invalid
|
||||
* @return true if essential parameters are missing
|
||||
*/
|
||||
public boolean isInvalid() {
|
||||
return StringUtils.isAnyBlank(host, port);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.job.protocol;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* Zookeeper service discovery protocol
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ZookeeperSdProtocol implements Protocol{
|
||||
|
||||
private String url;
|
||||
|
||||
private String pathPrefix;
|
||||
}
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class Monitor {
|
||||
@Size(max = 100)
|
||||
private String app;
|
||||
|
||||
@Schema(title = "Scrape type: static | http_sd | dns_sd", example = "static", accessMode = READ_WRITE)
|
||||
@Schema(title = "Scrape type: static | http_sd | dns_sd | zookeeper_sd", example = "static", accessMode = READ_WRITE)
|
||||
@Size(max = 100)
|
||||
private String scrape;
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.common.timer;
|
||||
|
||||
import org.apache.hertzbeat.common.util.NetworkUtil;
|
||||
import org.slf4j.Logger;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.common.timer;
|
||||
|
||||
/**
|
||||
* Derived from Apache Dubbo org.apache.dubbo.common.timer.Timeout
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.common.timer;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
package org.apache.hertzbeat.common.timer;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ package org.apache.hertzbeat.collector.collect;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.CollectDataDispatch;
|
||||
import org.apache.hertzbeat.collector.dispatch.MetricsCollect;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
|
||||
@@ -150,8 +150,8 @@
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- email -->
|
||||
|
||||
+1
-4
@@ -150,10 +150,7 @@ public class ServiceDiscoveryWorker implements InitializingBean {
|
||||
// Thus, all monitors still in hostMonitorMap need to be cancelled.
|
||||
final Set<Long> needCancelMonitorIdSet = subMonitorBindMap.values().stream()
|
||||
.map(MonitorBind::getMonitorId).collect(Collectors.toSet());
|
||||
monitorService.cancelManageMonitors(needCancelMonitorIdSet);
|
||||
for (Long id : needCancelMonitorIdSet) {
|
||||
monitorBindDao.deleteMonitorBindByBizIdAndMonitorId(monitorId, id);
|
||||
}
|
||||
monitorService.deleteMonitors(needCancelMonitorIdSet);
|
||||
} catch (Exception exception) {
|
||||
log.error(exception.getMessage(), exception);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
@@ -31,9 +33,15 @@ public interface MonitorBindDao extends JpaRepository<MonitorBind, Long>, JpaSpe
|
||||
|
||||
List<MonitorBind> findMonitorBindsByBizId(Long bizId);
|
||||
|
||||
List<MonitorBind> findMonitorBindsByBizIdIn(Set<Long> bizIds);
|
||||
|
||||
|
||||
void deleteByMonitorId(Long monitorId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
void deleteMonitorBindByBizIdAndMonitorId(Long bizId, Long monitorId);
|
||||
|
||||
@Modifying
|
||||
void deleteMonitorBindByBizIdIn(Set<Long> bizIds);
|
||||
}
|
||||
|
||||
+11
-1
@@ -37,6 +37,7 @@ import org.apache.hertzbeat.common.entity.manager.Collector;
|
||||
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.ParamDefine;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
@@ -86,6 +87,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -529,12 +531,16 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByIdIn(ids);
|
||||
Set<Long> subMonitorIds = monitorBindDao.findMonitorBindsByBizIdIn(ids).stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet());
|
||||
Set<Long> allMonitorIds = new HashSet<>(ids);
|
||||
allMonitorIds.addAll(subMonitorIds);
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByIdIn(allMonitorIds);
|
||||
if (!monitors.isEmpty()) {
|
||||
monitorDao.deleteAll(monitors);
|
||||
paramDao.deleteParamsByMonitorIdIn(ids);
|
||||
Set<Long> monitorIds = monitors.stream().map(Monitor::getId).collect(Collectors.toSet());
|
||||
alertDefineBindDao.deleteAlertDefineMonitorBindsByMonitorIdIn(monitorIds);
|
||||
monitorBindDao.deleteMonitorBindByBizIdIn(monitorIds);
|
||||
for (Monitor monitor : monitors) {
|
||||
monitorBindDao.deleteByMonitorId(monitor.getId());
|
||||
collectorMonitorBindDao.deleteCollectorMonitorBindsByMonitorId(monitor.getId());
|
||||
@@ -650,6 +656,8 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
// Update monitoring status Delete corresponding monitoring periodic task
|
||||
// The jobId is not deleted, and the jobId is reused again after the management is started.
|
||||
Set<Long> subMonitorIds = monitorBindDao.findMonitorBindsByBizIdIn(ids).stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet());
|
||||
ids.addAll(subMonitorIds);
|
||||
List<Monitor> managedMonitors = monitorDao.findMonitorsByIdIn(ids)
|
||||
.stream().filter(monitor ->
|
||||
monitor.getStatus() != CommonConstants.MONITOR_PAUSED_CODE)
|
||||
@@ -666,6 +674,8 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
@Override
|
||||
public void enableManageMonitors(Set<Long> ids) {
|
||||
// Update monitoring status Add corresponding monitoring periodic task
|
||||
Set<Long> subMonitorIds = monitorBindDao.findMonitorBindsByBizIdIn(ids).stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet());
|
||||
ids.addAll(subMonitorIds);
|
||||
List<Monitor> unManagedMonitors = monitorDao.findMonitorsByIdIn(ids)
|
||||
.stream().filter(monitor ->
|
||||
monitor.getStatus() == CommonConstants.MONITOR_PAUSED_CODE)
|
||||
|
||||
+31
@@ -224,7 +224,9 @@ public class PluginServiceImpl implements PluginService {
|
||||
List<PluginItem> pluginItems = new ArrayList<>();
|
||||
AtomicInteger pluginImplementationCount = new AtomicInteger(0);
|
||||
try {
|
||||
validateFilePath(jarFile);
|
||||
URL jarUrl = new URL("file:" + jarFile.getAbsolutePath());
|
||||
validateJarUrl(jarUrl);
|
||||
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader());
|
||||
JarFile jar = new JarFile(jarFile)) {
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
@@ -272,6 +274,35 @@ public class PluginServiceImpl implements PluginService {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the file resides within the expected directory.
|
||||
*
|
||||
* @param file the file to validate
|
||||
*/
|
||||
private void validateFilePath(File file) {
|
||||
try {
|
||||
String canonicalPath = file.getCanonicalPath();
|
||||
String expectedDir = new File("plugin-lib").getCanonicalPath();
|
||||
if (!canonicalPath.startsWith(expectedDir)) {
|
||||
throw new CommonException("File is outside the allowed directory: " + canonicalPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error validating file path: {}", file.getAbsolutePath(), e);
|
||||
throw new CommonException("Error validating file path: " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the URL uses the 'file:' protocol and does not point to an external resource.
|
||||
*
|
||||
* @param url the URL to validate
|
||||
*/
|
||||
private void validateJarUrl(URL url) {
|
||||
if (!"file".equals(url.getProtocol())) {
|
||||
throw new CommonException("Invalid URL protocol: " + url.getProtocol());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateMetadata(PluginMetadata metadata) {
|
||||
if (metadataDao.countPluginMetadataByName(metadata.getName()) != 0) {
|
||||
throw new CommonException("A plugin named " + metadata.getName() + " already exists");
|
||||
|
||||
@@ -60,6 +60,9 @@ warehouse:
|
||||
url: http://localhost:8428
|
||||
username: root
|
||||
password: root
|
||||
insert:
|
||||
buffer-size: 1000
|
||||
flush-interval: 3
|
||||
td-engine:
|
||||
enabled: false
|
||||
driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
|
||||
|
||||
@@ -137,6 +137,9 @@ warehouse:
|
||||
url: http://localhost:8428
|
||||
username: root
|
||||
password: root
|
||||
insert:
|
||||
buffer-size: 1000
|
||||
flush-interval: 3
|
||||
cluster:
|
||||
enabled: false
|
||||
select:
|
||||
@@ -147,6 +150,8 @@ warehouse:
|
||||
url: http://localhost:8480
|
||||
username: root
|
||||
password: root
|
||||
buffer-size: 1000
|
||||
flush-interval: 3
|
||||
td-engine:
|
||||
enabled: false
|
||||
driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
|
||||
|
||||
@@ -27,7 +27,7 @@ help:
|
||||
zh-CN: HertzBeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jmx'> JMX 协议</a> 对 Apache ActiveMQ 消息中间件的运行状态,节点,Topic等相关指标(broker、topic、memory pool、class loading、thread)进行监测。<br><span class='help_module_span'>⚠️注意:您需要在 ActiveMQ 开启 JMX 服务。<a class='help_module_content' href='https://hertzbeat.apache.org/zh-cn/docs/help/activemq'>点击查看开启步骤</a>。</span>
|
||||
en-US: "HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jmx'> JMX protocol</a> to monitor the running status, nodes, topics, and other metrics of Apache ActiveMQ message-oriented middleware. <br><span class='help_module_span'>⚠️Note: You should enable the JMX service in ActiveMQ. <a class='help_module_content' href='https://hertzbeat.apache.org/docs/help/activemq'>Click here to view the specific steps.</a></span>"
|
||||
zh-TW: HertzBeat使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jmx'> JMX 協定</a> 對 Apache ActiveMQ 消息中介軟體的運行狀態,節點,Topic 等相關名額(broker、topic、memory pool、class loading、thread)進行監測。<br><span class='help_module_span'> ⚠️注意:您需要在 ActiveMQ 開啟 JMX 服務。<a class='help_module_content' href=' https://hertzbeat.apache.org/zh-cn/docs/help/activemq'>點擊查看開啟步驟</a>。</span>
|
||||
ja-JP: HertzBeatは <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jmx'> JMXプロトコルを介して</a> Apache ActiveMQメッセージングシステムのランタイムステータス、ノード、トピック、その他の関連指標を監視します。<br><span class='help_module_span'> ⚠️注意:ActiveMQ で JMX サービスを有効にする必要があります。<a class='help_module_content' href=' https://hertzbeat.apache.org/zh-cn/docs/help/activemq'>クリックしてガイドを見ます</a>。</span>
|
||||
ja-JP: HertzBeatは <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jmx'> JMXプロトコルを介して</a> Apache ActiveMQメッセージングシステムのランタイムステータス、ノード、トピック、その他の関連メトリックを監視します。<br><span class='help_module_span'> ⚠️注意:ActiveMQ で JMX サービスを有効にする必要があります。<a class='help_module_content' href=' https://hertzbeat.apache.org/zh-cn/docs/help/activemq'>クリックしてガイドを見ます</a>。</span>
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/activemq
|
||||
en-US: https://hertzbeat.apache.org/docs/help/activemq
|
||||
|
||||
@@ -27,7 +27,7 @@ help:
|
||||
zh-CN: Hertzbeat 对 Apache Airflow 通用性能指标(airflow health、airflow version)进行采集监控。<br>您可以点击“<i>新建 Apache Airflow </i>”并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: HertzBeat monitors Apache Airflow through general performance metrics such as airflow health and airflow version. You could click the "<i>New Apache Airflow</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: Hertzbeat對Apache Airflow通用性能指標(airflow health、airflow version)進行採集監控。<br>您可以點擊“<i>新建 Apache Airflow</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
|
||||
ja-JP: Hertzbeatは、Apache Airflowの一般的なパフォーマンス指標を監視します。 <br><i>新規Apache Airflow</i>をクリックして設定しましょう。
|
||||
ja-JP: Hertzbeatは、Apache Airflowの一般的なパフォーマンスのメトリックを監視します。 <br><i>新規Apache Airflow</i>をクリックして設定しましょう。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/airflow/
|
||||
en-US: https://hertzbeat.apache.org/docs/help/airflow/
|
||||
|
||||
@@ -27,7 +27,7 @@ help:
|
||||
zh-CN: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH 协议</a> 对 AlmaLinux 操作系统的通用性能指标 (系统信息、CPU、内存、磁盘、网卡、文件系统、TOP资源进程等) 进行采集监控。<br>您可以点击“<i>新建 AlmaLinux</i>”并配置HOST端口账户等相关参数进行添加,支持SSH账户密码或密钥认证。或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH protocol</a> to monitors AlmaLinux operating system's general performance metrics such as cpu, memory, disk, basic, interface, disk_free, top_process etc. <br>You can click the "<i>New AlmaLinux</i>" and config host port and other related params to add, auth support password or secretKey. Or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH 协议</a> 對 AlmaLinux 操作系统的通用性能指標 (系統信息、CPU、內存、磁盤、網卡、文件系統、TOP資源進程等) 進行採集監控。<br>您可以點擊“<i>新建AlmaLinux</i>”並配置HOST端口賬戶等相關參數進行添加,支持SSH賬戶密碼或密鑰認證。或者選擇“<i>更多操作</i>”,導入已有配寘。
|
||||
ja-JP: Hertzbeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSHプロトコルを介して</a> AlmaLinux 操作系统的通用性能指标 (系统信息、CPU、内存、磁盘、网卡、文件系统、TOP资源进程等) 进行采集监控。<br>「<i>新規 AlmaLinux</i>」をクリックしてホストなどのパラメタを設定した後、新規することができます。SSHまたはキー認証をサポートします。
|
||||
ja-JP: Hertzbeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSHプロトコルを介して</a> AlmaLinuxシステムの一般的なパフォーマンスのメトリック監視します。<br>「<i>新規 AlmaLinux</i>」をクリックしてホストなどのパラメタを設定した後、新規することができます。SSHまたはキー認証をサポートします。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/almalinux
|
||||
en-US: https://hertzbeat.apache.org/docs/help/almalinux
|
||||
|
||||
@@ -27,7 +27,7 @@ help:
|
||||
zh-CN: Hertzbeat 将调用 HTTP API 接口,查看接口是否可用,并以 ms 为指标单位对其响应时间等指标进行监测。您可以先点击“新增 HTTP API”按钮并进行配置,或在“更多操作”中导入已有配置。
|
||||
en-US: The platform will invoke an HTTP API endpoint to verify its accessibility and monitor various metrics, including response time, measured in milliseconds (ms). <br>To set up this functionality, you could click the "Add HTTP API" button and proceed with the configuration or import an existing setup through the "More Actions" menu.
|
||||
zh-TW: Hertzbeat將調用HTTP API介面,查看介面是否可用,並以ms為名額組織對其回應時間等名額進行監測。 您可以先點擊“新增HTTP API”按鈕並進行配寘,或在“更多操作”中導入已有配寘。
|
||||
ja-JP: Hertzbeatは、HTTP APIを呼び出して利用可能かどうかを確認し、応答時間(ms)などのメトリクスを監視します。「新規HTTP API」をクリックして設定しましょう。
|
||||
ja-JP: Hertzbeatは、HTTP APIを呼び出して利用可能かどうかを確認し、応答時間(ms)などのメトリックを監視します。「新規HTTP API」をクリックして設定しましょう。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/api
|
||||
en-US: https://hertzbeat.apache.org/docs/help/api
|
||||
|
||||
@@ -27,7 +27,7 @@ help:
|
||||
zh-CN: 监控 HTTP API 接口,对 API 返回的业务自定义状态码(非 Http 状态码)进行监控。此需通过配置 JsonPath 来解析您 API 的业务状态码路径。<br>您可以点击 “<i>新建 API Code</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: Monitor HTTP API to monitor business-defined status codes (non-HTTP status codes) returned by API. To do this, you need to configure JsonPath to resolve the business status code path of your API. <br>You could click the "<i>New API Codes</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: 監控 HTTP API 接口,對 API 返回的業務自定義狀態碼(非 Http 狀態碼)進行監控。此需通過配置 JsonPath 來解析您 API 的業務狀態碼路徑。<br>您可以點擊“<i>新建API Code</i>”並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
|
||||
ja-JP: 目標ホスト
|
||||
ja-JP: Hertzbeatは、HTTP APIを呼び出して利用可能かどうかを確認し、ビジネスステータスコード(HTTPステータスコードではない)のメトリックを監視します。これには、API のビジネスステータスコードのパスを解析するように JsonPath を構成する必要があります。「新規API Code」をクリックして設定しましょう。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/api_code
|
||||
en-US: https://hertzbeat.apache.org/docs/help/api_code
|
||||
|
||||
@@ -21,11 +21,13 @@ app: centos
|
||||
name:
|
||||
zh-CN: Centos Linux
|
||||
en-US: Centos Linux
|
||||
ja-JP: Centos Linux
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH 协议</a> 对 Centos 操作系统的通用性能指标 (系统信息、CPU、内存、磁盘、网卡、文件系统、TOP资源进程等) 进行采集监控。<br>您可以点击“<i>新建 Centos</i>”并配置HOST端口账户等相关参数进行添加,支持SSH账户密码或密钥认证。或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH protocol</a> to monitors Centos operating system's general performance metrics such as cpu, memory, disk, basic, interface, disk_free, top_process etc. <br>You can click the "<i>New Centos</i>" and config host port and other related params to add, auth support password or secretKey. Or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSH 协议</a> 對 Centos 操作系统的通用性能指標 (系統信息、CPU、內存、磁盤、網卡、文件系統、TOP資源進程等) 進行採集監控。<br>您可以點擊“<i>新建 Centos</i>”並配置HOST端口賬戶等相關參數進行添加,支持SSH賬戶密碼或密鑰認證。或者選擇“<i>更多操作</i>”,導入已有配寘。
|
||||
ja-JP: Hertzbeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-ssh'> SSHプロトコルを介して</a> Centosシステムの一般的なパフォーマンスのメトリック監視します。<br>「<i>新規 Centos Linux</i>」をクリックしてホストなどのパラメタを設定した後、新規することができます。SSHまたはキー認証をサポートします。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/centos/
|
||||
en-US: https://hertzbeat.apache.org/docs/help/centos/
|
||||
@@ -37,6 +39,7 @@ params:
|
||||
name:
|
||||
zh-CN: 目标Host
|
||||
en-US: Target Host
|
||||
ja-JP: 目標ホスト
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: host
|
||||
# required-true or false
|
||||
@@ -47,6 +50,7 @@ params:
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
ja-JP: ポート
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -61,6 +65,7 @@ params:
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
ja-JP: タイムアウト(ms)
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -76,6 +81,7 @@ params:
|
||||
name:
|
||||
zh-CN: 复用连接
|
||||
en-US: Reuse Connection
|
||||
ja-JP: コネクション再利用
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: boolean
|
||||
# required-true or false
|
||||
@@ -87,6 +93,7 @@ params:
|
||||
name:
|
||||
zh-CN: 使用代理
|
||||
en-US: Use Proxy Connection
|
||||
ja-JP: プロキシ利用
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: boolean
|
||||
# required-true or false
|
||||
@@ -98,6 +105,7 @@ params:
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
ja-JP: ユーザー名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -110,6 +118,7 @@ params:
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
ja-JP: パスワード
|
||||
# type-param field type(most mapping the html input tag)
|
||||
type: password
|
||||
# required-true or false
|
||||
@@ -120,6 +129,7 @@ params:
|
||||
name:
|
||||
zh-CN: 私钥
|
||||
en-US: PrivateKey
|
||||
ja-JP: 秘密鍵
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: textarea
|
||||
placeholder: -----BEGIN RSA PRIVATE KEY-----
|
||||
@@ -133,6 +143,7 @@ params:
|
||||
name:
|
||||
zh-CN: 密钥短语
|
||||
en-US: PrivateKey PassPhrase
|
||||
ja-JP: 秘密鍵フレーズ
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: password
|
||||
# required-true or false
|
||||
@@ -145,6 +156,7 @@ params:
|
||||
name:
|
||||
zh-CN: 代理主机
|
||||
en-US: Proxy Host
|
||||
ja-JP: プロキシホスト
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
@@ -156,6 +168,7 @@ params:
|
||||
name:
|
||||
zh-CN: 代理端口
|
||||
en-US: Proxy Port
|
||||
ja-JP: プロキシポート
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -172,6 +185,7 @@ params:
|
||||
name:
|
||||
zh-CN: 代理用户名
|
||||
en-US: Proxy Username
|
||||
ja-JP: プロキシユーザー名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -186,6 +200,7 @@ params:
|
||||
name:
|
||||
zh-CN: 代理密码
|
||||
en-US: Proxy Password
|
||||
ja-JP: プロキシパスワード
|
||||
# type-param field type(most mapping the html input tag)
|
||||
type: password
|
||||
# required-true or false
|
||||
@@ -198,6 +213,7 @@ params:
|
||||
name:
|
||||
zh-CN: 代理主机私钥
|
||||
en-US: proxyPrivateKey
|
||||
ja-JP: プロキシ秘密鍵
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: textarea
|
||||
placeholder: -----BEGIN RSA PRIVATE KEY-----
|
||||
@@ -212,6 +228,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 系统基本信息
|
||||
en-US: Basic Info
|
||||
ja-JP: システム基礎情報
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
@@ -224,16 +241,19 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 主机名称
|
||||
en-US: Host Name
|
||||
ja-JP: ホスト名
|
||||
- field: version
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 操作系统版本
|
||||
en-US: System Version
|
||||
ja-JP: システムバージョン
|
||||
- field: uptime
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 启动时间
|
||||
en-US: Uptime
|
||||
ja-JP: アップタイム
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: ssh
|
||||
# the config content when protocol is ssh
|
||||
@@ -273,6 +293,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: CPU 信息
|
||||
en-US: CPU Info
|
||||
ja-JP: CPU情報
|
||||
priority: 1
|
||||
fields:
|
||||
- field: info
|
||||
@@ -280,32 +301,38 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 型号
|
||||
en-US: Info
|
||||
ja-JP: バージョン
|
||||
- field: cores
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 核数
|
||||
en-US: Cores
|
||||
ja-JP: コア数
|
||||
- field: interrupt
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 中断数
|
||||
en-US: Interrupt
|
||||
ja-JP: 割り込み数
|
||||
- field: load
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 负载
|
||||
en-US: Load
|
||||
ja-JP: ロード
|
||||
- field: context_switch
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 上下文切换
|
||||
en-US: Context Switch
|
||||
ja-JP: コンテキストスイッチ
|
||||
- field: usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: 使用率
|
||||
en-US: Usage
|
||||
ja-JP: 使用率
|
||||
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
|
||||
aliasFields:
|
||||
- info
|
||||
@@ -352,6 +379,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 内存信息
|
||||
en-US: Memory Info
|
||||
ja-JP: メモリ情報
|
||||
priority: 2
|
||||
fields:
|
||||
- field: total
|
||||
@@ -360,36 +388,42 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 总内存容量
|
||||
en-US: Total Memory
|
||||
ja-JP: メモリ容量
|
||||
- field: used
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 用户程序内存量
|
||||
en-US: User Program Memory
|
||||
ja-JP: ユーザープログラムメモリ
|
||||
- field: free
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 空闲内存容量
|
||||
en-US: Free Memory
|
||||
ja-JP: 空きメモリ
|
||||
- field: buff_cache
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 缓存占用内存
|
||||
en-US: Buff Cache Memory
|
||||
ja-JP: バッファメモリ
|
||||
- field: available
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 剩余可用内存
|
||||
en-US: Available Memory
|
||||
ja-JP: 使用可能なメモリ
|
||||
- field: usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: 内存使用率
|
||||
en-US: Memory Usage
|
||||
ja-JP: メモリ使用率
|
||||
aliasFields:
|
||||
- total
|
||||
- used
|
||||
@@ -432,6 +466,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 磁盘信息
|
||||
en-US: Disk Info
|
||||
ja-JP: ディスク情報
|
||||
priority: 3
|
||||
fields:
|
||||
- field: disk_num
|
||||
@@ -439,27 +474,32 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 磁盘总数
|
||||
en-US: Disk Num
|
||||
ja-JP: ディスク番号
|
||||
- field: partition_num
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 分区总数
|
||||
en-US: Partition Num
|
||||
ja-JP: パーティション
|
||||
- field: block_write
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 写磁盘块数
|
||||
en-US: Block Write
|
||||
ja-JP: 書き込みディスクブロック数
|
||||
- field: block_read
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 读磁盘块数
|
||||
en-US: Block Read
|
||||
ja-JP: 読み取りブロック数
|
||||
- field: write_rate
|
||||
type: 0
|
||||
unit: iops
|
||||
i18n:
|
||||
zh-CN: 磁盘写速率
|
||||
en-US: Write Rate
|
||||
ja-JP: ディスク書き込み速度
|
||||
protocol: ssh
|
||||
ssh:
|
||||
host: ^_^host^_^
|
||||
@@ -489,6 +529,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 网卡信息
|
||||
en-US: Interface Info
|
||||
ja-JP: ネットワークカード情報
|
||||
priority: 4
|
||||
fields:
|
||||
- field: interface_name
|
||||
@@ -497,18 +538,21 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 网卡名称
|
||||
en-US: Interface Name
|
||||
ja-JP: ネットワークカード名
|
||||
- field: receive_bytes
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 入站数据流量
|
||||
en-US: Receive Bytes
|
||||
ja-JP: 受信バイト数
|
||||
- field: transmit_bytes
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 出站数据流量
|
||||
en-US: Transmit Bytes
|
||||
ja-JP: 送信バイト数
|
||||
units:
|
||||
- receive_bytes=B->MB
|
||||
- transmit_bytes=B->MB
|
||||
@@ -541,6 +585,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 文件系统
|
||||
en-US: Disk Free
|
||||
ja-JP: ファイルシステム
|
||||
priority: 5
|
||||
fields:
|
||||
- field: filesystem
|
||||
@@ -548,30 +593,35 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 文件系统
|
||||
en-US: Filesystem
|
||||
ja-JP: ファイルシステム
|
||||
- field: used
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 已使用量
|
||||
en-US: Used
|
||||
ja-JP: 使用済み
|
||||
- field: available
|
||||
type: 0
|
||||
unit: Mb
|
||||
i18n:
|
||||
zh-CN: 可用量
|
||||
en-US: Available
|
||||
ja-JP: 使用可能
|
||||
- field: usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: 使用率
|
||||
en-US: Usage
|
||||
ja-JP: 使用率
|
||||
- field: mounted
|
||||
type: 1
|
||||
label: true
|
||||
i18n:
|
||||
zh-CN: 挂载点
|
||||
en-US: Mounted
|
||||
ja-JP: マウント
|
||||
protocol: ssh
|
||||
ssh:
|
||||
host: ^_^host^_^
|
||||
@@ -601,6 +651,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: Top10 CPU 进程
|
||||
en-US: Top10 CPU Process
|
||||
ja-JP: トップ10 CPUプロセス
|
||||
priority: 6
|
||||
fields:
|
||||
- field: pid
|
||||
@@ -609,23 +660,27 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 进程ID
|
||||
en-US: PID
|
||||
ja-JP: プロセスID
|
||||
- field: cpu_usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: CPU占用率
|
||||
en-US: CPU Usage
|
||||
ja-JP: CPU使用率
|
||||
- field: mem_usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: 内存占用率
|
||||
en-US: Memory Usage
|
||||
ja-JP: メモリ使用率
|
||||
- field: command
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 执行命令
|
||||
en-US: Command
|
||||
ja-JP: 指令
|
||||
protocol: ssh
|
||||
ssh:
|
||||
host: ^_^host^_^
|
||||
@@ -655,6 +710,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: Top10 内存进程
|
||||
en-US: Top10 Memory Process
|
||||
ja-JP: トップ10 メモリプロセス
|
||||
priority: 7
|
||||
fields:
|
||||
- field: pid
|
||||
@@ -663,23 +719,27 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 进程ID
|
||||
en-US: PID
|
||||
ja-JP: プロセスID
|
||||
- field: mem_usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: 内存占用率
|
||||
en-US: Memory Usage
|
||||
ja-JP: メモリ使用率
|
||||
- field: cpu_usage
|
||||
type: 0
|
||||
unit: '%'
|
||||
i18n:
|
||||
zh-CN: CPU占用率
|
||||
en-US: CPU Usage
|
||||
ja-JP: CPU使用率
|
||||
- field: command
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 执行命令
|
||||
en-US: Command
|
||||
ja-JP: 指令
|
||||
protocol: ssh
|
||||
ssh:
|
||||
host: ^_^host^_^
|
||||
|
||||
@@ -21,11 +21,13 @@ app: cisco_switch
|
||||
name:
|
||||
zh-CN: 思科通用交换机
|
||||
en-US: Cisco Switch
|
||||
ja-JP: シスコ・スイッチングハブ
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP 协议</a> 对 思科交换机 的通用指标(可用性,系统信息,端口流量等)进行采集监控。<br>您可以点击 “<i>新建 思科通用交换机</i>” 并进行配置SNMP相关参数添加,或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP Protocol</a> to monitoring Cisco Switch general performance metrics. <br>You can click the "<i>New Cisco Switch</i>" button and config snmp params to add monitor or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP 協議</a> 對 思科交換機 的通用指標(可用性,系統信息,端口流量等)進行采集監控。<br>您可以點擊 “<i>新建 思科通用交換機</i>” 並進行配置SNMP相關參數添加,或者選擇“<i>更多操作</i>”,導入已有配置。
|
||||
ja-JP: Hertzbeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP プロトコルを介して</a> シスコ・スイッチングハブの一般的なメトリック監視します。<br>「<i>新規 シスコ・スイッチングハブ</i>」をクリックしてSNMPなどのパラメタを設定した後、新規することができます。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/cisco_switch
|
||||
en-US: https://hertzbeat.apache.org/docs/help/cisco_switch
|
||||
@@ -37,6 +39,7 @@ params:
|
||||
name:
|
||||
zh-CN: 目标Host
|
||||
en-US: Target Host
|
||||
ja-JP: 目標ホスト
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: host
|
||||
# required-true or false
|
||||
@@ -47,6 +50,7 @@ params:
|
||||
name:
|
||||
zh-CN: 端口
|
||||
en-US: Port
|
||||
ja-JP: ポート
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -61,6 +65,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP 版本
|
||||
en-US: SNMP Version
|
||||
ja-JP: SNMPバージョン
|
||||
# type-param field type(radio mapping the html radio tag)
|
||||
type: radio
|
||||
# required-true or false
|
||||
@@ -79,6 +84,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP 团体字
|
||||
en-US: SNMP Community
|
||||
ja-JP: SNMPコミュニティ
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -98,6 +104,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP username
|
||||
en-US: SNMP username
|
||||
ja-JP: SNMPユーザー名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -116,6 +123,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP contextName
|
||||
en-US: SNMP contextName
|
||||
ja-JP: SNMPコンテキスト名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -134,6 +142,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP authPassword
|
||||
en-US: SNMP authPassword
|
||||
ja-JP: SNMP認証パスワード
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -152,6 +161,7 @@ params:
|
||||
name:
|
||||
zh-CN: authPassword 加密方式
|
||||
en-US: authPassword Encryption
|
||||
ja-JP: 認証暗号
|
||||
# type-param field type(radio mapping the html radio tag)
|
||||
type: radio
|
||||
# required-true or false
|
||||
@@ -172,6 +182,7 @@ params:
|
||||
name:
|
||||
zh-CN: SNMP privPassphrase
|
||||
en-US: SNMP privPassphrase
|
||||
ja-JP: SNMPパスワードフレーズ
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -190,6 +201,7 @@ params:
|
||||
name:
|
||||
zh-CN: privPassword 加密方式
|
||||
en-US: privPassword Encryption
|
||||
ja-JP: パスワードの暗号
|
||||
# type-param field type(radio mapping the html radio tag)
|
||||
type: radio
|
||||
# required-true or false
|
||||
@@ -210,6 +222,7 @@ params:
|
||||
name:
|
||||
zh-CN: 超时时间(ms)
|
||||
en-US: Timeout(ms)
|
||||
ja-JP: タイムアウト(ms)
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -228,6 +241,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 系统信息
|
||||
en-US: System Info
|
||||
ja-JP: システム情報
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
@@ -239,32 +253,38 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 主机名称
|
||||
en-US: Host Name
|
||||
ja-JP: ホスト名
|
||||
- field: descr
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 描述信息
|
||||
en-US: Description
|
||||
ja-JP: 説明
|
||||
- field: uptime
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 运行时长
|
||||
en-US: Uptime
|
||||
ja-JP: アップタイム
|
||||
- field: location
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 位置
|
||||
en-US: Location
|
||||
ja-JP: 位置
|
||||
- field: contact
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 联系人
|
||||
en-US: Contact
|
||||
ja-JP: 連絡先
|
||||
- field: responseTime
|
||||
type: 0
|
||||
unit: ms
|
||||
i18n:
|
||||
zh-CN: 响应时间
|
||||
en-US: Response Time
|
||||
ja-JP: 応答時間
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: snmp
|
||||
# the config content when protocol is snmp
|
||||
@@ -305,6 +325,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 接口详情
|
||||
en-US: Interfaces Detail
|
||||
ja-JP: ネットワークカード詳細
|
||||
priority: 1
|
||||
fields:
|
||||
- field: index
|
||||
@@ -312,68 +333,79 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 编号
|
||||
en-US: Index
|
||||
ja-JP: 番号
|
||||
- field: descr
|
||||
type: 1
|
||||
label: true
|
||||
i18n:
|
||||
zh-CN: 接口名称
|
||||
en-US: Interface Name
|
||||
ja-JP: ネットワークカード名
|
||||
- field: mtu
|
||||
type: 0
|
||||
unit: 'byte'
|
||||
i18n:
|
||||
zh-CN: MTU
|
||||
en-US: MTU
|
||||
ja-JP: MTU
|
||||
- field: speed
|
||||
type: 0
|
||||
unit: 'MB/s'
|
||||
i18n:
|
||||
zh-CN: 接口速率
|
||||
en-US: Interface Speed
|
||||
ja-JP: ネットワークカード速度
|
||||
- field: in_octets
|
||||
type: 0
|
||||
unit: 'MByte'
|
||||
i18n:
|
||||
zh-CN: 入流量
|
||||
en-US: In Octets
|
||||
ja-JP: 受信バイト数
|
||||
- field: in_discards
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 入丢包数
|
||||
en-US: In Discards
|
||||
ja-JP: 受信パケットロス数
|
||||
- field: in_errors
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 入错包数
|
||||
en-US: In Errors
|
||||
ja-JP: 受信異常パケット数
|
||||
- field: out_octets
|
||||
type: 0
|
||||
unit: 'MByte'
|
||||
i18n:
|
||||
zh-CN: 出流量
|
||||
en-US: Out Octets
|
||||
ja-JP: 送信バイト数
|
||||
- field: out_discards
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 出丢包数
|
||||
en-US: Out Discards
|
||||
ja-JP: 送信パケットロス数
|
||||
- field: out_errors
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 出错包数
|
||||
en-US: Out Errors
|
||||
ja-JP: 送信異常パケット数
|
||||
- field: admin_status
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 配置状态
|
||||
en-US: Config Status
|
||||
en-US: Config Status
|
||||
ja-JP: 設定ステータス
|
||||
- field: oper_status
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 当前状态
|
||||
en-US: Current Status
|
||||
ja-JP: ステータス
|
||||
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
|
||||
# (可选)监控指标别名, 做为中间字段与采集数据字段和指标字段映射转换
|
||||
aliasFields:
|
||||
- ifIndex
|
||||
- ifDescr
|
||||
|
||||
@@ -21,11 +21,13 @@ app: clickhouse
|
||||
name:
|
||||
zh-CN: ClickHouse
|
||||
en-US: ClickHouse
|
||||
ja-JP: ClickHouse
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: Hertzbeat 对 ClickHouse 数据库监控通用指标进行测量监控。<br>您可以点击 “<i>新建 ClickHouse 数据库</i>” 并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: Hertzbeat monitors the status codes which returned by the API. You could click the "<i>New ClickHouse Database</i>" button and proceed with the configuration or import an existing setup through the "<i>More Actions</i>" menu.
|
||||
zh-TW: Hertzbeat 對 ClickHouse 資料庫監控通用名額進行量測監控。<br>您可以點擊 “<i>新建ClickHouse資料庫</i>” 並進行配寘,或者選擇“<i>更多操作</i>”,導入已有配寘。
|
||||
ja-JP: Hertzbeat は ClickHouse データベースの一般的なメトリック監視します。<br>「<i>新規 ClickHouse データベース</i>」をクリックしてパラメタを設定した後、新規することができます。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/clickhouse
|
||||
en-US: https://hertzbeat.apache.org/docs/help/clickhouse
|
||||
@@ -37,6 +39,7 @@ params:
|
||||
name:
|
||||
zh-CN: 目标Host
|
||||
en-US: Target Host
|
||||
ja-JP: 目標ホスト
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: host
|
||||
# required-true or false
|
||||
@@ -47,6 +50,7 @@ params:
|
||||
name:
|
||||
zh-CN: HTTP端口
|
||||
en-US: HTTP Port
|
||||
ja-JP: HTTPポート
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# when type is number, range is required
|
||||
@@ -61,6 +65,7 @@ params:
|
||||
name:
|
||||
zh-CN: 查询超时时间(ms)
|
||||
en-US: Query Timeout(ms)
|
||||
ja-JP: クエリタイムアウト(ms)
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: number
|
||||
# required-true or false
|
||||
@@ -75,6 +80,7 @@ params:
|
||||
name:
|
||||
zh-CN: 数据库名称
|
||||
en-US: Database Name
|
||||
ja-JP: データベース名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
@@ -85,6 +91,7 @@ params:
|
||||
name:
|
||||
zh-CN: 用户名
|
||||
en-US: Username
|
||||
ja-JP: ユーザー名
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# when type is text, use limit to limit string length
|
||||
@@ -97,6 +104,7 @@ params:
|
||||
name:
|
||||
zh-CN: 密码
|
||||
en-US: Password
|
||||
ja-JP: パスワード
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: password
|
||||
# required-true or false
|
||||
@@ -108,6 +116,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: ping的可用性
|
||||
en-US: ping_available
|
||||
ja-JP: サービス利用可能
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
@@ -120,6 +129,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 响应时间
|
||||
en-US: Response Time
|
||||
ja-JP: 応答時間
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: http
|
||||
# the config content when protocol is http
|
||||
@@ -143,6 +153,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: system.metrics表的数据
|
||||
en-US: Data in the system.metrics table
|
||||
ja-JP: system.metricsデータ
|
||||
fields:
|
||||
- field: metric
|
||||
type: 1
|
||||
@@ -150,16 +161,19 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 指标名称
|
||||
en-US: metric
|
||||
ja-JP: メトリック名
|
||||
- field: value
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 指标值
|
||||
en-US: value
|
||||
ja-JP: メトリック値
|
||||
- field: description
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 说明
|
||||
en-US: description
|
||||
ja-JP: メトリック説明
|
||||
protocol: jdbc
|
||||
jdbc:
|
||||
host: ^_^host^_^
|
||||
@@ -176,6 +190,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: system.events表的数据
|
||||
en-US: Data for the system.events table
|
||||
ja-JP: system.eventsデータ
|
||||
fields:
|
||||
- field: event
|
||||
type: 1
|
||||
@@ -183,16 +198,19 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 事件信息
|
||||
en-US: event
|
||||
ja-JP: イベント名
|
||||
- field: value
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 值
|
||||
en-US: value
|
||||
ja-JP: 発生したイベントの数
|
||||
- field: description
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 说明
|
||||
en-US: description
|
||||
ja-JP: イベントの説明
|
||||
protocol: jdbc
|
||||
jdbc:
|
||||
host: ^_^host^_^
|
||||
@@ -210,6 +228,7 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: system.asynchronous_metrics表的数据
|
||||
en-US: Data from the system.asynchronous_metrics table
|
||||
ja-JP: system.asynchronous_metricsデータ
|
||||
fields:
|
||||
- field: metric
|
||||
type: 1
|
||||
@@ -217,16 +236,19 @@ metrics:
|
||||
i18n:
|
||||
zh-CN: 指标名称
|
||||
en-US: metric
|
||||
ja-JP: メトリック名
|
||||
- field: value
|
||||
type: 0
|
||||
i18n:
|
||||
zh-CN: 指标值
|
||||
en-US: value
|
||||
ja-JP: メトリック値
|
||||
- field: description
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 说明
|
||||
en-US: description
|
||||
ja-JP: メトリック説明
|
||||
protocol: jdbc
|
||||
jdbc:
|
||||
host: ^_^host^_^
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# 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: __system__
|
||||
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
|
||||
app: nacos_sd
|
||||
# The monitoring i18n name
|
||||
name:
|
||||
zh-CN: Nacos 服务发现
|
||||
en-US: Nacos Service Discovery
|
||||
# Input params define for app api(render web ui by the definition)
|
||||
params:
|
||||
# field-param field key
|
||||
- field: __nacos_sd_host__
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: Nacos 服务发现 Host
|
||||
en-US: Nacos Service Discovery Host
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: true
|
||||
- field: __nacos_sd_port__
|
||||
name:
|
||||
zh-CN: Nacos 服务发现 Port
|
||||
en-US: Nacos Service Discovery Port
|
||||
type: number
|
||||
required: true
|
||||
# - field: __nacos_namespace__
|
||||
# name:
|
||||
# zh-CN: Nacos 命名空间
|
||||
# en-US: Nacos Namespace
|
||||
# type: text
|
||||
# required: false
|
||||
# - field: __nacos_username__
|
||||
# name:
|
||||
# zh-CN: Nacos 用户名
|
||||
# en-US: Nacos Username
|
||||
# type: text
|
||||
# required: false
|
||||
# - field: __nacos_password__
|
||||
# name:
|
||||
# zh-CN: Nacos 密码
|
||||
# en-US: Nacos Password
|
||||
# type: password
|
||||
# required: false
|
||||
|
||||
metrics:
|
||||
- name: target
|
||||
i18n:
|
||||
zh-CN: 监控目标
|
||||
en-US: Monitor Target
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
# collect metrics content
|
||||
fields:
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: host
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Host
|
||||
en-US: Host
|
||||
- field: port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Port
|
||||
en-US: Port
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: nacos_sd
|
||||
# the config content when protocol is nacos_sd
|
||||
nacos_sd:
|
||||
host: ^_^__nacos_sd_host__^_^
|
||||
port: ^_^__nacos_sd_port__^_^
|
||||
# namespace: ^_^__nacos_namespace__^_^
|
||||
# username: ^_^__nacos_username__^_^
|
||||
# password: ^_^__nacos_password__^_^
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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: __system__
|
||||
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
|
||||
app: zookeeper_sd
|
||||
# The app api i18n name
|
||||
name:
|
||||
zh-CN: Zookeeper Service Discovery
|
||||
en-US: Zookeeper Service Discovery
|
||||
# Input params define for app api(render web ui by the definition)
|
||||
params:
|
||||
# field-param field key
|
||||
- field: __sd_url__
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 服务发现地址
|
||||
en-US: Service Discovery Url
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: true
|
||||
|
||||
- field: __sd_path_prefix__
|
||||
name:
|
||||
zh-CN: 服务发现路径前缀
|
||||
en-US: Service Discovery Path Prefix
|
||||
type: text
|
||||
required: true
|
||||
|
||||
metrics:
|
||||
- name: target
|
||||
i18n:
|
||||
zh-CN: 监控目标
|
||||
en-US: Monitor Target
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
# collect metrics content
|
||||
fields:
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: host
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Host
|
||||
en-US: Host
|
||||
- field: port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Port
|
||||
en-US: Port
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: zookeeper_sd
|
||||
# the config content when protocol is zookeeper_sd
|
||||
zookeeper_sd:
|
||||
url: ^_^__sd_url__^_^
|
||||
pathPrefix: ^_^__sd_path_prefix__^_^
|
||||
@@ -37,7 +37,7 @@ import org.apache.hertzbeat.collector.dispatch.DispatchProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.MetricsCollectorQueue;
|
||||
import org.apache.hertzbeat.collector.dispatch.WorkerPool;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatcher;
|
||||
import org.apache.hertzbeat.collector.timer.TimerDispatcher;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.impl.DataSizeConvert;
|
||||
import org.apache.hertzbeat.common.config.CommonConfig;
|
||||
import org.apache.hertzbeat.common.config.CommonProperties;
|
||||
|
||||
+8
-6
@@ -126,7 +126,7 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
return;
|
||||
}
|
||||
String monitorId = String.valueOf(metricsData.getId());
|
||||
String tableName = getTableName(metricsData.getApp(), metricsData.getMetrics());
|
||||
String tableName = getTableName(metricsData.getId(), metricsData.getMetrics());
|
||||
TableSchema.Builder tableSchemaBuilder = TableSchema.newBuilder(tableName);
|
||||
|
||||
tableSchemaBuilder.addTag("instance", DataType.String)
|
||||
@@ -194,7 +194,7 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
@Override
|
||||
public Map<String, List<Value>> getHistoryMetricData(Long monitorId, String app, String metrics, String metric,
|
||||
String label, String history) {
|
||||
String name = getTableName(app, metrics);
|
||||
String name = getTableName(monitorId, metrics);
|
||||
String timeSeriesSelector = LABEL_KEY_NAME + "=\"" + name + "\""
|
||||
+ "," + LABEL_KEY_INSTANCE + "=\"" + monitorId + "\"";
|
||||
if (!CommonConstants.PROMETHEUS.equals(app)) {
|
||||
@@ -227,6 +227,7 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
log.error("history time error: {}. use default: 6h", e.getMessage());
|
||||
start = now.minus(6, ChronoUnit.HOURS).getEpochSecond();
|
||||
}
|
||||
|
||||
long end = now.getEpochSecond();
|
||||
String step = "60s";
|
||||
if (end - start < Duration.ofDays(7).getSeconds() && end - start > Duration.ofDays(1).getSeconds()) {
|
||||
@@ -234,13 +235,15 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
} else if (end - start >= Duration.ofDays(7).getSeconds()) {
|
||||
step = "4h";
|
||||
}
|
||||
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(greptimeProperties.httpEndpoint() + QUERY_RANGE_PATH)
|
||||
URI uri = UriComponentsBuilder.fromUriString(greptimeProperties.httpEndpoint() + QUERY_RANGE_PATH)
|
||||
.queryParam(URLEncoder.encode("query", StandardCharsets.UTF_8), URLEncoder.encode("{" + timeSeriesSelector + "}", StandardCharsets.UTF_8))
|
||||
.queryParam("start", start)
|
||||
.queryParam("end", end)
|
||||
.queryParam("step", step)
|
||||
.build(true).toUri();
|
||||
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
@@ -258,7 +261,6 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
long timestamp = ((Double) valueArr[0]).longValue();
|
||||
String value = new BigDecimal(String.valueOf(valueArr[1])).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
|
||||
// read timestamp here is s unit
|
||||
valueList.add(new Value(value, timestamp * 1000));
|
||||
}
|
||||
}
|
||||
@@ -273,8 +275,8 @@ public class GreptimeDbDataStorage extends AbstractHistoryDataStorage {
|
||||
return instanceValuesMap;
|
||||
}
|
||||
|
||||
private String getTableName(String app, String metrics) {
|
||||
return app + SPILT + metrics;
|
||||
private String getTableName(Long monitorId, String metrics) {
|
||||
return "hzb" + SPILT + monitorId + SPILT + metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ package org.apache.hertzbeat.warehouse.store.history.vm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
+140
-29
@@ -28,6 +28,7 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -35,6 +36,9 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -52,6 +56,9 @@ import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
|
||||
import org.apache.hertzbeat.common.entity.dto.Value;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.timer.HashedWheelTimer;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.TimerTask;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
@@ -93,12 +100,19 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
private static final String SPILT = "_";
|
||||
private static final String MONITOR_METRICS_KEY = "__metrics__";
|
||||
private static final String MONITOR_METRIC_KEY = "__metric__";
|
||||
private static final long MAX_WAIT_MS = 500L;
|
||||
private static final int MAX_RETRIES = 3;
|
||||
|
||||
private final VictoriaMetricsClusterProperties vmClusterProps;
|
||||
private final VictoriaMetricsInsertProperties vmInsertProps;
|
||||
private final VictoriaMetricsSelectProperties vmSelectProps;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final BlockingQueue<VictoriaMetricsDataStorage.VictoriaMetricsContent> metricsBufferQueue;
|
||||
|
||||
private HashedWheelTimer metricsFlushTimer = null;
|
||||
private MetricsFlushTask metricsFlushtask = null;
|
||||
private boolean isBatchImportEnabled = false;
|
||||
|
||||
|
||||
public VictoriaMetricsClusterDataStorage(VictoriaMetricsClusterProperties vmClusterProps,
|
||||
RestTemplate restTemplate) {
|
||||
@@ -111,6 +125,21 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
this.vmInsertProps = vmClusterProps.insert();
|
||||
this.vmSelectProps = vmClusterProps.select();
|
||||
serverAvailable = checkVictoriaMetricsDatasourceAvailable();
|
||||
metricsBufferQueue = new LinkedBlockingQueue<>(vmInsertProps.bufferSize());
|
||||
isBatchImportEnabled = vmInsertProps.flushInterval() != 0 && vmInsertProps.bufferSize() != 0;
|
||||
if (isBatchImportEnabled){
|
||||
initializeFlushTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeFlushTimer() {
|
||||
this.metricsFlushTimer = new HashedWheelTimer(r -> {
|
||||
Thread thread = new Thread(r, "victoria-metrics-flush-timer");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
}, 1, TimeUnit.SECONDS, 512);
|
||||
metricsFlushtask = new MetricsFlushTask();
|
||||
this.metricsFlushTimer.newTimeout(metricsFlushtask, 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private boolean checkVictoriaMetricsDatasourceAvailable() {
|
||||
@@ -228,46 +257,25 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (contentList.isEmpty()) {
|
||||
log.info("[warehouse victoria-metrics] flush metrics data {} is empty, ignore.", metricsData.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (StringUtils.hasText(vmInsertProps.username())
|
||||
&& StringUtils.hasText(vmInsertProps.password())) {
|
||||
String authStr = vmInsertProps.username() + ":" + vmInsertProps.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (VictoriaMetricsDataStorage.VictoriaMetricsContent content : contentList) {
|
||||
stringBuilder.append(JsonUtil.toJson(content)).append("\n");
|
||||
}
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(stringBuilder.toString(), headers);
|
||||
String importUrl = vmClusterProps.insert().url() + VM_INSERT_BASE_PATH.formatted(vmClusterProps.accountID(), IMPORT_PATH);
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(importUrl,
|
||||
httpEntity, String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("insert metrics data to victoria-metrics success.");
|
||||
} else {
|
||||
log.error("insert metrics data to victoria-metrics failed. {}", responseEntity.getBody());
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("flush metrics data to victoria-metrics error: {}.", e.getMessage(), e);
|
||||
if (!isBatchImportEnabled){
|
||||
doSaveData(contentList);
|
||||
return;
|
||||
}
|
||||
sendVictoriaMetrics(contentList);
|
||||
} catch (Exception e) {
|
||||
log.error("flush metrics data to victoria-metrics error: {}.", e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (metricsFlushTimer != null && !metricsFlushTimer.isStop()) {
|
||||
metricsFlushTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -555,6 +563,109 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
return instanceValuesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save metric data to victoria-metric via HTTP call
|
||||
*/
|
||||
public void doSaveData(List<VictoriaMetricsDataStorage.VictoriaMetricsContent> contentList){
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (StringUtils.hasText(vmInsertProps.username())
|
||||
&& StringUtils.hasText(vmInsertProps.password())) {
|
||||
String authStr = vmInsertProps.username() + ":" + vmInsertProps.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (VictoriaMetricsDataStorage.VictoriaMetricsContent content : contentList) {
|
||||
stringBuilder.append(JsonUtil.toJson(content)).append("\n");
|
||||
}
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(stringBuilder.toString(), headers);
|
||||
String importUrl = vmClusterProps.insert().url() + VM_INSERT_BASE_PATH.formatted(vmClusterProps.accountID(), IMPORT_PATH);
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(importUrl,
|
||||
httpEntity, String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("insert metrics data to victoria-metrics success.");
|
||||
} else {
|
||||
log.error("insert metrics data to victoria-metrics failed. {}", responseEntity.getBody());
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("flush metrics data to victoria-metrics error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add victoriaMetricsContent to buffer
|
||||
* @param contentList victoriaMetricsContent List
|
||||
*/
|
||||
private void sendVictoriaMetrics(List<VictoriaMetricsDataStorage.VictoriaMetricsContent> contentList) {
|
||||
for (VictoriaMetricsDataStorage.VictoriaMetricsContent content : contentList) {
|
||||
boolean offered = false;
|
||||
int retryCount = 0;
|
||||
while (!offered && retryCount < MAX_RETRIES) {
|
||||
try {
|
||||
// Attempt to add to the queue for a limited time
|
||||
offered = metricsBufferQueue.offer(content, MAX_WAIT_MS, TimeUnit.MILLISECONDS);
|
||||
if (!offered) {
|
||||
// If the queue is still full, trigger an immediate refresh to free up space
|
||||
if (retryCount == 0) {
|
||||
log.debug("victoria metrics buffer queue is full, triggering immediate flush");
|
||||
triggerImmediateFlush();
|
||||
}
|
||||
retryCount++;
|
||||
// The short sleep allows the queue to clear out
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
Thread.sleep(100L * retryCount);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("[Victoria Metrics] Interrupted while offering metrics to buffer queue", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// When the maximum number of retries is reached, if it still cannot be added to the queue, the data is saved directly
|
||||
if (!offered) {
|
||||
log.warn("[Victoria Metrics] Failed to add metrics to buffer after {} retries, saving directly", MAX_RETRIES);
|
||||
try {
|
||||
doSaveData(contentList);
|
||||
} catch (Exception e) {
|
||||
log.error("[Victoria Metrics] Failed to save metrics directly: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// Refresh in advance to avoid waiting
|
||||
if (metricsBufferQueue.size() >= vmInsertProps.bufferSize() * 0.8) {
|
||||
triggerImmediateFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerImmediateFlush() {
|
||||
metricsFlushTimer.newTimeout(metricsFlushtask, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regularly refresh the buffer queue to the vm
|
||||
*/
|
||||
private class MetricsFlushTask implements TimerTask {
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
List<VictoriaMetricsDataStorage.VictoriaMetricsContent> batch = new ArrayList<>(vmInsertProps.bufferSize());
|
||||
metricsBufferQueue.drainTo(batch, vmInsertProps.bufferSize());
|
||||
if (!batch.isEmpty()) {
|
||||
doSaveData(batch);
|
||||
log.debug("[Victoria Metrics] Flushed {} metrics items", batch.size());
|
||||
}
|
||||
if (metricsFlushTimer != null && !metricsFlushTimer.isStop()) {
|
||||
metricsFlushTimer.newTimeout(this, vmInsertProps.flushInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[VictoriaMetrics] flush task error: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* victoria metrics content
|
||||
*/
|
||||
|
||||
+146
-30
@@ -25,12 +25,16 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -46,6 +50,9 @@ import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
|
||||
import org.apache.hertzbeat.common.entity.dto.Value;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.timer.HashedWheelTimer;
|
||||
import org.apache.hertzbeat.common.timer.Timeout;
|
||||
import org.apache.hertzbeat.common.timer.TimerTask;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
@@ -87,10 +94,16 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
private static final String SPILT = "_";
|
||||
private static final String MONITOR_METRICS_KEY = "__metrics__";
|
||||
private static final String MONITOR_METRIC_KEY = "__metric__";
|
||||
private static final long MAX_WAIT_MS = 500L;
|
||||
private static final int MAX_RETRIES = 3;
|
||||
|
||||
private final VictoriaMetricsProperties victoriaMetricsProp;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final BlockingQueue<VictoriaMetricsDataStorage.VictoriaMetricsContent> metricsBufferQueue;
|
||||
|
||||
private boolean isBatchImportEnabled = false;
|
||||
private HashedWheelTimer metricsFlushTimer = null;
|
||||
private MetricsFlushTask metricsFlushtask = null;
|
||||
|
||||
public VictoriaMetricsDataStorage(VictoriaMetricsProperties victoriaMetricsProperties, RestTemplate restTemplate) {
|
||||
if (victoriaMetricsProperties == null) {
|
||||
@@ -100,6 +113,22 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
this.restTemplate = restTemplate;
|
||||
victoriaMetricsProp = victoriaMetricsProperties;
|
||||
serverAvailable = checkVictoriaMetricsDatasourceAvailable();
|
||||
serverAvailable = checkVictoriaMetricsDatasourceAvailable();
|
||||
metricsBufferQueue = new LinkedBlockingQueue<>(victoriaMetricsProperties.insert().bufferSize());
|
||||
isBatchImportEnabled = victoriaMetricsProperties.insert().flushInterval() != 0 && victoriaMetricsProperties.insert().bufferSize() != 0;
|
||||
if (isBatchImportEnabled){
|
||||
initializeFlushTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeFlushTimer() {
|
||||
this.metricsFlushTimer = new HashedWheelTimer(r -> {
|
||||
Thread thread = new Thread(r, "victoria-metrics-cluster-flush-timer");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
}, 1, TimeUnit.SECONDS, 512);
|
||||
metricsFlushtask = new MetricsFlushTask();
|
||||
this.metricsFlushTimer.newTimeout(metricsFlushtask, 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private boolean checkVictoriaMetricsDatasourceAvailable() {
|
||||
@@ -135,7 +164,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
return;
|
||||
}
|
||||
if (metricsData.getValues().isEmpty()) {
|
||||
log.info("[warehouse victoria-metrics] flush metrics data {} {} {} is null, ignore.",
|
||||
log.info("[warehouse victoria-metrics] flush metrics data {} {} {} is null, ignore.",
|
||||
metricsData.getId(), metricsData.getApp(), metricsData.getMetrics());
|
||||
return;
|
||||
}
|
||||
@@ -215,36 +244,20 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
log.info("[warehouse victoria-metrics] flush metrics data {} is empty, ignore.", metricsData.getId());
|
||||
return;
|
||||
}
|
||||
if (!isBatchImportEnabled){
|
||||
doSaveData(contentList);
|
||||
return;
|
||||
}
|
||||
sendVictoriaMetrics(contentList);
|
||||
}
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(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);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (VictoriaMetricsContent content : contentList) {
|
||||
stringBuilder.append(JsonUtil.toJson(content)).append("\n");
|
||||
}
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(stringBuilder.toString(), headers);
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(victoriaMetricsProp.url() + IMPORT_PATH,
|
||||
httpEntity, String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("insert metrics data to victoria-metrics success.");
|
||||
} else {
|
||||
log.error("insert metrics data to victoria-metrics failed. {}", responseEntity.getBody());
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("flush metrics data to victoria-metrics error: {}.", e.getMessage(), e);
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (metricsFlushTimer != null && !metricsFlushTimer.isStop()) {
|
||||
metricsFlushTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, List<Value>> getHistoryMetricData(Long monitorId, String app, String metrics, String metric, String label, String history) {
|
||||
String labelName = metrics + SPILT + metric;
|
||||
@@ -388,7 +401,7 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
long timestamp = Long.parseLong(String.valueOf(valueArr[0]));
|
||||
String value = new BigDecimal(String.valueOf(valueArr[1])).setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
|
||||
// read timestamp here is s unit
|
||||
valueList.add(new Value(value, timestamp * 1000));
|
||||
valueList.add(new Value(value, timestamp * 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,4 +545,107 @@ public class VictoriaMetricsDataStorage extends AbstractHistoryDataStorage {
|
||||
*/
|
||||
private Long[] timestamps;
|
||||
}
|
||||
|
||||
/**
|
||||
* add victoriaMetricsContent to buffer
|
||||
* @param contentList victoriaMetricsContent List
|
||||
*/
|
||||
private void sendVictoriaMetrics(List<VictoriaMetricsDataStorage.VictoriaMetricsContent> contentList) {
|
||||
for (VictoriaMetricsDataStorage.VictoriaMetricsContent content : contentList) {
|
||||
boolean offered = false;
|
||||
int retryCount = 0;
|
||||
while (!offered && retryCount < MAX_RETRIES) {
|
||||
try {
|
||||
// Attempt to add to the queue for a limited time
|
||||
offered = metricsBufferQueue.offer(content, MAX_WAIT_MS, TimeUnit.MILLISECONDS);
|
||||
if (!offered) {
|
||||
// If the queue is still full, trigger an immediate refresh to free up space
|
||||
if (retryCount == 0) {
|
||||
log.debug("victoria metrics buffer queue is full, triggering immediate flush");
|
||||
triggerImmediateFlush();
|
||||
}
|
||||
retryCount++;
|
||||
// The short sleep allows the queue to clear out
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
Thread.sleep(100L * retryCount);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("[Victoria Metrics] Interrupted while offering metrics to buffer queue", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// When the maximum number of retries is reached, if it still cannot be added to the queue, the data is saved directly
|
||||
if (!offered) {
|
||||
log.warn("[Victoria Metrics] Failed to add metrics to buffer after {} retries, saving directly", MAX_RETRIES);
|
||||
try {
|
||||
doSaveData(contentList);
|
||||
} catch (Exception e) {
|
||||
log.error("[Victoria Metrics] Failed to save metrics directly: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// Refresh in advance to avoid waiting
|
||||
if (metricsBufferQueue.size() >= victoriaMetricsProp.insert().bufferSize() * 0.8) {
|
||||
triggerImmediateFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerImmediateFlush() {
|
||||
metricsFlushTimer.newTimeout(metricsFlushtask, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regularly refresh the buffer queue to the vm
|
||||
*/
|
||||
private class MetricsFlushTask implements TimerTask {
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
List<VictoriaMetricsDataStorage.VictoriaMetricsContent> batch = new ArrayList<>(victoriaMetricsProp.insert().bufferSize());
|
||||
metricsBufferQueue.drainTo(batch, victoriaMetricsProp.insert().bufferSize());
|
||||
if (!batch.isEmpty()) {
|
||||
doSaveData(batch);
|
||||
log.debug("[Victoria Metrics] Flushed {} metrics items", batch.size());
|
||||
}
|
||||
if (metricsFlushTimer != null && !metricsFlushTimer.isStop()) {
|
||||
metricsFlushTimer.newTimeout(this, victoriaMetricsProp.insert().flushInterval(), TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[VictoriaMetrics] flush task error: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save metric data to victoria-metric via HTTP call
|
||||
*/
|
||||
private void doSaveData(List<VictoriaMetricsContent> contentList) {
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(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);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (VictoriaMetricsContent content : contentList) {
|
||||
stringBuilder.append(JsonUtil.toJson(content)).append("\n");
|
||||
}
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(stringBuilder.toString(), headers);
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(victoriaMetricsProp.url() + IMPORT_PATH,
|
||||
httpEntity, String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("insert metrics data to victoria-metrics success.");
|
||||
} else {
|
||||
log.error("insert metrics data to victoria-metrics failed. {}", responseEntity.getBody());
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("flush metrics data to victoria-metrics error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-1
@@ -17,12 +17,16 @@
|
||||
|
||||
package org.apache.hertzbeat.warehouse.store.history.vm;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
/**
|
||||
* vminsert configuration information
|
||||
*/
|
||||
public record VictoriaMetricsInsertProperties(
|
||||
String url,
|
||||
String username,
|
||||
String password
|
||||
String password,
|
||||
@DefaultValue("1000") int bufferSize,
|
||||
@DefaultValue("3") int flushInterval
|
||||
) {
|
||||
}
|
||||
|
||||
+8
-6
@@ -27,13 +27,15 @@ import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
* Victoria metrics configuration information.
|
||||
*/
|
||||
|
||||
@ConfigurationProperties(prefix = ConfigConstants.FunctionModuleConstants.WAREHOUSE
|
||||
+ SignConstants.DOT
|
||||
+ WarehouseConstants.STORE
|
||||
+ SignConstants.DOT
|
||||
+ WarehouseConstants.HistoryName.VM)
|
||||
@ConfigurationProperties(prefix = ConfigConstants.FunctionModuleConstants.WAREHOUSE + SignConstants.DOT + WarehouseConstants.STORE + SignConstants.DOT + WarehouseConstants.HistoryName.VM)
|
||||
public record VictoriaMetricsProperties(@DefaultValue("false") boolean enabled,
|
||||
@DefaultValue("http://localhost:8428") String url,
|
||||
String username,
|
||||
String password) {
|
||||
String password,
|
||||
InsertConfig insert) {
|
||||
|
||||
record InsertConfig(@DefaultValue("1000") int bufferSize,
|
||||
@DefaultValue("3") int flushInterval) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ Detailed config refer to [Install HertzBeat via Docker](https://hertzbeat.com/do
|
||||
* Update app-zookeeper.yml by @hurenjie1 in <https://github.com/apache/hertzbeat/pull/1458>
|
||||
* add hurenjie1 as a contributor for code by @allcontributors in <https://github.com/apache/hertzbeat/pull/1459>
|
||||
* update dashboard ui, remove ssh custom SignatureFactories, update app name by @tomsun28 in <https://github.com/apache/hertzbeat/pull/1460>
|
||||
* [Task] Monitoring Template Yml Metrics I18n | 监控模版指标国际化任务认领 #1212 by @tslj1024 in <https://github.com/apache/hertzbeat/pull/1461>
|
||||
* [Task] Monitoring Template Yml Metrics I18n | 监控模板指标国际化任务认领 #1212 by @tslj1024 in <https://github.com/apache/hertzbeat/pull/1461>
|
||||
* add tslj1024 as a contributor for code by @allcontributors in <https://github.com/apache/hertzbeat/pull/1462>
|
||||
* Add alarm trigger time for alarm restore by @Calvin979 in <https://github.com/apache/hertzbeat/pull/1464>
|
||||
* bugfix history range query not work when victoria-metrics store by @tomsun28 in <https://github.com/apache/hertzbeat/pull/1463>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "From User to PMC Member: Contributing to Open Source with Passion at Apache HertzBeat"
|
||||
author: liutianyou
|
||||
author_title: liutianyou
|
||||
author_url: https://github.com/Liutianyou
|
||||
author_image_url: https://avatars.githubusercontent.com/u/30208283?v=4
|
||||
tags: [opensource, practice]
|
||||
keywords: [open source monitoring system, alerting system]
|
||||
---
|
||||
|
||||
### From Contributor to PMC
|
||||
|
||||
About a year ago, I discovered Apache HertzBeat - an agentless monitoring system with out-of-the-box features that immediately caught my interest through its pure web-based operations. After starting to use it, I made my first code contribution. When that initial PR got merged, my confidence in open source contribution skyrocketed. Since then, I've continuously looked for opportunities to contribute through documentation improvements, feature testing, code refactoring, and more in the community issue list.
|
||||
|
||||
As my contributions grew, I gradually evolved from an "occasional code submitter" to a "deep project participant". Fixing bugs, optimizing documentation, designing new features... This journey not only enhanced my technical skills but more importantly taught me how to collaborate effectively within a community.
|
||||
|
||||
After becoming a Committer, my responsibilities expanded beyond coding to include community discussions, PR reviews, and helping new members onboard. This experience gave me a profound realization: **Open source isn't just about code - it's fundamentally about human collaboration**.
|
||||
|
||||
This year, I was honored to be nominated as a PMC member. Just like when I first received the Committer invitation, I felt the same excitement. I understand this recognition carries not just acknowledgment of past contributions but also the community's trust and expectations. As PMC, my responsibilities now include ensuring project health through strategic planning, community event organization, and requirement coordination. This role enables deeper involvement in core decision-making, from technical direction to ecosystem development, driving HertzBeat's continuous evolution.
|
||||
|
||||
### Personal Growth
|
||||
|
||||
My deep involvement in open source over this past year has brought significant growth:
|
||||
|
||||
- **Learning Best Practices**: Exposure to standardized code, excellent designs, and community best practices for tools like Git, CI/CD, and e2e testing
|
||||
- **Collaboration & Communication**: Learning to write clear issue descriptions, participate in technical discussions, and resolve differences in distributed teams
|
||||
- **Product Thinking Development**: Understanding user needs of open source projects and transitioning from developer to maintainer perspective
|
||||
|
||||
### Looking Ahead
|
||||
|
||||
For the future, I plan to focus on:
|
||||
|
||||
- **User Support**: Helping resolve user issues
|
||||
- **Technology Promotion**: Making Apache HertzBeat a top choice in monitoring through wider adoption
|
||||
- **Contributor Ecosystem**: Nurturing and developing more contributors for Apache HertzBeat
|
||||
|
||||
### What is Apache HertzBeat?
|
||||
|
||||
Apache HertzBeat is an open source real-time monitoring system emphasizing ease-of-use and lightweight design. It supports monitoring and alerting for servers, databases, middleware, and application services, providing a simple yet powerful solution for IT infrastructure monitoring.
|
||||
|
||||
### How to Contribute?
|
||||
|
||||
We welcome contributions of code, documentation, and ideas. My open source journey taught me that the hardest parts are starting and persisting. If interested, visit our contribution guide:
|
||||
<https://hertzbeat.apache.org/zh-cn/docs/community/contribution>
|
||||
Feel free to email me at <liutianyou@apache.org> - I'm happy to help you take your first open source steps!
|
||||
|
||||
- Official Site: <https://hertzbeat.apache.org>
|
||||
- GitHub: <https://github.com/apache/hertzbeat>
|
||||
- Mailing List: <dev@hertzbeat.apache.org>
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
Special thanks to **Tom**, **Qingran Zhao**, **TJxiaobao**, **Jast** for guidance, and all Apache HertzBeat community members for support.
|
||||
@@ -96,9 +96,9 @@ name:
|
||||
en-US: EXAMPLE APP
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模版 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模版菜单 -> 新增监控类型 -> 配置自定义监控模版YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模板 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模板菜单 -> 新增监控类型 -> 配置自定义监控模板YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
en-US: "HertzBeat supports custom monitoring, and you only need to configure the monitoring template YML to adapt to a custom monitoring type. <br>Definition process as follow: HertzBeat Pages -> Main Menu -> Monitor Template -> edit and save -> apply this template."
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模版YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模版選單->新增監控類型->配寘自定義監控模版YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模板YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模板選單->新增監控類型->配寘自定義監控模板YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/
|
||||
en-US: https://hertzbeat.apache.org/docs/advanced/extend-point/
|
||||
|
||||
@@ -45,9 +45,9 @@ name:
|
||||
en-US: EXAMPLE APP
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模版 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模版菜单 -> 新增监控类型 -> 配置自定义监控模版YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模板 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模板菜单 -> 新增监控类型 -> 配置自定义监控模板YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
en-US: "HertzBeat supports custom monitoring, and you only need to configure the monitoring template YML to adapt to a custom monitoring type. <br>Definition process as follow: HertzBeat Pages -> Main Menu -> Monitor Template -> edit and save -> apply this template."
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模版YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模版選單->新增監控類型->配寘自定義監控模版YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模板YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模板選單->新增監控類型->配寘自定義監控模板YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/
|
||||
en-US: https://hertzbeat.apache.org/docs/advanced/extend-point/
|
||||
|
||||
@@ -40,9 +40,9 @@ name:
|
||||
en-US: EXAMPLE WEBSITE
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模版 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模版菜单 -> 新增监控类型 -> 配置自定义监控模版YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
zh-CN: HertzBeat 支持自定义监控,您只需配置监控模板 YML 就能适配一款自定义的监控类型。<br>定义流程如下:HertzBeat 页面 -> 监控模板菜单 -> 新增监控类型 -> 配置自定义监控模板YML -> 点击保存应用 -> 使用新监控类型添加监控。
|
||||
en-US: "HertzBeat supports custom monitoring, and you only need to configure the monitoring template YML to adapt to a custom monitoring type. <br>Definition process as follow: HertzBeat Pages -> Main Menu -> Monitor Template -> edit and save -> apply this template."
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模版YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模版選單->新增監控類型->配寘自定義監控模版YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
zh-TW: HertzBeat支持自定義監控,您只需配寘監控模板YML就能適配一款自定義的監控類型。<br>定義流程如下:HertzBeat頁面->監控模板選單->新增監控類型->配寘自定義監控模板YML ->點擊保存應用->使用新監控類型添加監控。
|
||||
helpLink:
|
||||
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/advanced/extend-point/
|
||||
en-US: https://hertzbeat.apache.org/docs/advanced/extend-point/
|
||||
|
||||
@@ -14,7 +14,7 @@ WeChat Group : Add friend `ahertzbeat` and invite to the group.
|
||||
|
||||
WeChat Public : Search ID `usthecom`.
|
||||
|
||||
[QQ Group](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : Group num `630061200`
|
||||
[QQ Group](https://qm.qq.com/q/xxqecSC2cw) : Group num `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
|
||||
@@ -441,7 +441,6 @@ One item of the email content is `Vote thread`, and the link is obtained here: <
|
||||
Send a voting email in the incubator community requires at least three `+1` and no `-1`.
|
||||
|
||||
> `Send to`: <general@incubator.apache.org> <br />
|
||||
> `cc`: <dev@hertzbeat.apache.org>、<private@hertzbeat.apache.org> <br />
|
||||
> `Title`: [VOTE] Release Apache HertzBeat (incubating) 1.6.0-rc1 <br />
|
||||
> `Body`:
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ ${FORMATTER [{ + | - }<DURATION> <TIME_UNIT>]}
|
||||
#### Usage Examples
|
||||
|
||||
1. Simple expression
|
||||
- `${now}` gets the current time and formats it as `yyyy-MM-dd HH:mm:ss`
|
||||
- `${time+1h}` calculates the time one hour from now and formats it as `HH:mm:ss`
|
||||
- `${time+1h+15s+30s}` calculates the time one hour, 15 minutes, and 30 seconds from now and formats it as `HH:mm:ss`
|
||||
- `${@now}` gets the current time and formats it as `yyyy-MM-dd HH:mm:ss`
|
||||
- `${@time+1H}` calculates the time one hour from now and formats it as `HH:mm:ss`
|
||||
- `${@time+1H+15m+30s}` calculates the time one hour, 15 minutes, and 30 seconds from now and formats it as `HH:mm:ss`
|
||||
2. Complex expression template (if the built-in formatter does not meet your needs, you can combine multiple expressions)
|
||||
- `${@year}年${@month}月${@day}日` returns the current date formatted as yyyy年MM月dd日
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
id: greptime-init
|
||||
title: Use Time Series Database GreptimeDB to Store Metrics Data (Optional)
|
||||
sidebar_label: Metrics Store GreptimeDB
|
||||
title: Use Time Series Database Greptime to Store Metrics Data (Recommended)
|
||||
sidebar_label: Metrics Store Greptime (Recommended)
|
||||
---
|
||||
|
||||
Apache HertzBeat (incubating)'s historical data storage relies on the time series database, you can choose one of them to install and initialize, or not to install (note ⚠️ but it is strongly recommended to configure in the production environment)
|
||||
|
||||
> It is recommended to use VictoriaMetrics as metrics storage.
|
||||
> It is recommended to use Greptime as metrics storage.
|
||||
|
||||
[GreptimeDB](https://github.com/GreptimeTeam/greptimedb) is an open-source time-series database with a special focus on scalability, analytical capabilities and efficiency.
|
||||
[Greptime](https://github.com/GreptimeTeam/greptimedb) is an Open-source, cloud-native, unified observability database for metrics, logs and traces, supporting SQL/PromQL/Streaming.
|
||||
|
||||
It's designed to work on infrastructure of the cloud era, and users benefit from its elasticity and commodity storage.
|
||||
|
||||
**⚠️ If you do not configure a time series database, only the last hour of historical data is retained.**
|
||||
|
||||
### Install GreptimeDB via Docker
|
||||
### Install GreptimeDvia Docker
|
||||
|
||||
1. Download and install Docker environment
|
||||
Docker tools download refer to [Docker official document](https://docs.docker.com/get-docker/).
|
||||
@@ -25,7 +25,7 @@ After the installation you can check if the Docker version normally output at th
|
||||
Docker version 20.10.12, build e91ed57
|
||||
```
|
||||
|
||||
2. Install GreptimeDB with Docker
|
||||
2. Install Greptime with Docker
|
||||
|
||||
```shell
|
||||
$ docker run -d -p 127.0.0.1:4000-4003:4000-4003 \
|
||||
@@ -38,7 +38,7 @@ After the installation you can check if the Docker version normally output at th
|
||||
--postgres-addr 0.0.0.0:4003
|
||||
```
|
||||
|
||||
`-v "$(pwd)/greptimedb:/tmp/greptimedb"` is local persistent mount of greptimedb data directory. `$(pwd)/greptimedb` should be replaced with the actual local directory, default is the `greptimedb` directory under the current directory.
|
||||
`-v "$(pwd)/greptimedb:/tmp/greptimedb"` is local persistent mount of greptime data directory. `$(pwd)/greptimedb` should be replaced with the actual local directory, default is the `greptimedb` directory under the current directory.
|
||||
use```$ docker ps``` to check if the database started successfully
|
||||
|
||||
### Configure the database connection in hertzbeat `application.yml` configuration file
|
||||
@@ -69,6 +69,6 @@ use```$ docker ps``` to check if the database started successfully
|
||||
|
||||
### FAQ
|
||||
|
||||
1. Do both the time series databases Greptime, IoTDB or TDengine need to be configured? Can they both be used?
|
||||
1. Do both the time series databases need to be configured? Can they both be used?
|
||||
|
||||
> You don't need to configure all of them, you can choose one of them. Use the enable parameter to control whether it is used or not. You can also install and configure neither, which only affects the historical chart data.
|
||||
|
||||
@@ -434,7 +434,7 @@
|
||||
"message": "{docker} {br}集监控-告警-通知为一体,支持应用服务,Web,数据库,缓存,操作系统,中间件,大数据,云原生,网络等监控阈值告警通知一步到位。{br} 易用友好,无需Agent,全WEB页面操作,鼠标点一点就能监控告警,零上手学习成本。{br}安全是最重要的,数据密钥全链路加密。"
|
||||
},
|
||||
"custom-multi-support-content": {
|
||||
"message": "将 Http,Jmx,Ssh,Snmp,Jdbc 等协议规范可配置模版化,只需在线配置YML就可自定义监控指标。{br} 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。{br}自由的告警阈值规则,邮箱,短信,钉钉,企业微信,飞书,Webhook等消息及时送达。{br} 您相信只需配置下就能适配新K8s监控类型吗?"
|
||||
"message": "将 Http,Jmx,Ssh,Snmp,Jdbc 等协议规范可配置模板化,只需在线配置YML就可自定义监控指标。{br} 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。{br}自由的告警阈值规则,邮箱,短信,钉钉,企业微信,飞书,Webhook等消息及时送达。{br} 您相信只需配置下就能适配新K8s监控类型吗?"
|
||||
},
|
||||
"opensource-content": {
|
||||
"message": "Apache HertzBeat (incubating) 是开源的,拥有一个包容开放的社区。{br}欢迎任何对此有兴趣的同学参与其中,无论是代码文档或者错别字,尊重社区的每一位,一起进步成长。{br}我们的代码正被部署到全球成千上万机器上。{github}"
|
||||
|
||||
@@ -60,7 +60,7 @@ Online <https://console.tancloud.cn>.
|
||||
> 当然,我们也提供了对应的[SAAS云监控版本](https://console.tancloud.cn),中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,[登录即可免费开始](https://console.tancloud.cn)监控之旅。
|
||||
> HertzBeat 支持自定义监控,只用通过配置YML文件我们就可以自定义需要的监控类型和指标,来满足常见的个性化需求。
|
||||
> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版,邮件钉钉微信飞书等及时通知送达
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模板,邮件钉钉微信飞书等及时通知送达
|
||||
> 欢迎登录 HertzBeat 的 [云环境TanCloud](https://console.tancloud.cn) 试用发现更多。
|
||||
> 我们正在快速迭代中,欢迎参与加入共建项目开源生态。
|
||||
>
|
||||
|
||||
@@ -61,7 +61,7 @@ Have Fun!
|
||||
> 当然,我们也提供了对应的[SAAS云监控版本](https://console.tancloud.cn),中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,[登录即可免费开始](https://console.tancloud.cn)监控之旅。
|
||||
> HertzBeat 支持自定义监控,只用通过配置YML文件我们就可以自定义需要的监控类型和指标,来满足常见的个性化需求。
|
||||
> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版,邮件钉钉微信飞书等及时通知送达
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模板,邮件钉钉微信飞书等及时通知送达
|
||||
> 欢迎登录 HertzBeat 的 [云环境TanCloud](https://console.tancloud.cn) 试用发现更多。
|
||||
> 我们正在快速迭代中,欢迎参与加入共建项目开源生态。
|
||||
>
|
||||
|
||||
@@ -61,7 +61,7 @@ Have Fun!
|
||||
> 当然,我们也提供了对应的[SAAS云监控版本](https://console.tancloud.cn),中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,[登录即可免费开始](https://console.tancloud.cn)监控之旅。
|
||||
> HertzBeat 支持自定义监控,只用通过配置YML文件我们就可以自定义需要的监控类型和指标,来满足常见的个性化需求。
|
||||
> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版,邮件钉钉微信飞书等及时通知送达
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模板,邮件钉钉微信飞书等及时通知送达
|
||||
> 欢迎登录 HertzBeat 的 [云环境TanCloud](https://console.tancloud.cn) 试用发现更多。
|
||||
> 我们正在快速迭代中,欢迎参与加入共建项目开源生态。
|
||||
>
|
||||
|
||||
@@ -50,7 +50,7 @@ Have Fun!
|
||||
> 当然,我们也提供了对应的[SAAS云监控版本](https://console.tancloud.cn),中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,[登录即可免费开始](https://console.tancloud.cn)监控之旅。
|
||||
> HertzBeat 支持自定义监控,只用通过配置YML文件我们就可以自定义需要的监控类型和指标,来满足常见的个性化需求。
|
||||
> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版,邮件钉钉微信飞书等及时通知送达
|
||||
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模板,邮件钉钉微信飞书等及时通知送达
|
||||
> 欢迎登录 HertzBeat 的 [云环境TanCloud](https://console.tancloud.cn) 试用发现更多。
|
||||
> 我们正在快速迭代中,欢迎参与加入共建项目开源生态。
|
||||
>
|
||||
|
||||
@@ -61,7 +61,7 @@ gitee: <https://gitee.com/hertzbeat/hertzbeat>
|
||||
|
||||

|
||||
|
||||
> 配置阈值,选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
> 配置阈值,选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模板信息等。
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ github:gcdd1993 (进击的阿晨)
|
||||
* 实现了对`Redis`数据库的指标监控
|
||||
* 优化`spring.factories` 配置项
|
||||
* 实现支持了 `Telegram`、`Discord`、`Slack` 的消息通知渠道
|
||||
* 使用 `Thymeleaf` 重构了告警文本,告警通知模版更规范化
|
||||
* 使用 `Thymeleaf` 重构了告警文本,告警通知模板更规范化
|
||||
|
||||
### 感谢社区小伙伴
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ COMMIT;
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Website, Port Telnet, Http Api, Ping Connect, Jvm, SiteMap, Ssl Certificate, SpringBoot2, FTP Server, SpringBoot3
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, DM, OpenGauss, ClickHouse, IoTDB, Redis Cluster, Redis Sentinel
|
||||
@@ -58,7 +58,7 @@ COMMIT;
|
||||
- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ
|
||||
- Kubernetes, Docker
|
||||
- CiscoSwitch, HpeSwitch, HuaweiSwitch, TpLinkSwitch, H3cSwitch
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 Discord Slack Telegram 邮件 钉钉 微信 飞书 短信 Webhook。
|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +27,7 @@ Cloud: **[GreptimePlay](https://greptime.com/playground)**
|
||||
|
||||
- 集 **监控+告警+通知** All in one,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控,阈值告警通知一步到位。
|
||||
- 更自由化的阈值规则(计算表达式),`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式及时送达。
|
||||
- 将`Http, Jmx, Ssh, Snmp, Jdbc, Prometheus`等协议规范可配置化,只需在浏览器配置`YML`监控模版就能使用这些协议去自定义采集想要的指标。
|
||||
- 将`Http, Jmx, Ssh, Snmp, Jdbc, Prometheus`等协议规范可配置化,只需在浏览器配置`YML`监控模板就能使用这些协议去自定义采集想要的指标。
|
||||
|
||||
> `HertzBeat`的强大自定义,多类型支持,易扩展,低耦合,希望能帮助开发者和中小团队快速搭建自有监控系统。
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Website, Port Telnet, Http Api, Ping Connect, Jvm, SiteMap, Ssl Certificate, SpringBoot2, FTP Server, SpringBoot3
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, DM, OpenGauss, ClickHouse, IoTDB, Redis Cluster, Redis Sentinel
|
||||
@@ -60,7 +60,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
- Tomcat, Nacos, Zookeeper, RabbitMQ, Flink, Kafka, ShenYu, DynamicTp, Jetty, ActiveMQ
|
||||
- Kubernetes, Docker
|
||||
- CiscoSwitch, HpeSwitch, HuaweiSwitch, TpLinkSwitch, H3cSwitch
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 Discord Slack Telegram 邮件 钉钉 微信 飞书 短信 Webhook。
|
||||
|
||||
---
|
||||
|
||||
@@ -18,7 +18,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。
|
||||
|
||||
@@ -65,7 +65,7 @@ HertzBeat 提供云边协同能力,可以在多个隔离网络部署边缘采
|
||||
- 用户流量。开源项目免费提供给用户和开发者,吸引用户使用,宣传等方面都有优势。
|
||||
- 用户信任。开源的产品天生容易获取用户的信任和使用耐心,或者说降低用户的信任门槛。
|
||||
- 社区协作。开源的产品可以吸引到顶级贡献者一起贡献,接收用户的反馈issue,pr贡献等,在社区的驱动下使开源项目越来越好,正向反馈后也会有更多人参与和使用。社区协作我觉得这是开源的意义,而且这样不仅仅只是程序员之间的贡献代码协作,用户都是协作对象(比如我们这个项目有大量的运维朋友贡献代码和文档),如果是仅仅代码开源而不社区协作,那还不如放个安装包给别人免费使用下载就好。
|
||||
- 产品生态。这对一些需要生态的产品是需要的,比如hertzbeat,需要支持对接各种类型协议的监控类型,大量的监控模版。一个好的开源项目生态才能吸引到其它贡献者贡献和分享,在生态中互通有无,最终大家在生态中都受益。这在闭源程序中是很难做到的。
|
||||
- 产品生态。这对一些需要生态的产品是需要的,比如hertzbeat,需要支持对接各种类型协议的监控类型,大量的监控模板。一个好的开源项目生态才能吸引到其它贡献者贡献和分享,在生态中互通有无,最终大家在生态中都受益。这在闭源程序中是很难做到的。
|
||||
|
||||
上面几点,重在社区协作和产品生态,这也是开源集群版的原因,只有卷开源产品卷自己到更强的产品力,比如集群这一技术特性天生会吸引到开发者(而且集群本身就是我们社区协作的产物),会吸引到更多的用户和贡献者使用反馈大家一起迭代,社区驱动进而正向促进开源项目和满足用户功能体验。
|
||||
而对于开源商业化,开源商业化的前提是得有个真正好的,受欢迎,被广泛使用的开源产品,然后在此基础上做商业化挣钱。
|
||||
@@ -155,8 +155,8 @@ HertzBeat 提供云边协同能力,可以在多个隔离网络部署边缘采
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster
|
||||
@@ -166,7 +166,7 @@ HertzBeat 提供云边协同能力,可以在多个隔离网络部署边缘采
|
||||
- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch
|
||||
- and more for your custom monitoring.
|
||||
- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`.
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook`。
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ HertzBeat 赫兹跳动是一个拥有强大自定义监控能力,高性能集
|
||||
|
||||
* 集 **监控+告警+通知** 为一体,支持对应用服务,数据库,操作系统,中间件,云原生,网络等监控阈值告警通知一步到位。
|
||||
* 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
* 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
* 将 `Http,Jmx,Ssh,Snmp,Jdbc` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
* 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
* 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
- **采集器集群的UI界面管理** @Ceilzcx @tomsun28
|
||||
- **功能页面帮助信息模块和阈值表达式增强** 开源之夏和GLCC课题 @YutingNie @mikezzb
|
||||
- **新的控制台登陆界面和欢迎页面**
|
||||
- **监控指标名称国际化** 用户可以看指标的中英文名称啦,欢迎一起完善监控模版里面的i18n国际化资源
|
||||
- **监控指标名称国际化** 用户可以看指标的中英文名称啦,欢迎一起完善监控模板里面的i18n国际化资源
|
||||
- **支持kubernetes helm charts一键部署** 见 <https://artifacthub.io/packages/search?repo=hertzbeat>
|
||||
|
||||
**更多的特性和BUG修复,稳定性提示** 感谢 @zqr10159 @Carpe-Wang @luxx-lq @l646505418 @LINGLUOJUN @luelueking @qyaaaa @novohit @gcdd1993
|
||||
@@ -54,7 +54,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` 等方式消息及时送达。
|
||||
|
||||
@@ -94,8 +94,8 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster
|
||||
@@ -105,7 +105,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch
|
||||
- and more for your custom monitoring.
|
||||
- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`.
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: HertzBeat v1.4.2 版本发布,自定义消息通知模版
|
||||
title: HertzBeat v1.4.2 版本发布,自定义消息通知模板
|
||||
author: tom
|
||||
author_title: tom
|
||||
author_url: https://github.com/tomsun28
|
||||
@@ -14,8 +14,8 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
### 总结起来如下
|
||||
|
||||
- **消息通知模版特性,开源之夏课题**
|
||||
- **支持华为云OBS存储监控模版文件**
|
||||
- **消息通知模板特性,开源之夏课题**
|
||||
- **支持华为云OBS存储监控模板文件**
|
||||
- **支持MQTT消息服务器 emqx 监控**
|
||||
- **支持对 udp 端口可用性监控**
|
||||
- **更多的特性功能支持和BUG修复**
|
||||
@@ -31,7 +31,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
|
||||
@@ -71,8 +71,8 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster
|
||||
@@ -82,7 +82,7 @@ keywords: [open source monitoring system, alerting system, Linux monitoring]
|
||||
- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch
|
||||
- and more for your custom monitoring.
|
||||
- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`.
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
---
|
||||
|
||||
@@ -16,7 +16,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
@@ -66,8 +66,8 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster
|
||||
@@ -77,7 +77,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch
|
||||
- and more for your custom monitoring.
|
||||
- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`.
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
---
|
||||
|
||||
@@ -122,7 +122,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
|
||||
@@ -16,7 +16,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
@@ -67,8 +67,8 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为yml监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- Site Monitor, Port Availability, Http Api, Ping Connectivity, Jvm, SiteMap Full Site, Ssl Certificate, SpringBoot, FTP Server
|
||||
- Mysql, PostgreSQL, MariaDB, Redis, ElasticSearch, SqlServer, Oracle, MongoDB, Damon, OpenGauss, ClickHouse, IoTDB, Redis Cluster
|
||||
@@ -78,7 +78,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
- Huawei Switch, HPE Switch, TP-LINK Switch, Cisco Switch
|
||||
- and more for your custom monitoring.
|
||||
- Notifications support `Discord` `Slack` `Telegram` `Mail` `Pinning` `WeChat` `FlyBook` `SMS` `Webhook`.
|
||||
- 和更多自定义监控模版。
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
---
|
||||
@@ -137,7 +137,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
- Update app-zookeeper.yml by @hurenjie1 in <https://github.com/apache/hertzbeat/pull/1458>
|
||||
- add hurenjie1 as a contributor for code by @allcontributors in <https://github.com/apache/hertzbeat/pull/1459>
|
||||
- update dashboard ui, remove ssh custom SignatureFactories, update app name by @tomsun28 in <https://github.com/apache/hertzbeat/pull/1460>
|
||||
- Task Monitoring Template Yml Metrics I18n | 监控模版指标国际化任务认领 #1212 by @tslj1024 in <https://github.com/apache/hertzbeat/pull/1461>
|
||||
- Task Monitoring Template Yml Metrics I18n | 监控模板指标国际化任务认领 #1212 by @tslj1024 in <https://github.com/apache/hertzbeat/pull/1461>
|
||||
- add tslj1024 as a contributor for code by @allcontributors in <https://github.com/apache/hertzbeat/pull/1462>
|
||||
- Add alarm trigger time for alarm restore by @Calvin979 in <https://github.com/apache/hertzbeat/pull/1464>
|
||||
- bugfix history range query not work when victoria-metrics store by @tomsun28 in <https://github.com/apache/hertzbeat/pull/1463>
|
||||
|
||||
@@ -28,7 +28,7 @@ HertzBeat 于 2022 年 1 月在 Dromara 开源社区正式开源,经过两年
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
## HertzBeat 课题
|
||||
|
||||
### 1、实现监控模版市场商店
|
||||
### 1、实现监控模板市场商店
|
||||
|
||||
**项目难度:进阶/Advanced**
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
对我们想要监控的指标进行采集。一些不同用户对一些中间件的数据的指标需求可能不同,`HertzBeat` 官方自带
|
||||
|
||||
的 yml 配置可能并不能满足每一个用户,所以我们的目的就是去让用户贡献自己的 yml 模版去让更多的人受惠。
|
||||
的 yml 配置可能并不能满足每一个用户,所以我们的目的就是去让用户贡献自己的 yml 模板去让更多的人受惠。
|
||||
|
||||
这样不仅能让 `HertzBeat` 的生态更加完善,也能让用户的体验变的更好!
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
1. 使用Java17, springboot3编写后端代码,Angular(建议)或Vue编写前端代码。
|
||||
2. 实现yml模板文件的搜索、下载、分享功能(无需登录)。
|
||||
3. 模板页面展示下载量、分类、模板描述信息、模版历史版本(可选)。
|
||||
3. 模板页面展示下载量、分类、模板描述信息、模板历史版本(可选)。
|
||||
4. 实现用户个人页面注册、登录(后期),上传模板。
|
||||
|
||||
**产出:**
|
||||
|
||||
@@ -29,7 +29,7 @@ keywords: [open source, monitoring, alerting]
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
|
||||
@@ -28,7 +28,7 @@ keywords: [open source monitoring system, alerting system]
|
||||
|
||||
## 开始贡献
|
||||
|
||||
我的第一个 PR 是修改了一个 PR 模版里的错误路径,社区处理的非常快,所以第一次贡献就好感倍增。
|
||||
我的第一个 PR 是修改了一个 PR 模板里的错误路径,社区处理的非常快,所以第一次贡献就好感倍增。
|
||||
|
||||
于是我便尝试开始阅读 HertzBeat 的一些核心采集代码,发现当时许多逻辑都没有提供单测,所以我就开始补全这些测试。
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
|
||||
3. 数据备份
|
||||
|
||||
> 1. 若使用了自定义监控模版
|
||||
> 1. 若使用了自定义监控模板
|
||||
>
|
||||
> - 需要备份 `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define` 当前运行 pod里面的 `/opt/hertzbeat/define` 目录到当前主机下,如果做了持久化 请拷贝持久化目录
|
||||
> - `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user