Compare commits

..

4 Commits

Author SHA1 Message Date
huacheng
3d65fe3861 feat: Alarm and receiving Chinese and English support #huacheng 2022-04-10 18:22:05 +08:00
huacheng
f3bd9930f1 fix: 企业微信更正名称 #huacheng 2022-04-06 21:21:33 +08:00
huacheng
dc61dd066c fix: msgtype更正名称 #huacheng 2022-04-05 22:21:56 +08:00
huacheng
4ad7972956 fix: 代码名称优化 #huacheng 2022-04-05 22:11:24 +08:00
68 changed files with 250 additions and 1141 deletions

6
.gitignore vendored
View File

@@ -37,10 +37,4 @@ nbdist/
### VS Code ###
.vscode/
# dependencies
node_modules
.docusaurus
yarn.lock
# debug env
application-dev.yml

View File

@@ -35,7 +35,7 @@
----
[![tancloud](tancloud.gif)](https://www.bilibili.com/video/BV1DY4y1i7ts)
[![tancloud](tancloud.gif)](https://www.bilibili.com/video/BV1Vi4y1f7i8)
----
@@ -76,7 +76,7 @@
##### 安装TDengine
1. docker安装TDengine
`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine:2.4.0.12`
`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/tcp -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine:2.4.0.12`
2. 创建名称为hertzbeat的数据库
详细步骤参考 [依赖服务TDengine安装初始化](https://hertzbeat.com/docs/start/tdengine-init)
@@ -93,7 +93,6 @@
1. 下载您系统环境对应的安装包 [GITEE Release](https://gitee.com/dromara/hertzbeat/releases) [GITHUB Release](https://github.com/dromara/hertzbeat/releases)
2. 配置HertzBeat的配置文件 hertzbeat/config/application.yml
3. 部署启动 `$ ./startup.sh `
4. 浏览器访问 localhost:1157 即可开始,默认账号密码 admin/admin
详细步骤参考 [通过安装包安装HertzBeat](https://hertzbeat.com/docs/start/package-deploy)

View File

@@ -110,7 +110,7 @@ public class CalculateAlarm {
} else {
// 其他异常
alertBuilder.target(CommonConstants.AVAILABLE)
.content("监控可用性告警: " + metricsData.getCode().name() + " : " + metricsData.getMsg());
.content("监控紧急可用性告警: " + metricsData.getCode().name());
triggeredMonitorStateAlertMap.put(monitorId, metricsData.getCode());
dataQueue.addAlertData(alertBuilder.build());
}

View File

@@ -7,43 +7,34 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
/**
* AlertDefineBind database operations 数据库操作
*
* AlertDefineBind 数据库操作
* @author tom
* @date 2021/12/9 10:03
*/
public interface AlertDefineBindDao extends JpaRepository<AlertDefineMonitorBind, Long>, JpaSpecificationExecutor<AlertDefineMonitorBind> {
/**
* Delete the alarm definition and monitor association based on the alarm definition ID
* 根据告警定义ID删除告警定义与监控关联
*
* @param alertDefineId Alarm Definition ID 告警定义ID
* @param alertDefineId 告警定义ID
*/
void deleteAlertDefineBindsByAlertDefineIdEquals(Long alertDefineId);
/**
* Deleting alarms based on monitoring IDs defines monitoring associations
* 根据监控ID删除告警定义监控关联
*
* @param monitorId Monitor Id 监控ID
* @param monitorId 监控ID
*/
void deleteAlertDefineMonitorBindsByMonitorIdEquals(Long monitorId);
/**
* Delete alarm definition monitoring association based on monitoring ID list
* 根据监控ID列表删除告警定义监控关联
*
* @param monitorIds Monitoring ID List 监控ID列表
* @param monitorIds 监控ID列表
*/
void deleteAlertDefineMonitorBindsByMonitorIdIn(List<Long> monitorIds);
/**
* Query monitoring related information based on alarm definition ID
* 根据告警定义ID查询监控关联信息
*
* @param alertDefineId Alarm Definition ID 告警定义ID
* @return Associated monitoring information 关联监控信息
* @param alertDefineId 告警定义ID
* @return 关联监控信息
*/
List<AlertDefineMonitorBind> getAlertDefineBindsByAlertDefineIdEquals(Long alertDefineId);
}

View File

@@ -108,8 +108,8 @@ public class CommonHttpClient {
.setConnectTimeout(CONNECT_TIMEOUT)
// 数据传输最大响应间隔时间
.setSocketTimeout(SOCKET_TIMEOUT)
// 遇到301 302自动重定向跳转
.setRedirectsEnabled(true)
// 遇到301 302自动重定向跳转
.setRedirectsEnabled(false)
.build();
// 连接池
connectionManager = new PoolingHttpClientConnectionManager(registry);

View File

@@ -7,7 +7,6 @@ import com.google.gson.JsonParser;
import com.usthe.collector.collect.AbstractCollect;
import com.usthe.collector.collect.common.http.CommonHttpClient;
import com.usthe.collector.dispatch.DispatchConstants;
import com.usthe.collector.util.CollectUtil;
import com.usthe.collector.util.CollectorConstants;
import com.usthe.collector.util.JsonPathParser;
import com.usthe.common.entity.job.Metrics;
@@ -101,7 +100,7 @@ public class HttpCollectImpl extends AbstractCollect {
String parseType = metrics.getHttp().getParseType();
try {
if (DispatchConstants.PARSE_DEFAULT.equals(parseType)) {
parseResponseByDefault(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
parseResponseByDefault(resp, metrics.getAliasFields(), builder, responseTime);
} else if (DispatchConstants.PARSE_JSON_PATH.equals(parseType)) {
parseResponseByJsonPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
} else if (DispatchConstants.PARSE_PROMETHEUS.equals(parseType)) {
@@ -109,11 +108,11 @@ public class HttpCollectImpl extends AbstractCollect {
} else if (DispatchConstants.PARSE_XML_PATH.equals(parseType)) {
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
} else if (DispatchConstants.PARSE_WEBSITE.equals(parseType)){
parseResponseByWebsite(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
parseResponseByWebsite(resp, metrics.getAliasFields(), builder, responseTime);
} else if (DispatchConstants.PARSE_SITE_MAP.equals(parseType)) {
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
} else {
parseResponseByDefault(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
parseResponseByDefault(resp, metrics.getAliasFields(), builder, responseTime);
}
} catch (Exception e) {
log.info("parse error: {}.", e.getMessage(), e);
@@ -170,16 +169,13 @@ public class HttpCollectImpl extends AbstractCollect {
}
}
private void parseResponseByWebsite(String resp, List<String> aliasFields, HttpProtocol http,
private void parseResponseByWebsite(String resp, List<String> aliasFields,
CollectRep.MetricsData.Builder builder, Long responseTime) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
// 网站关键词数量监测
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
// todo resp 网站关键监测
for (String alias : aliasFields) {
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(Integer.toString(keywordNum));
} else {
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
}
@@ -281,7 +277,6 @@ public class HttpCollectImpl extends AbstractCollect {
private void parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
CollectRep.MetricsData.Builder builder, Long responseTime) {
List<Map<String, Object>> results = JsonPathParser.parseContentWithJsonPath(resp, http.getParseScript());
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
for (Map<String, Object> stringMap : results) {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
for (String alias : aliasFields) {
@@ -291,8 +286,6 @@ public class HttpCollectImpl extends AbstractCollect {
} else {
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(Integer.toString(keywordNum));
} else {
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
}
@@ -307,10 +300,9 @@ public class HttpCollectImpl extends AbstractCollect {
}
private void parseResponseByDefault(String resp, List<String> aliasFields, HttpProtocol http,
private void parseResponseByDefault(String resp, List<String> aliasFields,
CollectRep.MetricsData.Builder builder, Long responseTime) {
JsonElement element = JsonParser.parseString(resp);
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
for (JsonElement jsonElement : array) {
@@ -325,8 +317,6 @@ public class HttpCollectImpl extends AbstractCollect {
} else {
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(responseTime.toString());
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
valueRowBuilder.addColumns(Integer.toString(keywordNum));
} else {
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
}

View File

@@ -105,13 +105,9 @@ public class CommonDispatcher implements MetricsTaskDispatch, CollectDataDispatc
.setId(timerJob.getJob().getMonitorId())
.setApp(timerJob.getJob().getApp())
.setMetrics(metricsTime.getMetrics().getName())
.setPriority(metricsTime.getMetrics().getPriority())
.setTime(System.currentTimeMillis())
.setCode(CollectRep.Code.TIMEOUT).setMsg("collect timeout").build();
log.error("[Collect Timeout]: \n{}", metricsData);
if (metricsData.getPriority() == 0) {
dispatchCollectData(metricsTime.timeout, metricsTime.getMetrics(), metricsData);
}
dispatchCollectData(metricsTime.timeout, metricsTime.getMetrics(), metricsData);
metricsTimeoutMonitorMap.remove(entry.getKey());
}
}
@@ -169,8 +165,8 @@ public class CommonDispatcher implements MetricsTaskDispatch, CollectDataDispatc
metricsSet.forEach(metricItem -> {
MetricsCollect metricsCollect = new MetricsCollect(metricItem, timeout, this);
jobRequestQueue.addJob(metricsCollect);
metricsTimeoutMonitorMap.put(job.getId() + "-" + metricItem.getName(),
new MetricsTime(System.currentTimeMillis(), metricItem, timeout));
metricsTimeoutMonitorMap.put(job.getId() + "-" + metrics.getName(),
new MetricsTime(System.currentTimeMillis(), metrics, timeout));
});
} else {
// 当前执行级别的指标组列表未全执行完成,
@@ -189,8 +185,8 @@ public class CommonDispatcher implements MetricsTaskDispatch, CollectDataDispatc
metricsSet.forEach(metricItem -> {
MetricsCollect metricsCollect = new MetricsCollect(metricItem, timeout, this);
jobRequestQueue.addJob(metricsCollect);
metricsTimeoutMonitorMap.put(job.getId() + "-" + metricItem.getName(),
new MetricsTime(System.currentTimeMillis(), metricItem, timeout));
metricsTimeoutMonitorMap.put(job.getId() + "-" + metrics.getName(),
new MetricsTime(System.currentTimeMillis(), metrics, timeout));
});
} else {
// 当前执行级别的指标组列表未全执行完成,

View File

@@ -34,10 +34,6 @@ import java.util.stream.Collectors;
@Slf4j
@Data
public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
/**
* 调度告警阈值时间 100ms
*/
private static final long WARN_DISPATCH_TIME = 100;
/**
* 监控ID
*/
@@ -247,10 +243,6 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
value = aliasFieldValueMap.get(realField);
}
}
// 处理可能带单位的指标数值 比如 34%, 34Mb并将数值小数点限制到4位
if (CommonConstants.TYPE_NUMBER == field.getType()) {
value = CommonUtil.parseDoubleStr(value, field.getUnit());
}
if (value == null) {
value = CommonConstants.NULL_VALUE;
}
@@ -275,15 +267,11 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
private CollectRep.MetricsData validateResponse(CollectRep.MetricsData.Builder builder) {
long endTime = System.currentTimeMillis();
builder.setTime(endTime);
long runningTime = endTime - startTime;
long allTime = endTime - newTime;
if (startTime - newTime >= WARN_DISPATCH_TIME) {
log.warn("[Collector Dispatch Warn, Dispatch Use {}ms.", startTime - newTime);
}
log.debug("[Collect]: newTime: {}, startTime: {}, spendTime: {}.", newTime, startTime, endTime - startTime);
if (builder.getCode() != CollectRep.Code.SUCCESS) {
log.info("[Collect Failed, Run {}ms, All {}ms] Reason: {}", runningTime, allTime, builder.getMsg());
log.info("[Collect Fail] Reason: {}", builder.getMsg());
} else {
log.info("[Collect Success, Run {}ms, All {}ms].", runningTime, allTime);
log.info("[Collect Success].");
}
return builder.build();
}

View File

@@ -15,7 +15,6 @@ import java.util.concurrent.TimeUnit;
/**
* 采集job管理提供api接口
*
* @author tomsun28
* @date 2021/11/6 13:58
*/
@@ -27,11 +26,9 @@ public class CollectJobService {
private TimerDispatch timerDispatch;
/**
* Execute a one-time collection task and get the collected data response
* 执行一次性采集任务,获取采集数据响应
*
* @param job Collect task details 采集任务详情
* @return Collection results 采集结果
* @param job 采集任务详情
* @return 采集结果
*/
public List<CollectRep.MetricsData> collectSyncJobData(Job job) {
final List<CollectRep.MetricsData> metricsData = new LinkedList<>();
@@ -55,11 +52,9 @@ public class CollectJobService {
}
/**
* Issue periodic asynchronous collection tasks
* 下发周期性异步采集任务
*
* @param job Collect task details 采集任务详情
* @return long Job ID 任务ID
* @param job 采集任务详情
* @return long 任务ID
*/
public long addAsyncCollectJob(Job job) {
if (job.getId() == 0L) {
@@ -71,10 +66,8 @@ public class CollectJobService {
}
/**
* Update the periodic asynchronous collection tasks that have been delivered
* 更新已经下发的周期性异步采集任务
*
* @param modifyJob Collect task details 采集任务详情
* @param modifyJob 采集任务详情
*/
public void updateAsyncCollectJob(Job modifyJob) {
timerDispatch.deleteJob(modifyJob.getId(), true);
@@ -82,10 +75,8 @@ public class CollectJobService {
}
/**
* Cancel periodic asynchronous collection tasks
* 取消周期性异步采集任务
*
* @param jobId Job ID 任务ID
* @param jobId 任务ID
*/
public void cancelAsyncCollectJob(Long jobId) {
timerDispatch.deleteJob(jobId, true);

View File

@@ -10,44 +10,36 @@ import java.util.concurrent.TimeUnit;
/**
* 时间轮调度接口
*
* @author tomsun28
* @date 2021/10/17 22:14
*/
public interface TimerDispatch {
/**
* Add new job
* 增加新的job
*
* @param addJob job
* @param eventListener One-time synchronous task listener, asynchronous task does not need listener一次性同步任务监听器异步任务不需要listener
* @param addJob job
* @param eventListener 一次性同步任务监听器异步任务不需要listener
*/
void addJob(Job addJob, CollectResponseEventListener eventListener);
/**
* 调度循环周期性job
*
* @param timerTask timerTask
* @param interval 开始调度的间隔时间
* @param timeUnit 时间单位
* @param interval 开始调度的间隔时间
* @param timeUnit 时间单位
*/
void cyclicJob(WheelTimerTask timerTask, long interval, TimeUnit timeUnit);
/**
* Delete existing job
* 删除存在的job
*
* @param jobId jobId
* @param isCyclic Whether it is a periodic task, true is, false is a temporary task
* 是否是周期性任务,true是, false为临时性任务
* @param jobId jobId
* @param isCyclic 是否是周期性任务,true是, false为临时性任务
*/
void deleteJob(long jobId, boolean isCyclic);
/**
* 一次性同步采集任务采集结果通知监听器
*
* @param jobId jobId
* @param jobId jobId
* @param metricsDataTemps 采集结果数据
*/
void responseSyncJobData(long jobId, List<CollectRep.MetricsData> metricsDataTemps);

View File

@@ -18,22 +18,18 @@ import java.util.concurrent.TimeUnit;
public class TimerDispatcher implements TimerDispatch {
/**
* time round schedule
* 时间轮调度
*/
private Timer wheelTimer;
/**
* Existing periodic scheduled tasks
* 已存在的周期性调度任务
*/
private Map<Long, Timeout> currentCyclicTaskMap;
/**
* Existing temporary scheduled tasks
* 已存在的临时性调度任务
*/
private Map<Long, Timeout> currentTempTaskMap;
/**
* One-time task response listener holds
* 一次性任务响应监听器持有
* jobId - listener
*/

View File

@@ -22,9 +22,7 @@ import java.util.Map;
import java.util.stream.Collectors;
/**
* Timer Task implementation
* TimerTask实现
*
* @author tomsun28
* @date 2021/11/1 17:18
*/
@@ -38,15 +36,12 @@ public class WheelTimerTask implements TimerTask {
public WheelTimerTask(Job job) {
this.metricsTaskDispatch = SpringContextHolder.getBean(MetricsTaskDispatch.class);
this.job = job;
// The initialization job will monitor the actual parameter value and replace the collection field
// 初始化job 将监控实际参数值对采集字段进行替换
initJobMetrics(job);
}
/**
* Initialize job fill information
* 初始化job填充信息
*
* @param job job
*/
private void initJobMetrics(Job job) {
@@ -78,10 +73,9 @@ public class WheelTimerTask implements TimerTask {
}
/**
* json parameter replacement json参数替换
*
* json参数替换
* @param jsonElement json
* @param configmap parameter map 参数map
* @param configmap 参数map
* @return json
*/
private JsonElement replaceSpecialValue(JsonElement jsonElement, Map<String, Configmap> configmap) {
@@ -92,29 +86,26 @@ public class WheelTimerTask implements TimerTask {
Map.Entry<String, JsonElement> entry = iterator.next();
JsonElement element = entry.getValue();
String key = entry.getKey();
// Replace the attributes of the KEY-VALUE case such as http headers params
// 替换KEY-VALUE情况的属性 比如http headers params
if (key != null && key.startsWith("^_^") && key.endsWith("^_^")) {
key = key.replaceAll("\\^_\\^", "");
Configmap param = configmap.get(key);
if (param != null && param.getType() == (byte) 3) {
String jsonValue = (String) param.getValue();
Map<String, String> map = GsonUtil.fromJson(jsonValue, Map.class);
if (map != null) {
map.forEach((name, value) -> {
if (name != null && !"".equals(name.trim())) {
jsonObject.addProperty(name, value);
}
});
}
Map<String, String> map = GsonUtil.fromJson(jsonValue, Map.class);
if (map != null) {
map.forEach((name, value) -> {
if (name != null && !"".equals(name.trim())) {
jsonObject.addProperty(name, value);
}
});
}
}
iterator.remove();
continue;
}
// Replace normal VALUE value
// 替换正常的VALUE值
if (element.isJsonPrimitive()) {
// Check if there are special characters Replace
// 判断是否含有特殊字符 替换
String value = element.getAsString();
if (value.startsWith("^_^") && value.endsWith("^_^")) {
@@ -138,7 +129,6 @@ public class WheelTimerTask implements TimerTask {
while (iterator.hasNext()) {
JsonElement element = iterator.next();
if (element.isJsonPrimitive()) {
// Check if there are special characters Replace
// 判断是否含有特殊字符 替换
String value = element.getAsString();
if (value.startsWith("^_^") && value.endsWith("^_^")) {

View File

@@ -1,35 +0,0 @@
package com.usthe.collector.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 采集器工具类
* @author tom
* @date 2022/4/6 09:35
*/
public class CollectUtil {
/**
* 关键字匹配计数
* @param content 内容
* @param keyword 关键字
* @return 匹配次数
*/
public static int countMatchKeyword(String content, String keyword) {
if (content == null || "".equals(content) || keyword == null || "".equals(keyword.trim())) {
return 0;
}
try {
Pattern pattern = Pattern.compile(keyword);
Matcher matcher = pattern.matcher(content);
int count = 0;
while (matcher.find()) {
count++;
}
return count;
} catch (Exception e) {
return 0;
}
}
}

View File

@@ -9,8 +9,6 @@ public interface CollectorConstants {
String RESPONSE_TIME = "responseTime";
String KEYWORD = "keyword";
String STATUS_CODE = "statusCode";
String ERROR_MSG = "errorMsg";

View File

@@ -4,7 +4,6 @@ import org.apache.commons.net.telnet.TelnetClient;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.ConnectException;
import static org.junit.jupiter.api.Assertions.*;
@@ -21,7 +20,7 @@ class TelnetCollectImplTest {
telnetClient = new TelnetClient("vt200");
telnetClient.setConnectTimeout(5000);
TelnetClient finalTelnetClient = telnetClient;
assertThrows(ConnectException.class,() -> finalTelnetClient.connect("127.0.0.1",0));
assertDoesNotThrow(() -> finalTelnetClient.connect("baidu.com",80));
telnetClient.disconnect();
} catch (IOException e) {
e.printStackTrace();

View File

@@ -6,12 +6,8 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Monitoring configuration parameter properties and values
* 监控配置参数属性及值
* During the process, you need to replace the content with the identifier ^_^key^_^
* in the protocol configuration parameter with the real value in the configuration parameter
* 过程中需要将协议配置参数里面的标识符为^_^key^_^的内容替换为配置参数里的真实值
*
* @author tomsun28
* @date 2021/10/29 22:04
*/
@@ -22,20 +18,16 @@ import lombok.NoArgsConstructor;
public class Configmap {
/**
* Parameter key, replace the content with the identifier ^^_key_^^ in the protocol
* configuration parameter with the real value in the configuration parameter
* <p>
* 参数key,将协议配置参数里面的标识符为^^_key_^^的内容替换为配置参数里的真实值
*/
private String key;
/**
* parameter value 参数value
* 参数value
*/
private Object value;
/**
* Parameter type 0: number 1: string 2: encrypted string 3: json string mapped by map
* 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
* number,string,secret
* 数字,非加密字符串,加密字符串

View File

@@ -21,9 +21,7 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
* Collect task details
* 采集任务详情
*
* @author tomsun28
* @date 2021/10/17 21:19
*/
@@ -37,72 +35,61 @@ public class Job {
private static final String AVAILABILITY = "availability";
/**
* Task id 任务ID
* 任务ID
*/
private long id;
/**
* Monitoring ID Application ID
* 监控ID 应用ID
*/
private long monitorId;
/**
* Large categories of monitoring 监控的大类别
* service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
* 监控的大类别
* service-应用服务监控 db-数据库监控 custom-自定义监控 os-操作系统监控
*/
private String category;
/**
* Type of monitoring eg: linux | mysql | jvm
* 监控的类型 eg: linux | mysql | jvm
*/
private String app;
/**
* The internationalized name of the monitoring type 监控类型的国际化名称
* 监控类型的国际化名称
* zh-CN: PING连通性
* en-US: PING CONNECT
*/
private Map<String, String> name;
/**
* Task dispatch start timestamp
* 任务派发开始时间戳
*/
private long timestamp;
/**
* Task collection time interval (unit: second) eg: 30,60,600
* 任务采集时间间隔(单位秒) eg: 30,60,600
*/
private long interval = 600L;
/**
* Whether it is a recurring periodic task true is yes, false is no
* 是否是循环周期性任务 true为是,false为否
*/
private boolean isCyclic = false;
/**
* Indicator group configuration eg: cpu memory
* 指标组配置 eg: cpu memory
*/
private List<Metrics> metrics;
/**
* Monitoring configuration parameter properties and values eg: username password timeout host
* 监控配置参数属性及值 eg: username password timeout host
*/
private List<Configmap> configmap;
/**
* collector use - timestamp when the task was scheduled by the time wheel
* collector使用 - 任务被时间轮开始调度的时间戳
*/
@JsonIgnore
private transient long dispatchTime;
/**
* collector use - task version, this field is not stored in etcd
* collector使用 - 任务版本,此字段不存储于etcd
*/
@JsonIgnore
private transient long version;
/**
* collector usage - metric group task execution priority view
* collector使用 - 指标组任务执行优先级视图
* 0 - availability
* 1 - cpu | memory
@@ -116,32 +103,27 @@ public class Job {
private transient List<Set<Metrics>> priorMetrics;
/**
* collector use - Temporarily store one-time task indicator group response data
* collector使用 - 临时存储一次性任务指标组响应数据
*/
@JsonIgnore
private transient List<CollectRep.MetricsData> responseDataTemp;
/**
* collector uses - construct to initialize metrics group execution view
* collector使用 - 构造初始化指标组执行视图
*/
public synchronized void constructPriorMetrics() {
Map<Byte, List<Metrics>> map = metrics.stream()
.peek(metric -> {
// Determine whether to configure aliasFields If not, configure the default
// 判断是否配置aliasFields 没有则配置默认
if (metric.getAliasFields() == null || metric.getAliasFields().isEmpty()) {
metric.setAliasFields(metric.getFields().stream().map(Metrics.Field::getField).collect(Collectors.toList()));
}
// Set the default indicator group execution priority, if not filled, the default last priority
// 设置默认的指标组执行优先级,不填则默认最后优先级
if (metric.getPriority() == null) {
metric.setPriority(Byte.MAX_VALUE);
}
})
.collect(Collectors.groupingBy(Metrics::getPriority));
// Construct a linked list of task execution order of the indicator group
// 构造指标组任务执行顺序链表
priorMetrics = new LinkedList<>();
map.values().forEach(metric -> {
@@ -159,18 +141,12 @@ public class Job {
}
/**
* collector use - to get the next set of priority metric group tasks
* collector使用 - 获取下一组优先级的指标组任务
*
* @param metrics Current indicator group 当前指标组
* @param first Is it the first time to get 是否是第一次获取
* @return Metric Group Tasks 指标组任务
* Returning null means: the job has been completed, and the collection of all indicator groups has ended
* @param metrics 当前指标组
* @param first 是否是第一次获取
* @return 指标组任务
* 返回null表示job已完成,所有指标组采集结束
* Returning the empty set indicates that there are still indicator group collection tasks at the current
* level that have not been completed,and the next level indicator group task collection cannot be performed.
* 返回empty的集合表示当前级别下还有指标组采集任务未结束,无法进行下一级别的指标组任务采集
* Returns a set of data representation: get the next set of priority index group tasks
* 返回有数据集合表示:获取到下一组优先级的指标组任务
*/
public synchronized Set<Metrics> getNextCollectMetrics(Metrics metrics, boolean first) {
@@ -213,7 +189,7 @@ public class Job {
@Override
public Job clone() {
// deep clone 深度克隆
// 深度克隆
return GsonUtil.fromJson(GsonUtil.toJson(this), Job.class);
}
}

View File

@@ -15,10 +15,7 @@ import java.util.List;
import java.util.Objects;
/**
* Details of the collection of indicators collected by monitoring
* eg: cpu | memory | health
* 监控采集的指标集合详情 eg: cpu | memory | health
*
* @author tomsun28
* @date 2021/10/17 21:24
*/
@@ -29,7 +26,6 @@ import java.util.Objects;
public class Metrics {
/**
* public property-name eg: cpu | memory | health
* 公共属性-名称 eg: cpu | memory | health
*/
private String name;
@@ -38,27 +34,20 @@ public class Metrics {
*/
private String protocol;
/**
* Range (0-127) indicator group scheduling priority, the smaller the value, the higher the priority
* The collection task of the next priority indicator group will be scheduled only after the scheduled collection with the higher priority is completed.
* The default priority of the availability indicator group is 0, and the range of other common indicator groups is 1-127, that is,
* the subsequent indicator group tasks will only be scheduled after the availability is collected successfully.
* 范围(0-127)指标组调度优先级,数值越小优先级越高
* 优先级高的调度采集完成后才会调度下一优先级的指标组采集任务
* 可用性指标组(availability)默认优先级为0,其它普通指标组范围为1-127,即需要等availability采集成功后才会调度后面的指标组任务
*/
private Byte priority;
/**
* Public attribute - collection and monitoring final result attribute set eg: speed | times | size
* 公共属性-采集监控的最终结果属性集合 eg: speed | times | size
*/
private List<Field> fields;
/**
* Public attribute - collection and monitoring pre-query attribute set eg: size1 | size2 | speedSize
* 公共属性-采集监控的前置查询属性集合 eg: size1 | size2 | speedSize
*/
private List<String> aliasFields;
/**
* Public attribute - expression calculation, map the pre-query attribute (pre Fields) with the final attribute (fields), and calculate the final attribute (fields) value
* 公共属性-表达式计算,将前置查询属性(preFields)与最终属性(fields)映射,计算出最终属性(fields)值
* eg: size = size1 + size2, speed = speedSize
* https://www.yuque.com/boyan-avfmj/aviatorscript/ban32m
@@ -66,32 +55,26 @@ public class Metrics {
private List<String> calculates;
/**
* Monitoring configuration information using the http protocol
* 使用http协议的监控配置信息
*/
private HttpProtocol http;
/**
* Monitoring configuration information for ping using the icmp protocol
* 使用icmp协议进行ping的监控配置信息
*/
private IcmpProtocol icmp;
/**
* Monitoring configuration information using the telnet protocol
* 使用telnet协议的监控配置信息
*/
private TelnetProtocol telnet;
/**
* Use tcp or ucp implemented by socket for service port detection configuration information
* 使用socket实现的tcp或ucp进行服务端口探测配置信息
*/
private TcpUdpProtocol tcpUdp;
/**
* Database configuration information implemented using the public jdbc specification
* 使用公共的jdbc规范实现的数据库配置信息
*/
private JdbcProtocol jdbc;
/**
* Monitoring configuration information using the public ssh protocol
* 使用公共的ssh协议的监控配置信息
*/
private SshProtocol ssh;
@@ -118,22 +101,18 @@ public class Metrics {
@NoArgsConstructor
public static class Field {
/**
* Indicator name
* 指标名称
*/
private String field;
/**
* Indicator type 0-number: number 1-string: string
* 指标类型 0-number:数字 1-string:字符串
*/
private byte type = 1;
/**
* Whether this field is the instance primary key
* 此字段是否为实例主键
*/
private boolean instance = false;
/**
* Indicator unit
* 指标单位
*/
private String unit;

View File

@@ -49,6 +49,7 @@ public class HttpProtocol {
* http请求携带的请求体
*/
private String payload;
/**
* 认证信息
*/
@@ -65,10 +66,6 @@ public class HttpProtocol {
* 数据解析脚本 当解析方式为 jsonPath or xmlPath时存在
*/
private String parseScript;
/**
* 内容关键字
*/
private String keyword;
/**
* 认证信息

View File

@@ -22,9 +22,7 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/**
* Monitor Entity
* 监控实体
*
* @author tomsun28
* @date 2021/11/14 9:53
*/
@@ -34,11 +32,10 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "en: Monitor Entity,zh: 监控实体")
@ApiModel(description = "监控实体")
public class Monitor {
/**
* Monitor ID
* 主键ID
*/
@Id
@@ -46,14 +43,12 @@ public class Monitor {
private Long id;
/**
* Job ID
* 监控对应下发的任务ID
*/
@ApiModelProperty(value = "任务ID", example = "43243543543", accessMode = READ_ONLY, position = 1)
private Long jobId;
/**
* Monitor Name
* 监控的名称
*/
@ApiModelProperty(value = "监控名称", example = "Api-TanCloud.cn", accessMode = READ_WRITE, position = 2)
@@ -61,7 +56,6 @@ public class Monitor {
private String name;
/**
* Type of monitoring: linux, mysql, jvm...
* 监控的类型:linux,mysql,jvm...
*/
@ApiModelProperty(value = "监控类型", example = "TanCloud", accessMode = READ_WRITE, position = 3)
@@ -69,7 +63,6 @@ public class Monitor {
private String app;
/**
* Monitored peer host: ipv4, ipv6, domain name
* 监控的对端host:ipv4,ipv6,域名
*/
@ApiModelProperty(value = "监控的对端host", example = "192.167.25.11", accessMode = READ_WRITE, position = 4)
@@ -78,7 +71,6 @@ public class Monitor {
private String host;
/**
* Monitoring collection interval time, in seconds
* 监控的采集间隔时间,单位秒
*/
@ApiModelProperty(value = "监控的采集间隔时间,单位秒", example = "600", accessMode = READ_WRITE, position = 5)
@@ -86,7 +78,6 @@ public class Monitor {
private Integer intervals;
/**
* Monitoring status 0: Unmonitored, 1: Available, 2: Unavailable, 3: Unreachable, 4: Suspended
* 监控状态 0:未监控,1:可用,2:不可用,3:不可达,4:挂起
*/
@ApiModelProperty(value = "监控状态 0:未监控,1:可用,2:不可用,3:不可达,4:挂起", accessMode = READ_WRITE, position = 6)
@@ -95,7 +86,6 @@ public class Monitor {
private byte status;
/**
* Monitoring note description
* 监控备注描述
*/
@ApiModelProperty(value = "监控备注描述", example = "对SAAS网站TanCloud的可用性监控", accessMode = READ_WRITE, position = 7)
@@ -103,21 +93,18 @@ public class Monitor {
private String description;
/**
* The creator of this record
* 此条记录创建者
*/
@ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 8)
private String creator;
/**
* This record was last modified by
* 此条记录最新修改者
*/
@ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 9)
private String modifier;
/**
* record creation time (millisecond timestamp)
* 记录创建时间
*/
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 10)
@@ -125,7 +112,6 @@ public class Monitor {
private LocalDateTime gmtCreate;
/**
* Record the latest modification time (timestamp in milliseconds)
* 记录最新修改时间
*/
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 11)

View File

@@ -23,9 +23,7 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/**
* Monitor parameter values
* 监控参数值
*
* @author tomsun28
* @date 2021/11/13 22:19
*/
@@ -35,26 +33,21 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "嗯: Parameter Entity,zh: 参数实体")
@ApiModel(description = "参数实体")
public class Param {
/**
* Parameter primary key index ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "参数主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0)
private Long id;
/**
* Monitor ID
* 监控ID
*/
@ApiModelProperty(value = "监控ID", example = "875846754543", accessMode = READ_WRITE, position = 1)
private Long monitorId;
/**
* Parameter Field Identifier
* 参数字段标识符
*/
@ApiModelProperty(value = "参数标识符字段", example = "port", accessMode = READ_WRITE, position = 2)
@@ -63,7 +56,6 @@ public class Param {
private String field;
/**
* Param Value
* 参数值
*/
@ApiModelProperty(value = "参数值", example = "8080", accessMode = READ_WRITE, position = 3)
@@ -71,7 +63,6 @@ public class Param {
private String value;
/**
* Parameter type 0: number 1: string 2: encrypted string 3: json string mapped by map
* 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
*/
@ApiModelProperty(value = "参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串", accessMode = READ_WRITE, position = 4)
@@ -80,7 +71,6 @@ public class Param {
private byte type;
/**
* Record Creation Time
* 记录创建时间
*/
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 5)
@@ -88,7 +78,6 @@ public class Param {
private LocalDateTime gmtCreate;
/**
* Record the latest modification time
* 记录最新修改时间
*/
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 6)

View File

@@ -122,12 +122,6 @@ public class ParamDefine {
@ApiModelProperty(value = "当type为key-value时有效,表示value的别名描述", example = "Value", accessMode = READ_WRITE, position = 10)
private String valueAlias;
/**
* 是否是高级隐藏参数 true-是 false-否
*/
@ApiModelProperty(value = "是否是高级隐藏参数 true-是 false-否", example = "true", accessMode = READ_WRITE, position = 11)
private boolean hide = false;
/**
* 此条记录创建者
*/

View File

@@ -1,208 +1,175 @@
package com.usthe.common.util;
/**
* Public Constant
* 公共常量
*
* @author tomsun28
* @date 2021/11/14 12:06
*/
public interface CommonConstants {
/**
* Response status code: generic success
* 响应状态码: 通用成功
*/
byte SUCCESS_CODE = 0x00;
/**
* Response status code: generic failure
* 响应状态码: 通用失败
*/
byte FAIL_CODE = 0x0F;
/**
* Response status code: Parameter verification failed
* 响应状态码: 参数校验失败
*/
byte PARAM_INVALID_CODE = 0x01;
/**
* Response Status Code: Probe Failed
* 响应状态码: 探测失败
*/
byte DETECT_FAILED_CODE = 0x02;
/**
* Response status code: monitoring does not exist
* 响应状态码: 监控不存在
*/
byte MONITOR_NOT_EXIST_CODE = 0x03;
/**
* Response Status Code: Monitor Service Conflict
* 响应状态码: 监控服务冲突
*/
byte MONITOR_CONFLICT_CODE = 0x04;
/**
* Response status code: Incorrect login account password
* 响应状态码: 登录账户密码错误
*/
byte MONITOR_LOGIN_FAILED_CODE = 0x05;
/**
* Response status code: Registration failed exception
* 响应状态码: 注册失败异常
*/
byte MONITOR_REGISTER_FAILED_CODE = 0x06;
/**
* Monitoring Status Code: Unmanaged
* 监控状态码: 未管理
*/
byte UN_MANAGE_CODE = 0x00;
/**
* Monitoring Status Code: Available
* 监控状态码: 可用
*/
byte AVAILABLE_CODE = 0x01;
/**
* Monitoring Status Code: Not Available
* 监控状态码: 不可用
*/
byte UN_AVAILABLE_CODE = 0x02;
/**
* Monitoring Status Code: Unreachable
* 监控状态码: 不可达
*/
byte UN_REACHABLE_CODE = 0x03;
/**
* Monitoring Status Code: Pending
* 监控状态码: 挂起
*/
byte SUSPENDING_CODE = 0x04;
/**
* Alarm status: 0 - normal alarm (to be processed)
* 告警状态: 0-正常告警(待处理)
*/
byte ALERT_STATUS_CODE_PENDING = 0x00;
/**
* Alarm Status: 1 - Threshold triggered but not reached the number of alarms
* 告警状态: 1-阈值触发但未达到告警次数
*/
byte ALERT_STATUS_CODE_NOT_REACH = 0x01;
/**
* Alarm Status: 2-Restore Alarm
* 告警状态: 2-恢复告警
*/
byte ALERT_STATUS_CODE_RESTORED = 0x02;
/**
* Alert Status: 3-Handled
* 告警状态: 3-已处理
*/
byte ALERT_STATUS_CODE_SOLVED = 0x03;
/**
* Alarm level: 0: high-emergency-emergency-red
* 告警级别: 0:高-emergency-紧急告警-红色
*/
byte ALERT_PRIORITY_CODE_EMERGENCY = 0x00;
/**
* Alarm severity: 1: medium-critical-critical alarm-orange
* 告警级别: 1:中-critical-严重告警-橙色
*/
byte ALERT_PRIORITY_CODE_CRITICAL = 0x01;
/**
* Warning level: 2: low-warning-warning warning-yellow
* 告警级别: 2:低-warning-警告告警-黄色
*/
byte ALERT_PRIORITY_CODE_WARNING = 0x02;
/**
* Field parameter type: number
* 字段参数类型: 数字
*/
byte TYPE_NUMBER = 0;
/**
* Field parameter type: String
* 字段参数类型: 字符串
*/
byte TYPE_STRING = 1;
/**
* Field parameter type: encrypted string
* 字段参数类型: 加密字符串
*/
byte TYPE_SECRET = 2;
/**
* Collection indicator value: null placeholder for empty value
* 采集指标值null空值占位符
*/
String NULL_VALUE = "&nbsp;";
/**
* Availability Object
* 可用性对象
*/
String AVAILABLE = "available";
/**
* Reachability Object可达性对象
* 可达性对象
*/
String REACHABLE = "reachable";
/**
* Parameter Type Number
* 参数类型 数字
*/
byte PARAM_TYPE_NUMBER = 0;
/**
* Parameter Type String
* 参数类型 字符串
*/
byte PARAM_TYPE_STRING = 1;
/**
* Parameter Type Password
* 参数类型 密码
*/
byte PARAM_TYPE_PASSWORD = 2;
/**
* Authentication type Account password
* 认证类型 账户密码
*/
byte AUTH_TYPE_PASSWORD = 1;
/**
* Authentication type GITHUB three-party login
* 认证类型 GITHUB三方登录
*/
byte AUTH_TYPE_GITHUB = 2;
/**
* Authentication type WeChat three-party login
* 认证类型 微信三方登录
*/
byte AUTH_TYPE_WEIXIN = 3;
/**
* Authentication type GITEE three-party login
* 认证类型 GITEE三方登录
*/
byte AUTH_TYPE_GITEE = 5;

View File

@@ -2,8 +2,6 @@ package com.usthe.common.util;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -17,7 +15,7 @@ public class CommonUtil {
private static final Pattern EMAIL_PATTERN = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");
private static final Pattern PHONE_PATTERN = Pattern.compile("^(((13[0-9])|(14[0-9])|(15[0-9])|(16[0-9])|(19[0-9])|(18[0-9])|(17[0-9]))+\\d{8})?$");
private static final Pattern PHONE_PATTERN = Pattern.compile("^(((13[0-9])|(15[0-9])|(18[0-9])|(17[0-9]))+\\d{8})?$");
private static final int PHONE_LENGTH = 11;
@@ -38,30 +36,6 @@ public class CommonUtil {
}
}
/**
* 将字符串str,此字符串可能带单位,转换为double数字类型
* 将数值小数点限制到4位
* @param str string
* @param unit 字符串单位
* @return string格式的 double 数字 小数点最大到4位
*/
public static String parseDoubleStr(String str, String unit) {
if (str == null || "".equals(str)) {
return null;
}
try {
if (unit != null && str.endsWith(unit)) {
str = str.substring(0, str.length() - unit.length());
}
BigDecimal bigDecimal = new BigDecimal(str);
double value = bigDecimal.setScale(4, RoundingMode.HALF_UP).doubleValue();
return String.valueOf(value);
} catch (Exception e) {
log.debug(e.getMessage(), e);
return null;
}
}
/**
* 邮箱格式校验
* @param email 邮箱

View File

@@ -1,9 +1,7 @@
package com.usthe.common.util;
/**
* Snowflake Algorithm Generator Tool
* 雪花算法生成器工具
*
* @author tomsun28
* @date 2021/11/10 11:04
*/

View File

@@ -1,40 +0,0 @@
package com.usthe.common.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author tom
* @date 2022/4/7 17:18
*/
class CommonUtilTest {
@Test
void testParseDoubleStr() {
assertEquals("9.3454",CommonUtil.parseDoubleStr("9.345435345", null));
assertEquals("9.3454",CommonUtil.parseDoubleStr("9.345435345%", "%"));
assertEquals("10.0",CommonUtil.parseDoubleStr("10%", "%"));
assertEquals("588.0",CommonUtil.parseDoubleStr("588Mb", "Mb"));
}
@Test
void validateEmail() {
assertTrue(CommonUtil.validateEmail("tom@usthe.com"));
assertTrue(CommonUtil.validateEmail("demo@qq.com"));
assertFalse(CommonUtil.validateEmail("tom.usthe.com"));
}
@Test
void validatePhoneNum() {
assertTrue(CommonUtil.validatePhoneNum("19234554432"));
assertTrue(CommonUtil.validatePhoneNum("13234554432"));
assertTrue(CommonUtil.validatePhoneNum("14234554432"));
assertTrue(CommonUtil.validatePhoneNum("16234554432"));
assertFalse(CommonUtil.validatePhoneNum("12234554432"));
assertFalse(CommonUtil.validatePhoneNum("11234554432"));
assertFalse(CommonUtil.validatePhoneNum("35234554432"));
assertFalse(CommonUtil.validatePhoneNum("46234554432"));
}
}

View File

@@ -1,92 +0,0 @@
---
title: HertzBeat入GVP啦并 v1.0.beta.7 发布,易用友好的云监控系统
author: tom
author_title: tom
author_url: https://github.com/tomsun28
author_image_url: https://avatars.githubusercontent.com/u/24788200?s=400&v=4
tags: [opensource]
---
HertzBeat赫兹跳动 是一个由Dromara孵化的支持网站APIPING端口数据库全站操作系统等监控类型支持阈值告警告警通知(邮箱webhook钉钉企业微信飞书机器人),拥有易用友好的可视化操作界面的开源监控告警项目。
很高兴Hertzbeat被评定为GVP - Gitee最有价值开源项目
![截屏2022-04-08 09.14.44.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8899bc4e836943dba2ec9efeec4ff629~tplv-k3u1fbpfcp-watermark.image?)
老哥们帮忙在Gitee STAR起来https://gitee.com/dromara/hertzbeat
官网:hertzbeat.com | tancloud.cn
然后来说说最新的版本这个版本看这么多feature其实简单来说主要是这几个
支持了ORACLE数据库的监控包括ORACLE的基本信息表空间连接数TPSQPS等指标
支持了LINUX的CPU利用率内存利用率磁盘占用相关指标使LINUX监控贴合实际业务
还有前端参数支持了KEY-VALUE以后我们就可以在页面上配置HTTP Headers等类似参数了还有就是参数配置那优化改版把非常用告警参数隐藏起来了稍微好看些然后支持了windows下bat启动脚本更多的就是稳定性的提升和一些其它的小修复小需求啦
版本特性:
1. feature 支持oracle数据库监控类型-xgf 由 @gf-8 贡献 thanks
2. feature oracle监控支持tablespace,连接数,qps,tps等指标
3. feature linux监控支持设置超时时间 (#49)
4. feature 检测网站SSL证书是否过期 (#50) 由 @weihongbin 提出 thanks
5. feature 页面配置参数支持KEY-VALUE数组(#57)
6. feature API和网站监控支持页面配置Headers和Params (#58)(#59)
7. feature API和网站监控支持页面配置 basic auth, digest auth (#60)
8. feature http 端口跟随SSL是否启用变更443或80 (#61)
9. feature 修改默认超时时间3000毫秒为6000毫秒 (#55)
10. feature:make tdengine optional, not required (#62)
11. feature:support win bat service (#65)
12. feature:support hide advanced params define (#68)
13. feature:enable auto redirect when 301 302 http code (#69)
14. feature:only collect available metrics when detect (#70)
15. feature:[website api]monitor support keyword match (#72)
16. feature:support linux cpu usage,memory usage,disk free (#76)
BUG修复
1. 添加sqlserver关联文档fix connection指标入库tdengine失败 (#41)
2. 使用docker部署TDengine开放tcp访问端口!16 由 @老姜bei 贡献 thanks
3. 补充sureness配置文档 避免误配导致权限异常
4. bugfix:monitors always timeout alert (#67)
5. code format and optimization 由 @学习代码的小白 贡献 thanks
6. bugfix: remove oracle field - database_type due 11g not support 由 @syongaaa 贡献 thanks
7. bugfix:fix linux interface metrics no instance (#75)
欢迎在线试用 https://console.tancloud.cn.
-----------------------
> [HertzBeat赫兹跳动](https://github.com/dromara/hertzbeat) 是一个支持网站APIPING端口数据库操作系统等监控类型拥有易用友好的可视化操作界面的开源监控告警项目。
> 我们也提供了对应的 **[SAAS版本监控云](https://console.tancloud.cn)**,中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,**[登录即可免费开始](https://console.tancloud.cn)**。
> HertzBeat 支持[自定义监控](https://hertzbeat.com/docs/advanced/extend-point) ,只用通过配置YML文件我们就可以自定义需要的监控类型和指标来满足常见的个性化需求。
> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。
> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版,邮件钉钉微信飞书等及时通知送达
> 欢迎登录 HertzBeat 的 [云环境TanCloud](https://console.tancloud.cn) 试用发现更多。
> 我们正在快速迭代中,欢迎参与加入一起共建项目开源生态。
> `HertzBeat`的多类型支持,易扩展,低耦合,希望能帮助开发者和中小团队快速搭建自有监控系统。
老铁们可以通过演示视频来直观了解功能: https://www.bilibili.com/video/BV1DY4y1i7ts
欢迎在线试用 [https://console.tancloud.cn](https://gitee.com/link?target=https%3A%2F%2Fconsole.tancloud.cn)
优化后的参数输入界面:
![输入图片说明](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4b07908ba5a4b50a094a02dde6a38f3~tplv-k3u1fbpfcp-zoom-1.image "截屏2022-04-07 21.32.52.png")
Linux新增指标:
![输入图片说明](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/92828224f8cd4cac84245aa4217b29e7~tplv-k3u1fbpfcp-zoom-1.image "截屏2022-04-07 17.50.22.png")
ORACLE监控:
哦豁oracle环境不在了之前没有截图先脑补一张
**仓库地址**
[Github](https://github.com/dromara/hertzbeat) https://github.com/dromara/hertzbeat
[Gitee](https://gitee.com/dromara/hertzbeat) https://gitee.com/dromara/hertzbeat
看到这里不妨给个Star支持下哦灰常感谢弯腰!!

View File

@@ -38,7 +38,6 @@ sidebar_label: Linux操作系统
| interrupt | 个数 | CPU中断数量 |
| load | 无 | CPU最近1/5/15分钟的平均负载 |
| context_switch | 个数 | 当前上下文切换数量 |
| usage | % | CPU使用率 |
#### 指标集合memory
@@ -50,7 +49,6 @@ sidebar_label: Linux操作系统
| free | Mb | 空闲内存容量 |
| buff_cache | Mb | 缓存占用内存 |
| available | Mb | 剩余可用内存容 |
| usage | % | 内存使用率 |
#### 指标集合disk
@@ -70,12 +68,3 @@ sidebar_label: Linux操作系统
| receive_bytes | byte | 入站数据流量(bytes) |
| transmit_bytes | byte | 出站数据流量(bytes) |
#### 指标集合disk_free
| 指标名称 | 指标单位 | 指标帮助描述 |
| ----------- | ----------- | ----------- |
| filesystem | 无 | 文件系统的名称 |
| used | Mb | 已使用磁盘大小 |
| available | Mb | 可用磁盘大小 |
| usage | % | 使用率 |
| mounted | 无 | 挂载点目录 |

View File

@@ -18,11 +18,10 @@ TDengine是一款国产的开源物联网时序型数据库我们使用其替
```
2. Docker安装TDengine
```
$ docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp -v /opt/taosdata:/var/lib/taos --name tdengine -e TZ=Asia/Shanghai tdengine/tdengine:2.4.0.12
$ docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp -v /opt/taosdata:/var/lib/taos --name tdengine tdengine/tdengine:2.4.0.12
526aa188da767ae94b244226a2b2eec2b5f17dd8eff594533d9ec0cd0f3a1ccd
```
`-v /opt/taosdata:/var/lib/taos` 为tdengine数据目录本地持久化挂载需将`/opt/taosdata`替换为实际本地存在的目录
`-e TZ="Asia/Shanghai"` 为tdengine设置时区这里可选设置对应的时区
`-v /opt/taosdata:/var/lib/taos` 为tdengine数据目录本地持久化挂载需将`/opt/taosdata`替换为实际本地存在的目录
使用```$ docker ps```查看数据库是否启动成功
### 创建数据库实例
@@ -52,4 +51,4 @@ TDengine是一款国产的开源物联网时序型数据库我们使用其替
```
**注意⚠若是安装包安装的TDengine2.3+版本**
> 除了启动server外还需执行 `systemctl start taosadapter` 启动 adapter
> 除了启动server外还需执行 `systemctl start taosadapter` 启动 adapter

View File

@@ -28,7 +28,7 @@ function Home() {
<h1 className="hero__title">
<img style={{width: '500px', marginTop: '100px'}} src={cdnTransfer('img/hertzbeat-brand.svg')} alt={'#'}/>
</h1>
<p className="hero__subtitle"><Translate>易用友好的监控系统</Translate></p>
<p className="hero__subtitle"><Translate>易用友好的监控告警系统</Translate></p>
<div className={styles.social}>
<a href="https://console.tancloud.cn"><img src={cdnTransfer('img/badge/web-monitor.svg')} alt={''}/></a>
<a href="https://console.tancloud.cn"><img src={cdnTransfer('img/badge/ping-connect.svg')} alt={''}/></a>

View File

@@ -1,7 +1,6 @@
package com.usthe.manager.controller;
import com.usthe.common.entity.dto.Message;
import com.usthe.common.entity.manager.Monitor;
import com.usthe.manager.pojo.dto.MonitorDto;
import com.usthe.manager.service.MonitorService;
import io.swagger.annotations.Api;
@@ -24,13 +23,11 @@ import static com.usthe.common.util.CommonConstants.MONITOR_NOT_EXIST_CODE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* Monitoring management API
* 监控管理API
*
* @author tomsun28
* @date 2021/11/14 10:57
*/
@Api(tags = "en: Monitoring management API,zh: 监控管理API")
@Api(tags = "监控管理API")
@RestController
@RequestMapping(path = "/monitor", produces = {APPLICATION_JSON_VALUE})
public class MonitorController {
@@ -39,12 +36,12 @@ public class MonitorController {
private MonitorService monitorService;
@PostMapping
@ApiOperation(value = "Add a monitoring application", notes = "新增一个监控应用")
@ApiOperation(value = "新增监控", notes = "新增一个监控应用")
public ResponseEntity<Message<Void>> addNewMonitor(@Valid @RequestBody MonitorDto monitorDto) {
// Verify request data 校验请求数据
// 校验请求数据
monitorService.validate(monitorDto, false);
if (monitorDto.isDetected()) {
// Probe 进行探测
// 进行探测
monitorService.detectMonitor(monitorDto.getMonitor(), monitorDto.getParams());
}
monitorService.addMonitor(monitorDto.getMonitor(), monitorDto.getParams());
@@ -52,12 +49,12 @@ public class MonitorController {
}
@PutMapping
@ApiOperation(value = "Modify an existing monitoring application", notes = "修改一个已存在监控应用")
@ApiOperation(value = "修改监控", notes = "修改一个已存在监控应用")
public ResponseEntity<Message<Void>> modifyMonitor(@Valid @RequestBody MonitorDto monitorDto) {
// Verify request data 校验请求数据
// 校验请求数据
monitorService.validate(monitorDto, true);
if (monitorDto.isDetected()) {
// Probe 进行探测
// 进行探测
monitorService.detectMonitor(monitorDto.getMonitor(), monitorDto.getParams());
}
monitorService.modifyMonitor(monitorDto.getMonitor(), monitorDto.getParams());
@@ -65,10 +62,9 @@ public class MonitorController {
}
@GetMapping(path = "/{id}")
@ApiOperation(value = "Obtain monitoring information based on monitoring ID", notes = "根据监控ID获取监控信息")
@ApiOperation(value = "查询监控", notes = "根据监控ID获取监控信息")
public ResponseEntity<Message<MonitorDto>> getMonitor(
@ApiParam(value = "监控ID", example = "6565463543") @PathVariable("id") final long id) {
// Get monitoring information
// 获取监控信息
MonitorDto monitorDto = monitorService.getMonitorDto(id);
Message.MessageBuilder<MonitorDto> messageBuilder = Message.builder();
@@ -81,20 +77,16 @@ public class MonitorController {
}
@DeleteMapping(path = "/{id}")
@ApiOperation(value = "Delete monitoring application based on monitoring ID", notes = "根据监控ID删除监控应用")
@ApiOperation(value = "删除监控", notes = "根据监控ID删除监控应用,监控不存在也是删除成功")
public ResponseEntity<Message<Void>> deleteMonitor(
@ApiParam(value = "en: Monitor ID,zh: 监控ID", example = "6565463543") @PathVariable("id") final long id) {
// delete monitor 删除监控
Monitor monitor = monitorService.getMonitor(id);
if (monitor == null) {
return ResponseEntity.ok(new Message<>("The specified monitoring was not queried, please check whether the parameters are correct"));
}
@ApiParam(value = "监控ID", example = "6565463543") @PathVariable("id") final long id) {
// 删除监控,监控不存在或删除成功都返回成功
monitorService.deleteMonitor(id);
return ResponseEntity.ok(new Message<>("Delete success"));
}
@PostMapping(path = "/detect")
@ApiOperation(value = "Perform availability detection on this monitoring based on monitoring information", notes = "根据监控信息去对此监控进行可用性探测")
@ApiOperation(value = "探测监控", notes = "根据监控信息去对此监控进行可用性探测")
public ResponseEntity<Message<Void>> detectMonitor(@Valid @RequestBody MonitorDto monitorDto) {
monitorService.validate(monitorDto, null);
monitorService.detectMonitor(monitorDto.getMonitor(), monitorDto.getParams());

View File

@@ -29,13 +29,11 @@ import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* Monitor and manage batch API
* 监控管理批量API
*
* @author tom
* @date 2021/12/1 20:43
*/
@Api(tags = "en: Monitor and manage batch API,zh: 监控列表API")
@Api(tags = "监控列表API")
@RestController
@RequestMapping(path = "/monitors", produces = {APPLICATION_JSON_VALUE})
public class MonitorsController {
@@ -46,23 +44,22 @@ public class MonitorsController {
private MonitorService monitorService;
@GetMapping
@ApiOperation(value = "Obtain a list of monitoring information based on query filter items",
notes = "根据查询过滤项获取监控信息列表")
@ApiOperation(value = "查询监控列表", notes = "根据查询过滤项获取监控信息列表")
public ResponseEntity<Message<Page<Monitor>>> getMonitors(
@ApiParam(value = "en: Monitor ID,zh: 监控ID", example = "6565463543") @RequestParam(required = false) final List<Long> ids,
@ApiParam(value = "en: Monitor Type,zh: 监控类型", example = "linux") @RequestParam(required = false) final String app,
@ApiParam(value = "en: Monitor Name,zh: 监控名称,模糊查询", example = "linux-127.0.0.1") @RequestParam(required = false) final String name,
@ApiParam(value = "en: Monitor Host,zh: 监控Host模糊查询", example = "127.0.0.1") @RequestParam(required = false) final String host,
@ApiParam(value = "en: Monitor Status,zh: 监控状态 0:未监控,1:可用,2:不可用,3:不可达,4:挂起,9:全部状态", example = "1") @RequestParam(required = false) final Byte status,
@ApiParam(value = "en: Sort Field,default id,zh: 排序字段默认id", example = "name") @RequestParam(defaultValue = "id") final String sort,
@ApiParam(value = "en: Sort by,zh: 排序方式asc:升序desc:降序", example = "desc") @RequestParam(defaultValue = "desc") final String order,
@ApiParam(value = "en: List current page,zh: 列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@ApiParam(value = "en: Number of list pagination,zh: 列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
@ApiParam(value = "监控ID", example = "6565463543") @RequestParam(required = false) final List<Long> ids,
@ApiParam(value = "监控类型", example = "linux") @RequestParam(required = false) final String app,
@ApiParam(value = "监控名称,模糊查询", example = "linux-127.0.0.1") @RequestParam(required = false) final String name,
@ApiParam(value = "监控Host模糊查询", example = "127.0.0.1") @RequestParam(required = false) final String host,
@ApiParam(value = "监控状态 0:未监控,1:可用,2:不可用,3:不可达,4:挂起,9:全部状态", example = "1") @RequestParam(required = false) final Byte status,
@ApiParam(value = "排序字段默认id", example = "name") @RequestParam(defaultValue = "id") final String sort,
@ApiParam(value = "排序方式asc:升序desc:降序", example = "desc") @RequestParam(defaultValue = "desc") final String order,
@ApiParam(value = "列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@ApiParam(value = "列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
Specification<Monitor> specification = (root, query, criteriaBuilder) -> {
List<Predicate> andList = new ArrayList<>();
if (ids != null && !ids.isEmpty()) {
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
CriteriaBuilder.In<Long> inPredicate= criteriaBuilder.in(root.get("id"));
for (long id : ids) {
inPredicate.value(id);
}
@@ -98,10 +95,10 @@ public class MonitorsController {
} else if (orPredicate.getExpressions().isEmpty()) {
return query.where(andPredicate).getRestriction();
} else {
return query.where(andPredicate, orPredicate).getRestriction();
return query.where(andPredicate,orPredicate).getRestriction();
}
};
// Pagination is a must 分页是必须的
// 分页是必须的
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
Page<Monitor> monitorPage = monitorService.getMonitors(specification, pageRequest);
@@ -110,20 +107,18 @@ public class MonitorsController {
}
@GetMapping(path = "/{app}")
@ApiOperation(value = "Filter all acquired monitoring information lists of the specified monitoring type according to the query",
notes = "根据查询过滤指定监控类型的所有获取监控信息列表")
@ApiOperation(value = "查询指定监控类型的监控列表", notes = "根据查询过滤指定监控类型的所有获取监控信息列表")
public ResponseEntity<Message<List<Monitor>>> getAppMonitors(
@ApiParam(value = "en: Monitoring type,zh: 监控类型", example = "linux") @PathVariable(required = false) final String app) {
@ApiParam(value = "监控类型", example = "linux") @PathVariable(required = false) final String app) {
List<Monitor> monitors = monitorService.getAppMonitors(app);
Message<List<Monitor>> message = new Message<>(monitors);
return ResponseEntity.ok(message);
}
@DeleteMapping
@ApiOperation(value = "Delete monitoring items in batches according to the monitoring ID list",
notes = "根据监控ID列表批量删除监控项")
@ApiOperation(value = "批量删除监控", notes = "根据监控ID列表批量删除监控项")
public ResponseEntity<Message<Void>> deleteMonitors(
@ApiParam(value = "en: Monitoring ID List,zh: 监控ID列表", example = "6565463543") @RequestParam(required = false) List<Long> ids
@ApiParam(value = "监控IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
) {
if (ids != null && !ids.isEmpty()) {
monitorService.deleteMonitors(new HashSet<>(ids));
@@ -133,10 +128,9 @@ public class MonitorsController {
}
@DeleteMapping("manage")
@ApiOperation(value = "Unmanaged monitoring items in batches according to the monitoring ID list",
notes = "根据监控ID列表批量取消纳管监控项")
@ApiOperation(value = "批量取消纳管监控", notes = "根据监控ID列表批量取消纳管监控项")
public ResponseEntity<Message<Void>> cancelManageMonitors(
@ApiParam(value = "en: Monitoring ID List,zh: 监控ID列表", example = "6565463543") @RequestParam(required = false) List<Long> ids
@ApiParam(value = "监控IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
) {
if (ids != null && !ids.isEmpty()) {
monitorService.cancelManageMonitors(new HashSet<>(ids));
@@ -146,10 +140,9 @@ public class MonitorsController {
}
@GetMapping("manage")
@ApiOperation(value = "Start the managed monitoring items in batches according to the monitoring ID list",
notes = "根据监控ID列表批量启动纳管监控项")
@ApiOperation(value = "批量启动纳管监控", notes = "根据监控ID列表批量启动纳管监控项")
public ResponseEntity<Message<Void>> enableManageMonitors(
@ApiParam(value = "en: Monitor ID List,zh: 监控ID列表", example = "6565463543") @RequestParam(required = false) List<Long> ids
@ApiParam(value = "监控IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
) {
if (ids != null && !ids.isEmpty()) {
monitorService.enableManageMonitors(new HashSet<>(ids));

View File

@@ -60,10 +60,8 @@ public class NoticeConfigController {
@ApiOperation(value = "Delete existing recipient information", notes = "删除已存在的接收人信息")
public ResponseEntity<Message<Void>> deleteNoticeReceiver(
@ApiParam(value = "en: Recipient ID,zh: 接收人ID", example = "6565463543") @PathVariable("id") final Long receiverId) {
NoticeReceiver noticeReceiver = noticeConfigService.getReceiverById(receiverId);
if (noticeReceiver == null) {
return ResponseEntity.ok(new Message<>("The relevant information of the recipient could not be found, please check whether the parameters are correct"));
}
// Returns success if it does not exist or if the deletion is successful
// todo 不存在或删除成功都返回成功
noticeConfigService.deleteReceiver(receiverId);
return ResponseEntity.ok(new Message<>("Delete success"));
}
@@ -74,6 +72,7 @@ public class NoticeConfigController {
public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers(
@ApiParam(value = "en: Recipient name,zh: 接收人名称,模糊查询", example = "tom") @RequestParam(required = false) final String name) {
//todo Writing can be optimized 写法可优化
Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (name != null && !"".equals(name)) {
@@ -108,10 +107,6 @@ public class NoticeConfigController {
@ApiParam(value = "en: Notification Policy ID,zh: 通知策略ID", example = "6565463543") @PathVariable("id") final Long ruleId) {
// Returns success if it does not exist or if the deletion is successful
// todo 不存在或删除成功都返回成功
NoticeRule noticeRule = noticeConfigService.getNoticeRulesById(ruleId);
if (noticeRule == null) {
return ResponseEntity.ok(new Message<>("The specified notification rule could not be queried, please check whether the parameters are correct"));
}
noticeConfigService.deleteNoticeRule(ruleId);
return ResponseEntity.ok(new Message<>("Delete success"));
}

View File

@@ -22,37 +22,33 @@ public interface MonitorDao extends JpaRepository<Monitor, Long>, JpaSpecificati
/**
* Delete monitor based on monitor ID list
* 根据监控ID列表删除监控
*
* @param monitorIds Monitoring ID List 监控ID列表
* @param monitorIds 监控ID列表
*/
void deleteAllByIdIn(Set<Long> monitorIds);
/**
* Query monitoring based on monitoring ID list
* 根据监控ID列表查询监控
*
* @param monitorIds Monitoring ID List 监控ID列表
* @return Monitor List 监控列表
* @param monitorIds 监控ID列表
* @return 监控列表
*/
List<Monitor> findMonitorsByIdIn(Set<Long> monitorIds);
/**
* Query monitoring by monitoring type
* 根据监控类型查询监控
*
* @param app Monitor Type 监控类型
* @return Monitor List 监控列表
* @param app 监控类型
* @return 监控列表
*/
List<Monitor> findMonitorsByAppEquals(String app);
/**
* Querying Monitoring of Sent Collection Tasks
* 查询已下发采集任务的监控
*
* @param status Monitor Status 监控状态
* @return Monitor List 监控列表
* @param status 监控状态
* @return 监控列表
*/
List<Monitor> findMonitorsByStatusNotInAndAndJobIdNotNull(List<Byte> status);

View File

@@ -8,34 +8,27 @@ import java.util.Set;
/**
* ParamDao 数据库操作
*
* @author tomsun28
* @date 2021/11/14 11:26
*/
public interface ParamDao extends JpaRepository<Param, Long> {
/**
* Query the list of parameters associated with the monitoring ID'
* 根据监控ID查询与之关联的参数列表
*
* @param monitorId Monitor ID 监控ID
* @return list of parameter values 参数值列表
* @param monitorId 监控ID
* @return 参数值列表
*/
List<Param> findParamsByMonitorId(long monitorId);
/**
* Remove the parameter list associated with the monitoring ID based on it
* 根据监控ID删除与之关联的参数列表
*
* @param monitorId Monitor Id 监控ID
* @param monitorId 监控ID
*/
void deleteParamsByMonitorId(long monitorId);
/**
* Remove the parameter list associated with the monitoring ID list based on it
* 根据监控ID列表删除与之关联的参数列表
*
* @param monitorIds Monitoring ID List 监控ID列表
* @param monitorIds 监控ID列表
*/
void deleteParamsByMonitorIdIn(Set<Long> monitorIds);
}

View File

@@ -14,18 +14,15 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/**
* Monitoring Information External Interaction Entities
* 监控信息对外交互实体
*
* @author tomsun28
* @date 2021/11/14 10:13
*/
@Data
@ApiModel(description = "en: Monitoring information entities,zh: 监控信息实体")
@ApiModel(description = "监控信息实体")
public class MonitorDto {
/**
* Monitoring entity
* 监控实体
*/
@ApiModelProperty(value = "监控实体", accessMode = READ_WRITE, position = 0)
@@ -34,22 +31,17 @@ public class MonitorDto {
private Monitor monitor;
/**
* Params 参数
* 参数
*/
@ApiModelProperty(value = "监控参数", accessMode = READ_WRITE, position = 1)
@NotNull
@Valid
private List<Param> params;
/**
* List of indicator groups
* 指标组列表
*/
@ApiModelProperty(value = "指标组列表", accessMode = READ_ONLY, position = 2)
private List<String> metrics;
/**
* Whether to detect
* 是否探测
*/
@ApiModelProperty(value = "是否进行探测", accessMode = READ_WRITE, position = 3)

View File

@@ -9,7 +9,6 @@ import java.util.Map;
/**
* 监控类型管理接口
*
* @author tomsun28
* @date 2021/11/14 17:12
*/
@@ -17,26 +16,21 @@ public interface AppService {
/**
* 根据监控类型查询定义的参数结构
*
* @param app 监控类型
* @return 参数结构列表
*/
List<ParamDefine> getAppParamDefines(String app);
/**
* Get monitor structure definition based on monitor type name
* 根据监控类型名称获取监控结构定义
*
* @param app Monitoring type name 监控类型名称
* @return Monitoring Structure Definition 监控结构定义
* @throws IllegalArgumentException Thrown when there is no monitoring type with the corresponding name that is not supported
* 当不存在即不支持对应名称的监控类型时抛出
* @param app 监控类型名称
* @return 监控结构定义
* @throws IllegalArgumentException 当不存在即不支持对应名称的监控类型时抛出
*/
Job getAppDefine(String app) throws IllegalArgumentException;
/**
* 获取定义的监控I18N资源
*
* @param lang 语言类型
* @return I18N资源
*/
@@ -44,7 +38,6 @@ public interface AppService {
/**
* 查询所有监控的类型-指标组-指标层级
*
* @param lang 语言
* @return 层级信息
*/

View File

@@ -23,103 +23,93 @@ public interface MonitorService {
/**
* Monitoring Availability Probes
* 监控可用性探测
*
* @param monitor Monitoring entity information 监控实体信息
* @param params Parameter information 参数信息
* @throws MonitorDetectException Probe failure throws 探测失败抛出
* @param monitor 监控实体信息
* @param params 参数信息
* @throws MonitorDetectException 探测失败抛出
*/
void detectMonitor(Monitor monitor, List<Param> params) throws MonitorDetectException;
/**
* Add monitoring 新增监控
* 新增监控
*
* @param monitor Monitoring Entity 监控实体
* @param params Parameter information 参数信息
* @throws RuntimeException Add process exception throw 新增过程异常抛出
* @param monitor 监控实体
* @param params 参数信息
* @throws RuntimeException 新增过程异常抛出
*/
void addMonitor(Monitor monitor, List<Param> params) throws RuntimeException;
/**
* Verify the correctness of request data parameters
* 校验请求数据参数正确性
*
* @param monitorDto monitorDto
* @param isModify Whether it is a modification monitoring 是否是修改监控
* @throws IllegalArgumentException Validation parameter error thrown 校验参数错误抛出
* @param isModify 是否是修改监控
* @throws IllegalArgumentException 校验参数错误抛出
*/
void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgumentException;
/**
* Modify update monitoring
* 修改更新监控
*
* @param monitor Monitor Entity 监控实体
* @param params Parameter information 参数信息
* @throws RuntimeException Exception thrown during modification 修改过程中异常抛出
* @param monitor 监控实体
* @param params 参数信息
* @throws RuntimeException 修改过程中异常抛出
*/
void modifyMonitor(Monitor monitor, List<Param> params) throws RuntimeException;
/**
* Delete Monitor
* 删除监控
*
* @param id Monitor ID 监控ID
* @throws RuntimeException Exception thrown during deletion 删除过程中异常抛出
* @param id 监控ID
* @throws RuntimeException 删除过程中异常抛出
*/
void deleteMonitor(long id) throws RuntimeException;
/**
* Batch delete monitoring
* 批量删除监控
*
* @param ids Monitoring ID List 监控ID列表
* @throws RuntimeException Exception thrown during deletion 删除过程中异常抛出
* @param ids 监控ID
* @throws RuntimeException 删除过程中异常抛出
*/
void deleteMonitors(Set<Long> ids) throws RuntimeException;
/**
* Get monitoring information
* 获取监控信息
*
* @param id Monitor ID 监控ID
* @return MonitorDto Monitor Entity 監控实体
* @throws RuntimeException Exception thrown during query 查询过程中异常抛出
* @param id 监控ID
* @return MonitorDto
* @throws RuntimeException 查询过程中异常抛出
*/
MonitorDto getMonitorDto(long id) throws RuntimeException;
/**
* Dynamic conditional query
* 动态条件查询
*
* @param specification Query conditions 查询条件
* @param pageRequest Pagination parameters 分页参数
* @return Search Result 查询结果
* @param specification 查询条件
* @param pageRequest 分页参数
* @return 查询结果
*/
Page<Monitor> getMonitors(Specification<Monitor> specification, PageRequest pageRequest);
/**
* Unmanaged monitoring items in batches according to the monitoring ID list
* 根据监控ID列表批量取消纳管监控项
*
* @param ids Monitoring ID List 监控ID列表
* @param ids 监控IDs
*/
void cancelManageMonitors(HashSet<Long> ids);
/**
* Start the managed monitoring items in batches according to the monitoring ID list
* 根据监控ID列表批量启动纳管监控项
*
* @param ids Monitoring ID List 监控ID列表
* @param ids 监控IDs
*/
void enableManageMonitors(HashSet<Long> ids);
/**
* Query the monitoring category and its corresponding monitoring quantity
* 查询监控类别及其对应的监控数量
*
* @return Monitoring Category and Monitoring Quantity Mapping 监控类别与监控数量映射
* @return 监控类别与监控数量映射
*/
List<AppCount> getAllAppMonitorsCount();
@@ -142,11 +132,10 @@ public interface MonitorService {
void updateMonitorStatus(Long monitorId, byte status);
/**
* Query the list of all monitoring information under the specified monitoring type
* 查询指定监控类型下的所有监控信息列表
*
* @param app Monitor Type 监控类型
* @return Monitor Entity List 监控列表
* @param app 监控类型
* @return 监控列表
*/
List<Monitor> getAppMonitors(String app);
}

View File

@@ -90,22 +90,4 @@ public interface NoticeConfigService {
* @return Receiver 接收人
*/
List<NoticeReceiver> getReceiverFilterRule(Alert alert);
/**
* Query recipient information based on recipient ID (primary key Id)
* 根据接收人ID(主键Id)查询接收人信息
*
* @param receiverId Receiver ID (primary key ID) 接收人ID(主键ID)
* @return Recipient Entity 接收人实体
*/
NoticeReceiver getReceiverById(Long receiverId);
/**
* Query specific notification rules according to the rule ID (primary key ID)
* 根据规则ID(主键ID)查询具体通知规则
*
* @param ruleId Rule ID 规则ID(主键ID)
* @return Notification Rule Entity 通知规则实体
*/
NoticeRule getNoticeRulesById(Long ruleId);
}

View File

@@ -81,13 +81,7 @@ public class MonitorServiceImpl implements MonitorService {
List<Configmap> configmaps = params.stream().map(param ->
new Configmap(param.getField(), param.getValue(), param.getType())).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
// To detect availability, you only need to collect the set of availability indicators with a priority of 0.
// 探测可用性只需要采集优先级为0的可用性指标集合
List<Metrics> availableMetrics = appDefine.getMetrics().stream()
.filter(item -> item.getPriority() == 0).collect(Collectors.toList());
appDefine.setMetrics(availableMetrics);
List<CollectRep.MetricsData> collectRep = collectJobService.collectSyncJobData(appDefine);
// If the detection result fails, a detection exception is thrown
// 判断探测结果 失败则抛出探测异常
if (collectRep == null || collectRep.isEmpty()) {
throw new MonitorDetectException("No collector response");
@@ -100,9 +94,9 @@ public class MonitorServiceImpl implements MonitorService {
@Override
@Transactional(rollbackFor = Exception.class)
public void addMonitor(Monitor monitor, List<Param> params) throws RuntimeException {
// Apply for monitor id 申请 monitor id
// 申请 monitor id
long monitorId = SnowFlakeIdGenerator.generateId();
// Construct the collection task Job entity 构造采集任务Job实体
// 构造采集任务Job实体
Job appDefine = appService.getAppDefine(monitor.getApp());
appDefine.setMonitorId(monitorId);
appDefine.setInterval(monitor.getIntervals());
@@ -113,10 +107,8 @@ public class MonitorServiceImpl implements MonitorService {
return new Configmap(param.getField(), param.getValue(), param.getType());
}).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
// Send the collection task to get the job ID
// 下发采集任务得到jobId
long jobId = collectJobService.addAsyncCollectJob(appDefine);
// Brush the library after the download is successful
// 下发成功后刷库
try {
monitor.setId(monitorId);
@@ -126,7 +118,6 @@ public class MonitorServiceImpl implements MonitorService {
paramDao.saveAll(params);
} catch (Exception e) {
log.error(e.getMessage(), e);
// Repository brushing abnormally cancels the previously delivered task
// 刷库异常取消之前的下发任务
collectJobService.cancelAsyncCollectJob(jobId);
throw new MonitorDatabaseException(e.getMessage());
@@ -136,7 +127,6 @@ public class MonitorServiceImpl implements MonitorService {
@Override
@Transactional(readOnly = true)
public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgumentException {
// The request monitoring parameter matches the monitoring parameter definition mapping check
// 请求监控参数与监控参数定义映射校验匹配
Monitor monitor = monitorDto.getMonitor();
monitor.setHost(monitor.getHost().trim());
@@ -149,7 +139,7 @@ public class MonitorServiceImpl implements MonitorService {
param.setValue(value);
})
.collect(Collectors.toMap(Param::getField, param -> param));
// Check name uniqueness 校验名称唯一性
// 校验名称唯一性
if (isModify != null) {
Optional<Monitor> monitorOptional = monitorDao.findMonitorByNameEquals(monitor.getName());
if (monitorOptional.isPresent()) {
@@ -164,7 +154,7 @@ public class MonitorServiceImpl implements MonitorService {
}
}
// Parameter definition structure verification 参数定义结构校验
// 参数定义结构校验
List<ParamDefine> paramDefines = appService.getAppParamDefines(monitorDto.getMonitor().getApp());
if (paramDefines != null) {
for (ParamDefine paramDefine : paramDefines) {
@@ -210,7 +200,6 @@ public class MonitorServiceImpl implements MonitorService {
}
break;
case "password":
// The plaintext password needs to be encrypted for transmission and storage
// 明文密码需加密传输存储
String passwordValue = param.getValue();
if (!AesUtil.isCiphertext(passwordValue)) {
@@ -220,7 +209,7 @@ public class MonitorServiceImpl implements MonitorService {
param.setType(CommonConstants.PARAM_TYPE_PASSWORD);
break;
case "boolean":
// boolean check
// boolean校验
String booleanValue = param.getValue();
try {
Boolean.parseBoolean(booleanValue);
@@ -230,7 +219,7 @@ public class MonitorServiceImpl implements MonitorService {
}
break;
case "radio":
// radio single value check radio单选值校验
// radio单选值校验
List<ParamDefine.Option> options = paramDefine.getOptions();
boolean invalid = true;
if (options != null) {
@@ -252,8 +241,7 @@ public class MonitorServiceImpl implements MonitorService {
case "key-value":
// todo key-value校验
break;
// todo More parameter definitions and actual value format verification
// 更多参数定义与实际值格式校验
// todo 更多参数定义与实际值格式校验
default:
throw new IllegalArgumentException("ParamDefine type " + paramDefine.getType() + " is invalid.");
}
@@ -263,10 +251,8 @@ public class MonitorServiceImpl implements MonitorService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void modifyMonitor(Monitor monitor, List<Param> params) throws RuntimeException {
long monitorId = monitor.getId();
// Check to determine whether the monitor corresponding to the monitor Id exists
// 查判断monitorId对应的此监控是否存在
Optional<Monitor> queryOption = monitorDao.findById(monitorId);
if (!queryOption.isPresent()) {
@@ -274,11 +260,9 @@ public class MonitorServiceImpl implements MonitorService {
}
Monitor preMonitor = queryOption.get();
if (!preMonitor.getApp().equals(monitor.getApp())) {
// The type of monitoring cannot be modified
// 监控的类型不能修改
throw new IllegalArgumentException("Can not modify monitor's app type");
}
// Construct the collection task Job entity
// 构造采集任务Job实体
Job appDefine = appService.getAppDefine(monitor.getApp());
appDefine.setId(preMonitor.getJobId());
@@ -289,16 +273,14 @@ public class MonitorServiceImpl implements MonitorService {
List<Configmap> configmaps = params.stream().map(param ->
new Configmap(param.getField(), param.getValue(), param.getType())).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
// After the update is successfully released, refresh the library
// 更新采集任务
collectJobService.updateAsyncCollectJob(appDefine);
// 下发更新成功后刷库
try {
monitor.setJobId(preMonitor.getJobId());
monitor.setStatus(preMonitor.getStatus());
monitorDao.save(monitor);
paramDao.saveAll(params);
// Update the collection task after the storage is completed
// 入库完成后更新采集任务
collectJobService.updateAsyncCollectJob(appDefine);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new MonitorDatabaseException(e.getMessage());
@@ -359,9 +341,7 @@ public class MonitorServiceImpl implements MonitorService {
@Override
public void cancelManageMonitors(HashSet<Long> ids) {
// Update monitoring status Delete corresponding monitoring periodic task
// 更新监控状态 删除对应的监控周期性任务
// The jobId is not deleted, and the jobId is reused again after the management is started.
// jobId不删除 待启动纳管之后再次复用jobId
List<Monitor> managedMonitors = monitorDao.findMonitorsByIdIn(ids)
.stream().filter(monitor ->
@@ -378,7 +358,6 @@ public class MonitorServiceImpl implements MonitorService {
@Override
public void enableManageMonitors(HashSet<Long> ids) {
// Update monitoring status Add corresponding monitoring periodic task
// 更新监控状态 新增对应的监控周期性任务
List<Monitor> unManagedMonitors = monitorDao.findMonitorsByIdIn(ids)
.stream().filter(monitor ->
@@ -388,7 +367,6 @@ public class MonitorServiceImpl implements MonitorService {
if (!unManagedMonitors.isEmpty()) {
monitorDao.saveAll(unManagedMonitors);
for (Monitor monitor : unManagedMonitors) {
// Construct the collection task Job entity
// 构造采集任务Job实体
Job appDefine = appService.getAppDefine(monitor.getApp());
appDefine.setMonitorId(monitor.getId());
@@ -399,7 +377,7 @@ public class MonitorServiceImpl implements MonitorService {
List<Configmap> configmaps = params.stream().map(param ->
new Configmap(param.getField(), param.getValue(), param.getType())).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
// Issue collection tasks 下发采集任务
// 下发采集任务
collectJobService.addAsyncCollectJob(appDefine);
}
}

View File

@@ -18,7 +18,6 @@ import java.util.stream.Collectors;
/**
* 消息通知配置实现
*
* @author tom
* @date 2021/12/16 16:16
*/
@@ -75,24 +74,13 @@ public class NoticeConfigServiceImpl implements NoticeConfigService {
@Override
public List<NoticeReceiver> getReceiverFilterRule(Alert alert) {
// todo use cache 使用缓存
// todo 使用缓存
List<NoticeRule> rules = noticeRuleDao.findNoticeRulesByEnableTrue();
// todo The temporary rule is to forward all, and then implement more matching rules: alarm status selection, monitoring type selection, etc.
// 暂时规则是全部转发 后面实现更多匹配规则:告警状态选择 监控类型选择等
// todo 暂时规则是全部转发 后面实现更多匹配规则:告警状态选择 监控类型选择等
Set<Long> receiverIds = rules.stream()
.filter(NoticeRule::isFilterAll)
.map(NoticeRule::getReceiverId)
.collect(Collectors.toSet());
return noticeReceiverDao.findAllById(receiverIds);
}
@Override
public NoticeReceiver getReceiverById(Long receiverId) {
return noticeReceiverDao.getOne(receiverId);
}
@Override
public NoticeRule getNoticeRulesById(Long ruleId) {
return noticeRuleDao.getOne(ruleId);
}
}

View File

@@ -32,8 +32,6 @@ configmap:
type: 3
- key: params
type: 3
- key: keyword
type: 1
# 指标组列表
metrics:
# 第一个监控指标组 cpu
@@ -48,9 +46,6 @@ metrics:
- field: responseTime
type: 0
unit: ms
- field: keyword
type: 0
unit: 次数
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# 当protocol为http协议时具体的采集配置
@@ -83,5 +78,4 @@ metrics:
digestAuthPassword: ^_^password^_^
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
# todo xmlPath-xmlPath脚本,prometheus-Prometheus数据规则
parseType: website
keyword: ^_^keyword^_^
parseType: website

View File

@@ -68,26 +68,6 @@ metrics:
- field: context_switch
type: 0
unit: 个数
- field: usage
type: 0
unit: '%'
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- info
- cores
- interrupt
- load
- context_switch
- idle
# (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- info=info
- cores=cores
- interrupt=interrupt
- load=load
- context_switch=context_switch
- usage=100-idle
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: ssh
# 当protocol为http协议时具体的采集配置
@@ -99,7 +79,7 @@ metrics:
username: ^_^username^_^
password: ^_^password^_^
timeout: ^_^timeout^_^
script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}';vmstat 1 1 | awk 'NR==3{print $15}'"
script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}'"
parseType: oneRow
- name: memory
@@ -121,25 +101,6 @@ metrics:
- field: available
type: 0
unit: Mb
- field: usage
type: 0
unit: '%'
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- total
- used
- free
- buff_cache
- available
# (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- total=total
- used=used
- free=free
- buff_cache=buff_cache
- available=available
- usage=(used / total) * 100
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: ssh
# 当protocol为http协议时具体的采集配置
@@ -193,7 +154,6 @@ metrics:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: interface_name
type: 1
instance: true
- field: receive_bytes
type: 0
unit: byte
@@ -212,36 +172,4 @@ metrics:
password: ^_^password^_^
timeout: ^_^timeout^_^
script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}'
parseType: multiRow
- name: disk_free
priority: 5
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: filesystem
type: 1
- field: used
type: 0
unit: Mb
- field: available
type: 0
unit: Mb
- field: usage
type: 0
unit: '%'
- field: mounted
type: 1
instance: true
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: ssh
# 当protocol为http协议时具体的采集配置
ssh:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
username: ^_^username^_^
password: ^_^password^_^
timeout: ^_^timeout^_^
script: df -m | tail -n +2 | awk 'BEGIN{ print "filesystem used available usage mounted"} {print $1,$3,$4,$5,$6}'
parseType: multiRow

View File

@@ -32,6 +32,8 @@ metrics:
- field: database_version
type: 1
instance: true
- field: database_type
type: 1
- field: hostname
type: 1
- field: instance_name
@@ -43,6 +45,7 @@ metrics:
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- VERSION
- DATABASE_TYPE
- HOST_NAME
- INSTANCE_NAME
- STARTUP_TIME
@@ -51,6 +54,7 @@ metrics:
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- database_version=VERSION
- database_type=DATABASE_TYPE
- hostname=HOST_NAME
- instance_name=INSTANCE_NAME
- startup_time=STARTUP_TIME
@@ -175,4 +179,4 @@ metrics:
queryType: columns
# sql
sql: select metric_name, value from gv$sysmetric where metric_name = 'I/O Megabytes per Second' or metric_name = 'User Transaction Per Sec' or metric_name = 'I/O Requests per Second'
url: ^_^url^_^
url: ^_^url^_^

View File

@@ -0,0 +1,25 @@
category: service
app: telnet
name:
zh-CN: TELNET端口可用性
en-US: PORT TELNET
configmap:
- key: host
type: 1
- key: port
type: 0
- key: timeout
type: 0
metrics:
- name: summary
priority: 0
fields:
- field: responseTime
type: 0
unit: ms
protocol: telnet
# 当protocol为telnet协议时具体的采集配置
telnet:
host: ^_^host^_^
port: ^_^port^_^
timeout: ^_^timeout^_^

View File

@@ -18,12 +18,10 @@ configmap:
type: 1
- key: password
type: 2
- key: keyword
type: 1
# 指标组列表
metrics:
# 第一个监控指标组 cpu
# 注意:内置监控指标有 (responseTime - 响应时间, keyword - 关键字数量)
# 注意:内置监控指标有 (responseTime - 响应时间)
- name: summary
# 指标组调度优先级(0-127)越小优先级越高,优先级低的指标组会等优先级高的指标组采集完成后才会被调度,相同优先级的指标组会并行调度采集
# 优先级为0的指标组为可用性指标组,即它会被首先调度,采集成功才会继续调度其它指标组,采集失败则中断调度
@@ -34,9 +32,6 @@ metrics:
- field: responseTime
type: 0
unit: ms
- field: keyword
type: 0
unit: 次数
# 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http
# 当protocol为http协议时具体的采集配置
@@ -60,5 +55,4 @@ metrics:
digestAuthPassword: ^_^password^_^
# 响应数据解析方式: default-系统规则,jsonPath-jsonPath脚本,website-网站可用性指标监控
# todo xmlPath-xmlPath脚本,prometheus-Prometheus数据规则
parseType: website
keyword: ^_^keyword^_^
parseType: website

View File

@@ -26,12 +26,10 @@ param:
# 当type为text时,用limit表示字符串限制大小
limit: 20
required: false
hide: true
- field: password
name: 密码
type: password
required: false
hide: true
- field: ssl
name: 启动SSL
# 当type为boolean时,前端用switch展示开关

View File

@@ -60,18 +60,15 @@ param:
type: text
placeholder: '请求BODY资源类型'
required: false
hide: true
- field: payload
name: 请求BODY
type: textarea
placeholder: 'POST PUT请求时有效'
required: false
hide: true
- field: authType
name: 认证方式
type: radio
required: false
hide: true
# 当type为radio单选框,checkbox复选框时,option表示可选项值列表 {name1:value1,name2:value2}
options:
- label: Basic Auth
@@ -84,14 +81,7 @@ param:
# 当type为text时,用limit表示字符串限制大小
limit: 20
required: false
hide: true
- field: password
name: 密码
type: password
required: false
hide: true
- field: keyword
name: 关键字
type: text
required: false
hide: true

View File

@@ -17,7 +17,6 @@ param:
required: false
defaultValue: 6000
placeholder: '查询超时时间'
hide: true
- field: database
name: 数据库名称
type: text
@@ -34,5 +33,4 @@ param:
- field: url
name: URL
type: text
required: false
hide: true
required: false

View File

@@ -15,7 +15,6 @@ param:
name: 查询超时时间
type: number
required: false
hide: true
defaultValue: 6000
placeholder: '查询超时时间'
- field: database
@@ -34,5 +33,4 @@ param:
- field: url
name: URL
type: text
required: false
hide: true
required: false

View File

@@ -15,7 +15,6 @@ param:
name: 查询超时时间
type: number
required: false
hide: true
defaultValue: 6000
placeholder: '查询超时时间'
- field: database
@@ -34,5 +33,4 @@ param:
- field: url
name: URL
type: text
required: false
hide: true
required: false

View File

@@ -15,7 +15,6 @@ param:
name: 查询超时时间
type: number
required: false
hide: true
defaultValue: 6000
placeholder: '查询超时时间'
- field: database
@@ -34,5 +33,4 @@ param:
- field: url
name: URL
type: text
required: false
hide: true
required: false

View File

@@ -15,7 +15,6 @@ param:
name: 查询超时时间
type: number
required: false
hide: true
defaultValue: 6000
placeholder: '查询超时时间'
- field: database
@@ -34,5 +33,4 @@ param:
- field: url
name: URL
type: text
required: false
hide: true
required: false

View File

@@ -0,0 +1,27 @@
# 监控应用类型名称(与文件名保持一致) eg: linux windows tomcat mysql aws...
app: telnet
# 强制固定必须参数 - host(ipv4,ipv6,域名)
param:
# field-字段名称标识符
- field: host
# name-参数字段显示名称
name: 主机Host
# type-字段类型,样式(大部分映射input标签type属性)
type: host
# 是否是必输项 true-必填 false-可选
required: true
- field: port
name: 端口
type: number
# 当type为number时,用range表示范围
range: '[0,65535]'
required: true
defaultValue: 80
- field: timeout
name: Telnet超时时间
type: number
# 当type为number时,用range表示范围
range: '[0,100000]'
required: true
placeholder: '请输入超时时间,单位毫秒'
defaultValue: 6000

View File

@@ -33,7 +33,6 @@ param:
name: 认证方式
type: radio
required: false
hide: true
# 当type为radio单选框,checkbox复选框时,option表示可选项值列表 {name1:value1,name2:value2}
options:
- label: Basic Auth
@@ -46,14 +45,7 @@ param:
# 当type为text时,用limit表示字符串限制大小
limit: 20
required: false
hide: true
- field: password
name: 密码
type: password
required: false
hide: true
- field: keyword
name: 关键字
type: text
required: false
hide: true
required: false

View File

@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd
http://maven.apache.org/ASSEMBLY/2.0.0 ">
<!--必填,会追加到打包文件名称的末尾-->
<id>1.0-beta.7</id>
<id>1.0-beta.6</id>
<!--打包类型,可以设置多种类型,打包的时候不同的类型都会打包打出来-->
<formats>
<format>tar</format>

View File

@@ -28,7 +28,9 @@ if [ ! -d $LOGS_DIR ]; then
fi
# JVM Configuration
JAVA_MEM_OPTS=" -server -XX:SurvivorRatio=6 -XX:+UseParallelGC "
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
JAVA_MEM_OPTS=" -server -Xms256m -Xmx1024m -XX:SurvivorRatio=2 -XX:+UseParallelGC "
# 加载外部log文件的配置
LOG_IMPL_FILE=logback-spring.xml
@@ -39,4 +41,4 @@ then
fi
CONFIG_FILES=" -Dlogging.path=$LOGS_DIR $LOGGING_CONFIG -Dspring.config.location=$CONF_DIR/ "
echo -e "Starting the $SERVER_NAME ..."
java $JAVA_MEM_OPTS $CONFIG_FILES -jar $DEPLOY_DIR/$JAR_NAME --spring.profiles.active=prod
java $JAVA_OPTS $JAVA_MEM_OPTS $CONFIG_FILES -jar $DEPLOY_DIR/$JAR_NAME --spring.profiles.active=prod

View File

@@ -34,9 +34,9 @@ rem 项目日志输出绝对路径
set LOGS_DIR=%DEPLOY_DIR%\logs
rem JVM Configuration
set JAVA_OPTS= -Duser.timezone=Asia/Shanghai
set JAVA_OPTS= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Duser.timezone=Asia/Shanghai
set JAVA_MEM_OPTS= -server -XX:SurvivorRatio=6 -XX:+UseParallelGC
set JAVA_MEM_OPTS= -server -Xms256m -Xmx1024m -XX:SurvivorRatio=2 -XX:+UseParallelGC
rem 加载外部log文件的配置
set LOGGING_CONFIG=-Dlogging.config=%CONF_DIR%\logback-spring.xml

View File

@@ -66,9 +66,9 @@ if [ ! -d $LOGS_DIR ]; then
fi
# JVM Configuration
JAVA_OPTS=" -Duser.timezone=Asia/Shanghai"
JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Duser.timezone=Asia/Shanghai"
JAVA_MEM_OPTS=" -server -XX:SurvivorRatio=6 -XX:+UseParallelGC "
JAVA_MEM_OPTS=" -server -Xms256m -Xmx1024m -XX:SurvivorRatio=2 -XX:+UseParallelGC "
# 加载外部log文件的配置
LOG_IMPL_FILE=logback-spring.xml

View File

@@ -35,7 +35,7 @@ services:
- heartzbeat
hertzbeat:
image: "tancloud/hertzbeat:1.0-beta.7"
image: "tancloud/hertzbeat:1.0-beta.6"
container_name: hertzbeat
hostname: hertzbeat
restart: always

View File

@@ -2,7 +2,7 @@ FROM openjdk:8-alpine
MAINTAINER tomsun28 "tomsun28@outlook.com"
ADD hertzbeat-1.0-beta.7.tar /opt/
ADD hertzbeat-1.0-beta.6.tar /opt/
RUN apk add --no-cache tzdata

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 30 MiB

View File

@@ -12,6 +12,4 @@ export class ParamDefine {
// 当type为key-value时有效,表示别名描述
keyAlias!: string;
valueAlias!: string;
// 此参数是否隐藏 即默认不显示, 在高级设置区显示
hide: boolean = false;
}

View File

@@ -162,151 +162,11 @@
</nz-form-control>
</nz-form-item>
<nz-collapse [nzGhost]="true">
<nz-collapse-panel nzHeader="高级" [nzHeader]="extraColHeader" [nzShowArrow]="false">
<nz-form-item *ngFor="let paramDefine of advancedParamDefines; let i = index">
<nz-form-label
*ngIf="paramDefine.field !== 'host' && paramDefine.type === 'text'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control
*ngIf="paramDefine.field !== 'host' && paramDefine.type === 'text'"
nzSpan="8"
[nzErrorTip]="'validation.required' | i18n"
>
<input
nz-input
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[type]="paramDefine.type"
[id]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
/>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'textarea'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'textarea'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<textarea
nz-input
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[id]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
rows="3"
></textarea>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'password'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input
[type]="passwordVisible ? 'text' : 'password'"
nz-input
placeholder="input password"
[required]="paramDefine.required"
[(ngModel)]="advancedParams[i].value"
[id]="paramDefine.field"
[name]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
/>
</nz-input-group>
<ng-template #suffixTemplate>
<i nz-icon [nzType]="passwordVisible ? 'eye-invisible' : 'eye'" (click)="passwordVisible = !passwordVisible"></i>
</ng-template>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'number'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'number'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-number
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[nzMin]="-1000"
[nzMax]="65535"
[nzStep]="1"
[nzPlaceHolder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
[name]="paramDefine.field"
[id]="paramDefine.field"
></nz-input-number>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'boolean'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'boolean'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-switch
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[id]="paramDefine.field"
></nz-switch>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'radio'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'radio'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-radio-group
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
nzButtonStyle="solid"
[name]="paramDefine.field"
[id]="paramDefine.field"
>
<label nz-radio-button [nzValue]="optionItem.value" *ngFor="let optionItem of paramDefine.options">
{{ optionItem.label }}
</label>
</nz-radio-group>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'key-value'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="advancedParams[i].value"
[id]="paramDefine.field"
keyAlias="Header Name"
valueAlias="Header Value"
></app-key-value-input>
</nz-form-control>
</nz-form-item>
</nz-collapse-panel>
</nz-collapse>
<ng-template #extraColHeader>
<button style="top: -10px; margin-left: 40%" nz-button nzType="dashed" nz-tooltip nzTooltipTitle="设置高级可选参数">
<span>高级设置</span>
<i nz-icon nzType="down-circle" nzTheme="outline"></i>
</button>
</ng-template>
<nz-divider></nz-divider>
<nz-form-item>
<nz-form-label nzSpan="7" nzFor="intervals" nzTooltipTitle="监控周期性采集数据间隔时间,单位秒"> 采集间隔 </nz-form-label>
<nz-form-control nzSpan="8">
<nz-form-control nzSpan="10">
<nz-input-number [(ngModel)]="monitor.intervals" [nzMin]="10" [nzMax]="604800" [nzStep]="60" name="intervals" id="intervals">
</nz-input-number>
</nz-form-control>

View File

@@ -30,8 +30,6 @@ export class MonitorEditComponent implements OnInit {
paramDefines!: ParamDefine[];
params!: Param[];
advancedParamDefines!: ParamDefine[];
advancedParams!: Param[];
paramValueMap = new Map<String, Param>();
monitor = new Monitor();
profileForm: FormGroup = new FormGroup({});
@@ -61,6 +59,7 @@ export class MonitorEditComponent implements OnInit {
this.paramValueMap.set(item.field, item);
});
}
this.params = message.data.params;
this.detected = message.data.detected ? message.data.detected : true;
} else {
console.warn(message.msg);
@@ -72,11 +71,9 @@ export class MonitorEditComponent implements OnInit {
)
.subscribe(message => {
if (message.code === 0) {
this.paramDefines = message.data;
this.params = [];
this.advancedParams = [];
this.paramDefines = [];
this.advancedParamDefines = [];
message.data.forEach(define => {
this.paramDefines.forEach(define => {
let param = this.paramValueMap.get(define.field);
if (param === undefined) {
param = new Param();
@@ -103,13 +100,7 @@ export class MonitorEditComponent implements OnInit {
}
}
}
if (define.hide) {
this.advancedParams.push(param);
this.advancedParamDefines.push(define);
} else {
this.params.push(param);
this.paramDefines.push(define);
}
this.params.push(param);
});
} else {
console.warn(message.msg);
@@ -153,15 +144,10 @@ export class MonitorEditComponent implements OnInit {
param.value = (param.value as string).trim();
}
});
this.advancedParams.forEach(param => {
if (param.value != null && typeof param.value == 'string') {
param.value = (param.value as string).trim();
}
});
let addMonitor = {
detected: this.detected,
monitor: this.monitor,
params: this.params.concat(this.advancedParams)
params: this.params
};
this.isSpinning = true;
this.monitorSvc.editMonitor(addMonitor).subscribe(
@@ -202,15 +188,10 @@ export class MonitorEditComponent implements OnInit {
param.value = (param.value as string).trim();
}
});
this.advancedParams.forEach(param => {
if (param.value != null && typeof param.value == 'string') {
param.value = (param.value as string).trim();
}
});
let detectMonitor = {
detected: this.detected,
monitor: this.monitor,
params: this.params.concat(this.advancedParams)
params: this.params
};
this.isSpinning = true;
this.monitorSvc.detectMonitor(detectMonitor).subscribe(

View File

@@ -171,146 +171,6 @@
</nz-form-control>
</nz-form-item>
<nz-collapse [nzGhost]="true">
<nz-collapse-panel nzHeader="高级" [nzHeader]="extraColHeader" [nzShowArrow]="false">
<nz-form-item *ngFor="let paramDefine of advancedParamDefines; let i = index">
<nz-form-label
*ngIf="paramDefine.field !== 'host' && paramDefine.type === 'text'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control
*ngIf="paramDefine.field !== 'host' && paramDefine.type === 'text'"
nzSpan="8"
[nzErrorTip]="'validation.required' | i18n"
>
<input
nz-input
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[type]="paramDefine.type"
[id]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
/>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'textarea'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'textarea'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<textarea
nz-input
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[id]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
rows="3"
></textarea>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'password'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input
[type]="passwordVisible ? 'text' : 'password'"
nz-input
placeholder="input password"
[required]="paramDefine.required"
[(ngModel)]="advancedParams[i].value"
[id]="paramDefine.field"
[name]="paramDefine.field"
[placeholder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
/>
</nz-input-group>
<ng-template #suffixTemplate>
<i nz-icon [nzType]="passwordVisible ? 'eye-invisible' : 'eye'" (click)="passwordVisible = !passwordVisible"></i>
</ng-template>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'number'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'number'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-number
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[nzMin]="-1000"
[nzMax]="65535"
[nzStep]="1"
[nzPlaceHolder]="paramDefine.placeholder ? paramDefine.placeholder : ''"
[name]="paramDefine.field"
[id]="paramDefine.field"
></nz-input-number>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'boolean'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'boolean'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-switch
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
[name]="paramDefine.field"
[id]="paramDefine.field"
></nz-switch>
</nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'radio'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'radio'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-radio-group
[(ngModel)]="advancedParams[i].value"
[required]="paramDefine.required"
nzButtonStyle="solid"
[name]="paramDefine.field"
[id]="paramDefine.field"
>
<label nz-radio-button [nzValue]="optionItem.value" *ngFor="let optionItem of paramDefine.options">
{{ optionItem.label }}
</label>
</nz-radio-group>
</nz-form-control>
<nz-form-label
*ngIf="paramDefine.type === 'key-value'"
nzSpan="7"
[nzRequired]="paramDefine.required"
[nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="advancedParams[i].value"
[id]="paramDefine.field"
keyAlias="Header Name"
valueAlias="Header Value"
></app-key-value-input>
</nz-form-control>
</nz-form-item>
</nz-collapse-panel>
</nz-collapse>
<ng-template #extraColHeader>
<button style="top: -10px; margin-left: 40%" nz-button nzType="dashed" nz-tooltip nzTooltipTitle="设置高级可选参数">
<span>高级设置</span>
<i nz-icon nzType="down-circle" nzTheme="outline"></i>
</button>
</ng-template>
<nz-divider></nz-divider>
<nz-form-item>

View File

@@ -20,8 +20,6 @@ import { MonitorService } from '../../../service/monitor.service';
export class MonitorNewComponent implements OnInit {
paramDefines!: ParamDefine[];
params!: Param[];
advancedParamDefines!: ParamDefine[];
advancedParams!: Param[];
monitor!: Monitor;
detected: boolean = true;
passwordVisible: boolean = false;
@@ -55,11 +53,9 @@ export class MonitorNewComponent implements OnInit {
)
.subscribe(message => {
if (message.code === 0) {
this.paramDefines = message.data;
this.params = [];
this.advancedParams = [];
this.paramDefines = [];
this.advancedParamDefines = [];
message.data.forEach(define => {
this.paramDefines.forEach(define => {
let param = new Param();
param.field = define.field;
if (define.type === 'number') {
@@ -81,13 +77,7 @@ export class MonitorNewComponent implements OnInit {
param.value = define.defaultValue;
}
}
if (define.hide) {
this.advancedParams.push(param);
this.advancedParamDefines.push(define);
} else {
this.params.push(param);
this.paramDefines.push(define);
}
this.params.push(param);
});
} else {
console.warn(message.msg);
@@ -135,15 +125,10 @@ export class MonitorNewComponent implements OnInit {
param.value = (param.value as string).trim();
}
});
this.advancedParams.forEach(param => {
if (param.value != null && typeof param.value == 'string') {
param.value = (param.value as string).trim();
}
});
let addMonitor = {
detected: this.detected,
monitor: this.monitor,
params: this.params.concat(this.advancedParams)
params: this.params
};
this.isSpinning = true;
this.monitorSvc.newMonitor(addMonitor).subscribe(
@@ -184,15 +169,10 @@ export class MonitorNewComponent implements OnInit {
param.value = (param.value as string).trim();
}
});
this.advancedParams.forEach(param => {
if (param.value != null && typeof param.value == 'string') {
param.value = (param.value as string).trim();
}
});
let detectMonitor = {
detected: true,
monitor: this.monitor,
params: this.params.concat(this.advancedParams)
params: this.params
};
this.isSpinning = true;
this.monitorSvc.detectMonitor(detectMonitor).subscribe(

View File

@@ -16,7 +16,6 @@ import { MonitorEditComponent } from './monitor-edit/monitor-edit.component';
import { MonitorListComponent } from './monitor-list/monitor-list.component';
import { MonitorNewComponent } from './monitor-new/monitor-new.component';
import { MonitorRoutingModule } from './monitor-routing.module';
import { NzCollapseModule } from 'ng-zorro-antd/collapse';
const COMPONENTS: Array<Type<void>> = [
MonitorNewComponent,
@@ -38,8 +37,7 @@ const COMPONENTS: Array<Type<void>> = [
NzRadioModule,
NgxEchartsModule,
NzLayoutModule,
NzSpaceModule,
NzCollapseModule
NzSpaceModule
],
declarations: COMPONENTS
})