Compare commits

..

13 Commits

Author SHA1 Message Date
tomsun28
0e6bf1618a [collector]bugfix: non-supported character set when monitor GBK oracle 2022-04-11 16:20:32 +08:00
会编程的王学长
4f13875e01 [monitor]feature: Alarm and receiving Chinese and English support (#82)
* fix: 代码名称优化 #huacheng

* fix: msgtype更正名称 #huacheng

* fix: 企业微信更正名称 #huacheng

* feat: Alarm and receiving Chinese and English support #huacheng

Co-authored-by: tomsun28 <tomsun28@outlook.com>
2022-04-10 20:51:30 +08:00
tomsun28
c79f66dd9f [script]update service startup script 2022-04-08 21:28:30 +08:00
tomsun28
835df039e8 [home]add v1.0-beat.7 publish blog 2022-04-08 19:50:45 +08:00
tomsun28
f23ea9ffd5 [docs]update gif show 2022-04-08 08:00:44 +08:00
tomsun28
7994d7ef15 [script,docs]change version 1.0-beta.6 to 1.0-beta.7 2022-04-07 20:33:52 +08:00
tomsun28
323e5f8981 [git]ignore dependency 2022-04-07 19:59:01 +08:00
tomsun28
e1916b937e [monitor]feature:support linux cpu usage,memory usage,disk free (#76) 2022-04-07 17:51:48 +08:00
tomsun28
2ecf40e873 [manager]bugfix:fix linux interface metrics no instance (#75) 2022-04-07 14:25:11 +08:00
常清静矣
63ea0a87f7 [manager] bugfix: remove oracle field - database_type due 11g not support 2022-04-06 14:49:31 +00:00
学习代码的小白
a3f4e42034 [manager]code format and optimization 2022-04-06 14:46:49 +00:00
tomsun28
018db2a14f [manager]feature:[website api]monitor support keyword match (#72) 2022-04-06 10:26:33 +08:00
tomsun28
f238a1d4ea [manager]feature:only collect available metrics when detect (#70) 2022-04-06 08:32:38 +08:00
48 changed files with 737 additions and 217 deletions

6
.gitignore vendored
View File

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

View File

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

View File

@@ -30,11 +30,12 @@ import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/** /**
* 告警管理API * Alarm Management API 告警管理API
*
* @author tom * @author tom
* @date 2021/12/9 10:32 * @date 2021/12/9 10:32
*/ */
@Api(tags = "告警批量管理API") @Api(tags = "en: Alarm batch management API, zh:告警批量管理API")
@RestController @RestController
@RequestMapping(path = "/alerts", produces = {APPLICATION_JSON_VALUE}) @RequestMapping(path = "/alerts", produces = {APPLICATION_JSON_VALUE})
public class AlertsController { public class AlertsController {
@@ -43,23 +44,23 @@ public class AlertsController {
private AlertService alertService; private AlertService alertService;
@GetMapping @GetMapping
@ApiOperation(value = "查询告警列表", notes = "根据查询过滤项获取告警信息列表") @ApiOperation(value = "Get a list of alarm information based on query filter items", notes = "根据查询过滤项获取告警信息列表")
public ResponseEntity<Message<Page<Alert>>> getAlerts( public ResponseEntity<Message<Page<Alert>>> getAlerts(
@ApiParam(value = "告警ID", example = "6565466456") @RequestParam(required = false) List<Long> ids, @ApiParam(value = "en: Alarm ID List,zh: 告警IDS", example = "6565466456") @RequestParam(required = false) List<Long> ids,
@ApiParam(value = "告警监控对象ID", example = "6565463543") @RequestParam(required = false) Long monitorId, @ApiParam(value = "en: Alarm monitor object ID,zh: 告警监控对象ID", example = "6565463543") @RequestParam(required = false) Long monitorId,
@ApiParam(value = "告警级别", example = "6565463543") @RequestParam(required = false) Byte priority, @ApiParam(value = "en: Alarm level,zh: 告警级别", example = "6565463543") @RequestParam(required = false) Byte priority,
@ApiParam(value = "告警状态", example = "6565463543") @RequestParam(required = false) Byte status, @ApiParam(value = "en: Alarm Status,zh: 告警状态", example = "6565463543") @RequestParam(required = false) Byte status,
@ApiParam(value = "告警内容模糊查询", example = "linux") @RequestParam(required = false) String content, @ApiParam(value = "en: Alarm content fuzzy query,zh:告警内容模糊查询", example = "linux") @RequestParam(required = false) String content,
@ApiParam(value = "排序字段默认id", example = "name") @RequestParam(defaultValue = "id") String sort, @ApiParam(value = "en: Sort field, default id,zh: 排序字段默认id", example = "name") @RequestParam(defaultValue = "id") String sort,
@ApiParam(value = "排序方式asc:升序desc:降序", example = "desc") @RequestParam(defaultValue = "desc") String order, @ApiParam(value = "en: Sort Type,zh: 排序方式asc:升序desc:降序", example = "desc") @RequestParam(defaultValue = "desc") String order,
@ApiParam(value = "列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex, @ApiParam(value = "en: List current page,zh: 列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@ApiParam(value = "列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) { @ApiParam(value = "en: Number of list pagination,zh: 列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
Specification<Alert> specification = (root, query, criteriaBuilder) -> { Specification<Alert> specification = (root, query, criteriaBuilder) -> {
List<Predicate> andList = new ArrayList<>(); List<Predicate> andList = new ArrayList<>();
if (ids != null && !ids.isEmpty()) { 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) { for (long id : ids) {
inPredicate.value(id); inPredicate.value(id);
} }
@@ -92,10 +93,9 @@ public class AlertsController {
} }
@DeleteMapping @DeleteMapping
@ApiOperation(value = "批量删除告警", notes = "根据告警ID列表批量删除告警") @ApiOperation(value = "Delete alarms in batches", notes = "根据告警ID列表批量删除告警")
public ResponseEntity<Message<Void>> deleteAlertDefines( public ResponseEntity<Message<Void>> deleteAlertDefines(
@ApiParam(value = "告警IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids @ApiParam(value = "en:Alarm List ID,zh: 告警IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
) {
if (ids != null && !ids.isEmpty()) { if (ids != null && !ids.isEmpty()) {
alertService.deleteAlerts(new HashSet<>(ids)); alertService.deleteAlerts(new HashSet<>(ids));
} }
@@ -104,10 +104,10 @@ public class AlertsController {
} }
@PutMapping(path = "/status/{status}") @PutMapping(path = "/status/{status}")
@ApiOperation(value = "批量修改告警状态", notes = "批量修改告警状态,设置已读未读") @ApiOperation(value = "Batch modify alarm status, set read and unread", notes = "批量修改告警状态,设置已读未读")
public ResponseEntity<Message<Void>> applyAlertDefinesStatus( public ResponseEntity<Message<Void>> applyAlertDefinesStatus(
@ApiParam(value = "告警状态值", example = "0") @PathVariable Byte status, @ApiParam(value = "en:Alarm status value,zh: 告警状态值", example = "0") @PathVariable Byte status,
@ApiParam(value = "告警IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids) { @ApiParam(value = "en:Alarm List IDS,zh: 告警IDS", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
if (ids != null && status != null && !ids.isEmpty()) { if (ids != null && status != null && !ids.isEmpty()) {
alertService.editAlertStatus(status, ids); alertService.editAlertStatus(status, ids);
} }
@@ -116,7 +116,7 @@ public class AlertsController {
} }
@GetMapping(path = "/summary") @GetMapping(path = "/summary")
@ApiOperation(value = "获取告警统计信息", notes = "获取告警统计信息") @ApiOperation(value = "Get alarm statistics", notes = "获取告警统计信息")
public ResponseEntity<Message<AlertSummary>> getAlertsSummary() { public ResponseEntity<Message<AlertSummary>> getAlertsSummary() {
AlertSummary alertSummary = alertService.getAlertsSummary(); AlertSummary alertSummary = alertService.getAlertsSummary();
Message<AlertSummary> message = new Message<>(alertSummary); Message<AlertSummary> message = new Message<>(alertSummary);

View File

@@ -12,20 +12,23 @@ import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* Alert 数据库操作 * Alert Database Operations Alert数据库操作
*
* @author tom * @author tom
* @date 2021/12/9 10:03 * @date 2021/12/9 10:03
*/ */
public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert> { public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert> {
/** /**
* 根据ID列表删除告警 * Delete alerts based on ID list 根据ID列表删除告警
* @param alertIds 告警ID列表 *
* @param alertIds Alert ID List 告警ID列表
*/ */
void deleteAlertsByIdIn(Set<Long> alertIds); void deleteAlertsByIdIn(Set<Long> alertIds);
/** /**
* 根据告警ID-状态值 更新告警状态 * 根据告警ID-状态值 更新告警状态
*
* @param status 状态值 * @param status 状态值
* @param ids 告警ID列表 * @param ids 告警ID列表
*/ */
@@ -34,8 +37,10 @@ public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationEx
void updateAlertsStatus(@Param(value = "status") Byte status, @Param(value = "ids") List<Long> ids); void updateAlertsStatus(@Param(value = "status") Byte status, @Param(value = "ids") List<Long> ids);
/** /**
* Query the number of unhandled alarms of each alarm severity
* 查询各个告警级别的未处理告警数量 * 查询各个告警级别的未处理告警数量
* @return 告警数量 *
* @return Number of alerts 告警数量
*/ */
@Query("select new com.usthe.alert.dto.AlertPriorityNum(mo.priority, count(mo.id)) from Alert mo where mo.status = 0 group by mo.priority") @Query("select new com.usthe.alert.dto.AlertPriorityNum(mo.priority, count(mo.id)) from Alert mo where mo.status = 0 group by mo.priority")
List<AlertPriorityNum> findAlertPriorityNum(); List<AlertPriorityNum> findAlertPriorityNum();

View File

@@ -4,7 +4,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
/** /**
* 监控级别告警数量 * Number of monitoring level alarms 监控级别告警数量
*
* @author tom * @author tom
* @date 2022/3/6 19:52 * @date 2022/3/6 19:52
*/ */
@@ -12,7 +13,13 @@ import lombok.Data;
@AllArgsConstructor @AllArgsConstructor
public class AlertPriorityNum { public class AlertPriorityNum {
/**
* Alarm level 告警级别
*/
private byte priority; private byte priority;
/**
* count 数量
*/
private long num; private long num;
} }

View File

@@ -9,31 +9,43 @@ import lombok.NoArgsConstructor;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
/** /**
* 告警统计信息 * Alarm Statistics Information 告警统计信息
*
* @author tom * @author tom
* @date 2022/3/6 19:25 * @date 2022/3/6 19:25
*/ */
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@ApiModel(description = "告警统计信息") @ApiModel(description = "en:Alarm Statistics Information,zh: 告警统计信息")
public class AlertSummary { public class AlertSummary {
@ApiModelProperty(value = "告警总数量(包括已处理未处理告警)", example = "134", accessMode = READ_ONLY, position = 0) @ApiModelProperty(value = "Total number of alerts (including processed and unprocessed alerts)",
notes = "告警总数量(包括已处理未处理告警)",
example = "134", accessMode = READ_ONLY, position = 0)
private long total; private long total;
@ApiModelProperty(value = "已处理告警数量", example = "34", accessMode = READ_ONLY, position = 1) @ApiModelProperty(value = "Number of alerts handled",
notes = "已处理告警数量",
example = "34", accessMode = READ_ONLY, position = 1)
private long dealNum; private long dealNum;
@ApiModelProperty(value = "告警处理率", example = "39.34", accessMode = READ_ONLY, position = 2) @ApiModelProperty(value = "Alarm handling rate",
notes = "告警处理率",
example = "39.34", accessMode = READ_ONLY, position = 2)
private float rate; private float rate;
@ApiModelProperty(value = "告警级别为警告告警的告警数量(指未处理告警)", example = "43", accessMode = READ_ONLY, position = 3) @ApiModelProperty(value = "Number of alarms whose alarm severity is warning alarms (referring to unhandled alarms)",
notes = "告警级别为警告告警的告警数量(指未处理告警)",
example = "43", accessMode = READ_ONLY, position = 3)
private long priorityWarningNum; private long priorityWarningNum;
@ApiModelProperty(value = "告警级别为严重告警的告警数量(指未处理告警)", example = "56", accessMode = READ_ONLY, position = 4) @ApiModelProperty(value = "Number of alarms whose alarm severity is critical alarms (referring to unhandled alarms)",
notes = "告警级别为严重告警的告警数量(指未处理告警)",
example = "56", accessMode = READ_ONLY, position = 4)
private long priorityCriticalNum; private long priorityCriticalNum;
@ApiModelProperty(value = "告警级别为紧急告警的告警数量(指未处理告警)", example = "23", accessMode = READ_ONLY, position = 5) @ApiModelProperty(value = "Number of alarms whose alarm severity is urgent alarms (referring to unhandled alarms)",
notes = "告警级别为紧急告警的告警数量(指未处理告警)", example = "23", accessMode = READ_ONLY, position = 5)
private long priorityEmergencyNum; private long priorityEmergencyNum;
} }

View File

@@ -10,43 +10,54 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
/** /**
* Alarm information management interface
* 告警信息管理接口 * 告警信息管理接口
*
* @author tom * @author tom
* @date 2021/12/9 10:06 * @date 2021/12/9 10:06
*/ */
public interface AlertService { public interface AlertService {
/** /**
* 新增告警 * Add alarm record
* @param alert 告警实体 * 新增告警记录
* @throws RuntimeException 新增过程异常抛出 *
* @param alert Alert entity 告警实体
* @throws RuntimeException Add process exception throw 新增过程异常抛出
*/ */
void addAlert(Alert alert) throws RuntimeException; void addAlert(Alert alert) throws RuntimeException;
/** /**
* Dynamic conditional query
* 动态条件查询 * 动态条件查询
* @param specification 查询条件 *
* @param pageRequest 分页参数 * @param specification Query conditions 查询条件
* @return 查询结果 * @param pageRequest pagination parameters 分页参数
* @return search result 查询结果
*/ */
Page<Alert> getAlerts(Specification<Alert> specification, PageRequest pageRequest); Page<Alert> getAlerts(Specification<Alert> specification, PageRequest pageRequest);
/** /**
* Delete alarms in batches according to the alarm ID list
* 根据告警ID列表批量删除告警 * 根据告警ID列表批量删除告警
* @param ids 告警IDs *
* @param ids Alarm ID List 告警IDS
*/ */
void deleteAlerts(HashSet<Long> ids); void deleteAlerts(HashSet<Long> ids);
/** /**
* Update the alarm status according to the alarm ID-status value
* 根据告警ID-状态值 更新告警状态 * 根据告警ID-状态值 更新告警状态
* @param status 待修改为的告警状态 *
* @param ids 待修改的告警IDs * @param status Alarm status to be modified 待修改的告警状态
* @param ids Alarm ID List to be modified 待修改的告警ID集合
*/ */
void editAlertStatus(Byte status, List<Long> ids); void editAlertStatus(Byte status, List<Long> ids);
/** /**
* 获取告警统计信息 * Get alarm statistics information 获取告警统计信息
* @return 告警统计 *
* @return Alarm statistics information 告警统计
*/ */
AlertSummary getAlertsSummary(); AlertSummary getAlertsSummary();

View File

@@ -20,7 +20,8 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
/** /**
* 告警信息服务实现 * Realization of Alarm Information Service 告警信息服务实现
*
* @author tom * @author tom
* @date 2021/12/10 15:39 * @date 2021/12/10 15:39
*/ */
@@ -55,18 +56,24 @@ public class AlertServiceImpl implements AlertService {
@Override @Override
public AlertSummary getAlertsSummary() { public AlertSummary getAlertsSummary() {
AlertSummary alertSummary = new AlertSummary(); AlertSummary alertSummary = new AlertSummary();
//Statistics on the alarm information in the alarm state
//统计正在告警状态下的告警信息
List<AlertPriorityNum> priorityNums = alertDao.findAlertPriorityNum(); List<AlertPriorityNum> priorityNums = alertDao.findAlertPriorityNum();
if (priorityNums != null) { if (priorityNums != null) {
for (AlertPriorityNum priorityNum : priorityNums) { for (AlertPriorityNum priorityNum : priorityNums) {
switch (priorityNum.getPriority()) { switch (priorityNum.getPriority()) {
case CommonConstants case CommonConstants
.ALERT_PRIORITY_CODE_WARNING: .ALERT_PRIORITY_CODE_WARNING:
alertSummary.setPriorityWarningNum(priorityNum.getNum());break; alertSummary.setPriorityWarningNum(priorityNum.getNum());
break;
case CommonConstants.ALERT_PRIORITY_CODE_CRITICAL: case CommonConstants.ALERT_PRIORITY_CODE_CRITICAL:
alertSummary.setPriorityCriticalNum(priorityNum.getNum());break; alertSummary.setPriorityCriticalNum(priorityNum.getNum());
break;
case CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY: case CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY:
alertSummary.setPriorityEmergencyNum(priorityNum.getNum());break; alertSummary.setPriorityEmergencyNum(priorityNum.getNum());
default: break; break;
default:
break;
} }
} }
} }

View File

@@ -115,6 +115,11 @@
<artifactId>ojdbc8</artifactId> <artifactId>ojdbc8</artifactId>
<version>21.5.0.0</version> <version>21.5.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.oracle.database.nls</groupId>
<artifactId>orai18n</artifactId>
<version>21.5.0.0</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

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

View File

@@ -247,6 +247,10 @@ public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
value = aliasFieldValueMap.get(realField); value = aliasFieldValueMap.get(realField);
} }
} }
// 处理可能带单位的指标数值 比如 34%, 34Mb并将数值小数点限制到4位
if (CommonConstants.TYPE_NUMBER == field.getType()) {
value = CommonUtil.parseDoubleStr(value, field.getUnit());
}
if (value == null) { if (value == null) {
value = CommonConstants.NULL_VALUE; value = CommonConstants.NULL_VALUE;
} }

View File

@@ -0,0 +1,35 @@
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,6 +9,8 @@ public interface CollectorConstants {
String RESPONSE_TIME = "responseTime"; String RESPONSE_TIME = "responseTime";
String KEYWORD = "keyword";
String STATUS_CODE = "statusCode"; String STATUS_CODE = "statusCode";
String ERROR_MSG = "errorMsg"; String ERROR_MSG = "errorMsg";

View File

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

View File

@@ -23,7 +23,8 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/** /**
* 告警记录 * Alarm record entity 告警记录实体
*
* @author tom * @author tom
* @date 2021/12/9 15:37 * @date 2021/12/9 15:37
*/ */
@@ -33,52 +34,68 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@ApiModel(description = "告警记录实体") @ApiModel(description = "en: Alarm record entity zh: 告警记录实体")
public class Alert { public class Alert {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "告警记录实体主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0) @ApiModelProperty(value = "Alarm record entity primary key index ID",
notes = "告警记录实体主键索引ID",
example = "87584674384", accessMode = READ_ONLY, position = 0)
private Long id; private Long id;
@ApiModelProperty(value = "告警目标对象: 监控可用性-available 指标-app.metrics.field", @ApiModelProperty(value = "Alert target object: monitor availability-available metrics-app.metrics.field",
notes = "告警目标对象: 监控可用性-available 指标-app.metrics.field",
example = "1", accessMode = READ_WRITE, position = 1) example = "1", accessMode = READ_WRITE, position = 1)
@Length(max = 255) @Length(max = 255)
private String target; private String target;
@ApiModelProperty(value = "告警对象关联的监控ID", example = "87432674336", accessMode = READ_WRITE, position = 2) @ApiModelProperty(value = "Monitoring ID associated with the alarm object",
notes = "告警对象关联的监控ID",
example = "87432674336", accessMode = READ_WRITE, position = 2)
private Long monitorId; private Long monitorId;
@ApiModelProperty(value = "告警对象关联的监控名称", example = "Linux_192.132.23.1", @ApiModelProperty(value = "Monitoring name associated with the alarm object",
accessMode = READ_WRITE, position = 3) notes = "告警对象关联的监控名称",
example = "Linux_192.132.23.1", accessMode = READ_WRITE, position = 3)
private String monitorName; private String monitorName;
@ApiModelProperty(value = "告警关联的告警定义ID", example = "8743267443543", accessMode = READ_WRITE, position = 4) @ApiModelProperty(value = "Alarm definition ID associated with the alarm",
notes = "告警关联的告警定义ID",
example = "8743267443543", accessMode = READ_WRITE, position = 4)
private Long alertDefineId; private Long alertDefineId;
@ApiModelProperty(value = "告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色", @ApiModelProperty(value = "Alarm level 0: high-emergency-critical alarm-red 1: medium-critical-critical alarm-orange 2: low-warning-warning alarm-yellow",
notes = "告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色",
example = "1", accessMode = READ_WRITE, position = 5) example = "1", accessMode = READ_WRITE, position = 5)
@Min(0) @Min(0)
@Max(2) @Max(2)
private byte priority; private byte priority;
@ApiModelProperty(value = "告警通知实际内容", example = "linux_192.134.32.1: 534543534 cpu usage high", @ApiModelProperty(value = "The actual content of the alarm notification",
notes = "告警通知实际内容",
example = "linux_192.134.32.1: 534543534 cpu usage high",
accessMode = READ_WRITE, position = 6) accessMode = READ_WRITE, position = 6)
@Length(max = 1024) @Length(max = 1024)
private String content; private String content;
@ApiModelProperty(value = "告警状态: 0-正常告警(待处理) 1-阈值触发但未达到告警次数 2-恢复告警 3-已处理", @ApiModelProperty(value = "Alarm status: 0-normal alarm (to be processed) 1-threshold triggered but not reached the number of alarms 2-recovered alarm 3-processed",
notes = "告警状态: 0-正常告警(待处理) 1-阈值触发但未达到告警次数 2-恢复告警 3-已处理",
example = "1", accessMode = READ_WRITE, position = 7) example = "1", accessMode = READ_WRITE, position = 7)
@Min(0) @Min(0)
@Max(2) @Max(2)
private byte status; private byte status;
@ApiModelProperty(value = "告警阈值触发次数", example = "3", accessMode = READ_WRITE, position = 8) @ApiModelProperty(value = "Alarm threshold trigger times",
notes = "告警阈值触发次数",
example = "3", accessMode = READ_WRITE, position = 8)
@Min(0) @Min(0)
@Max(10) @Max(10)
private int times; private int times;
@ApiModelProperty(value = "告警触发时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 9) @ApiModelProperty(value = "Alarm trigger time (timestamp in milliseconds)",
notes = "告警触发时间(毫秒时间戳)",
example = "1612198922000", accessMode = READ_ONLY, position = 9)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtCreate; private LocalDateTime gmtCreate;

View File

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

View File

@@ -23,7 +23,9 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/** /**
* Message notification recipient entity
* 消息通知接收人实体 * 消息通知接收人实体
*
* @author tomsun28 * @author tomsun28
* @date 2021/11/13 22:19 * @date 2021/11/13 22:19
*/ */
@@ -33,56 +35,80 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@ApiModel(description = "消息通知接收人实体") @ApiModel(description = "en: Message notification recipient entity,zh:消息通知接收人实体")
public class NoticeReceiver { public class NoticeReceiver {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "接收人实体主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0) @ApiModelProperty(value = "Recipient entity primary key index ID",
notes = "接收人实体主键索引ID",
example = "87584674384", accessMode = READ_ONLY, position = 0)
private Long id; private Long id;
@ApiModelProperty(value = "接收人名称", example = "tom", accessMode = READ_WRITE, position = 1) @ApiModelProperty(value = "Recipient name",
notes = "接收人名称",
example = "tom", accessMode = READ_WRITE, position = 1)
@Length(max = 100) @Length(max = 100)
@NotNull @NotNull
private String name; private String name;
@ApiModelProperty(value = "通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人", accessMode = READ_WRITE, position = 2) @ApiModelProperty(value = "Notification information method: 0-SMS 1-Email 2-webhook 3-WeChat Official Account 4-Enterprise WeChat Robot 5-DingTalk Robot 6-FeiShu Robot",
notes = "通知信息方式: 0-手机短信 1-邮箱 2-webhook 3-微信公众号 4-企业微信机器人 5-钉钉机器人 6-飞书机器人",
accessMode = READ_WRITE, position = 2)
@Min(0) @Min(0)
@Max(8) @Max(8)
@NotNull @NotNull
private Byte type; private Byte type;
@ApiModelProperty(value = "手机号, 通知方式为手机短信时有效", example = "18923435643", accessMode = READ_WRITE, position = 3) @ApiModelProperty(value = "Mobile number: Valid when the notification method is SMS",
notes = "手机号 : 通知方式为手机短信时有效",
example = "18923435643", accessMode = READ_WRITE, position = 3)
@Length(max = 100) @Length(max = 100)
private String phone; private String phone;
@ApiModelProperty(value = "邮箱账号, 通知方式为邮箱时有效", example = "tom@qq.com", accessMode = READ_WRITE, position = 4) @ApiModelProperty(value = "Email account: Valid when the notification method is email",
notes = "邮箱账号 : 通知方式为邮箱时有效",
example = "tom@qq.com", accessMode = READ_WRITE, position = 4)
@Length(max = 100) @Length(max = 100)
private String email; private String email;
@ApiModelProperty(value = "URL地址, 通知方式为webhook有效", example = "https://www.tancloud.cn", accessMode = READ_WRITE, position = 5) @ApiModelProperty(value = "URL address: The notification method is valid for webhook",
notes = "URL地址 : 通知方式为webhook有效",
example = "https://www.tancloud.cn", accessMode = READ_WRITE, position = 5)
@Length(max = 300) @Length(max = 300)
private String hookUrl; private String hookUrl;
@ApiModelProperty(value = "openId, 通知方式为微信公众号或企业微信机器人有效", example = "343432", accessMode = READ_WRITE, position = 6) @ApiModelProperty(value = "openId : The notification method is valid for WeChat official account or enterprise WeChat robot",
notes = "openId : 通知方式为微信公众号或企业微信机器人有效",
example = "343432", accessMode = READ_WRITE, position = 6)
@Length(max = 300) @Length(max = 300)
private String wechatId; private String wechatId;
@ApiModelProperty(value = "访问token, 通知方式为钉钉机器人有效", example = "34823984635647", accessMode = READ_WRITE, position = 7) @ApiModelProperty(value = "Access token : The notification method is valid for DingTalk robot",
notes = "访问token : 通知方式为钉钉机器人有效",
example = "34823984635647", accessMode = READ_WRITE, position = 7)
@Length(max = 300) @Length(max = 300)
private String accessToken; private String accessToken;
@ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 7) @ApiModelProperty(value = "The creator of this record",
notes = "此条记录创建者",
example = "tom", accessMode = READ_ONLY, position = 7)
private String creator; private String creator;
@ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 8) @ApiModelProperty(value = "This record was last modified by",
notes = "此条记录最新修改者",
example = "tom", accessMode = READ_ONLY, position = 8)
private String modifier; private String modifier;
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 9) @ApiModelProperty(value = "Record creation time (millisecond timestamp)",
notes = "记录创建时间(毫秒时间戳)",
example = "1612198922000", accessMode = READ_ONLY, position = 9)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtCreate; private LocalDateTime gmtCreate;
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 10) @ApiModelProperty(value = "Record the latest modification time (timestamp in milliseconds)",
notes = "记录最新修改时间(毫秒时间戳)",
example = "1612198444000", accessMode = READ_ONLY, position = 10)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtUpdate; private LocalDateTime gmtUpdate;

View File

@@ -21,7 +21,9 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY;
import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
/** /**
* Notification strategy entity
* 通知策略 * 通知策略
*
* @author tomsun28 * @author tomsun28
* @date 2021/11/13 22:19 * @date 2021/11/13 22:19
*/ */
@@ -31,45 +33,65 @@ import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@ApiModel(description = "通知策略实体") @ApiModel(description = "en: Notify Policy Entity,zh: 通知策略实体")
public class NoticeRule { public class NoticeRule {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "通知策略实体主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0) @ApiModelProperty(value = "Notification Policy Entity Primary Key Index ID",
notes = "通知策略实体主键索引ID",
example = "87584674384", accessMode = READ_ONLY, position = 0)
private Long id; private Long id;
@ApiModelProperty(value = "策略名称", example = "dispatch-1", accessMode = READ_WRITE, position = 1) @ApiModelProperty(value = "Policy name",
notes = "策略名称",
example = "dispatch-1", accessMode = READ_WRITE, position = 1)
@Length(max = 100) @Length(max = 100)
@NotNull @NotNull
private String name; private String name;
@ApiModelProperty(value = "接收人ID", example = "4324324", accessMode = READ_WRITE, position = 2) @ApiModelProperty(value = "Recipient ID",
notes = "接收人ID",
example = "4324324", accessMode = READ_WRITE, position = 2)
@NotNull @NotNull
private Long receiverId; private Long receiverId;
@ApiModelProperty(value = "接收人标识", example = "tom", accessMode = READ_WRITE, position = 3) @ApiModelProperty(value = "Recipient identification",
notes = "接收人标识",
example = "tom", accessMode = READ_WRITE, position = 3)
@Length(max = 100) @Length(max = 100)
@NotNull @NotNull
private String receiverName; private String receiverName;
@ApiModelProperty(value = "是否启用此策略", example = "true", accessMode = READ_WRITE, position = 4) @ApiModelProperty(value = "Whether to enable this policy",
notes = "是否启用此策略",
example = "true", accessMode = READ_WRITE, position = 4)
private boolean enable = true; private boolean enable = true;
@ApiModelProperty(value = "是否转发所有", example = "false", accessMode = READ_WRITE, position = 5) @ApiModelProperty(value = "Whether to forward all",
notes = "是否转发所有",
example = "false", accessMode = READ_WRITE, position = 5)
private boolean filterAll = true; private boolean filterAll = true;
@ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 7) @ApiModelProperty(value = "The creator of this record",
notes = "此条记录创建者",
example = "tom", accessMode = READ_ONLY, position = 7)
private String creator; private String creator;
@ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 8) @ApiModelProperty(value = "This record was last modified by",
notes = "此条记录最新修改者",
example = "tom", accessMode = READ_ONLY, position = 8)
private String modifier; private String modifier;
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 9) @ApiModelProperty(value = "This record creation time (millisecond timestamp)",
notes = "记录创建时间(毫秒时间戳)",
example = "1612198922000", accessMode = READ_ONLY, position = 9)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtCreate; private LocalDateTime gmtCreate;
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 10) @ApiModelProperty(value = "Record the latest modification time (timestamp in milliseconds)",
notes = "记录最新修改时间(毫秒时间戳)",
example = "1612198444000", accessMode = READ_ONLY, position = 10)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtUpdate; private LocalDateTime gmtUpdate;

View File

@@ -2,6 +2,8 @@ package com.usthe.common.util;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -15,7 +17,7 @@ public class CommonUtil {
private static final Pattern EMAIL_PATTERN = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"); 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])|(15[0-9])|(18[0-9])|(17[0-9]))+\\d{8})?$"); 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 int PHONE_LENGTH = 11; private static final int PHONE_LENGTH = 11;
@@ -36,6 +38,30 @@ 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 邮箱 * @param email 邮箱

View File

@@ -0,0 +1,40 @@
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

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

View File

@@ -18,10 +18,11 @@ TDengine是一款国产的开源物联网时序型数据库我们使用其替
``` ```
2. Docker安装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 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 -e TZ=Asia/Shanghai tdengine/tdengine:2.4.0.12
526aa188da767ae94b244226a2b2eec2b5f17dd8eff594533d9ec0cd0f3a1ccd 526aa188da767ae94b244226a2b2eec2b5f17dd8eff594533d9ec0cd0f3a1ccd
``` ```
`-v /opt/taosdata:/var/lib/taos` 为tdengine数据目录本地持久化挂载需将`/opt/taosdata`替换为实际本地存在的目录 `-v /opt/taosdata:/var/lib/taos` 为tdengine数据目录本地持久化挂载需将`/opt/taosdata`替换为实际本地存在的目录
`-e TZ="Asia/Shanghai"` 为tdengine设置时区这里可选设置对应的时区
使用```$ docker ps```查看数据库是否启动成功 使用```$ docker ps```查看数据库是否启动成功
### 创建数据库实例 ### 创建数据库实例

View File

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

View File

@@ -30,7 +30,9 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* Alarm information storage and distribution
* 告警信息入库分发 * 告警信息入库分发
*
* @author tom * @author tom
* @date 2021/12/10 12:58 * @date 2021/12/10 12:58
*/ */
@@ -70,7 +72,7 @@ public class DispatchAlarm {
try { try {
Alert alert = dataQueue.pollAlertData(); Alert alert = dataQueue.pollAlertData();
if (alert != null) { if (alert != null) {
// 判断告警类型入库 // Determining alarm type storage 判断告警类型入库
storeAlertData(alert); storeAlertData(alert);
// 通知分发 // 通知分发
sendAlertDataListener(alert); sendAlertDataListener(alert);
@@ -86,7 +88,7 @@ public class DispatchAlarm {
} }
private void storeAlertData(Alert alert) { private void storeAlertData(Alert alert) {
// todo 使用缓存不直接操作库 // todo Using the cache does not directly manipulate the library 使用缓存不直接操作库
Monitor monitor = monitorService.getMonitor(alert.getMonitorId()); Monitor monitor = monitorService.getMonitor(alert.getMonitorId());
if (monitor == null) { if (monitor == null) {
log.warn("Dispatch alarm the monitorId: {} not existed, ignored.", alert.getMonitorId()); log.warn("Dispatch alarm the monitorId: {} not existed, ignored.", alert.getMonitorId());
@@ -94,50 +96,70 @@ public class DispatchAlarm {
} }
alert.setMonitorName(monitor.getName()); alert.setMonitorName(monitor.getName());
if (monitor.getStatus() == CommonConstants.UN_MANAGE_CODE) { if (monitor.getStatus() == CommonConstants.UN_MANAGE_CODE) {
// When monitoring is not managed, ignore and silence its alarm messages
// 当监控未管理时 忽略静默其告警信息 // 当监控未管理时 忽略静默其告警信息
return; return;
} }
if (monitor.getStatus() == CommonConstants.AVAILABLE_CODE) { if (monitor.getStatus() == CommonConstants.AVAILABLE_CODE) {
if (CommonConstants.AVAILABLE.equals(alert.getTarget())) { if (CommonConstants.AVAILABLE.equals(alert.getTarget())) {
// Availability Alarm Need to change the monitoring status to unavailable
// 可用性告警 需变更监控状态为不可用 // 可用性告警 需变更监控状态为不可用
monitorService.updateMonitorStatus(monitor.getId(), CommonConstants.UN_AVAILABLE_CODE); monitorService.updateMonitorStatus(monitor.getId(), CommonConstants.UN_AVAILABLE_CODE);
} else if (CommonConstants.REACHABLE.equals(alert.getTarget())) { } else if (CommonConstants.REACHABLE.equals(alert.getTarget())) {
// Reachability alarm The monitoring status needs to be changed to unreachable
// 可达性告警 需变更监控状态为不可达 // 可达性告警 需变更监控状态为不可达
monitorService.updateMonitorStatus(monitor.getId(), CommonConstants.UN_REACHABLE_CODE); monitorService.updateMonitorStatus(monitor.getId(), CommonConstants.UN_REACHABLE_CODE);
} }
} else { } else {
// If the alarm is restored, the monitoring state needs to be restored
// 若是恢复告警 需对监控状态进行恢复 // 若是恢复告警 需对监控状态进行恢复
if (alert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) { if (alert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) {
monitorService.updateMonitorStatus(alert.getMonitorId(), CommonConstants.AVAILABLE_CODE); monitorService.updateMonitorStatus(alert.getMonitorId(), CommonConstants.AVAILABLE_CODE);
} }
} }
// 告警落库 // Alarm drop library 告警落库
alertService.addAlert(alert); alertService.addAlert(alert);
} }
private void sendAlertDataListener(Alert alert) { private void sendAlertDataListener(Alert alert) {
// todo 转发配置的邮件 微信 webhook // todo Forward configured email WeChat webhook 转发配置的邮件 微信 webhook
List<NoticeReceiver> receivers = matchReceiverByNoticeRules(alert); List<NoticeReceiver> receivers = matchReceiverByNoticeRules(alert);
// todo 发送通知这里暂时单线程 // todo Send notification here temporarily single thread 发送通知这里暂时单线程
for (NoticeReceiver receiver : receivers) { for (NoticeReceiver receiver : receivers) {
switch (receiver.getType()) { switch (receiver.getType()) {
// todo 短信通知 // todo SMS notification 短信通知
case 0: break; case 0:
case 1: sendEmailAlert(receiver, alert); break; break;
case 2: sendWebHookAlert(receiver, alert); break; case 1:
case 3: sendWeChatAlert(receiver, alert); break; sendEmailAlert(receiver, alert);
case 4: sendWeWorkRobotAlert(receiver, alert); break; break;
case 5: sendDingTalkRobotAlert(receiver, alert); break; case 2:
case 6: sendFlyBookAlert(receiver,alert); break; sendWebHookAlert(receiver, alert);
default: break; break;
case 3:
sendWeChatAlert(receiver, alert);
break;
case 4:
sendWeWorkRobotAlert(receiver, alert);
break;
case 5:
sendDingTalkRobotAlert(receiver, alert);
break;
case 6:
sendFlyBookAlert(receiver, alert);
break;
default:
break;
} }
} }
} }
/** /**
* Send alert information through FeiShu
* 通过飞书发送告警信息 * 通过飞书发送告警信息
* @param receiver 接收人 *
* @param alert 告警信息 * @param receiver Notification configuration information 通知配置信息
* @param alert Alarm information 告警信息
*/ */
private void sendFlyBookAlert(NoticeReceiver receiver, Alert alert) { private void sendFlyBookAlert(NoticeReceiver receiver, Alert alert) {
FlyBookWebHookDto flyBookWebHookDto = new FlyBookWebHookDto(); FlyBookWebHookDto flyBookWebHookDto = new FlyBookWebHookDto();
@@ -183,9 +205,11 @@ public class DispatchAlarm {
} }
/** /**
* Send alarm information through DingTalk robot
* 通过钉钉机器人发送告警信息 * 通过钉钉机器人发送告警信息
* @param receiver 通知配置信息 *
* @param alert 告警信息 * @param receiver Notification configuration information 通知配置信息
* @param alert Alarm information 告警信息
*/ */
private void sendDingTalkRobotAlert(NoticeReceiver receiver, Alert alert) { private void sendDingTalkRobotAlert(NoticeReceiver receiver, Alert alert) {
DingTalkWebHookDto dingTalkWebHookDto = new DingTalkWebHookDto(); DingTalkWebHookDto dingTalkWebHookDto = new DingTalkWebHookDto();
@@ -216,9 +240,11 @@ public class DispatchAlarm {
} }
/** /**
* Send alarm information through enterprise WeChat
* 通过企业微信发送告警信息 * 通过企业微信发送告警信息
* @param receiver 通知配置信息 *
* @param alert 告警信息 * @param receiver Notification configuration information 通知配置信息
* @param alert Alarm information 告警信息
*/ */
private void sendWeWorkRobotAlert(NoticeReceiver receiver, Alert alert) { private void sendWeWorkRobotAlert(NoticeReceiver receiver, Alert alert) {
WeWorkWebHookDto weWorkWebHookDTO = new WeWorkWebHookDto(); WeWorkWebHookDto weWorkWebHookDTO = new WeWorkWebHookDto();
@@ -231,7 +257,7 @@ public class DispatchAlarm {
if (alert.getPriority() < CommonConstants.ALERT_PRIORITY_CODE_WARNING) { if (alert.getPriority() < CommonConstants.ALERT_PRIORITY_CODE_WARNING) {
content.append("告警级别 : <font color=\"warning\">") content.append("告警级别 : <font color=\"warning\">")
.append(CommonUtil.transferAlertPriority(alert.getPriority())).append("</font>\n"); .append(CommonUtil.transferAlertPriority(alert.getPriority())).append("</font>\n");
}else { } else {
content.append("告警级别 : <font color=\"comment\">") content.append("告警级别 : <font color=\"comment\">")
.append(CommonUtil.transferAlertPriority(alert.getPriority())).append("</font>\n"); .append(CommonUtil.transferAlertPriority(alert.getPriority())).append("</font>\n");
} }
@@ -273,28 +299,28 @@ public class DispatchAlarm {
} }
private void sendEmailAlert(final NoticeReceiver receiver,final Alert alert){ private void sendEmailAlert(final NoticeReceiver receiver, final Alert alert) {
try{ try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage(); MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8"); MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
messageHelper.setSubject("TanCloud探云-监控告警"); messageHelper.setSubject("TanCloud探云-监控告警");
//设置发件人Email //Set sender Email 设置发件人Email
messageHelper.setFrom(emailFromUser); messageHelper.setFrom(emailFromUser);
//设定收件人Email //Set recipient Email 设定收件人Email
messageHelper.setTo(receiver.getEmail()); messageHelper.setTo(receiver.getEmail());
messageHelper.setSentDate(new Date()); messageHelper.setSentDate(new Date());
//构建邮件模版 //Build email templates 构建邮件模版
String process = mailService.buildAlertHtmlTemplate(alert); String process = mailService.buildAlertHtmlTemplate(alert);
//设置邮件内容模版 //Set Email Content Template 设置邮件内容模版
messageHelper.setText(process,true); messageHelper.setText(process, true);
javaMailSender.send(mimeMessage); javaMailSender.send(mimeMessage);
}catch (Exception e){ } catch (Exception e) {
log.error("[邮箱告警] errorException information={}",e.getMessage()); log.error("[Email Alert] ExceptionException information={}", e.getMessage());
} }
} }
private List<NoticeReceiver> matchReceiverByNoticeRules(Alert alert) { private List<NoticeReceiver> matchReceiverByNoticeRules(Alert alert) {
// todo 使用缓存 // todo use cache 使用缓存
return noticeConfigService.getReceiverFilterRule(alert); return noticeConfigService.getReceiverFilterRule(alert);
} }

View File

@@ -28,11 +28,13 @@ import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/** /**
* Message Notification Configuration API
* 消息通知配置API * 消息通知配置API
*
* @author tom * @author tom
* @date 2021/12/16 16:18 * @date 2021/12/16 16:18
*/ */
@Api(tags = "消息通知配置API") @Api(tags = "en: Message Notification Configuration API,zh: 消息通知配置API")
@RestController() @RestController()
@RequestMapping(value = "/notice", produces = {APPLICATION_JSON_VALUE}) @RequestMapping(value = "/notice", produces = {APPLICATION_JSON_VALUE})
public class NoticeConfigController { public class NoticeConfigController {
@@ -41,33 +43,36 @@ public class NoticeConfigController {
private NoticeConfigService noticeConfigService; private NoticeConfigService noticeConfigService;
@PostMapping(path = "/receiver") @PostMapping(path = "/receiver")
@ApiOperation(value = "新增接收人", notes = "新增一个接收人") @ApiOperation(value = "Add a recipient", notes = "新增一个接收人")
public ResponseEntity<Message<Void>> addNewNoticeReceiver(@Valid @RequestBody NoticeReceiver noticeReceiver) { public ResponseEntity<Message<Void>> addNewNoticeReceiver(@Valid @RequestBody NoticeReceiver noticeReceiver) {
noticeConfigService.addReceiver(noticeReceiver); noticeConfigService.addReceiver(noticeReceiver);
return ResponseEntity.ok(new Message<>("Add success")); return ResponseEntity.ok(new Message<>("Add success"));
} }
@PutMapping(path = "/receiver") @PutMapping(path = "/receiver")
@ApiOperation(value = "修改接收人", notes = "修改已存在的接收人信息") @ApiOperation(value = "Modify existing recipient information", notes = "修改已存在的接收人信息")
public ResponseEntity<Message<Void>> editNoticeReceiver(@Valid @RequestBody NoticeReceiver noticeReceiver) { public ResponseEntity<Message<Void>> editNoticeReceiver(@Valid @RequestBody NoticeReceiver noticeReceiver) {
noticeConfigService.editReceiver(noticeReceiver); noticeConfigService.editReceiver(noticeReceiver);
return ResponseEntity.ok(new Message<>("Edit success")); return ResponseEntity.ok(new Message<>("Edit success"));
} }
@DeleteMapping(path = "/receiver/{id}") @DeleteMapping(path = "/receiver/{id}")
@ApiOperation(value = "删除指定接收人", notes = "删除已存在的接收人信息") @ApiOperation(value = "Delete existing recipient information", notes = "删除已存在的接收人信息")
public ResponseEntity<Message<Void>> deleteNoticeReceiver( public ResponseEntity<Message<Void>> deleteNoticeReceiver(
@ApiParam(value = "接收人ID", example = "6565463543") @PathVariable("id") final Long receiverId) { @ApiParam(value = "en: Recipient ID,zh: 接收人ID", example = "6565463543") @PathVariable("id") final Long receiverId) {
// 不存在或删除成功都返回成功 // Returns success if it does not exist or if the deletion is successful
// todo 不存在或删除成功都返回成功
noticeConfigService.deleteReceiver(receiverId); noticeConfigService.deleteReceiver(receiverId);
return ResponseEntity.ok(new Message<>("Delete success")); return ResponseEntity.ok(new Message<>("Delete success"));
} }
@GetMapping(path = "/receivers") @GetMapping(path = "/receivers")
@ApiOperation(value = "查询消息通知接收人", notes = "根据查询过滤项获取消息通知接收人列表") @ApiOperation(value = "Get a list of message notification recipients based on query filter items",
notes = "根据查询过滤项获取消息通知接收人列表")
public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers( public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers(
@ApiParam(value = "接收人名称,模糊查询", example = "tom") @RequestParam(required = false) final String name) { @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) -> { Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction(); Predicate predicate = criteriaBuilder.conjunction();
if (name != null && !"".equals(name)) { if (name != null && !"".equals(name)) {
@@ -83,32 +88,34 @@ public class NoticeConfigController {
@PostMapping(path = "/rule") @PostMapping(path = "/rule")
@ApiOperation(value = "新增通知策略", notes = "新增一个通知策略") @ApiOperation(value = "Add a notification policy", notes = "新增一个通知策略")
public ResponseEntity<Message<Void>> addNewNoticeRule(@Valid @RequestBody NoticeRule noticeRule) { public ResponseEntity<Message<Void>> addNewNoticeRule(@Valid @RequestBody NoticeRule noticeRule) {
noticeConfigService.addNoticeRule(noticeRule); noticeConfigService.addNoticeRule(noticeRule);
return ResponseEntity.ok(new Message<>("Add success")); return ResponseEntity.ok(new Message<>("Add success"));
} }
@PutMapping(path = "/rule") @PutMapping(path = "/rule")
@ApiOperation(value = "修改通知策略", notes = "修改已存在的通知策略信息") @ApiOperation(value = "Modify existing notification policy information", notes = "修改已存在的通知策略信息")
public ResponseEntity<Message<Void>> editNoticeRule(@Valid @RequestBody NoticeRule noticeRule) { public ResponseEntity<Message<Void>> editNoticeRule(@Valid @RequestBody NoticeRule noticeRule) {
noticeConfigService.editNoticeRule(noticeRule); noticeConfigService.editNoticeRule(noticeRule);
return ResponseEntity.ok(new Message<>("Edit success")); return ResponseEntity.ok(new Message<>("Edit success"));
} }
@DeleteMapping(path = "/rule/{id}") @DeleteMapping(path = "/rule/{id}")
@ApiOperation(value = "删除指定通知策略", notes = "删除已存在的通知策略信息") @ApiOperation(value = "Delete existing notification policy information", notes = "删除已存在的通知策略信息")
public ResponseEntity<Message<Void>> deleteNoticeRule( public ResponseEntity<Message<Void>> deleteNoticeRule(
@ApiParam(value = "通知策略ID", example = "6565463543") @PathVariable("id") final Long ruleId) { @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 不存在或删除成功都返回成功
noticeConfigService.deleteNoticeRule(ruleId); noticeConfigService.deleteNoticeRule(ruleId);
return ResponseEntity.ok(new Message<>("Delete success")); return ResponseEntity.ok(new Message<>("Delete success"));
} }
@GetMapping(path = "/rules") @GetMapping(path = "/rules")
@ApiOperation(value = "查询消息通知策略", notes = "根据查询过滤项获取消息通知策略列表") @ApiOperation(value = "Get a list of message notification policies based on query filter items",
notes = "根据查询过滤项获取消息通知策略列表")
public ResponseEntity<Message<List<NoticeRule>>> getRules( public ResponseEntity<Message<List<NoticeRule>>> getRules(
@ApiParam(value = "接收人名称,模糊查询", example = "rule1") @RequestParam(required = false) final String name) { @ApiParam(value = "en: Recipient name,zh: 接收人名称,模糊查询", example = "rule1") @RequestParam(required = false) final String name) {
Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> { Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction(); Predicate predicate = criteriaBuilder.conjunction();

View File

@@ -18,11 +18,13 @@ import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/** /**
* System Summary Statistics API
* 系统摘要统计API * 系统摘要统计API
*
* @author tom * @author tom
* @date 2021/12/7 15:57 * @date 2021/12/7 15:57
*/ */
@Api(tags = "系统摘要统计API") @Api(tags = "en: System Summary Statistics API,zh: 系统摘要统计API")
@RestController @RestController
@RequestMapping(path = "/summary", produces = {APPLICATION_JSON_VALUE}) @RequestMapping(path = "/summary", produces = {APPLICATION_JSON_VALUE})
public class SummaryController { public class SummaryController {
@@ -31,7 +33,7 @@ public class SummaryController {
private MonitorService monitorService; private MonitorService monitorService;
@GetMapping @GetMapping
@ApiOperation(value = "查询应用类别监控统计", notes = "查询所有应用类别监控统计信息") @ApiOperation(value = "Query all application category monitoring statistics", notes = "查询所有应用类别监控统计信息")
public ResponseEntity<Message<Dashboard>> appMonitors() { public ResponseEntity<Message<Dashboard>> appMonitors() {
List<AppCount> appsCount = monitorService.getAllAppMonitorsCount(); List<AppCount> appsCount = monitorService.getAllAppMonitorsCount();
Message<Dashboard> message = new Message<>(new Dashboard(appsCount)); Message<Dashboard> message = new Message<>(new Dashboard(appsCount));

View File

@@ -14,6 +14,7 @@ import java.util.Set;
/** /**
* AuthResources 数据库操作 * AuthResources 数据库操作
*
* @author tomsun28 * @author tomsun28
* @date 2021/11/14 11:24 * @date 2021/11/14 11:24
*/ */
@@ -22,12 +23,14 @@ public interface MonitorDao extends JpaRepository<Monitor, Long>, JpaSpecificati
/** /**
* 根据监控ID列表删除监控 * 根据监控ID列表删除监控
*
* @param monitorIds 监控ID列表 * @param monitorIds 监控ID列表
*/ */
void deleteAllByIdIn(Set<Long> monitorIds); void deleteAllByIdIn(Set<Long> monitorIds);
/** /**
* 根据监控ID列表查询监控 * 根据监控ID列表查询监控
*
* @param monitorIds 监控ID列表 * @param monitorIds 监控ID列表
* @return 监控列表 * @return 监控列表
*/ */
@@ -35,6 +38,7 @@ public interface MonitorDao extends JpaRepository<Monitor, Long>, JpaSpecificati
/** /**
* 根据监控类型查询监控 * 根据监控类型查询监控
*
* @param app 监控类型 * @param app 监控类型
* @return 监控列表 * @return 监控列表
*/ */
@@ -42,29 +46,35 @@ public interface MonitorDao extends JpaRepository<Monitor, Long>, JpaSpecificati
/** /**
* 查询已下发采集任务的监控 * 查询已下发采集任务的监控
*
* @param status 监控状态 * @param status 监控状态
* @return 监控列表 * @return 监控列表
*/ */
List<Monitor> findMonitorsByStatusNotInAndAndJobIdNotNull(List<Byte> status); List<Monitor> findMonitorsByStatusNotInAndAndJobIdNotNull(List<Byte> status);
/** /**
* 根据监控名称查询监控 * Query monitoring by monitoring name 根据监控名称查询监控
* @param name 监控名称 *
* @return 监控列表 * @param name monitoring name 监控名称
* @return monitoring list 监控列表
*/ */
Optional<Monitor> findMonitorByNameEquals(String name); Optional<Monitor> findMonitorByNameEquals(String name);
/** /**
* Query the monitoring category - the number of monitoring corresponding to the status
* 查询监控类别-状态对应的监控数量 * 查询监控类别-状态对应的监控数量
* @return 监控类别-状态与监控数量映射 *
* @return Monitoring Category-Status and Monitoring Quantity Mapping 监控类别-状态与监控数量映射
*/ */
@Query("select new com.usthe.manager.pojo.dto.AppCount(mo.app, mo.status, COUNT(mo.id)) from Monitor mo group by mo.app, mo.status") @Query("select new com.usthe.manager.pojo.dto.AppCount(mo.app, mo.status, COUNT(mo.id)) from Monitor mo group by mo.app, mo.status")
List<AppCount> findAppsStatusCount(); List<AppCount> findAppsStatusCount();
/** /**
* Update the status of the specified monitor
* 更新指定监控的状态 * 更新指定监控的状态
* @param id 监控ID *
* @param status 监控状态 * @param id Monitor ID 监控ID
* @param status 监控状态 Monitor Status
*/ */
@Modifying @Modifying
@Query("update Monitor set status = :status where id = :id") @Query("update Monitor set status = :status where id = :id")

View File

@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
/** /**
* 企业微信机器人请求消息体 * 企业微信机器人请求消息体
*
* @author 花城 * @author 花城
* @version 1.0 * @version 1.0
* @date 2022/2/21 6:55 下午 * @date 2022/2/21 6:55 下午
@@ -18,7 +19,14 @@ import lombok.NoArgsConstructor;
public class WeWorkWebHookDto { public class WeWorkWebHookDto {
public static final String WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key="; public static final String WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=";
/**
* markdown格式
*/
private static final String MARKDOWN = "markdown"; private static final String MARKDOWN = "markdown";
/**
* 文本格式
*/
private static final String TEXT = "TEXT";
/** /**
* 消息类型 * 消息类型

View File

@@ -6,7 +6,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 邮箱发送服务 * Email delivery service 邮箱发送服务
* *
* @author 花城 * @author 花城
* @version 1.0 * @version 1.0
@@ -15,9 +15,11 @@ import org.springframework.stereotype.Service;
public interface MailService { public interface MailService {
/** /**
* Build an alert email template
* 构建告警邮件模版 * 构建告警邮件模版
* @param alert 告警信息 *
* @return 邮件内容 * @param alert Alarm data element information 告警数据元信息
* @return content of email 邮件内容
*/ */
String buildAlertHtmlTemplate(Alert alert); String buildAlertHtmlTemplate(Alert alert);
} }

View File

@@ -15,6 +15,7 @@ import java.util.Set;
/** /**
* 监控管理服务 * 监控管理服务
*
* @author tomsun28 * @author tomsun28
* @date 2021/11/14 11:28 * @date 2021/11/14 11:28
*/ */
@@ -23,6 +24,7 @@ public interface MonitorService {
/** /**
* 监控可用性探测 * 监控可用性探测
*
* @param monitor 监控实体信息 * @param monitor 监控实体信息
* @param params 参数信息 * @param params 参数信息
* @throws MonitorDetectException 探测失败抛出 * @throws MonitorDetectException 探测失败抛出
@@ -31,6 +33,7 @@ public interface MonitorService {
/** /**
* 新增监控 * 新增监控
*
* @param monitor 监控实体 * @param monitor 监控实体
* @param params 参数信息 * @param params 参数信息
* @throws RuntimeException 新增过程异常抛出 * @throws RuntimeException 新增过程异常抛出
@@ -39,6 +42,7 @@ public interface MonitorService {
/** /**
* 校验请求数据参数正确性 * 校验请求数据参数正确性
*
* @param monitorDto monitorDto * @param monitorDto monitorDto
* @param isModify 是否是修改监控 * @param isModify 是否是修改监控
* @throws IllegalArgumentException 校验参数错误抛出 * @throws IllegalArgumentException 校验参数错误抛出
@@ -47,6 +51,7 @@ public interface MonitorService {
/** /**
* 修改更新监控 * 修改更新监控
*
* @param monitor 监控实体 * @param monitor 监控实体
* @param params 参数信息 * @param params 参数信息
* @throws RuntimeException 修改过程中异常抛出 * @throws RuntimeException 修改过程中异常抛出
@@ -55,6 +60,7 @@ public interface MonitorService {
/** /**
* 删除监控 * 删除监控
*
* @param id 监控ID * @param id 监控ID
* @throws RuntimeException 删除过程中异常抛出 * @throws RuntimeException 删除过程中异常抛出
*/ */
@@ -62,6 +68,7 @@ public interface MonitorService {
/** /**
* 批量删除监控 * 批量删除监控
*
* @param ids 监控ID * @param ids 监控ID
* @throws RuntimeException 删除过程中异常抛出 * @throws RuntimeException 删除过程中异常抛出
*/ */
@@ -69,6 +76,7 @@ public interface MonitorService {
/** /**
* 获取监控信息 * 获取监控信息
*
* @param id 监控ID * @param id 监控ID
* @return MonitorDto * @return MonitorDto
* @throws RuntimeException 查询过程中异常抛出 * @throws RuntimeException 查询过程中异常抛出
@@ -77,6 +85,7 @@ public interface MonitorService {
/** /**
* 动态条件查询 * 动态条件查询
*
* @param specification 查询条件 * @param specification 查询条件
* @param pageRequest 分页参数 * @param pageRequest 分页参数
* @return 查询结果 * @return 查询结果
@@ -85,38 +94,46 @@ public interface MonitorService {
/** /**
* 根据监控ID列表批量取消纳管监控项 * 根据监控ID列表批量取消纳管监控项
*
* @param ids 监控IDs * @param ids 监控IDs
*/ */
void cancelManageMonitors(HashSet<Long> ids); void cancelManageMonitors(HashSet<Long> ids);
/** /**
* 根据监控ID列表批量启动纳管监控项 * 根据监控ID列表批量启动纳管监控项
*
* @param ids 监控IDs * @param ids 监控IDs
*/ */
void enableManageMonitors(HashSet<Long> ids); void enableManageMonitors(HashSet<Long> ids);
/** /**
* 查询监控类别及其对应的监控数量 * 查询监控类别及其对应的监控数量
*
* @return 监控类别与监控数量映射 * @return 监控类别与监控数量映射
*/ */
List<AppCount> getAllAppMonitorsCount(); List<AppCount> getAllAppMonitorsCount();
/** /**
* Query monitoring
* 查询监控 * 查询监控
* @param monitorId 监控ID *
* @return 监控信息 * @param monitorId Monitor ID 监控ID
* @return Monitor information 监控信息
*/ */
Monitor getMonitor(Long monitorId); Monitor getMonitor(Long monitorId);
/** /**
* Update the status of the specified monitor
* 更新指定监控的状态 * 更新指定监控的状态
* @param monitorId 监控ID *
* @param status 监控状态 * @param monitorId monitorId 监控ID
* @param status monitor status 监控状态
*/ */
void updateMonitorStatus(Long monitorId, byte status); void updateMonitorStatus(Long monitorId, byte status);
/** /**
* 查询指定监控类型下的所有监控信息列表 * 查询指定监控类型下的所有监控信息列表
*
* @param app 监控类型 * @param app 监控类型
* @return 监控列表 * @return 监控列表
*/ */

View File

@@ -8,66 +8,86 @@ import org.springframework.data.jpa.domain.Specification;
import java.util.List; import java.util.List;
/** /**
* Message notification configuration interface
* 消息通知配置接口 * 消息通知配置接口
*
* @author tom * @author tom
* @date 2021/12/16 16:14 * @date 2021/12/16 16:14
*/ */
public interface NoticeConfigService { public interface NoticeConfigService {
/** /**
* Dynamic conditional query
* 动态条件查询 * 动态条件查询
* @param specification 查询条件 *
* @return 查询结果 * @param specification Query conditions 查询条件
* @return Search result 查询结果
*/ */
List<NoticeReceiver> getNoticeReceivers(Specification<NoticeReceiver> specification); List<NoticeReceiver> getNoticeReceivers(Specification<NoticeReceiver> specification);
/** /**
* Dynamic conditional query
* 动态条件查询 * 动态条件查询
* @param specification 查询条件 *
* @return 查询结果 * @param specification Query conditions 查询条件
* @return Search result 查询结果
*/ */
List<NoticeRule> getNoticeRules(Specification<NoticeRule> specification); List<NoticeRule> getNoticeRules(Specification<NoticeRule> specification);
/** /**
* Add a notification recipient
* 新增一个通知接收人 * 新增一个通知接收人
* @param noticeReceiver 接收人信息 *
* @param noticeReceiver recipient information 接收人信息
*/ */
void addReceiver(NoticeReceiver noticeReceiver); void addReceiver(NoticeReceiver noticeReceiver);
/** /**
* Modify notification recipients
* 修改通知接收人 * 修改通知接收人
* @param noticeReceiver 接收人信息 *
* @param noticeReceiver recipient information 接收人信息
*/ */
void editReceiver(NoticeReceiver noticeReceiver); void editReceiver(NoticeReceiver noticeReceiver);
/** /**
* Delete recipient information based on recipient ID
* 根据接收人ID删除接收人信息 * 根据接收人ID删除接收人信息
* @param receiverId 接收人ID *
* @param receiverId Recipient ID 接收人ID
*/ */
void deleteReceiver(Long receiverId); void deleteReceiver(Long receiverId);
/** /**
* Added notification policy
* 新增通知策略 * 新增通知策略
* @param noticeRule 通知策略 *
* @param noticeRule Notification strategy 通知策略
*/ */
void addNoticeRule(NoticeRule noticeRule); void addNoticeRule(NoticeRule noticeRule);
/** /**
* Modify Notification Policy
* 修改通知策略 * 修改通知策略
* @param noticeRule 通知策略 *
* @param noticeRule Notification strategy 通知策略
*/ */
void editNoticeRule(NoticeRule noticeRule); void editNoticeRule(NoticeRule noticeRule);
/** /**
* Delete the specified notification policy
* 删除指定的通知策略 * 删除指定的通知策略
* @param ruleId 通知策略ID *
* @param ruleId Notification Policy ID 通知策略ID
*/ */
void deleteNoticeRule(Long ruleId); void deleteNoticeRule(Long ruleId);
/** /**
* According to the alarm information matching all notification policies, filter out the recipients who need to be notified
* 根据告警信息与所有通知策略匹配,过滤出需要通知的接收人 * 根据告警信息与所有通知策略匹配,过滤出需要通知的接收人
* @param alert 告警信息 *
* @return 接收人 * @param alert Alarm information 告警信息
* @return Receiver 接收人
*/ */
List<NoticeReceiver> getReceiverFilterRule(Alert alert); List<NoticeReceiver> getReceiverFilterRule(Alert alert);
} }

View File

@@ -40,6 +40,7 @@ import java.util.stream.Collectors;
/** /**
* 监控管理服务实现 * 监控管理服务实现
*
* @author tomsun28 * @author tomsun28
* @date 2021/11/14 13:06 * @date 2021/11/14 13:06
*/ */
@@ -391,7 +392,8 @@ public class MonitorServiceImpl implements MonitorService {
if (appCounts == null) { if (appCounts == null) {
return null; return null;
} }
// 关联大类别信息 计算每个状态对应数量 //Statistical category information, calculate the number of corresponding states for each monitor
//统计类别信息,计算每个监控分别对应状态的数量
Map<String, AppCount> appCountMap = new HashMap<>(appCounts.size()); Map<String, AppCount> appCountMap = new HashMap<>(appCounts.size());
for (AppCount item : appCounts) { for (AppCount item : appCounts) {
AppCount appCount = appCountMap.getOrDefault(item.getApp(), new AppCount()); AppCount appCount = appCountMap.getOrDefault(item.getApp(), new AppCount());
@@ -409,10 +411,13 @@ public class MonitorServiceImpl implements MonitorService {
case CommonConstants.UN_REACHABLE_CODE: case CommonConstants.UN_REACHABLE_CODE:
appCount.setUnReachableSize(appCount.getUnReachableSize() + item.getSize()); appCount.setUnReachableSize(appCount.getUnReachableSize() + item.getSize());
break; break;
default: break; default:
break;
} }
appCountMap.put(item.getApp(), appCount); appCountMap.put(item.getApp(), appCount);
} }
//Traverse the map obtained by statistics and convert it into a List<App Count> result set
//遍历统计得到的map转换成List<App Count>结果集
return appCountMap.values().stream().peek(item -> { return appCountMap.values().stream().peek(item -> {
item.setSize(item.getAvailableSize() + item.getUnManageSize() item.setSize(item.getAvailableSize() + item.getUnManageSize()
+ item.getUnReachableSize() + item.getUnAvailableSize()); + item.getUnReachableSize() + item.getUnAvailableSize());

View File

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

View File

@@ -68,6 +68,26 @@ metrics:
- field: context_switch - field: context_switch
type: 0 type: 0
unit: 个数 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 # 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: ssh protocol: ssh
# 当protocol为http协议时具体的采集配置 # 当protocol为http协议时具体的采集配置
@@ -79,7 +99,7 @@ metrics:
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^ 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}'" 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}'"
parseType: oneRow parseType: oneRow
- name: memory - name: memory
@@ -101,6 +121,25 @@ metrics:
- field: available - field: available
type: 0 type: 0
unit: Mb 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 # 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: ssh protocol: ssh
# 当protocol为http协议时具体的采集配置 # 当protocol为http协议时具体的采集配置
@@ -154,6 +193,7 @@ metrics:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: interface_name - field: interface_name
type: 1 type: 1
instance: true
- field: receive_bytes - field: receive_bytes
type: 0 type: 0
unit: byte unit: byte
@@ -173,3 +213,35 @@ metrics:
timeout: ^_^timeout^_^ timeout: ^_^timeout^_^
script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}' script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}'
parseType: multiRow 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,8 +32,6 @@ metrics:
- field: database_version - field: database_version
type: 1 type: 1
instance: true instance: true
- field: database_type
type: 1
- field: hostname - field: hostname
type: 1 type: 1
- field: instance_name - field: instance_name
@@ -45,7 +43,6 @@ metrics:
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换 # (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields: aliasFields:
- VERSION - VERSION
- DATABASE_TYPE
- HOST_NAME - HOST_NAME
- INSTANCE_NAME - INSTANCE_NAME
- STARTUP_TIME - STARTUP_TIME
@@ -54,7 +51,6 @@ metrics:
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime # eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates: calculates:
- database_version=VERSION - database_version=VERSION
- database_type=DATABASE_TYPE
- hostname=HOST_NAME - hostname=HOST_NAME
- instance_name=INSTANCE_NAME - instance_name=INSTANCE_NAME
- startup_time=STARTUP_TIME - startup_time=STARTUP_TIME

View File

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

View File

@@ -90,3 +90,8 @@ param:
type: password type: password
required: false required: false
hide: true hide: true
- field: keyword
name: 关键字
type: text
required: false
hide: true

View File

@@ -52,3 +52,8 @@ param:
type: password type: password
required: false required: false
hide: true hide: true
- field: keyword
name: 关键字
type: text
required: false
hide: true

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 MiB

After

Width:  |  Height:  |  Size: 15 MiB

View File

@@ -1,8 +0,0 @@
.dynamic-button {
font-size: 20px;
margin-left: 45%;
}
.dynamic-button:hover {
font-size: 30px;
}

View File

@@ -16,7 +16,7 @@ import { MonitorService } from '../../../service/monitor.service';
@Component({ @Component({
selector: 'app-monitor-modify', selector: 'app-monitor-modify',
templateUrl: './monitor-edit.component.html', templateUrl: './monitor-edit.component.html',
styleUrls: ['./monitor-edit.component.less'] styles: []
}) })
export class MonitorEditComponent implements OnInit { export class MonitorEditComponent implements OnInit {
constructor( constructor(
@@ -72,7 +72,6 @@ export class MonitorEditComponent implements OnInit {
) )
.subscribe(message => { .subscribe(message => {
if (message.code === 0) { if (message.code === 0) {
this.paramDefines = message.data;
this.params = []; this.params = [];
this.advancedParams = []; this.advancedParams = [];
this.paramDefines = []; this.paramDefines = [];