From b9ee76f834c92fc36f51a5420b5232ba3dd820f2 Mon Sep 17 00:00:00 2001 From: MaxKey Date: Tue, 3 May 2022 11:41:47 +0800 Subject: [PATCH 01/27] notification bug --- .../src/app/core/interceptor/default.interceptor.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/web-app/src/app/core/interceptor/default.interceptor.ts b/web-app/src/app/core/interceptor/default.interceptor.ts index 551855eed32..9c8a5536211 100644 --- a/web-app/src/app/core/interceptor/default.interceptor.ts +++ b/web-app/src/app/core/interceptor/default.interceptor.ts @@ -44,6 +44,7 @@ const CODE_MESSAGE: { [key: number]: string } = { */ @Injectable() export class DefaultInterceptor implements HttpInterceptor { + private notified = false; // 是否正在刷新TOKEN过程 private refreshToking = false; private refreshToken$: BehaviorSubject = new BehaviorSubject(null); @@ -59,7 +60,10 @@ export class DefaultInterceptor implements HttpInterceptor { } private goTo(url: string): void { - setTimeout(() => this.injector.get(Router).navigateByUrl(url)); + setTimeout(() => { + this.injector.get(Router).navigateByUrl(url); + this.notified = false; + }); } private checkStatus(ev: HttpResponseBase): void { @@ -144,8 +148,11 @@ export class DefaultInterceptor implements HttpInterceptor { } private toLogin(): void { - this.notification.error(`未登录或登录已过期,请重新登录。`, ``); - this.goTo('/passport/login'); + if (!this.notified) { + this.notified = true; + this.notification.error(`未登录或登录已过期,请重新登录。`, ``); + this.goTo('/passport/login'); + } } private fillHeaders(headers?: HttpHeaders): { [name: string]: string } { From fcd28554fdee3cb85e5e23c91a4e42cd3cfd6ba4 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Tue, 3 May 2022 18:11:33 +0800 Subject: [PATCH 02/27] [monitor]feature: support tags, support alert notice dispatch by tags and priority (#111) [monitor]feature: add tags [monitor]feature: fix alert tags error [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [alerter]feature: add alert tags foreign rest api [monitor]feature: support tags, support alert notice dispatch by tags and priority [web-app]i18n Co-authored-by: xgf <13386772885@163.com> --- .../usthe/alert/calculate/CalculateAlarm.java | 15 +- .../alert/controller/AlertsController.java | 18 +- .../com/usthe/alert/service/AlertService.java | 7 + .../alert/service/impl/AlertServiceImpl.java | 43 ++++ .../usthe/common/entity/alerter/Alert.java | 34 +-- .../alerter/JsonMapAttributeConverter.java | 24 ++ .../usthe/common/entity/dto/AlertReport.java | 53 +++++ .../JsonByteListAttributeConverter.java | 33 +++ .../JsonOptionListAttributeConverter.java | 2 +- .../usthe/common/entity/manager/Monitor.java | 23 +- .../common/entity/manager/NoticeRule.java | 19 +- .../com/usthe/common/entity/manager/Tag.java | 87 +++++++ .../common/entity/manager/TagMonitorBind.java | 56 +++++ .../usthe/common/util/CommonConstants.java | 15 ++ .../component/alerter/AlertStoreHandler.java | 1 + .../alerter/impl/DbAlertStoreHandlerImpl.java | 24 +- .../DingTalkRobotAlertNotifyHandlerImpl.java | 26 ++- .../impl/FlyBookAlertNotifyHandlerImpl.java | 21 +- .../WeWorkRobotAlertNotifyHandlerImpl.java | 12 +- .../impl/WebHookAlertNotifyHandlerImpl.java | 2 +- .../manager/controller/AccountController.java | 2 +- .../manager/controller/TagController.java | 119 ++++++++++ .../java/com/usthe/manager/dao/TagDao.java | 21 ++ .../com/usthe/manager/service/TagService.java | 44 ++++ .../manager/service/impl/MailServiceImpl.java | 9 +- .../service/impl/MonitorServiceImpl.java | 30 ++- .../service/impl/NoticeConfigServiceImpl.java | 20 ++ .../manager/service/impl/TagServiceImpl.java | 54 +++++ manager/src/main/resources/sureness.yml | 10 +- script/docker-compose/conf/sureness.yml | 10 +- script/sureness.yml | 10 +- web-app/package.json | 1 + .../src/app/layout/basic/basic.component.ts | 4 + .../layout/basic/widgets/notify.component.ts | 2 +- .../layout/basic/widgets/search.component.ts | 9 +- .../layout/basic/widgets/user.component.ts | 2 +- web-app/src/app/pojo/Alert.ts | 3 + web-app/src/app/pojo/Monitor.ts | 3 + web-app/src/app/pojo/NoticeRule.ts | 3 + web-app/src/app/pojo/Tag.ts | 12 + .../alert-center/alert-center.component.html | 10 +- .../alert-center/alert-center.component.ts | 20 ++ .../alert-notice/alert-notice.component.html | 54 ++++- .../alert-notice/alert-notice.component.ts | 127 ++++++++--- .../routes/dashboard/dashboard.component.html | 2 +- .../monitor-data-table.component.ts | 4 +- .../monitor-detail.component.html | 2 +- .../monitor-edit/monitor-edit.component.html | 69 ++++++ .../monitor-edit/monitor-edit.component.ts | 80 +++++++ .../monitor-new/monitor-new.component.html | 69 ++++++ .../monitor-new/monitor-new.component.ts | 78 +++++++ .../src/app/routes/routes-routing.module.ts | 3 +- .../routes/setting/setting-routing.module.ts | 12 + .../src/app/routes/setting/setting.module.ts | 21 ++ .../routes/setting/tags/tags.component.html | 151 ++++++++++++ .../setting/tags/tags.component.spec.ts | 25 ++ .../app/routes/setting/tags/tags.component.ts | 214 ++++++++++++++++++ web-app/src/app/service/tag.service.spec.ts | 16 ++ web-app/src/app/service/tag.service.ts | 65 ++++++ web-app/src/assets/app-data.json | 8 +- web-app/src/assets/i18n/en-US.json | 18 ++ web-app/src/assets/i18n/zh-CN.json | 20 +- web-app/src/assets/i18n/zh-TW.json | 20 +- 63 files changed, 1842 insertions(+), 129 deletions(-) create mode 100644 common/src/main/java/com/usthe/common/entity/alerter/JsonMapAttributeConverter.java create mode 100644 common/src/main/java/com/usthe/common/entity/dto/AlertReport.java create mode 100644 common/src/main/java/com/usthe/common/entity/manager/JsonByteListAttributeConverter.java create mode 100644 common/src/main/java/com/usthe/common/entity/manager/Tag.java create mode 100644 common/src/main/java/com/usthe/common/entity/manager/TagMonitorBind.java create mode 100644 manager/src/main/java/com/usthe/manager/controller/TagController.java create mode 100644 manager/src/main/java/com/usthe/manager/dao/TagDao.java create mode 100644 manager/src/main/java/com/usthe/manager/service/TagService.java create mode 100644 manager/src/main/java/com/usthe/manager/service/impl/TagServiceImpl.java create mode 100644 web-app/src/app/pojo/Tag.ts create mode 100644 web-app/src/app/routes/setting/setting-routing.module.ts create mode 100644 web-app/src/app/routes/setting/setting.module.ts create mode 100644 web-app/src/app/routes/setting/tags/tags.component.html create mode 100644 web-app/src/app/routes/setting/tags/tags.component.spec.ts create mode 100644 web-app/src/app/routes/setting/tags/tags.component.ts create mode 100644 web-app/src/app/service/tag.service.spec.ts create mode 100644 web-app/src/app/service/tag.service.ts diff --git a/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java b/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java index 4e7ecb7adea..37cf0f20983 100644 --- a/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java +++ b/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java @@ -84,8 +84,11 @@ private void calculate(CollectRep.MetricsData metricsData) { if (metricsData.getPriority() == 0) { if (metricsData.getCode() != CollectRep.Code.SUCCESS) { // 采集异常 + Map tags = new HashMap<>(6); + tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitorId)); + tags.put(CommonConstants.TAG_MONITOR_APP, app); Alert.AlertBuilder alertBuilder = Alert.builder() - .monitorId(monitorId) + .tags(tags) .priority(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY) .status(CommonConstants.ALERT_STATUS_CODE_PENDING) .times(1); @@ -120,8 +123,11 @@ private void calculate(CollectRep.MetricsData metricsData) { CollectRep.Code stateCode = triggeredMonitorStateAlertMap.remove(monitorId); if (stateCode != null) { // 发送告警恢复 + Map tags = new HashMap<>(6); + tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitorId)); + tags.put(CommonConstants.TAG_MONITOR_APP, app); Alert resumeAlert = Alert.builder() - .monitorId(monitorId) + .tags(tags) .target(CommonConstants.AVAILABLE) .content("告警恢复通知, 此监控状态已恢复正常") .priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING) @@ -182,8 +188,11 @@ private void calculate(CollectRep.MetricsData metricsData) { fieldValueMap.put("app", app); fieldValueMap.put("metrics", metrics); fieldValueMap.put("metric", define.getField()); + Map tags = new HashMap<>(6); + tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitorId)); + tags.put(CommonConstants.TAG_MONITOR_APP, app); Alert alert = Alert.builder() - .monitorId(monitorId) + .tags(tags) .alertDefineId(define.getId()) .priority(define.getPriority()) .status(CommonConstants.ALERT_STATUS_CODE_PENDING) diff --git a/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java b/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java index 3c4d6b08517..a79fcdda71e 100644 --- a/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java +++ b/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java @@ -3,6 +3,7 @@ import com.usthe.alert.dto.AlertSummary; import com.usthe.common.entity.alerter.Alert; import com.usthe.alert.service.AlertService; +import com.usthe.common.entity.dto.AlertReport; import com.usthe.common.entity.dto.Message; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -13,16 +14,11 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Predicate; +import javax.validation.Valid; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -122,4 +118,12 @@ public ResponseEntity> getAlertsSummary() { Message message = new Message<>(alertSummary); return ResponseEntity.ok(message); } + + @PostMapping("/report") + @ApiOperation(value = "对外上报告警信息 接口", notes = "对外 新增一个告警") + public ResponseEntity> addNewAlertReport(@Valid @RequestBody AlertReport alertReport) { + // 校验请求数据 TODO + alertService.addNewAlertReport(alertReport); + return ResponseEntity.ok(new Message<>("Add report success")); + } } diff --git a/alerter/src/main/java/com/usthe/alert/service/AlertService.java b/alerter/src/main/java/com/usthe/alert/service/AlertService.java index 1037f3b4b16..9e10456360d 100644 --- a/alerter/src/main/java/com/usthe/alert/service/AlertService.java +++ b/alerter/src/main/java/com/usthe/alert/service/AlertService.java @@ -2,6 +2,7 @@ import com.usthe.alert.dto.AlertSummary; import com.usthe.common.entity.alerter.Alert; +import com.usthe.common.entity.dto.AlertReport; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; @@ -61,4 +62,10 @@ public interface AlertService { */ AlertSummary getAlertsSummary(); + /** + * 第三方 上报告警信息 + * @param alertReport 告警信息 + */ + void addNewAlertReport(AlertReport alertReport); + } diff --git a/alerter/src/main/java/com/usthe/alert/service/impl/AlertServiceImpl.java b/alerter/src/main/java/com/usthe/alert/service/impl/AlertServiceImpl.java index b0e00587f06..912eefb62b7 100644 --- a/alerter/src/main/java/com/usthe/alert/service/impl/AlertServiceImpl.java +++ b/alerter/src/main/java/com/usthe/alert/service/impl/AlertServiceImpl.java @@ -1,10 +1,12 @@ package com.usthe.alert.service.impl; +import com.usthe.alert.AlerterDataQueue; import com.usthe.alert.dao.AlertDao; import com.usthe.alert.dto.AlertPriorityNum; import com.usthe.alert.dto.AlertSummary; import com.usthe.common.entity.alerter.Alert; import com.usthe.alert.service.AlertService; +import com.usthe.common.entity.dto.AlertReport; import com.usthe.common.util.CommonConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -16,8 +18,12 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.HashSet; import java.util.List; +import java.util.Map; /** * Realization of Alarm Information Service 告警信息服务实现 @@ -33,6 +39,9 @@ public class AlertServiceImpl implements AlertService { @Autowired private AlertDao alertDao; + @Autowired + private AlerterDataQueue alerterDataQueue; + @Override public void addAlert(Alert alert) throws RuntimeException { alertDao.save(alert); @@ -96,4 +105,38 @@ public AlertSummary getAlertsSummary() { return alertSummary; } + @Override + public void addNewAlertReport(AlertReport alertReport) { + alerterDataQueue.addAlertData(buildAlertData(alertReport)); + } + + /** + * 对外告警信息 转换为Alert + * @param alertReport 对外告警信息 + * @return Alert实体 + */ + private Alert buildAlertData(AlertReport alertReport){ + Map annotations = alertReport.getAnnotations(); + StringBuilder sb = new StringBuilder(); + if(alertReport.getContent() == null || alertReport.getContent().length() <= 0){ + StringBuilder finalSb = sb; + annotations.forEach((k, v) -> { + finalSb.append(k).append(":").append(v).append("\n"); + }); + }else{ + sb = new StringBuilder(alertReport.getContent()); + } + + return Alert.builder() + .content("Alert Center\n" + sb) + .priority(alertReport.getPriority().byteValue()) + .status(CommonConstants.ALERT_STATUS_CODE_PENDING) + .tags(alertReport.getLabels()) + .target(CommonConstants.AVAILABLE) + .times(3) + // todo 时区问题 + .gmtCreate(LocalDateTime.ofInstant(Instant.ofEpochMilli(alertReport.getAlertTime()), ZoneOffset.of("+8"))) + .build(); + } + } diff --git a/common/src/main/java/com/usthe/common/entity/alerter/Alert.java b/common/src/main/java/com/usthe/common/entity/alerter/Alert.java index 089b18d4b98..a00de83a40b 100644 --- a/common/src/main/java/com/usthe/common/entity/alerter/Alert.java +++ b/common/src/main/java/com/usthe/common/entity/alerter/Alert.java @@ -8,16 +8,12 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; +import javax.persistence.*; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import java.time.LocalDateTime; +import java.util.Map; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; @@ -34,7 +30,7 @@ @Builder @AllArgsConstructor @NoArgsConstructor -@ApiModel(description = "en: Alarm record entity zh: 告警记录实体") +@ApiModel(description = "Alarm record entity | 告警记录实体") public class Alert { @Id @@ -50,16 +46,6 @@ public class Alert { @Length(max = 255) private String target; - @ApiModelProperty(value = "Monitoring ID associated with the alarm object", - notes = "告警对象关联的监控ID", - example = "87432674336", accessMode = READ_WRITE, position = 2) - private Long monitorId; - - @ApiModelProperty(value = "Monitoring name associated with the alarm object", - notes = "告警对象关联的监控名称", - example = "Linux_192.132.23.1", accessMode = READ_WRITE, position = 3) - private String monitorName; - @ApiModelProperty(value = "Alarm definition ID associated with the alarm", notes = "告警关联的告警定义ID", example = "8743267443543", accessMode = READ_WRITE, position = 4) @@ -93,10 +79,24 @@ public class Alert { @Max(10) private int times; + @ApiModelProperty(value = "告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{key1:value1}", accessMode = READ_WRITE, position = 8) + @Convert(converter = JsonMapAttributeConverter.class) + @SuppressWarnings("JpaAttributeTypeInspection") + private Map tags; + + @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 8) + private String creator; + + @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 9) + private String modifier; + @ApiModelProperty(value = "Alarm trigger time (timestamp in milliseconds)", notes = "告警触发时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 9) @Column(insertable = false, updatable = false) private LocalDateTime gmtCreate; + @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 11) + @Column(insertable = false, updatable = false) + private LocalDateTime gmtUpdate; } diff --git a/common/src/main/java/com/usthe/common/entity/alerter/JsonMapAttributeConverter.java b/common/src/main/java/com/usthe/common/entity/alerter/JsonMapAttributeConverter.java new file mode 100644 index 00000000000..158ac6ec83f --- /dev/null +++ b/common/src/main/java/com/usthe/common/entity/alerter/JsonMapAttributeConverter.java @@ -0,0 +1,24 @@ +package com.usthe.common.entity.alerter; + +import com.usthe.common.util.GsonUtil; + +import javax.persistence.AttributeConverter; +import java.util.Map; + +/** + * json 互转map对象字段为数据String字段 + * @author tom + * @date 2021/12/4 07:54 + */ +public class JsonMapAttributeConverter implements AttributeConverter, String> { + + @Override + public String convertToDatabaseColumn(Map attribute) { + return GsonUtil.toJson(attribute); + } + + @Override + public Map convertToEntityAttribute(String dbData) { + return GsonUtil.fromJson(dbData, Map.class); + } +} diff --git a/common/src/main/java/com/usthe/common/entity/dto/AlertReport.java b/common/src/main/java/com/usthe/common/entity/dto/AlertReport.java new file mode 100644 index 00000000000..f1425194bab --- /dev/null +++ b/common/src/main/java/com/usthe/common/entity/dto/AlertReport.java @@ -0,0 +1,53 @@ +package com.usthe.common.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; + +/** + * 告警 对外上报实体类 + * @author yuye + * @date 2022/04/30 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "告警对外上报字段") +public class AlertReport { + + @ApiModelProperty(value = "Alert record saas index ID", position = 0) + private String alertId; + + @ApiModelProperty(value = "Alert Name", position = 1) + private String alertName; + + @ApiModelProperty(value = "Alarm evaluation interval", position = 2) + private Integer alertDuration; + + @ApiModelProperty(value = "Time when the log service receives the alarm message", notes = "日志服务接收到告警消息的时间", example = "1648889320", accessMode = READ_WRITE, position = 3) + private long alertTime; + + @ApiModelProperty(value = "Alarm priority. 0: high emergency alarm red 1: medium critical serious alarm Orange 2: low warning warning alarm yellow", notes = "告警严重度。0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色", example = "1", accessMode = READ_WRITE, position = 4) + private Integer priority; + + @ApiModelProperty(value = "Alarm type. 0: the default 1 is business system exception reporting", notes = "告警类型。0:内部告警 1:外部系统上报", example = "0", accessMode = READ_WRITE, position = 5) + private Integer reportType; + + @ApiModelProperty(value = "Alarm tag information", notes = "告警标签信息((monitorId:xxx,monitorName:xxx))", example = "{key1:value1}", accessMode = READ_WRITE, position = 6) + private Map labels; + + @ApiModelProperty(value = " Alarm marking (monitorId:xxx,monitorName:xxx)", notes = "告警标注", example = "{key1:value1}", accessMode = READ_WRITE, position = 7) + private Map annotations; + + @ApiModelProperty(value = " Alarm content", notes = "告警内容", example = "对外报警内容", accessMode = READ_WRITE, position = 8) + private String content; + +} diff --git a/common/src/main/java/com/usthe/common/entity/manager/JsonByteListAttributeConverter.java b/common/src/main/java/com/usthe/common/entity/manager/JsonByteListAttributeConverter.java new file mode 100644 index 00000000000..8bb5dc10269 --- /dev/null +++ b/common/src/main/java/com/usthe/common/entity/manager/JsonByteListAttributeConverter.java @@ -0,0 +1,33 @@ +package com.usthe.common.entity.manager; + +import com.usthe.common.util.GsonUtil; + +import javax.persistence.AttributeConverter; +import java.util.LinkedList; +import java.util.List; + +/** + * json 互转list Byte 对象字段为数据String字段 + * @author tom + * @date 2021/12/4 07:54 + */ +public class JsonByteListAttributeConverter implements AttributeConverter, String> { + + @Override + public String convertToDatabaseColumn(List attribute) { + return GsonUtil.toJson(attribute); + } + + @Override + public List convertToEntityAttribute(String dbData) { + List list = GsonUtil.fromJson(dbData, List.class); + List bytes = new LinkedList<>(); + if (list != null) { + for (Object item : list) { + byte value = Double.valueOf(String.valueOf(item)).byteValue(); + bytes.add(value); + } + } + return bytes; + } +} diff --git a/common/src/main/java/com/usthe/common/entity/manager/JsonOptionListAttributeConverter.java b/common/src/main/java/com/usthe/common/entity/manager/JsonOptionListAttributeConverter.java index d6c14e3de94..c814d348f02 100644 --- a/common/src/main/java/com/usthe/common/entity/manager/JsonOptionListAttributeConverter.java +++ b/common/src/main/java/com/usthe/common/entity/manager/JsonOptionListAttributeConverter.java @@ -6,7 +6,7 @@ import java.util.List; /** - * json 互转map对象字段为数据String字段 + * json 互转list paramDefine.Option 对象字段为数据String字段 * @author tom * @date 2021/12/4 07:54 */ diff --git a/common/src/main/java/com/usthe/common/entity/manager/Monitor.java b/common/src/main/java/com/usthe/common/entity/manager/Monitor.java index 6c3f6ebae4e..b443ab718d2 100644 --- a/common/src/main/java/com/usthe/common/entity/manager/Monitor.java +++ b/common/src/main/java/com/usthe/common/entity/manager/Monitor.java @@ -1,6 +1,8 @@ package com.usthe.common.entity.manager; +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; import com.usthe.common.support.valid.HostValid; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -10,13 +12,11 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import javax.persistence.*; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import java.time.LocalDateTime; +import java.util.List; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; @@ -34,7 +34,7 @@ @Builder @AllArgsConstructor @NoArgsConstructor -@ApiModel(description = "en: Monitor Entity,zh: 监控实体") +@ApiModel(description = "Monitor Entity | 监控实体") public class Monitor { /** @@ -132,4 +132,17 @@ public class Monitor { @Column(insertable = false, updatable = false) private LocalDateTime gmtUpdate; + /** + * 多对多关联中,需设置第三张关联中间表JoinTable + * JoinTable name 为关联关系中间表名称 + * joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段 + * inverseJoinColumn:中间表的外键字段关联对方表的主键字段 + * JoinColumn name 中间表的关联字段名称 + * referencedColumnName 关联表的映射字段名称 + */ + @ManyToMany(targetEntity = Tag.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinTable(name = "tag_monitor_bind", + joinColumns = {@JoinColumn(name = "monitor_id", referencedColumnName = "id")}, + inverseJoinColumns = {@JoinColumn(name = "tag_id", referencedColumnName = "id")}) + private List tags; } diff --git a/common/src/main/java/com/usthe/common/entity/manager/NoticeRule.java b/common/src/main/java/com/usthe/common/entity/manager/NoticeRule.java index 91391a67bca..641ca4f5a67 100644 --- a/common/src/main/java/com/usthe/common/entity/manager/NoticeRule.java +++ b/common/src/main/java/com/usthe/common/entity/manager/NoticeRule.java @@ -1,5 +1,6 @@ package com.usthe.common.entity.manager; +import com.usthe.common.entity.alerter.JsonMapAttributeConverter; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -8,14 +9,11 @@ import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; +import javax.persistence.*; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; @@ -73,6 +71,15 @@ public class NoticeRule { example = "false", accessMode = READ_WRITE, position = 5) private boolean filterAll = true; + @ApiModelProperty(value = "过滤匹配告警级别,空为全部告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色", example = "[1]", accessMode = READ_WRITE, position = 8) + @Convert(converter = JsonByteListAttributeConverter.class) + private List priorities; + + @ApiModelProperty(value = "告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{key1:value1}", accessMode = READ_WRITE, position = 8) + @Convert(converter = JsonMapAttributeConverter.class) + @SuppressWarnings("JpaAttributeTypeInspection") + private Map tags; + @ApiModelProperty(value = "The creator of this record", notes = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 7) diff --git a/common/src/main/java/com/usthe/common/entity/manager/Tag.java b/common/src/main/java/com/usthe/common/entity/manager/Tag.java new file mode 100644 index 00000000000..59621819712 --- /dev/null +++ b/common/src/main/java/com/usthe/common/entity/manager/Tag.java @@ -0,0 +1,87 @@ +package com.usthe.common.entity.manager; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Objects; + +import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; +import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; + +/** + * Tag + * 标签 + * @author tomsun28 + * @date 2021/11/13 22:19 + */ +@Entity +@Table(name = "tag") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "Tag Entity | 标签实体") +public class Tag { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "Tag主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0) + private Long id; + + @ApiModelProperty(value = "Tag Field | 标签名称", example = "app", accessMode = READ_WRITE, position = 1) + @NotNull + private String name; + + @ApiModelProperty(value = "Tag Value | 标签值", example = "23", accessMode = READ_WRITE, position = 2) + private String value; + + @ApiModelProperty(value = "Tag Color | 标签颜色", example = "#ffff", accessMode = READ_WRITE, position = 3) + private String color; + + @ApiModelProperty(value = "标记类型 0:监控自动生成(monitorId,monitorName) 1: 用户生成 2: 系统预置", accessMode = READ_WRITE, position = 4) + @Min(0) + @Max(3) + private byte type; + + @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 5) + private String creator; + + @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 6) + private String modifier; + + @ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 7) + @Column(insertable = false, updatable = false) + private LocalDateTime gmtCreate; + + @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 8) + @Column(insertable = false, updatable = false) + private LocalDateTime gmtUpdate; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Tag tag = (Tag) o; + return Objects.equals(name, tag.name) && Objects.equals(value, tag.value); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 13 * hash + (name == null ? 0 : name.hashCode()) + (value == null ? 0 : value.hashCode()); + return hash; + } +} diff --git a/common/src/main/java/com/usthe/common/entity/manager/TagMonitorBind.java b/common/src/main/java/com/usthe/common/entity/manager/TagMonitorBind.java new file mode 100644 index 00000000000..b61227fd165 --- /dev/null +++ b/common/src/main/java/com/usthe/common/entity/manager/TagMonitorBind.java @@ -0,0 +1,56 @@ +package com.usthe.common.entity.manager; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.*; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_ONLY; +import static io.swagger.annotations.ApiModelProperty.AccessMode.READ_WRITE; + +/** + * Tag Bind Monitor + * 标签与监控关联实体 + * @author tomsun28 + * @date 2021/11/13 22:19 + */ +@Entity +@Table(name = "tag_monitor_bind") +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ApiModel(description = "Tag Bind Monitor | 标签与监控关联实体") +public class TagMonitorBind { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "主键索引ID", example = "87584674384", accessMode = READ_ONLY, position = 0) + private Long id; + + @ApiModelProperty(value = "TAG ID", example = "87432674384", accessMode = READ_WRITE, position = 1) + @Column(name = "tag_id") + private Long tagId; + + @ApiModelProperty(value = "监控ID", example = "87432674336", accessMode = READ_WRITE, position = 2) + @Column(name = "monitor_id") + private Long monitorId; + + @ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 3) + @Column(insertable = false, updatable = false) + private LocalDateTime gmtCreate; + + @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 4) + @Column(insertable = false, updatable = false) + private LocalDateTime gmtUpdate; + +} diff --git a/common/src/main/java/com/usthe/common/util/CommonConstants.java b/common/src/main/java/com/usthe/common/util/CommonConstants.java index 6c85b6469f1..f5b9df27209 100644 --- a/common/src/main/java/com/usthe/common/util/CommonConstants.java +++ b/common/src/main/java/com/usthe/common/util/CommonConstants.java @@ -206,4 +206,19 @@ public interface CommonConstants { * 认证类型 GITEE三方登录 */ byte AUTH_TYPE_GITEE = 5; + + /** + * 内有标签: monitorId 监控ID + */ + String TAG_MONITOR_ID = "monitorId"; + + /** + * 内有标签: monitorName 监控名称 + */ + String TAG_MONITOR_NAME = "monitorName"; + + /** + * 内有标签: app 监控类型 + */ + String TAG_MONITOR_APP = "app"; } diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/AlertStoreHandler.java b/manager/src/main/java/com/usthe/manager/component/alerter/AlertStoreHandler.java index 784f0ed755b..d5f56bb3c4a 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/AlertStoreHandler.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/AlertStoreHandler.java @@ -12,6 +12,7 @@ public interface AlertStoreHandler { /** * 持久化报警记录 + * 需在持久化的同时对alert的标签信息tags关联赋值 * * @param alert 报警 */ diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DbAlertStoreHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DbAlertStoreHandlerImpl.java index 8a2604be748..5ee6b3810e7 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DbAlertStoreHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DbAlertStoreHandlerImpl.java @@ -10,6 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.util.Map; + /** * 报警持久化 - 落地到数据库 * @@ -26,12 +28,26 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler { @Override public void store(Alert alert) { // todo Using the cache does not directly manipulate the library 使用缓存不直接操作库 - Monitor monitor = monitorService.getMonitor(alert.getMonitorId()); + Map tags = alert.getTags(); + String monitorIdStr = tags.get(CommonConstants.TAG_MONITOR_ID); + if (monitorIdStr == null) { + log.error("alert tags monitorId is null."); + return; + } + long monitorId = Long.parseLong(monitorIdStr); + Monitor monitor = monitorService.getMonitor(monitorId); if (monitor == null) { - log.warn("Dispatch alarm the monitorId: {} not existed, ignored.", alert.getMonitorId()); + log.warn("Dispatch alarm the monitorId: {} not existed, ignored.", monitorId); return; } - alert.setMonitorName(monitor.getName()); + if (monitor.getTags() != null) { + monitor.getTags().forEach(item -> { + tags.put(item.getName(), item.getValue()); + }); + } + if (!tags.containsKey(CommonConstants.TAG_MONITOR_NAME)) { + tags.put(CommonConstants.TAG_MONITOR_NAME, monitor.getName()); + } if (monitor.getStatus() == CommonConstants.UN_MANAGE_CODE) { // When monitoring is not managed, ignore and silence its alarm messages // 当监控未管理时 忽略静默其告警信息 @@ -51,7 +67,7 @@ public void store(Alert alert) { // If the alarm is restored, the monitoring state needs to be restored // 若是恢复告警 需对监控状态进行恢复 if (alert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) { - monitorService.updateMonitorStatus(alert.getMonitorId(), CommonConstants.AVAILABLE_CODE); + monitorService.updateMonitorStatus(monitorId, CommonConstants.AVAILABLE_CODE); } } // Alarm drop library 告警落库 diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java index 14a632573c7..6bf69a8a3ce 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java @@ -2,6 +2,7 @@ import com.usthe.common.entity.alerter.Alert; import com.usthe.common.entity.manager.NoticeReceiver; +import com.usthe.common.util.CommonConstants; import com.usthe.common.util.CommonUtil; import com.usthe.manager.component.alerter.AlertNotifyHandler; import com.usthe.manager.pojo.dto.DingTalkWebHookDto; @@ -28,16 +29,25 @@ final class DingTalkRobotAlertNotifyHandlerImpl implements AlertNotifyHandler { @Override public void send(NoticeReceiver receiver, Alert alert) { + String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); DingTalkWebHookDto dingTalkWebHookDto = new DingTalkWebHookDto(); DingTalkWebHookDto.MarkdownDTO markdownDTO = new DingTalkWebHookDto.MarkdownDTO(); - String content = "#### [TanCloud探云告警通知]\n##### **告警目标对象** : " + - alert.getTarget() + "\n " + - "##### **所属监控ID** : " + alert.getMonitorId() + "\n " + - "##### **所属监控名称** : " + alert.getMonitorName() + "\n " + - "##### **告警级别** : " + - CommonUtil.transferAlertPriority(alert.getPriority()) + "\n " + - "##### **内容详情** : " + alert.getContent(); - markdownDTO.setText(content); + StringBuilder contentBuilder = new StringBuilder("#### [TanCloud探云告警通知]\n##### **告警目标对象** : " + + alert.getTarget() + "\n "); + if (monitorId != null) { + contentBuilder.append("##### **所属监控ID** : ").append(monitorId) + .append("\n "); + } + if (monitorName != null) { + contentBuilder.append("##### **所属监控名称** : ").append(monitorName) + .append("\n "); + } + contentBuilder.append("##### **告警级别** : ") + .append(CommonUtil.transferAlertPriority(alert.getPriority())) + .append("\n "); + contentBuilder.append("##### **内容详情** : ").append(alert.getContent()); + markdownDTO.setText(contentBuilder.toString()); markdownDTO.setTitle("TanCloud探云告警通知"); dingTalkWebHookDto.setMarkdown(markdownDTO); String webHookUrl = DingTalkWebHookDto.WEBHOOK_URL + receiver.getAccessToken(); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java index 9520bed8c63..2ee17843407 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java @@ -2,6 +2,7 @@ import com.usthe.common.entity.alerter.Alert; import com.usthe.common.entity.manager.NoticeReceiver; +import com.usthe.common.util.CommonConstants; import com.usthe.common.util.CommonUtil; import com.usthe.manager.component.alerter.AlertNotifyHandler; import com.usthe.manager.pojo.dto.FlyBookWebHookDto; @@ -31,6 +32,8 @@ final class FlyBookAlertNotifyHandlerImpl implements AlertNotifyHandler { @Override public void send(NoticeReceiver receiver, Alert alert) { + String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); FlyBookWebHookDto flyBookWebHookDto = new FlyBookWebHookDto(); FlyBookWebHookDto.Content content = new FlyBookWebHookDto.Content(); FlyBookWebHookDto.Post post = new FlyBookWebHookDto.Post(); @@ -42,12 +45,18 @@ public void send(NoticeReceiver receiver, Alert alert) { List contents1 = new ArrayList<>(); FlyBookWebHookDto.FlyBookContent flyBookContent = new FlyBookWebHookDto.FlyBookContent(); flyBookContent.setTag("text"); - String text = "告警目标对象 :" + alert.getTarget() + - "\n所属监控ID :" + alert.getMonitorId() + - "\n所属监控名称 :" + alert.getMonitorName() + - "\n告警级别 :" + CommonUtil.transferAlertPriority(alert.getPriority()) + - "\n内容详情 : " + alert.getContent(); - flyBookContent.setText(text); + StringBuilder textBuilder = new StringBuilder("告警目标对象 :"); + textBuilder.append(alert.getTarget()); + if (monitorId != null) { + textBuilder.append("\n所属监控ID :").append(monitorId); + } + if (monitorName != null) { + textBuilder.append("\n所属监控名称 :").append(monitorName); + } + textBuilder.append("\n告警级别 :") + .append(CommonUtil.transferAlertPriority(alert.getPriority())); + textBuilder.append("\n内容详情 : ").append(alert.getContent()); + flyBookContent.setText(textBuilder.toString()); contents1.add(flyBookContent); FlyBookWebHookDto.FlyBookContent bookContent = new FlyBookWebHookDto.FlyBookContent(); bookContent.setTag("a"); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java index 9e13c17da5e..922ea0695e2 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java @@ -29,13 +29,19 @@ final class WeWorkRobotAlertNotifyHandlerImpl implements AlertNotifyHandler { @Override public void send(NoticeReceiver receiver, Alert alert) { + String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); WeWorkWebHookDto weWorkWebHookDTO = new WeWorkWebHookDto(); WeWorkWebHookDto.MarkdownDTO markdownDTO = new WeWorkWebHookDto.MarkdownDTO(); StringBuilder content = new StringBuilder(); content.append("[TanCloud探云告警通知]\n告警目标对象 : ") - .append(alert.getTarget()).append("\n") - .append("所属监控ID : ").append(alert.getMonitorId()).append("\n") - .append("所属监控名称 : ").append(alert.getMonitorName()).append("\n"); + .append(alert.getTarget()).append("\n"); + if (monitorId != null) { + content.append("所属监控ID : ").append(monitorId).append("\n"); + } + if (monitorName != null) { + content.append("所属监控名称 : ").append(monitorName).append("\n"); + } if (alert.getPriority() < CommonConstants.ALERT_PRIORITY_CODE_WARNING) { content.append("告警级别 : ") .append(CommonUtil.transferAlertPriority(alert.getPriority())).append("\n"); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java index 1e1680ff8d9..7b82239d500 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java @@ -33,7 +33,7 @@ public void send(NoticeReceiver receiver, Alert alert) { } catch (ResourceAccessException e) { log.warn("Send WebHook: {} Failed: {}.", receiver.getHookUrl(), e.getMessage()); } catch (Exception e) { - log.error(e.getMessage(), e); + log.warn(e.getMessage()); } } diff --git a/manager/src/main/java/com/usthe/manager/controller/AccountController.java b/manager/src/main/java/com/usthe/manager/controller/AccountController.java index f4644b60afc..f24483166ed 100644 --- a/manager/src/main/java/com/usthe/manager/controller/AccountController.java +++ b/manager/src/main/java/com/usthe/manager/controller/AccountController.java @@ -93,7 +93,7 @@ public ResponseEntity>> authGetToken(@RequestBody Lo @GetMapping("/refresh/{refreshToken}") @ApiOperation(value = "Use refresh TOKEN to re-acquire TOKEN", notes = "使用刷新TOKEN重新获取TOKEN") public ResponseEntity>> refreshToken( - @ApiParam(value = "en: Refresh TOKEN,zh: 刷新TOKEN", example = "xxx") + @ApiParam(value = "Refresh TOKEN | 刷新TOKEN", example = "xxx") @PathVariable("refreshToken") @NotNull final String refreshToken) { String userId; boolean isRefresh; diff --git a/manager/src/main/java/com/usthe/manager/controller/TagController.java b/manager/src/main/java/com/usthe/manager/controller/TagController.java new file mode 100644 index 00000000000..d923af35d5b --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/controller/TagController.java @@ -0,0 +1,119 @@ +package com.usthe.manager.controller; + +import com.usthe.common.entity.dto.Message; +import com.usthe.common.entity.manager.Tag; +import com.usthe.manager.service.TagService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Predicate; +import javax.validation.Valid; + +import java.util.ArrayList; + +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +/** + * Tags management API + * 标签管理API + * + * @author tomsun28 + * @date 2022/5/1 10:57 + */ +@Api(tags = "Tag Manage API | 标签管理API") +@RestController +@RequestMapping(path = "/tag", produces = {APPLICATION_JSON_VALUE}) +public class TagController { + + @Autowired + private TagService tagService; + + @PostMapping + @ApiOperation(value = "Add Tag", notes = "新增监控标签") + public ResponseEntity> addNewTags(@Valid @RequestBody List tags) { + // Verify request data 校验请求数据 去重 + tags = tags.stream().peek(tag -> { + tag.setType((byte) 1); + tag.setId(null); + }).filter(tag -> tag.getValue() != null) + .distinct().collect(Collectors.toList()); + tagService.addTags(tags); + return ResponseEntity.ok(new Message<>("Add success")); + } + + @PutMapping + @ApiOperation(value = "Modify an existing tag", notes = "修改一个已存在标签") + public ResponseEntity> modifyMonitor(@Valid @RequestBody Tag tag) { + // Verify request data 校验请求数据 + if (tag.getId() == null || tag.getName() == null) { + throw new IllegalArgumentException("The Tag not exist."); + } + tagService.modifyTag(tag); + return ResponseEntity.ok(new Message<>("Modify success")); + } + + @GetMapping() + @ApiOperation(value = "Get tags information", notes = "根据条件获取标签信息") + public ResponseEntity>> getTags( + @ApiParam(value = "Tag content search | 标签内容模糊查询", example = "status") @RequestParam(required = false) String search, + @ApiParam(value = "Tag type | 标签类型", example = "0") @RequestParam(required = false) Byte type, + @ApiParam(value = "List current page | 列表当前分页", example = "0") @RequestParam(defaultValue = "0") int pageIndex, + @ApiParam(value = "Number of list pagination | 列表分页数量", example = "8") @RequestParam(defaultValue = "8") int pageSize) { + // Get tag information + Specification specification = (root, query, criteriaBuilder) -> { + List andList = new ArrayList<>(); + if (type != null) { + Predicate predicateApp = criteriaBuilder.equal(root.get("type"), type); + andList.add(predicateApp); + } + Predicate[] andPredicates = new Predicate[andList.size()]; + Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates)); + + List orList = new ArrayList<>(); + if (search != null && !"".equals(search)) { + Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%"); + orList.add(predicateName); + Predicate predicateValue = criteriaBuilder.like(root.get("value"), "%" + search + "%"); + orList.add(predicateValue); + } + Predicate[] orPredicates = new Predicate[orList.size()]; + Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates)); + + if (andPredicate.getExpressions().isEmpty() && orPredicate.getExpressions().isEmpty()) { + return query.where().getRestriction(); + } else if (andPredicate.getExpressions().isEmpty()) { + return query.where(orPredicate).getRestriction(); + } else if (orPredicate.getExpressions().isEmpty()) { + return query.where(andPredicate).getRestriction(); + } else { + return query.where(andPredicate, orPredicate).getRestriction(); + } + }; + PageRequest pageRequest = PageRequest.of(pageIndex, pageSize); + Page alertPage = tagService.getTags(specification, pageRequest); + Message> message = new Message<>(alertPage); + return ResponseEntity.ok(message); + } + + @DeleteMapping() + @ApiOperation(value = "Delete tags based on ID", notes = "根据TAG ID删除TAG") + public ResponseEntity> deleteTags( + @ApiParam(value = "TAG IDs | 监控ID列表", example = "6565463543") @RequestParam(required = false) List ids) { + if (ids != null && !ids.isEmpty()) { + tagService.deleteTags(new HashSet<>(ids)); + } + return ResponseEntity.ok(new Message<>("Delete success")); + } +} diff --git a/manager/src/main/java/com/usthe/manager/dao/TagDao.java b/manager/src/main/java/com/usthe/manager/dao/TagDao.java new file mode 100644 index 00000000000..669f5d70bf0 --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/dao/TagDao.java @@ -0,0 +1,21 @@ +package com.usthe.manager.dao; + +import com.usthe.common.entity.manager.Tag; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.Set; + +/** + * tag repository + * @author tom + * @date 2022/5/1 13:55 + */ +public interface TagDao extends JpaRepository, JpaSpecificationExecutor { + + /** + * delete tags by tag id + * @param ids id list + */ + void deleteTagsByIdIn(Set ids); +} diff --git a/manager/src/main/java/com/usthe/manager/service/TagService.java b/manager/src/main/java/com/usthe/manager/service/TagService.java new file mode 100644 index 00000000000..409b0fa6622 --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/service/TagService.java @@ -0,0 +1,44 @@ +package com.usthe.manager.service; + +import com.usthe.common.entity.manager.Tag; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; + +import java.util.HashSet; +import java.util.List; + +/** + * 标签服务 + * + * @author tom + * @date 2022/5/1 11:22 + */ +public interface TagService { + + /** + * new tags + * @param tags tag + */ + void addTags(List tags); + + /** + * update tag + * @param tag tag + */ + void modifyTag(Tag tag); + + /** + * get tag page list + * @param specification 查询条件 + * @param pageRequest 分页条件 + * @return tags + */ + Page getTags(Specification specification, PageRequest pageRequest); + + /** + * delete tags + * @param ids tag id list + */ + void deleteTags(HashSet ids); +} diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java index e39aae8e86c..5cbaad5a5e8 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java @@ -2,6 +2,7 @@ import com.usthe.alert.AlerterProperties; import com.usthe.common.entity.alerter.Alert; +import com.usthe.common.util.CommonConstants; import com.usthe.common.util.CommonUtil; import com.usthe.manager.service.MailService; import lombok.extern.slf4j.Slf4j; @@ -30,12 +31,16 @@ public class MailServiceImpl implements MailService { @Override public String buildAlertHtmlTemplate(final Alert alert) { + String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + monitorId = monitorId == null? "External alarm, no ID" : monitorId; + monitorName = monitorName == null? "External alarm, no Name" : monitorName; // Introduce thymeleaf context parameters to render pages // 引入thymeleaf上下文参数渲染页面 Context context = new Context(); context.setVariable("target", alert.getTarget()); - context.setVariable("monitorId", alert.getMonitorId()); - context.setVariable("monitorName", alert.getMonitorName()); + context.setVariable("monitorId", monitorId); + context.setVariable("monitorName", monitorName); context.setVariable("priority", CommonUtil.transferAlertPriority(alert.getPriority())); context.setVariable("content", alert.getContent()); context.setVariable("consoleUrl", alerterProperties.getConsoleUrl()); diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java index fef4292be3a..9a885295613 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java @@ -5,6 +5,7 @@ import com.usthe.common.entity.job.Configmap; import com.usthe.common.entity.job.Job; import com.usthe.common.entity.job.Metrics; +import com.usthe.common.entity.manager.Tag; import com.usthe.common.entity.message.CollectRep; import com.usthe.common.util.AesUtil; import com.usthe.common.util.CommonConstants; @@ -30,12 +31,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** @@ -101,6 +97,14 @@ public void detectMonitor(Monitor monitor, List params) throws MonitorDet public void addMonitor(Monitor monitor, List params) throws RuntimeException { // Apply for monitor id 申请 monitor id long monitorId = SnowFlakeIdGenerator.generateId(); + // Init Set Default Tags: monitorId monitorName app + List tags = monitor.getTags(); + if (tags == null) { + tags = new LinkedList<>(); + monitor.setTags(tags); + } + tags.add(Tag.builder().name(CommonConstants.TAG_MONITOR_ID).value(String.valueOf(monitorId)).type((byte) 0).build()); + tags.add(Tag.builder().name(CommonConstants.TAG_MONITOR_NAME).value(String.valueOf(monitor.getName())).type((byte) 0).build()); // Construct the collection task Job entity 构造采集任务Job实体 Job appDefine = appService.getAppDefine(monitor.getApp()); appDefine.setMonitorId(monitorId); @@ -162,6 +166,7 @@ public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgu } } } + // todo 校验标签 // Parameter definition structure verification 参数定义结构校验 List paramDefines = appService.getAppParamDefines(monitorDto.getMonitor().getApp()); @@ -265,7 +270,7 @@ public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgu @Transactional(rollbackFor = Exception.class) public void modifyMonitor(Monitor monitor, List params) throws RuntimeException { long monitorId = monitor.getId(); - // Check to determine whether the monitor corresponding to the monitor Id exists + // Check to determine whether the monitor corresponding to the monitor id exists // 查判断monitorId对应的此监控是否存在 Optional queryOption = monitorDao.findById(monitorId); if (!queryOption.isPresent()) { @@ -277,6 +282,17 @@ public void modifyMonitor(Monitor monitor, List params) throws RuntimeExc // 监控的类型不能修改 throw new IllegalArgumentException("Can not modify monitor's app type"); } + // Auto Update Default Tags: monitorName + List tags = monitor.getTags(); + if (tags == null) { + tags = new LinkedList<>(); + monitor.setTags(tags); + } + for (Tag tag : tags) { + if (CommonConstants.TAG_MONITOR_NAME.equals(tag.getName())) { + tag.setValue(monitor.getName()); + } + } // Construct the collection task Job entity // 构造采集任务Job实体 Job appDefine = appService.getAppDefine(monitor.getApp()); diff --git a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java index 968ca2a4023..604f15ea877 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java @@ -1,5 +1,7 @@ package com.usthe.manager.service.impl; +import com.google.common.collect.MapDifference; +import com.google.common.collect.Maps; import com.usthe.common.entity.alerter.Alert; import com.usthe.manager.dao.NoticeReceiverDao; import com.usthe.manager.dao.NoticeRuleDao; @@ -13,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -77,12 +80,29 @@ public void deleteNoticeRule(Long ruleId) { public List getReceiverFilterRule(Alert alert) { // todo use cache 使用缓存 List rules = noticeRuleDao.findNoticeRulesByEnableTrue(); + // todo The temporary rule is to forward all, and then implement more matching rules: alarm status selection, monitoring type selection, etc. // 暂时规则是全部转发 后面实现更多匹配规则:告警状态选择 监控类型选择等 Set receiverIds = rules.stream() .filter(NoticeRule::isFilterAll) .map(NoticeRule::getReceiverId) .collect(Collectors.toSet()); + // 除了全部转发的 其他的按照tags标签和告警级别过滤匹配 + Set receiverIdsByMatch = rules.stream() + .filter(rule -> !rule.isFilterAll()) + .filter(rule -> { + MapDifference difference = Maps.difference(alert.getTags(), rule.getTags() == null ? Maps.newHashMap() : rule.getTags()); + Map difMap= difference.entriesInCommon(); + if (rule.getPriorities() == null || rule.getPriorities().isEmpty()) { + return !difMap.isEmpty(); + } else { + boolean priorityMatch = rule.getPriorities().stream().anyMatch(item -> item != null && item == alert.getPriority()); + return priorityMatch && !difMap.isEmpty(); + } + }).map(NoticeRule::getReceiverId) + .collect(Collectors.toSet()); + + receiverIds.addAll(receiverIdsByMatch); return noticeReceiverDao.findAllById(receiverIds); } diff --git a/manager/src/main/java/com/usthe/manager/service/impl/TagServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/TagServiceImpl.java new file mode 100644 index 00000000000..736e0e48416 --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/service/impl/TagServiceImpl.java @@ -0,0 +1,54 @@ +package com.usthe.manager.service.impl; + +import com.usthe.common.entity.manager.Tag; +import com.usthe.manager.dao.TagDao; +import com.usthe.manager.service.TagService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; + +/** + * @author tom + * @date 2022/5/1 13:53 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@Slf4j +public class TagServiceImpl implements TagService { + + @Autowired + private TagDao tagDao; + + @Override + public void addTags(List tags) { + tagDao.saveAll(tags); + } + + @Override + public void modifyTag(Tag tag) { + Optional tagOptional = tagDao.findById(tag.getId()); + if (tagOptional.isPresent()) { + tagDao.save(tag); + } else { + throw new IllegalArgumentException("The tag is not existed"); + } + } + + @Override + public Page getTags(Specification specification, PageRequest pageRequest) { + return tagDao.findAll(specification, pageRequest); + } + + @Override + public void deleteTags(HashSet ids) { + tagDao.deleteTagsByIdIn(ids); + } +} diff --git a/manager/src/main/resources/sureness.yml b/manager/src/main/resources/sureness.yml index 17e2ef9eb95..50d0b77ae01 100644 --- a/manager/src/main/resources/sureness.yml +++ b/manager/src/main/resources/sureness.yml @@ -27,6 +27,10 @@ resourceRole: - /notice/**===post===[admin,user] - /notice/**===put===[admin,user] - /notice/**===delete===[admin] + - /tag/**===get===[admin,user,guest] + - /tag/**===post===[admin,user] + - /tag/**===put===[admin,user] + - /tag/**===delete===[admin] - /summary/**===get===[admin,user,guest] - /summary/**===post===[admin,user] - /summary/**===put===[admin,user] @@ -61,13 +65,13 @@ excludedResource: # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin - credential: admin + credential: hertzbeat role: [admin,user] - appId: tom - credential: tom@123 + credential: hertzbeat role: [user] - appId: guest - credential: guest + credential: hertzbeat role: [guest] - appId: lili # 注意 Digest认证不支持加盐加密的密码账户 diff --git a/script/docker-compose/conf/sureness.yml b/script/docker-compose/conf/sureness.yml index 17e2ef9eb95..50d0b77ae01 100644 --- a/script/docker-compose/conf/sureness.yml +++ b/script/docker-compose/conf/sureness.yml @@ -27,6 +27,10 @@ resourceRole: - /notice/**===post===[admin,user] - /notice/**===put===[admin,user] - /notice/**===delete===[admin] + - /tag/**===get===[admin,user,guest] + - /tag/**===post===[admin,user] + - /tag/**===put===[admin,user] + - /tag/**===delete===[admin] - /summary/**===get===[admin,user,guest] - /summary/**===post===[admin,user] - /summary/**===put===[admin,user] @@ -61,13 +65,13 @@ excludedResource: # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin - credential: admin + credential: hertzbeat role: [admin,user] - appId: tom - credential: tom@123 + credential: hertzbeat role: [user] - appId: guest - credential: guest + credential: hertzbeat role: [guest] - appId: lili # 注意 Digest认证不支持加盐加密的密码账户 diff --git a/script/sureness.yml b/script/sureness.yml index 17e2ef9eb95..50d0b77ae01 100644 --- a/script/sureness.yml +++ b/script/sureness.yml @@ -27,6 +27,10 @@ resourceRole: - /notice/**===post===[admin,user] - /notice/**===put===[admin,user] - /notice/**===delete===[admin] + - /tag/**===get===[admin,user,guest] + - /tag/**===post===[admin,user] + - /tag/**===put===[admin,user] + - /tag/**===delete===[admin] - /summary/**===get===[admin,user,guest] - /summary/**===post===[admin,user] - /summary/**===put===[admin,user] @@ -61,13 +65,13 @@ excludedResource: # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin - credential: admin + credential: hertzbeat role: [admin,user] - appId: tom - credential: tom@123 + credential: hertzbeat role: [user] - appId: guest - credential: guest + credential: hertzbeat role: [guest] - appId: lili # 注意 Digest认证不支持加盐加密的密码账户 diff --git a/web-app/package.json b/web-app/package.json index b29b6ac81cd..a9a369517c5 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -44,6 +44,7 @@ "echarts": "^5.2.2", "ng-alain": "^12.4.2", "ng-zorro-antd": "^12.0.2", + "ngx-color-picker": "^12.0.1", "ngx-echarts": "^v7.1.0", "rxjs": "~6.6.0", "screenfull": "^5.1.0", diff --git a/web-app/src/app/layout/basic/basic.component.ts b/web-app/src/app/layout/basic/basic.component.ts index 3a72dbb8e58..487c30a1c6b 100644 --- a/web-app/src/app/layout/basic/basic.component.ts +++ b/web-app/src/app/layout/basic/basic.component.ts @@ -46,6 +46,10 @@ import { environment } from '@env/environment';
+
+ + {{ 'menu.extras.tags' | i18n }} +
diff --git a/web-app/src/app/layout/basic/widgets/notify.component.ts b/web-app/src/app/layout/basic/widgets/notify.component.ts index ea89ed0e292..d8583b638cb 100644 --- a/web-app/src/app/layout/basic/widgets/notify.component.ts +++ b/web-app/src/app/layout/basic/widgets/notify.component.ts @@ -69,7 +69,7 @@ export class HeaderNotifyComponent implements OnInit { let item = { id: alert.id, avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', - title: `${alert.monitorName}--${this.i18nSvc.fanyi(`alert.priority.${alert.priority}`)}`, + title: `${alert.tags['monitorName']}--${this.i18nSvc.fanyi(`alert.priority.${alert.priority}`)}`, datetime: alert.gmtCreate, color: 'blue', type: this.i18nSvc.fanyi('dashboard.alerts.title-no') diff --git a/web-app/src/app/layout/basic/widgets/search.component.ts b/web-app/src/app/layout/basic/widgets/search.component.ts index 610347cc62a..72277e9e6eb 100644 --- a/web-app/src/app/layout/basic/widgets/search.component.ts +++ b/web-app/src/app/layout/basic/widgets/search.component.ts @@ -38,8 +38,13 @@ import { MonitorService } from '../../../service/monitor.service'; /> - - + + {{ 'monitor.name' | i18n }} : {{ option.name }} {{ 'monitor.host' | i18n }} : {{ option.host }} diff --git a/web-app/src/app/layout/basic/widgets/user.component.ts b/web-app/src/app/layout/basic/widgets/user.component.ts index e8c1e86e2c7..28c4577b35b 100644 --- a/web-app/src/app/layout/basic/widgets/user.component.ts +++ b/web-app/src/app/layout/basic/widgets/user.component.ts @@ -7,7 +7,7 @@ import { LocalStorageService } from '../../../service/local-storage.service'; @Component({ selector: 'header-user', template: ` -
+
{{ 'menu.account.logout' | i18n }}
diff --git a/web-app/src/app/pojo/Alert.ts b/web-app/src/app/pojo/Alert.ts index 9aa8cdec21e..abf9de0fa16 100644 --- a/web-app/src/app/pojo/Alert.ts +++ b/web-app/src/app/pojo/Alert.ts @@ -9,5 +9,8 @@ export class Alert { status!: number; content!: string; times!: number; + tags!: Record; gmtCreate!: number; + gmtUpdate!: number; + tmp!: any; } diff --git a/web-app/src/app/pojo/Monitor.ts b/web-app/src/app/pojo/Monitor.ts index 64bb57bb3a9..89d7f73296a 100644 --- a/web-app/src/app/pojo/Monitor.ts +++ b/web-app/src/app/pojo/Monitor.ts @@ -1,3 +1,5 @@ +import { Tag } from './Tag'; + export class Monitor { id!: number; name!: string; @@ -11,4 +13,5 @@ export class Monitor { modifier!: string; gmtCreate!: number; gmtUpdate!: number; + tags!: Tag[]; } diff --git a/web-app/src/app/pojo/NoticeRule.ts b/web-app/src/app/pojo/NoticeRule.ts index 91a6e29da45..164d8da928a 100644 --- a/web-app/src/app/pojo/NoticeRule.ts +++ b/web-app/src/app/pojo/NoticeRule.ts @@ -6,6 +6,9 @@ export class NoticeRule { enable: boolean = true; // 是否转发所有 filterAll: boolean = true; + // 告警级别过滤 + priorities!: number[]; + tags!: Record; creator!: string; modifier!: string; gmtCreate!: number; diff --git a/web-app/src/app/pojo/Tag.ts b/web-app/src/app/pojo/Tag.ts new file mode 100644 index 00000000000..031be6f4319 --- /dev/null +++ b/web-app/src/app/pojo/Tag.ts @@ -0,0 +1,12 @@ +export class Tag { + id!: number; + name!: string; + value!: string; + color: string = '#ff4081'; + // 标记类型 0:监控自动生成(monitorId,monitorName) 1: 用户生成 2: 系统预置 + type!: number; + creator!: string; + modifier!: string; + gmtCreate!: number; + gmtUpdate!: number; +} diff --git a/web-app/src/app/routes/alert/alert-center/alert-center.component.html b/web-app/src/app/routes/alert/alert-center/alert-center.component.html index e2f0e4c4954..754a4be19fb 100644 --- a/web-app/src/app/routes/alert/alert-center/alert-center.component.html +++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.html @@ -89,6 +89,7 @@ {{ 'alert.center.monitor' | i18n }} {{ 'alert.center.priority' | i18n }} {{ 'alert.center.content' | i18n }} + {{ 'alert.center.tags' | i18n }} {{ 'alert.center.status' | i18n }} {{ 'alert.center.time' | i18n }} {{ 'common.edit' | i18n }} @@ -99,8 +100,8 @@ {{ data.target }} -
- {{ data.monitorName }} + + {{ data.tags?.monitorName }} @@ -118,6 +119,11 @@ {{ data.content }} + + + {{ sliceTagName(tag) }} + + {{ 'alert.status.' + data.status | i18n }} diff --git a/web-app/src/app/routes/alert/alert-center/alert-center.component.ts b/web-app/src/app/routes/alert/alert-center/alert-center.component.ts index ca33a32ef8b..62e70f92b18 100644 --- a/web-app/src/app/routes/alert/alert-center/alert-center.component.ts +++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.ts @@ -7,6 +7,7 @@ import { NzTableQueryParams } from 'ng-zorro-antd/table'; import { Alert } from '../../../pojo/Alert'; import { AlertService } from '../../../service/alert.service'; +import { Tag } from '../../../pojo/Tag'; @Component({ selector: 'app-alert-center', @@ -54,6 +55,17 @@ export class AlertCenterComponent implements OnInit { this.alerts = page.content; this.pageIndex = page.number + 1; this.total = page.totalElements; + this.alerts.forEach(item => { + item.tmp = []; + if (item.tags != undefined) { + Object.keys(item.tags).forEach(name => { + item.tmp.push({ + name: name, + value: item.tags[name] + }); + }); + } + }) } else { console.warn(message.msg); } @@ -215,4 +227,12 @@ export class AlertCenterComponent implements OnInit { this.loadAlertsTable(); } // end: 列表多选分页逻辑 + + sliceTagName(tag: Tag): string { + if (tag.value != undefined && tag.value.trim() != '') { + return `${tag.name}:${tag.value}`; + } else { + return tag.name; + } + } } diff --git a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html index 6e25a90193a..20580b8c8c8 100644 --- a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html +++ b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html @@ -307,12 +307,6 @@ - - {{ 'alert.notice.rule.all' | i18n }} - - - - {{ 'alert.notice.receiver.people' | i18n }} @@ -330,6 +324,54 @@ + + {{ 'alert.notice.rule.all' | i18n }} + + + + + + {{ 'alert.notice.rule.tag' | i18n }} + + + + + + + {{ 'alert.notice.rule.priority' | i18n }} + + + + + + + + + {{ 'alert.notice.rule.enable' | i18n }} diff --git a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts index 9d4764c9687..cf6d6c5c6d7 100644 --- a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts +++ b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts @@ -9,6 +9,8 @@ import { NoticeReceiver } from '../../../pojo/NoticeReceiver'; import { NoticeRule } from '../../../pojo/NoticeRule'; import { NoticeReceiverService } from '../../../service/notice-receiver.service'; import { NoticeRuleService } from '../../../service/notice-rule.service'; +import { TagService } from '../../../service/tag.service'; +import { Tag } from '../../../pojo/Tag'; @Component({ selector: 'app-alert-notice', @@ -21,6 +23,7 @@ export class AlertNoticeComponent implements OnInit { private noticeReceiverSvc: NoticeReceiverService, private modal: NzModalService, private noticeRuleSvc: NoticeRuleService, + private tagService: TagService, @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService ) {} @@ -224,6 +227,9 @@ export class AlertNoticeComponent implements OnInit { isManageRuleModalOkLoading: boolean = false; rule!: NoticeRule; receiversOption: any[] = []; + searchTag!: string; + tagsOption: any[] = []; + filterTags: string[] = []; onNewNoticeRule() { this.rule = new NoticeRule(); @@ -239,6 +245,17 @@ export class AlertNoticeComponent implements OnInit { value: rule.receiverId, label: rule.receiverName }); + this.filterTags = []; + if (rule.tags != undefined) { + Object.keys(rule.tags).forEach(name => { + let tag = `${name}:${rule.tags[name]}`; + this.filterTags.push(tag); + this.tagsOption.push({ + value: tag, + label: tag + }); + }); + } } loadReceiversOption() { @@ -247,36 +264,38 @@ export class AlertNoticeComponent implements OnInit { if (message.code === 0) { let data = message.data; this.receiversOption = []; - data.forEach(item => { - let label = `${item.name}-`; - switch (item.type) { - case 0: - label = `${label}Phone`; - break; - case 1: - label = `${label}Email`; - break; - case 2: - label = `${label}WebHook`; - break; - case 3: - label = `${label}WeChat`; - break; - case 4: - label = `${label}WeWork`; - break; - case 5: - label = `${label}DingDing`; - break; - case 6: - label = `${label}FeiShu`; - break; - } - this.receiversOption.push({ - value: item.id, - label: label + if (data != undefined) { + data.forEach(item => { + let label = `${item.name}-`; + switch (item.type) { + case 0: + label = `${label}Phone`; + break; + case 1: + label = `${label}Email`; + break; + case 2: + label = `${label}WebHook`; + break; + case 3: + label = `${label}WeChat`; + break; + case 4: + label = `${label}WeWork`; + break; + case 5: + label = `${label}DingDing`; + break; + case 6: + label = `${label}FeiShu`; + break; + } + this.receiversOption.push({ + value: item.id, + label: label + }); }); - }); + } } else { console.warn(message.msg); } @@ -289,6 +308,46 @@ export class AlertNoticeComponent implements OnInit { ); } + loadTagsOption() { + let tagsInit$ = this.tagService.loadTags(this.searchTag, undefined, 0, 1000).subscribe( + message => { + if (message.code === 0) { + let page = message.data; + this.tagsOption = []; + if (page.content != undefined) { + page.content.forEach(item => { + this.tagsOption.push({ + value: `${item.name}:${item.value}`, + label: `${item.name}:${item.value}` + }); + }); + } + } else { + console.warn(message.msg); + } + tagsInit$.unsubscribe(); + }, + error => { + tagsInit$.unsubscribe(); + console.error(error.msg); + } + ); + } + + onPrioritiesChange() { + if (this.rule.priorities != undefined) { + let isAll = false; + this.rule.priorities.forEach(item => { + if (item == 9) { + isAll = true; + } + }); + if (isAll) { + this.rule.priorities = [9, 0, 1, 2]; + } + } + } + onManageRuleModalCancel() { this.isManageRuleModalVisible = false; } @@ -299,6 +358,16 @@ export class AlertNoticeComponent implements OnInit { this.rule.receiverName = option.label; } }); + this.rule.tags = {}; + this.filterTags.forEach(tag => { + let tmp: string[] = tag.split(':'); + if (tmp.length == 2) { + this.rule.tags[tmp[0]] = tmp[1]; + } + }); + if (this.rule.priorities != undefined) { + this.rule.priorities = this.rule.priorities.filter(item => item != null && item != 9); + } this.isManageRuleModalOkLoading = true; if (this.isManageRuleModalAdd) { const modalOk$ = this.noticeRuleSvc diff --git a/web-app/src/app/routes/dashboard/dashboard.component.html b/web-app/src/app/routes/dashboard/dashboard.component.html index c078519f926..80eb01c54c7 100644 --- a/web-app/src/app/routes/dashboard/dashboard.component.html +++ b/web-app/src/app/routes/dashboard/dashboard.component.html @@ -134,7 +134,7 @@ {{ 'alert.priority.2' | i18n }} - [{{ alert.monitorName }}] + [{{ alert.tags.monitorName }}] {{ alert.content }}

diff --git a/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.ts b/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.ts index 8e949943f1c..cb8cee95daa 100644 --- a/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.ts +++ b/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.ts @@ -34,7 +34,7 @@ export class MonitorDataTableComponent { let metricData$ = this.monitorSvc.getMonitorMetricsData(this.monitorId, this.metrics).subscribe( message => { metricData$.unsubscribe(); - if (message.code === 0) { + if (message.code === 0 && message.data) { this.time = message.data.time; this.fields = message.data.fields; this.valueRows = message.data.valueRows; @@ -43,7 +43,7 @@ export class MonitorDataTableComponent { this.rowValues = this.valueRows[0].values; } } else { - console.error(message.msg); + console.info(`${this.metrics}:${message.msg}`); } }, error => { diff --git a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html index 2296dd804e9..800b7355e8a 100644 --- a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html +++ b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html @@ -15,7 +15,7 @@ {{ 'monitor.app.' + app | i18n }} {{ 'monitors.detail' | i18n }} - + {{ 'common.button.help' | i18n }} diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html index e5a2e932895..cc205cbc0ed 100644 --- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.html @@ -325,6 +325,29 @@
+ + + {{ 'tag.bind' | i18n }} + + + + {{ sliceTagName(tag) }} + + + + + {{ 'tag.new' | i18n }} + + + + + {{ 'monitor.description' | i18n }} @@ -346,3 +369,49 @@
+ + + +
+ + + + + + + {{ 'tag' | i18n }} + + + + + + + {{ data.name }} + + {{ data.name + ':' + data.value }} + + + + + +
+
diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts index 82293a2c9ce..0138f84864b 100644 --- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts @@ -11,8 +11,10 @@ import { Message } from '../../../pojo/Message'; import { Monitor } from '../../../pojo/Monitor'; import { Param } from '../../../pojo/Param'; import { ParamDefine } from '../../../pojo/ParamDefine'; +import { Tag } from '../../../pojo/Tag'; import { AppDefineService } from '../../../service/app-define.service'; import { MonitorService } from '../../../service/monitor.service'; +import { TagService } from '../../../service/tag.service'; @Component({ selector: 'app-monitor-modify', @@ -27,6 +29,7 @@ export class MonitorEditComponent implements OnInit { private router: Router, private titleSvc: TitleService, private notifySvc: NzNotificationService, + private tagSvc: TagService, @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService ) {} @@ -64,6 +67,9 @@ export class MonitorEditComponent implements OnInit { }); } this.detected = message.data.detected ? message.data.detected : true; + if (this.monitor.tags == undefined) { + this.monitor.tags = []; + } } else { console.warn(message.msg); this.notifySvc.error(this.i18nSvc.fanyi('monitors.not-found'), message.msg); @@ -236,4 +242,78 @@ export class MonitorEditComponent implements OnInit { app = app ? app : ''; this.router.navigateByUrl(`/monitors?app=${app}`); } + + onRemoveTag(tag: Tag) { + if (this.monitor != undefined && this.monitor.tags != undefined) { + this.monitor.tags = this.monitor.tags.filter(item => item !== tag); + } + } + + sliceTagName(tag: Tag): string { + if (tag.value != undefined && tag.value.trim() != '') { + return `${tag.name}:${tag.value}`; + } else { + return tag.name; + } + } + + // start Tag model + isManageModalVisible = false; + isManageModalOkLoading = false; + tagCheckedAll: boolean = false; + tagTableLoading = false; + tagSearch!: string; + tags!: Tag[]; + checkedTags = new Set(); + loadTagsTable() { + this.tagTableLoading = true; + let tagsReq$ = this.tagSvc.loadTags(this.tagSearch, 1, 0, 1000).subscribe( + message => { + this.tagTableLoading = false; + this.tagCheckedAll = false; + this.checkedTags.clear(); + if (message.code === 0) { + let page = message.data; + this.tags = page.content; + } else { + console.warn(message.msg); + } + tagsReq$.unsubscribe(); + }, + error => { + this.tagTableLoading = false; + tagsReq$.unsubscribe(); + } + ); + } + onShowTagsModal() { + this.isManageModalVisible = true; + this.loadTagsTable(); + } + onManageModalCancel() { + this.isManageModalVisible = false; + } + onManageModalOk() { + this.isManageModalOkLoading = true; + this.checkedTags.forEach(item => { + this.monitor.tags.push(item); + }); + this.isManageModalOkLoading = false; + this.isManageModalVisible = false; + } + onAllChecked(checked: boolean) { + if (checked) { + this.tags.forEach(tag => this.checkedTags.add(tag)); + } else { + this.checkedTags.clear(); + } + } + onItemChecked(tag: Tag, checked: boolean) { + if (checked) { + this.checkedTags.add(tag); + } else { + this.checkedTags.delete(tag); + } + } + // end tag model } diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html index f30f75d636b..4385e283160 100644 --- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.html @@ -334,6 +334,29 @@ + + + {{ 'tag.bind' | i18n }} + + + + {{ sliceTagName(tag) }} + + + + + {{ 'tag.new' | i18n }} + + + + + {{ 'monitor.description' | i18n }} @@ -355,3 +378,49 @@ + + + +
+ + + + + + + {{ 'tag' | i18n }} + + + + + + + {{ data.name }} + + {{ data.name + ':' + data.value }} + + + + + +
+
diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts index 68207475e71..b9f2dca30ad 100644 --- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts @@ -11,6 +11,8 @@ import { Param } from '../../../pojo/Param'; import { ParamDefine } from '../../../pojo/ParamDefine'; import { AppDefineService } from '../../../service/app-define.service'; import { MonitorService } from '../../../service/monitor.service'; +import { TagService } from '../../../service/tag.service'; +import { Tag } from '../../../pojo/Tag'; @Component({ selector: 'app-monitor-add', @@ -36,9 +38,11 @@ export class MonitorNewComponent implements OnInit { private cdr: ChangeDetectorRef, @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService, private titleSvc: TitleService, + private tagSvc: TagService, private formBuilder: FormBuilder ) { this.monitor = new Monitor(); + this.monitor.tags = []; } ngOnInit(): void { @@ -216,4 +220,78 @@ export class MonitorNewComponent implements OnInit { app = app ? app : ''; this.router.navigateByUrl(`/monitors?app=${app}`); } + + onRemoveTag(tag: Tag) { + if (this.monitor != undefined && this.monitor.tags != undefined) { + this.monitor.tags = this.monitor.tags.filter(item => item !== tag); + } + } + + sliceTagName(tag: Tag): string { + if (tag.value != undefined && tag.value.trim() != '') { + return `${tag.name}:${tag.value}`; + } else { + return tag.name; + } + } + + // start Tag model + isManageModalVisible = false; + isManageModalOkLoading = false; + tagCheckedAll: boolean = false; + tagTableLoading = false; + tagSearch!: string; + tags!: Tag[]; + checkedTags = new Set(); + loadTagsTable() { + this.tagTableLoading = true; + let tagsReq$ = this.tagSvc.loadTags(this.tagSearch, 1, 0, 1000).subscribe( + message => { + this.tagTableLoading = false; + this.tagCheckedAll = false; + this.checkedTags.clear(); + if (message.code === 0) { + let page = message.data; + this.tags = page.content; + } else { + console.warn(message.msg); + } + tagsReq$.unsubscribe(); + }, + error => { + this.tagTableLoading = false; + tagsReq$.unsubscribe(); + } + ); + } + onShowTagsModal() { + this.isManageModalVisible = true; + this.loadTagsTable(); + } + onManageModalCancel() { + this.isManageModalVisible = false; + } + onManageModalOk() { + this.isManageModalOkLoading = true; + this.checkedTags.forEach(item => { + this.monitor.tags.push(item); + }); + this.isManageModalOkLoading = false; + this.isManageModalVisible = false; + } + onAllChecked(checked: boolean) { + if (checked) { + this.tags.forEach(tag => this.checkedTags.add(tag)); + } else { + this.checkedTags.clear(); + } + } + onItemChecked(tag: Tag, checked: boolean) { + if (checked) { + this.checkedTags.add(tag); + } else { + this.checkedTags.delete(tag); + } + } + // end tag model } diff --git a/web-app/src/app/routes/routes-routing.module.ts b/web-app/src/app/routes/routes-routing.module.ts index 11e788bc4b9..40a89da92b9 100644 --- a/web-app/src/app/routes/routes-routing.module.ts +++ b/web-app/src/app/routes/routes-routing.module.ts @@ -25,7 +25,8 @@ const routes: Routes = [ { path: 'dashboard', component: DashboardComponent, data: { titleI18n: 'menu.dashboard' } }, { path: 'exception', loadChildren: () => import('./exception/exception.module').then(m => m.ExceptionModule) }, { path: 'monitors', loadChildren: () => import('./monitor/monitor.module').then(m => m.MonitorModule) }, - { path: 'alert', loadChildren: () => import('./alert/alert.module').then(m => m.AlertModule) } + { path: 'alert', loadChildren: () => import('./alert/alert.module').then(m => m.AlertModule) }, + { path: 'setting', loadChildren: () => import('./setting/setting.module').then(m => m.SettingModule) } ] }, // 空白布局 diff --git a/web-app/src/app/routes/setting/setting-routing.module.ts b/web-app/src/app/routes/setting/setting-routing.module.ts new file mode 100644 index 00000000000..07321b50dea --- /dev/null +++ b/web-app/src/app/routes/setting/setting-routing.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { SettingTagsComponent } from './tags/tags.component'; + +const routes: Routes = [{ path: 'tags', component: SettingTagsComponent }]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SettingRoutingModule {} diff --git a/web-app/src/app/routes/setting/setting.module.ts b/web-app/src/app/routes/setting/setting.module.ts new file mode 100644 index 00000000000..b50e8c4eed6 --- /dev/null +++ b/web-app/src/app/routes/setting/setting.module.ts @@ -0,0 +1,21 @@ +import { NgModule, Type } from '@angular/core'; +import { SharedModule } from '@shared'; + +import { SettingRoutingModule } from './setting-routing.module'; +import { SettingTagsComponent } from './tags/tags.component'; +import { NzDividerModule } from 'ng-zorro-antd/divider'; +import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb'; +import { NzCascaderModule } from 'ng-zorro-antd/cascader'; +import { NzCollapseModule } from 'ng-zorro-antd/collapse'; +import { NzListModule } from 'ng-zorro-antd/list'; +import { NzSwitchModule } from 'ng-zorro-antd/switch'; +import { ColorPickerModule } from 'ngx-color-picker'; +import { NzTagModule } from 'ng-zorro-antd/tag'; + +const COMPONENTS: Array> = [SettingTagsComponent]; + +@NgModule({ + imports: [SharedModule, SettingRoutingModule, NzDividerModule, NzBreadCrumbModule, NzCascaderModule, NzCollapseModule, NzListModule, NzSwitchModule, ColorPickerModule, NzTagModule], + declarations: COMPONENTS +}) +export class SettingModule {} diff --git a/web-app/src/app/routes/setting/tags/tags.component.html b/web-app/src/app/routes/setting/tags/tags.component.html new file mode 100644 index 00000000000..31b113197d6 --- /dev/null +++ b/web-app/src/app/routes/setting/tags/tags.component.html @@ -0,0 +1,151 @@ + + + + + + {{ 'menu.dashboard' | i18n }} + + + + + {{ 'menu.extras.tags' | i18n }} + + {{ 'common.button.help' | i18n }}  + + + + + + +
+ + + + + + + +
+ + + + + + {{ 'tag.name' | i18n }} + {{ 'tag.value' | i18n }} + {{ 'tag.color' | i18n }} + {{ 'tag.display' | i18n }} + {{ 'tag.update-time' | i18n }} + {{ 'common.edit' | i18n }} + + + + + + {{ data.name }} + {{ data.value }} + + {{ data.color }} + + + {{ data.name }} + {{ + data.name + ':' + data.value + }} + + {{ data.gmtUpdate | date: 'YYYY-MM-dd HH:mm:ss' }} + + + + + + + + + {{ 'common.total' | i18n }} {{ total }} + + + +
+
+ + {{ 'tag.name' | i18n }} + + + + + + {{ 'tag.value' | i18n }} + + + + + + {{ 'tag.color' | i18n }} + + + + + + {{ 'tag.display' | i18n }} + + {{ tag.name }} + {{ tag.name + ':' + tag.value }} + + +
+
+
diff --git a/web-app/src/app/routes/setting/tags/tags.component.spec.ts b/web-app/src/app/routes/setting/tags/tags.component.spec.ts new file mode 100644 index 00000000000..e77f8f1be94 --- /dev/null +++ b/web-app/src/app/routes/setting/tags/tags.component.spec.ts @@ -0,0 +1,25 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { SettingTagsComponent } from './tags.component'; + +describe('SettingTagsComponent', () => { + let component: SettingTagsComponent; + let fixture: ComponentFixture; + + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ SettingTagsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingTagsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/routes/setting/tags/tags.component.ts b/web-app/src/app/routes/setting/tags/tags.component.ts new file mode 100644 index 00000000000..a548cc16fe9 --- /dev/null +++ b/web-app/src/app/routes/setting/tags/tags.component.ts @@ -0,0 +1,214 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { I18NService } from '@core'; +import { ALAIN_I18N_TOKEN } from '@delon/theme'; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { NzNotificationService } from 'ng-zorro-antd/notification'; +import { NzTableQueryParams } from 'ng-zorro-antd/table'; + +import { Tag } from '../../../pojo/Tag'; +import { TagService } from '../../../service/tag.service'; +import { AlertDefine } from '../../../pojo/AlertDefine'; +import { finalize } from 'rxjs/operators'; +import { NoticeReceiver } from '../../../pojo/NoticeReceiver'; + +@Component({ + selector: 'app-setting-tags', + templateUrl: './tags.component.html' +}) +export class SettingTagsComponent implements OnInit { + constructor( + private notifySvc: NzNotificationService, + private modal: NzModalService, + private tagService: TagService, + @Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService + ) {} + + pageIndex: number = 1; + pageSize: number = 8; + total: number = 0; + tags!: Tag[]; + tableLoading: boolean = false; + checkedTagIds = new Set(); + // 搜索过滤相关属性 + search: string | undefined; + + ngOnInit(): void { + this.loadTagsTable(); + } + + sync() { + this.loadTagsTable(); + } + + loadTagsTable() { + this.tableLoading = true; + let tagsInit$ = this.tagService.loadTags(this.search, 1, this.pageIndex - 1, this.pageSize).subscribe( + message => { + this.tableLoading = false; + this.checkedAll = false; + this.checkedTagIds.clear(); + if (message.code === 0) { + let page = message.data; + this.tags = page.content; + this.pageIndex = page.number + 1; + this.total = page.totalElements; + } else { + console.warn(message.msg); + } + tagsInit$.unsubscribe(); + }, + error => { + this.tableLoading = false; + tagsInit$.unsubscribe(); + console.error(error.msg); + } + ); + } + + onDeleteTags() { + if (this.checkedTagIds == null || this.checkedTagIds.size === 0) { + this.notifySvc.warning(this.i18nSvc.fanyi('alert.center.notify.no-delete'), ''); + return; + } + this.modal.confirm({ + nzTitle: this.i18nSvc.fanyi('alert.center.confirm.delete-batch'), + nzOkText: this.i18nSvc.fanyi('common.button.ok'), + nzCancelText: this.i18nSvc.fanyi('common.button.cancel'), + nzOkDanger: true, + nzOkType: 'primary', + nzOnOk: () => this.deleteTags(this.checkedTagIds) + }); + } + + onDeleteOneTag(tagId: number) { + let alerts = new Set(); + alerts.add(tagId); + this.modal.confirm({ + nzTitle: this.i18nSvc.fanyi('common.confirm.delete'), + nzOkText: this.i18nSvc.fanyi('common.button.ok'), + nzCancelText: this.i18nSvc.fanyi('common.button.cancel'), + nzOkDanger: true, + nzOkType: 'primary', + nzOnOk: () => this.deleteTags(alerts) + }); + } + + deleteTags(tagIds: Set) { + this.tableLoading = true; + const deleteTags$ = this.tagService.deleteTags(tagIds).subscribe( + message => { + deleteTags$.unsubscribe(); + if (message.code === 0) { + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), ''); + this.loadTagsTable(); + } else { + this.tableLoading = false; + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg); + } + }, + error => { + this.tableLoading = false; + deleteTags$.unsubscribe(); + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), error.msg); + } + ); + } + + // begin: 列表多选分页逻辑 + checkedAll: boolean = false; + onAllChecked(checked: boolean) { + if (checked) { + this.tags.forEach(monitor => this.checkedTagIds.add(monitor.id)); + } else { + this.checkedTagIds.clear(); + } + } + onItemChecked(monitorId: number, checked: boolean) { + if (checked) { + this.checkedTagIds.add(monitorId); + } else { + this.checkedTagIds.delete(monitorId); + } + } + onTablePageChange(params: NzTableQueryParams) { + const { pageSize, pageIndex, sort, filter } = params; + this.pageIndex = pageIndex; + this.pageSize = pageSize; + this.loadTagsTable(); + } + // end: 列表多选分页逻辑 + + // start 新增修改Tag model + isManageModalVisible = false; + isManageModalOkLoading = false; + isManageModalAdd = true; + tag!: Tag; + onNewTag() { + this.tag = new Tag(); + this.isManageModalVisible = true; + this.isManageModalAdd = true; + } + onEditOneTag(tagValue: Tag) { + this.tag = tagValue; + this.isManageModalVisible = true; + this.isManageModalAdd = false; + } + onManageModalCancel() { + this.isManageModalVisible = false; + } + onManageModalOk() { + this.isManageModalOkLoading = true; + this.tag.name = this.tag.name.trim(); + if (this.tag.value != undefined) { + this.tag.value = this.tag.value.trim(); + } + if (this.isManageModalAdd) { + const modalOk$ = this.tagService + .newTag(this.tag) + .pipe( + finalize(() => { + modalOk$.unsubscribe(); + this.isManageModalOkLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.isManageModalVisible = false; + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.new-success'), ''); + this.loadTagsTable(); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.new-fail'), message.msg); + } + }, + error => { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.new-fail'), error.msg); + } + ); + } else { + const modalOk$ = this.tagService + .editTag(this.tag) + .pipe( + finalize(() => { + modalOk$.unsubscribe(); + this.isManageModalOkLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.isManageModalVisible = false; + this.notifySvc.success(this.i18nSvc.fanyi('common.notify.edit-success'), ''); + this.loadTagsTable(); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), message.msg); + } + }, + error => { + this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), error.msg); + } + ); + } + } + // end 新增修改告警定义model +} diff --git a/web-app/src/app/service/tag.service.spec.ts b/web-app/src/app/service/tag.service.spec.ts new file mode 100644 index 00000000000..f091e933f80 --- /dev/null +++ b/web-app/src/app/service/tag.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { TagService } from './tag.service'; + +describe('TagService', () => { + let service: TagService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TagService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web-app/src/app/service/tag.service.ts b/web-app/src/app/service/tag.service.ts new file mode 100644 index 00000000000..083072f0e66 --- /dev/null +++ b/web-app/src/app/service/tag.service.ts @@ -0,0 +1,65 @@ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { Message } from '../pojo/Message'; +import { Page } from '../pojo/Page'; +import { Tag } from '../pojo/Tag'; + +const tag_uri = '/tag'; + +@Injectable({ + providedIn: 'root' +}) +export class TagService { + constructor(private http: HttpClient) {} + + public loadTags( + search: string | undefined, + type: number | undefined, + pageIndex: number, + pageSize: number + ): Observable>> { + pageIndex = pageIndex ? pageIndex : 0; + pageSize = pageSize ? pageSize : 8; + // 注意HttpParams是不可变对象 需要保存set后返回的对象为最新对象 + let httpParams = new HttpParams(); + httpParams = httpParams.appendAll({ + pageIndex: pageIndex, + pageSize: pageSize + }); + if (type != undefined) { + httpParams = httpParams.append('type', type); + } + if (search != undefined && search != '' && search.trim() != '') { + httpParams = httpParams.append('search', search.trim()); + } + const options = { params: httpParams }; + return this.http.get>>(tag_uri, options); + } + + public newTags(body: Tag[]): Observable> { + return this.http.post>(tag_uri, body); + } + + public newTag(body: Tag): Observable> { + const tags = []; + tags.push(body); + return this.http.post>(tag_uri, tags); + } + + public editTag(body: Tag): Observable> { + return this.http.put>(tag_uri, body); + } + + public deleteTags(tagIds: Set): Observable> { + let httpParams = new HttpParams(); + tagIds.forEach(tagId => { + // 注意HttpParams是不可变对象 需要保存append后返回的对象为最新对象 + // append方法可以叠加同一key, set方法会把key之前的值覆盖只留一个key-value + httpParams = httpParams.append('ids', tagId); + }); + const options = { params: httpParams }; + return this.http.delete>(tag_uri, options); + } +} diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index c6dc2798e26..75161dffc86 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -6,7 +6,7 @@ "user": { "name": "Admin", "avatar": "./assets/img/avatar.svg", - "email": "管理员" + "email": "Administrator" }, "menu": [ { @@ -88,6 +88,12 @@ "group": true, "hideInBreadcrumb": true, "children": [ + { + "text": "tags", + "i18n": "menu.extras.tags", + "icon": "anticon-tags", + "link": "/setting/tags" + }, { "text": "helpCenter", "externalLink": "https://tancloud.cn/docs/help/guide", diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index 340920114e2..b3ae69fb433 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -32,6 +32,7 @@ }, "extras": { "": "More", + "tags": "Tags Manage", "help": "Help Center", "setting": "Setting" }, @@ -135,6 +136,7 @@ "alert.center.monitor": "Belong Monitor", "alert.center.priority": "Priority", "alert.center.content": "Alert Content", + "alert.center.tags": "Tags", "alert.center.status": "Status", "alert.center.time": "Alert Time", "alert.center.notify.no-delete": "No items selected for deletion!", @@ -172,6 +174,8 @@ "alert.notice.rule.name": "Rule Name", "alert.notice.rule.all": "Dispatch ALl", "alert.notice.rule.enable": "Enable", + "alert.notice.rule.tag": "Tag Filter", + "alert.notice.rule.priority": "Priority Filter", "dashboard.alerts.title": "Recently Alerts List", "dashboard.alerts.title-no": "Recently Pending Alerts", "dashboard.alerts.no": "No Pending Alerts", @@ -291,6 +295,20 @@ "app.login.tab-login-credentials": "Credentials", "app.login.remember-me": "Remember me", "app.login.login": "Login", + "tag.new": "New Tag", + "tag.edit": "Edit Tag", + "tag.search": "Search Tag", + "tag.delete": "Delete Tag", + "tag": "Tag", + "tag.setting": "Tags", + "tag.id": "ID", + "tag.name": "Tag Name", + "tag.value": "Tag Value", + "tag.color": "Color", + "tag.update-time": "Update Time", + "tag.display": "Display", + "tag.bind": "Bind Tags", + "tag.bind.tip": "You can use tags for classification management.eg: assign tags to resources in production environment and test environment.", "validation.email.required": "Please enter your email!", "validation.email.wrong-format": "The email address is in the wrong format!", "validation.password.required": "Please enter your password!", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index cdfb6aad500..52c797abfcc 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -32,6 +32,7 @@ }, "extras": { "": "更多", + "tags": "标签管理", "help": "帮助中心", "setting": "设置" }, @@ -131,10 +132,11 @@ "alert.center.search": "搜索告警内容", "alert.center.filter-status": "告警状态过滤", "alert.center.filter-priority": "告警级别过滤", - "alert.center.target": "告警指标", + "alert.center.target": "告警对象", "alert.center.monitor": "所属监控", "alert.center.priority": "级别", "alert.center.content": "告警内容", + "alert.center.tags": "标签", "alert.center.status": "状态", "alert.center.time": "告警时间", "alert.center.notify.no-delete": "未选中任何待删除项!", @@ -172,6 +174,8 @@ "alert.notice.rule.name": "策略名称", "alert.notice.rule.all": "转发所有", "alert.notice.rule.enable": "是否启用", + "alert.notice.rule.tag": "标签过滤", + "alert.notice.rule.priority": "级别过滤", "dashboard.alerts.title": "最近告警列表", "dashboard.alerts.title-no": "最近未处理告警", "dashboard.alerts.no": "暂无未处理告警", @@ -287,6 +291,20 @@ "app.login.tab-login-credentials": "账户密码登录", "app.login.remember-me": "自动登录", "app.login.login": "登录", + "tag.new": "新增标签", + "tag.edit": "编辑标签", + "tag.search": "搜索标签", + "tag.delete": "删除标签", + "tag": "标签", + "tag.setting": "管理标签", + "tag.id": "ID", + "tag.name": "标签名", + "tag.value": "标签值", + "tag.color": "颜色", + "tag.update-time": "更新时间", + "tag.display": "效果", + "tag.bind": "绑定标签", + "tag.bind.tip": "您可以使用标签(Tag)进行监控资源的分类管理, 例如给资源分别绑定生产环境、测试环境的标签。", "validation.email.required": "请输入邮箱地址!", "validation.email.wrong-format": "邮箱地址格式错误!", "validation.email.invalid": "无效的邮箱地址!", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 19489b3d752..80a627aa03e 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -32,6 +32,7 @@ }, "extras": { "": "更多", + "tags": "標簽管理", "help": "幫助中心", "setting": "設置" }, @@ -131,10 +132,11 @@ "alert.center.search": "搜索告警內容", "alert.center.filter-status": "告警狀態過濾", "alert.center.filter-priority": "告警級別過濾", - "alert.center.target": "告警指標", + "alert.center.target": "告警對象", "alert.center.monitor": "所屬監控", "alert.center.priority": "級別", "alert.center.content": "告警內容", + "alert.center.tags": "標簽", "alert.center.status": "狀態", "alert.center.time": "告警時間", "alert.center.notify.no-delete": "未選中任何待刪除項!", @@ -172,6 +174,8 @@ "alert.notice.rule.name": "策略名稱", "alert.notice.rule.all": "轉發所有", "alert.notice.rule.enable": "是否啓用", + "alert.notice.rule.tag": "標簽過濾", + "alert.notice.rule.priority": "級別過濾", "dashboard.alerts.title": "最近告警列表", "dashboard.alerts.title-no": "最近未處理告警", "dashboard.alerts.no": "暫無未處理告警", @@ -287,6 +291,20 @@ "app.login.tab-login-credentials": "賬戶密碼登錄", "app.login.remember-me": "自動登錄", "app.login.login": "登錄", + "tag.new": "新增標簽", + "tag.edit": "編輯標簽", + "tag.search": "搜索標簽", + "tag.delete": "刪除標簽", + "tag": "標簽", + "tag.setting": "管理標簽", + "tag.id": "ID", + "tag.name": "標簽名", + "tag.value": "標簽值", + "tag.color": "顔色", + "tag.update-time": "更新時間", + "tag.display": "效果", + "tag.bind": "綁定標簽", + "tag.bind.tip": "您可以使用標簽(Tag)進行監控資源的分類管理, 例如給資源分別綁定生産環境、測試環境的標簽。", "validation.email.required": "請輸入郵箱地址!", "validation.email.wrong-format": "郵箱地址格式錯誤!", "validation.email.invalid": "無效的郵箱地址!", From b8a785ef8e7d3b3e968b33a838e8d74e8d1f8cb7 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Tue, 3 May 2022 19:10:23 +0800 Subject: [PATCH 03/27] [collector]bugfix: expression evaluation error when value with spaces (#113) --- .../java/com/usthe/collector/dispatch/MetricsCollect.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collector/src/main/java/com/usthe/collector/dispatch/MetricsCollect.java b/collector/src/main/java/com/usthe/collector/dispatch/MetricsCollect.java index 465bf15283d..326f4bad177 100644 --- a/collector/src/main/java/com/usthe/collector/dispatch/MetricsCollect.java +++ b/collector/src/main/java/com/usthe/collector/dispatch/MetricsCollect.java @@ -203,8 +203,8 @@ private void calculateFields(Metrics metrics, CollectRep.MetricsData.Builder col .stream() .map(cal -> { int splitIndex = cal.indexOf("="); - String field = cal.substring(0, splitIndex); - String expressionStr = cal.substring(splitIndex + 1); + String field = cal.substring(0, splitIndex).trim(); + String expressionStr = cal.substring(splitIndex + 1).trim(); Expression expression = null; try { expression = AviatorEvaluator.compile(expressionStr, true); From e093a902fe32e480904720768001c1a748c314ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=9A=E7=BC=96=E7=A8=8B=E7=9A=84=E7=8E=8B=E5=AD=A6?= =?UTF-8?q?=E9=95=BF?= <71161318+wang1027-wqh@users.noreply.github.com> Date: Wed, 4 May 2022 16:56:21 +0800 Subject: [PATCH 04/27] [monitor] feature: Added zookeeper and middleware page support (#114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: [manager]feature: Add preliminary monitoring indicators of elasticsearch cluster #wqh feat: [manager]feature: Name change #wqh feat: [web-app、manager、collector]feature: Added zookeeper and middleware page support #wqh --- .../collector/collect/ssh/SshCollectImpl.java | 60 +++++-- .../main/resources/define/app/zookeeper.yml | 164 ++++++++++++++++++ .../main/resources/define/param/zookeeper.yml | 30 ++++ web-app/src/assets/app-data.json | 7 + 4 files changed, 250 insertions(+), 11 deletions(-) create mode 100644 manager/src/main/resources/define/app/zookeeper.yml create mode 100644 manager/src/main/resources/define/param/zookeeper.yml diff --git a/collector/src/main/java/com/usthe/collector/collect/ssh/SshCollectImpl.java b/collector/src/main/java/com/usthe/collector/collect/ssh/SshCollectImpl.java index 90a8ba3d27d..bc7f82a738d 100644 --- a/collector/src/main/java/com/usthe/collector/collect/ssh/SshCollectImpl.java +++ b/collector/src/main/java/com/usthe/collector/collect/ssh/SshCollectImpl.java @@ -21,15 +21,14 @@ import java.io.IOException; import java.net.ConnectException; import java.security.KeyPair; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** + * Ssh protocol collection implementation * ssh协议采集实现 + * * @author tom * @date 2022/03/11 15:10 */ @@ -38,8 +37,10 @@ public class SshCollectImpl extends AbstractCollect { private static final String PARSE_TYPE_ONE_ROW = "oneRow"; private static final String PARSE_TYPE_MULTI_ROW = "multiRow"; + private static final String PARSE_TYPE_NETCAT = "netcat"; - private SshCollectImpl(){} + private SshCollectImpl() { + } public static SshCollectImpl getInstance() { return SshCollectImpl.Singleton.INSTANCE; @@ -76,7 +77,7 @@ public void collect(CollectRep.MetricsData.Builder builder, long appId, String a List list = new ArrayList<>(); list.add(ClientChannelEvent.CLOSED); channel.waitFor(list, timeout); - Long responseTime = System.currentTimeMillis() - startTime; + Long responseTime = System.currentTimeMillis() - startTime; channel.close(); String result = response.toString(); if (!StringUtils.hasText(result)) { @@ -84,11 +85,18 @@ public void collect(CollectRep.MetricsData.Builder builder, long appId, String a builder.setMsg("采集数据失败"); } switch (sshProtocol.getParseType()) { + case PARSE_TYPE_NETCAT: + parseResponseDataByNetcat(result, metrics.getAliasFields(), builder, responseTime); + break; case PARSE_TYPE_ONE_ROW: parseResponseDataByOne(result, metrics.getAliasFields(), builder, responseTime); break; - default: parseResponseDataByMulti(result, metrics.getAliasFields(), builder, responseTime); - break; + case PARSE_TYPE_MULTI_ROW: + parseResponseDataByMulti(result, metrics.getAliasFields(), builder, responseTime); + break; + default: + parseResponseDataByMulti(result, metrics.getAliasFields(), builder, responseTime); + break; } } catch (ConnectException connectException) { log.debug(connectException.getMessage()); @@ -105,10 +113,40 @@ public void collect(CollectRep.MetricsData.Builder builder, long appId, String a } } + + private void parseResponseDataByNetcat(String result, List aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) { + String[] lines = result.split("\n"); + if (lines.length + 1 < aliasFields.size()) { + log.error("ssh response data not enough: {}", result); + } + boolean contains = lines[0].contains("="); + Map mapValue = Arrays.stream(lines) + .map(item -> { + if (contains) { + return item.split("="); + } else { + return item.split("\t"); + } + }) + .filter(item -> item.length == 2) + .collect(Collectors.toMap(x -> x[0], x -> x[1])); + + CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder(); + for (String field : aliasFields) { + String fieldValue = mapValue.get(field); + if (fieldValue == null) { + valueRowBuilder.addColumns(CommonConstants.NULL_VALUE); + } else { + valueRowBuilder.addColumns(fieldValue); + } + } + builder.addValues(valueRowBuilder.build()); + } + private void parseResponseDataByOne(String result, List aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) { String[] lines = result.split("\n"); if (lines.length + 1 < aliasFields.size()) { - log.error("ssh response data not enough: {}", result); + log.error("ssh response data not enough: {}", result); } CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder(); int aliasIndex = 0; @@ -129,7 +167,7 @@ private void parseResponseDataByMulti(String result, List aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) { String[] lines = result.split("\n"); if (lines.length <= 1) { - log.error("ssh response data only has header: {}", result); + log.error("ssh response data only has header: {}", result); } String[] fields = lines[0].split(" "); Map fieldMapping = new HashMap<>(fields.length); diff --git a/manager/src/main/resources/define/app/zookeeper.yml b/manager/src/main/resources/define/app/zookeeper.yml new file mode 100644 index 00000000000..1e4d711a9d4 --- /dev/null +++ b/manager/src/main/resources/define/app/zookeeper.yml @@ -0,0 +1,164 @@ +category: mid +app: zookeeper +name: + zh-CN: Zookeeper + en-US: Zookeeper +configmap: + - key: host + type: 1 + - key: port + type: 0 + - key: timeout + type: 0 + - key: username + type: 1 + - key: password + type: 2 +# 指标组列表 +metrics: + #集群状态 + - name: conf + priority: 0 + # 指标组中的具体监控指标 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: clientPort + type: 0 + # 数据快照文件目录 默认10万次操作生成一次快照 + - field: dataDir + type: 1 + - field: dataDirSize + type: 0 + unit: kb + # 事务日志文件目录,生产环境放在独立磁盘上 + - field: dataLogDir + type: 1 + # 事务日志文件大小 + - field: dataLogSize + type: 0 + unit: kb + # 服务器之间或客户端与服务器之间维持心跳的时间间隔 + - field: tickTime + type: 0 + unit: ms + # 最大连接数 + - field: maxClientCnxns + type: 1 + # 最小session超时时间 心跳时间x2 指定时间小于该时间默认使用此时间 + - field: minSessionTimeout + type: 0 + unit: ms + # 最大session超时时间 心跳时间x20 指定时间大于该时间默认使用此时间 + - field: maxSessionTimeout + type: 0 + unit: ms + # 服务器编号 + - field: serverId + type: 0 +# # 集群中follow与leader之间初始连接能容忍的最大心跳数 +# - field: initLimit +# type: 0 +# # 集群中follow与leader之间请求和应答能容忍的最大心跳数 +# - field: syncLimit +# type: 0 +# # 选举算法 3 基于TCP +# - field: electionAlg +# type: 1 +# # 选举端口 +# - field: electionPort +# type: 0 +# # 集群之间的通信端口 +# - field: quorumPort +# type: 0 +# # 是否观察者 1表示是 +# - field: peerType +# type: 0 + # 协议 + protocol: ssh + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + username: ^_^username^_^ + password: ^_^password^_^ + timeout: ^_^timeout^_^ + script: echo conf | nc 121.40.113.44 2181 + parseType: netcat + + - name: stats + priority: 0 + # 指标组中的具体监控指标 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: zk_version + type: 1 + # 服务器角色 + - field: zk_server_state + type: 1 + # 连接数 + - field: zk_num_alive_connections + type: 0 + unit: 个 + # 平均延时 + - field: zk_avg_latency + type: 0 + unit: ms + # 堆积请求数 + - field: zk_outstanding_requests + type: 0 + unit: 个 + # znode结点数量 + - field: zk_znode_count + type: 0 + unit: 个 + # 发包数 + - field: zk_packets_sent + type: 0 + unit: 个 + # 收包数 + - field: zk_packets_received + type: 0 + unit: 个 + # watch数量 + - field: zk_watch_count + type: 0 + unit: 个 + # 最大文件描述符数量 + - field: zk_max_file_descriptor_count + type: 0 + unit: 个 + # 数据大小 + - field: zk_approximate_data_size + type: 0 + unit: kb + # 打开的文件描述符数量 + - field: zk_open_file_descriptor_count + type: 0 + unit: 个 + # 最大延时 + - field: zk_max_latency + type: 0 + unit: ms + # 临时节点数 + - field: zk_ephemerals_count + type: 0 + unit: 个 + # 最小延时 + - field: zk_min_latency + type: 0 + unit: ms + + + # 协议 + protocol: ssh + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + username: ^_^username^_^ + password: ^_^password^_^ + timeout: ^_^timeout^_^ + script: echo mntr | nc 121.40.113.44 2181 + parseType: netcat \ No newline at end of file diff --git a/manager/src/main/resources/define/param/zookeeper.yml b/manager/src/main/resources/define/param/zookeeper.yml new file mode 100644 index 00000000000..d7cd73edee1 --- /dev/null +++ b/manager/src/main/resources/define/param/zookeeper.yml @@ -0,0 +1,30 @@ +app: zookeeper +# 强制固定必须参数 - host(ipv4,ipv6,域名) +param: + - field: host + name: 主机Host + type: host + required: true + - field: port + name: 端口 + type: number + range: '[0,65535]' + required: true + defaultValue: 2181 + placeholder: '请输入端口' + - field: timeout + name: 查询超时时间 + type: number + required: false + hide: true + defaultValue: 6000 + placeholder: '查询超时时间' + - field: username + name: 用户名 + type: text + limit: 20 + required: true + - field: password + name: 密码 + type: password + required: false \ No newline at end of file diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json index 75161dffc86..a2a98d43cce 100644 --- a/web-app/src/assets/app-data.json +++ b/web-app/src/assets/app-data.json @@ -48,6 +48,13 @@ "i18n": "menu.monitor.os", "icon": "anticon-windows" }, + { + "key": "mid", + "text": "中间件", + "hide": false, + "i18n": "menu.monitor.mid", + "icon": "anticon-merge-cells" + }, { "key": "custom", "text": "自定义", From fd16541e8a0aecf6ee9c127cd0d9f339d6d67759 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Wed, 4 May 2022 18:44:15 +0800 Subject: [PATCH 05/27] [manager]update banner --- manager/src/main/resources/banner.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/manager/src/main/resources/banner.txt b/manager/src/main/resources/banner.txt index f42c9fedecd..f86b16d51f5 100644 --- a/manager/src/main/resources/banner.txt +++ b/manager/src/main/resources/banner.txt @@ -1,6 +1,5 @@ -████████╗ █████╗ ███╗ ██╗ ██████╗██╗ ██████╗ ██╗ ██╗██████╗ -╚══██╔══╝██╔══██╗████╗ ██║ ██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗ - ██║ ███████║██╔██╗ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ - ██║ ██╔══██║██║╚██╗██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ Profile: ${spring.profiles.active} - ██║ ██║ ██║██║ ╚████║ ╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝ Name: ${spring.application.name} Port: ${server.port} Pid: ${pid} - ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ + _ _ _ ____ _ + | | | | ___ _ __| |_ ___| __ ) ___ __ _| |_ + | |_| |/ _ \ '__| __|_ / _ \ / _ \/ _` | __| Profile: ${spring.profiles.active} + | _ | __/ | | |_ / /| |_) | __/ (_| | |_ Name: ${spring.application.name} Port: ${server.port} Pid: ${pid} + |_| |_|\___|_| \__/___|____/ \___|\__,_|\__| From f46b8a32c2b48aff2cca36607ee34687c71e6eb6 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Wed, 4 May 2022 22:20:43 +0800 Subject: [PATCH 06/27] =?UTF-8?q?[monitor]update=20web=20endpoint=20'=20co?= =?UTF-8?q?nsole'=20to=20'=20',=20api=20service=20endpoint=20=E2=80=A6=20(?= =?UTF-8?q?#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [monitor]update web endpoint ' console' to ' ', api service endpoint ' ' to 'api' [monitor]update endpoint --- README.md | 4 +- README_EN.md | 4 +- .../controller/AlertDefineController.java | 2 +- .../controller/AlertDefinesController.java | 2 +- .../alert/controller/AlertsController.java | 2 +- home/docs/help/issue.md | 4 +- home/docs/start/docker-deploy.md | 175 +++++++++--------- home/docs/start/package-deploy.md | 43 ++--- home/docs/start/quickstart.md | 2 +- .../current/start/docker-deploy.md | 8 +- .../current/start/package-deploy.md | 8 +- .../current/start/quickstart.md | 4 +- .../manager/controller/AccountController.java | 2 +- .../manager/controller/AppController.java | 2 +- .../manager/controller/I18nController.java | 2 +- .../manager/controller/MonitorController.java | 2 +- .../controller/MonitorsController.java | 2 +- .../controller/NoticeConfigController.java | 2 +- .../manager/controller/SummaryController.java | 2 +- .../manager/controller/TagController.java | 2 +- manager/src/main/resources/application.yml | 2 +- manager/src/main/resources/sureness.yml | 79 ++++---- script/application.yml | 2 +- script/docker-compose/README.md | 2 +- script/docker-compose/conf/application.yml | 2 +- script/docker-compose/conf/sureness.yml | 19 +- script/sureness.yml | 17 +- .../controller/MetricsDataController.java | 4 +- web-app/README.md | 2 +- .../layout/basic/widgets/notify.component.ts | 2 +- web-app/src/environments/environment.prod.ts | 2 +- web-app/src/environments/environment.ts | 2 +- 32 files changed, 211 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index b09cf493593..1b5f8b1b854 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ 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 +4. 浏览器访问 localhost:1157 即可开始,默认账号密码 admin/hertzbeat 详细步骤参考 [通过安装包安装HertzBeat](https://hertzbeat.com/docs/start/package-deploy) @@ -103,7 +103,7 @@ 1. 此为前后端分离项目,本地代码调试需要分别启动后端工程manager和前端工程web-app 2. 后端:需要`maven3+`和`java8+`环境,修改YML配置信息并启动manager服务 3. 前端:需要`nodejs npm angular-cli`环境,待本地后端启动后,在web-app目录下启动 `ng serve --open` -4. 浏览器访问 localhost:4200 即可开始,默认账号密码 admin/admin +4. 浏览器访问 localhost:4200 即可开始,默认账号密码 admin/hertzbeat 详细步骤参考 [参与贡献之本地代码启动](CONTRIBUTING.md) diff --git a/README_EN.md b/README_EN.md index 020aaa36d41..ca4b7fe5cdc 100644 --- a/README_EN.md +++ b/README_EN.md @@ -95,7 +95,7 @@ Detailed steps refer to [Install HertzBeat via Docker](https://hertzbeat.com/doc 1. Download the installation package [GITEE Release](https://gitee.com/dromara/hertzbeat/releases) [GITHUB Release](https://github.com/dromara/hertzbeat/releases) 2. Configure the HertzBeat configuration yml file `hertzbeat/config/application.yml` 3. Run shell `$ ./startup.sh ` -4. Access `localhost:1157` to start, default account: `admin/admin` +4. Access `localhost:1157` to start, default account: `admin/hertzbeat` Detailed steps refer to [Install HertzBeat via package](https://hertzbeat.com/docs/start/package-deploy) @@ -103,7 +103,7 @@ Detailed steps refer to [Install HertzBeat via package](https://hertzbeat.com/do 1. Local source code debugging needs to start the back-end project manager and the front-end project web-app. 2. Backend:need `maven3+`, `java8+`, start the manager service. 3. Web:need `nodejs npm angular-cli` environment, Run `ng serve --open` in `web-app` directory after backend startup. -4. Access `localhost:4200` to start, default account: `admin/admin` +4. Access `localhost:4200` to start, default account: `admin/hertzbeat` Detailed steps refer to [CONTRIBUTING](CONTRIBUTING.md) diff --git a/alerter/src/main/java/com/usthe/alert/controller/AlertDefineController.java b/alerter/src/main/java/com/usthe/alert/controller/AlertDefineController.java index 3963c869ef4..878cdb0f049 100644 --- a/alerter/src/main/java/com/usthe/alert/controller/AlertDefineController.java +++ b/alerter/src/main/java/com/usthe/alert/controller/AlertDefineController.java @@ -33,7 +33,7 @@ */ @Api(tags = "Alert Define API | 告警定义管理API") @RestController -@RequestMapping(path = "/alert/define", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/alert/define", produces = {APPLICATION_JSON_VALUE}) public class AlertDefineController { @Autowired diff --git a/alerter/src/main/java/com/usthe/alert/controller/AlertDefinesController.java b/alerter/src/main/java/com/usthe/alert/controller/AlertDefinesController.java index 278ca8305c5..66c0adc6058 100644 --- a/alerter/src/main/java/com/usthe/alert/controller/AlertDefinesController.java +++ b/alerter/src/main/java/com/usthe/alert/controller/AlertDefinesController.java @@ -33,7 +33,7 @@ */ @Api(tags = "Alert Define Batch API | 告警定义管理API") @RestController -@RequestMapping(path = "/alert/defines", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/alert/defines", produces = {APPLICATION_JSON_VALUE}) public class AlertDefinesController { @Autowired diff --git a/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java b/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java index a79fcdda71e..69db2b854d4 100644 --- a/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java +++ b/alerter/src/main/java/com/usthe/alert/controller/AlertsController.java @@ -33,7 +33,7 @@ */ @Api(tags = "Alarm Manage Batch API | 告警批量管理API") @RestController -@RequestMapping(path = "/alerts", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/alerts", produces = {APPLICATION_JSON_VALUE}) public class AlertsController { @Autowired diff --git a/home/docs/help/issue.md b/home/docs/help/issue.md index 283700dac66..af2f1665f4e 100644 --- a/home/docs/help/issue.md +++ b/home/docs/help/issue.md @@ -24,7 +24,7 @@ sidebar_label: 常见问题 > 解决办法一:配置application.yml将数据库的连接地址由localhost修改为宿主机的对外IP > 解决办法二:使用Host网络模式启动Docker,即使Docker容器和宿主机共享网络 `docker run -d --network host .....` -2. **按照流程部署,访问 http://ip:1157/console 无界面** +2. **按照流程部署,访问 http://ip:1157/ 无界面** 请参考下面几点排查问题: > 一:依赖服务MYSQL数据库,TDENGINE数据库是否已按照启动成功,对应hertzbeat数据库是否已创建,SQL脚本是否执行 > 二:HertzBeat的配置文件 `application.yml` 里面的依赖服务IP账户密码等配置是否正确 @@ -36,7 +36,7 @@ sidebar_label: 常见问题 ### 安装包部署常见问题 -1. **按照流程部署,访问 http://ip:1157/console 无界面** +1. **按照流程部署,访问 http://ip:1157/ 无界面** 请参考下面几点排查问题: > 一:依赖服务MYSQL数据库,TDENGINE数据库是否已按照启动成功,对应hertzbeat数据库是否已创建,SQL脚本是否执行 > 二:HertzBeat的配置文件 `hertzbeat/config/application.yml` 里面的依赖服务IP账户密码等配置是否正确 diff --git a/home/docs/start/docker-deploy.md b/home/docs/start/docker-deploy.md index 41e5f745ef5..8efd2738ed0 100644 --- a/home/docs/start/docker-deploy.md +++ b/home/docs/start/docker-deploy.md @@ -25,7 +25,7 @@ sidebar_label: Docker方式部署 在主机目录下创建application.yml,eg:/opt/application.yml 配置文件内容参考 项目仓库[/script/application.yml](https://gitee.com/dromara/hertzbeat/raw/master/script/application.yml),需要替换里面的MYSQL服务和TDengine服务参数,IP端口账户密码(若使用邮件告警,需替换里面的邮件服务器参数) 具体替换参数如下: - ``` +``` spring.datasource.url spring.datasource.username spring.datasource.password @@ -38,11 +38,10 @@ sidebar_label: Docker方式部署 spring.mail.port spring.mail.username spring.mail.password - - ``` +``` 4. 配置用户配置文件(非必须,配置账户需要) - HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili + HertzBeat默认内置三个用户账户,分别为 admin/hertzbeat tom/hertzbeat guest/hertzbeat 若需要新增删除修改账户或密码,可以通过配置 `sureness.yml` 实现,若无此需求可忽略此步骤 在主机目录下创建sureness.yml,eg:/opt/sureness.yml 配置文件内容参考 项目仓库[/script/sureness.yml](https://gitee.com/dromara/hertzbeat/blob/master/script/sureness.yml) @@ -50,100 +49,102 @@ sidebar_label: Docker方式部署 ```yaml resourceRole: -- /account/auth/refresh===post===[admin,user,guest] -- /apps/**===get===[admin,user,guest] -- /monitor/**===get===[admin,user,guest] -- /monitor/**===post===[admin,user] -- /monitor/**===put===[admin,user] -- /monitor/**===delete==[admin] -- /monitors/**===get===[admin,user,guest] -- /monitors/**===post===[admin,user] -- /monitors/**===put===[admin,user] -- /monitors/**===delete===[admin] -- /alert/**===get===[admin,user,guest] -- /alert/**===post===[admin,user] -- /alert/**===put===[admin,user] -- /alert/**===delete===[admin] -- /alerts/**===get===[admin,user,guest] -- /alerts/**===post===[admin,user] -- /alerts/**===put===[admin,user] -- /alerts/**===delete===[admin] -- /notice/**===get===[admin,user,guest] -- /notice/**===post===[admin,user] -- /notice/**===put===[admin,user] -- /notice/**===delete===[admin] -- /summary/**===get===[admin,user,guest] -- /summary/**===post===[admin,user] -- /summary/**===put===[admin,user] -- /summary/**===delete===[admin] - + - /api/account/auth/refresh===post===[admin,user,guest] + - /api/apps/**===get===[admin,user,guest] + - /api/monitor/**===get===[admin,user,guest] + - /api/monitor/**===post===[admin,user] + - /api/monitor/**===put===[admin,user] + - /api/monitor/**===delete==[admin] + - /api/monitors/**===get===[admin,user,guest] + - /api/monitors/**===post===[admin,user] + - /api/monitors/**===put===[admin,user] + - /api/monitors/**===delete===[admin] + - /api/alert/**===get===[admin,user,guest] + - /api/alert/**===post===[admin,user] + - /api/alert/**===put===[admin,user] + - /api/alert/**===delete===[admin] + - /api/alerts/**===get===[admin,user,guest] + - /api/alerts/**===post===[admin,user] + - /api/alerts/**===put===[admin,user] + - /api/alerts/**===delete===[admin] + - /api/notice/**===get===[admin,user,guest] + - /api/notice/**===post===[admin,user] + - /api/notice/**===put===[admin,user] + - /api/notice/**===delete===[admin] + - /api/tag/**===get===[admin,user,guest] + - /api/tag/**===post===[admin,user] + - /api/tag/**===put===[admin,user] + - /api/tag/**===delete===[admin] + - /api/summary/**===get===[admin,user,guest] + - /api/summary/**===post===[admin,user] + - /api/summary/**===put===[admin,user] + - /api/summary/**===delete===[admin] + +# 需要被过滤保护的资源,不认证鉴权直接访问 +# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权 excludedResource: -- /account/auth/**===* -- /===get -- /i18n/**===get -- /apps/hierarchy===get -# web ui 静态资源 -- /console/**===get -- /**/*.html===get -- /**/*.js===get -- /**/*.css===get -- /**/*.ico===get -- /**/*.ttf===get -- /**/*.png===get -- /**/*.gif===get - - /**/*.png===* -# swagger ui 资源 -- /swagger-resources/**===get -- /v2/api-docs===get -- /v3/api-docs===get + - /api/account/auth/**===* + - /api/i18n/**===get + - /api/apps/hierarchy===get + # web ui 前端静态资源 + - /===get + - /dashboard/**===get + - /monitors/**===get + - /alert/**===get + - /account/**===get + - /setting/**===get + - /passport/**===get + - /**/*.html===get + - /**/*.js===get + - /**/*.css===get + - /**/*.ico===get + - /**/*.ttf===get + - /**/*.png===get + - /**/*.gif===get + - /**/*.jpg===get + - /**/*.svg===get + - /**/*.json===get + # swagger ui 资源 + - /swagger-resources/**===get + - /v2/api-docs===get + - /v3/api-docs===get +# 用户账户信息 +# 下面有 admin tom lili 三个账户 +# eg: admin 拥有[admin,user]角色,密码为hertzbeat +# eg: tom 拥有[user],密码为hertzbeat +# eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: -- appId: admin - credential: admin - role: [admin,user] -- appId: tom - credential: tom@123 - role: [user] -- appId: guest - credential: guest - role: [guest] -- appId: lili - # 注意 Digest认证不支持加盐加密的密码账户 - # 加盐加密的密码,通过 MD5(password+salt)计算 - # 此账户的原始密码为 lili - credential: 1A676730B0C7F54654B0E09184448289 - salt: 123 - role: [guest] - + - appId: admin + credential: hertzbeat + role: [admin,user] + - appId: tom + credential: hertzbeat + role: [user] + - appId: guest + credential: hertzbeat + role: [guest] ``` 修改sureness.yml的如下**部分参数**:**[注意⚠️sureness配置的其它默认参数需保留]** - + ```yaml - + # 用户账户信息 # 下面有 admin tom lili 三个账户 # eg: admin 拥有[admin,user]角色,密码为admin # eg: tom 拥有[user],密码为tom@123 # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: -- appId: admin - credential: admin - role: [admin,user] -- appId: tom - credential: tom@123 - role: [user] -- appId: guest - credential: guest - role: [guest] -- appId: lili - # 注意 Digest认证不支持加盐加密的密码账户 - # 加盐加密的密码,通过 MD5(password+salt)计算 - # 此账户的原始密码为 lili - credential: 1A676730B0C7F54654B0E09184448289 - salt: 123 - role: [guest] - + - appId: admin + credential: hertzbeat + role: [admin,user] + - appId: tom + credential: hertzbeat + role: [user] + - appId: guest + credential: hertzbeat + role: [guest] ``` 6. 启动HertzBeat Docker容器 @@ -160,7 +161,7 @@ account: - tancloud/hertzbeat:[版本tag] : 使用拉取的HertzBeat官方发布的应用镜像来启动容器,TAG可查看[官方镜像仓库](https://hub.docker.com/r/tancloud/hertzbeat/tags) 7. 开始探索HertzBeat - 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/admin。 + 浏览器访问 http://ip:1157/ 开始使用HertzBeat进行监控告警,默认账户密码 admin/hertzbeat。 **HAVE FUN** @@ -171,7 +172,7 @@ account: > 解决办法一:配置application.yml将数据库的连接地址由localhost修改为宿主机的对外IP > 解决办法二:使用Host网络模式启动Docker,即使Docker容器和宿主机共享网络 `docker run -d --network host .....` -2. **按照流程部署,访问 http://ip:1157/console 无界面** +2. **按照流程部署,访问 http://ip:1157/ 无界面** 请参考下面几点排查问题: > 一:依赖服务MYSQL数据库,TDENGINE数据库是否已按照启动成功,对应hertzbeat数据库是否已创建,SQL脚本是否执行 > 二:HertzBeat的配置文件 `application.yml` 里面的依赖服务IP账户密码等配置是否正确 diff --git a/home/docs/start/package-deploy.md b/home/docs/start/package-deploy.md index e084afc839a..9804096a773 100644 --- a/home/docs/start/package-deploy.md +++ b/home/docs/start/package-deploy.md @@ -30,7 +30,7 @@ sidebar_label: 安装包方式部署 修改位于 `hertzbeat/config/application.yml` 的配置文件 需要替换里面的MYSQL服务和TDengine服务参数,IP端口账户密码(若使用邮件告警,需替换里面的邮件服务器参数) 具体替换参数如下: - ``` +``` spring.datasource.url spring.datasource.username spring.datasource.password @@ -43,37 +43,30 @@ sidebar_label: 安装包方式部署 spring.mail.port spring.mail.username spring.mail.password - - ``` +``` + 4. 配置用户配置文件(非必须,配置账户需要) - HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili + HertzBeat默认内置三个用户账户,分别为 admin/hertzbeat tom/hertzbeat guest/hertzbeat 若需要新增删除修改账户或密码,可以通过修改位于 `hertzbeat/config/sureness.yml` 的配置文件实现,若无此需求可忽略此步骤 修改sureness.yml的如下**部分参数**:**[注意⚠️sureness配置的其它默认参数需保留]** ```yaml - + # 用户账户信息 # 下面有 admin tom lili 三个账户 -# eg: admin 拥有[admin,user]角色,密码为admin -# eg: tom 拥有[user],密码为tom@123 +# eg: admin 拥有[admin,user]角色,密码为hertzbeat +# eg: tom 拥有[user],密码为hertzbeat # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: -- appId: admin - credential: admin - role: [admin,user] -- appId: tom - credential: tom@123 - role: [user] -- appId: guest - credential: guest - role: [guest] -- appId: lili - # 注意 Digest认证不支持加盐加密的密码账户 - # 加盐加密的密码,通过 MD5(password+salt)计算 - # 此账户的原始密码为 lili - credential: 1A676730B0C7F54654B0E09184448289 - salt: 123 - role: [guest] + - appId: admin + credential: hertzbeat + role: [admin,user] + - appId: tom + credential: hertzbeat + role: [user] + - appId: guest + credential: hertzbeat + role: [guest] ``` @@ -83,13 +76,13 @@ account: $ ./startup.sh ``` 6. 开始探索HertzBeat - 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/admin。 + 浏览器访问 http://ip:1157/ 开始使用HertzBeat进行监控告警,默认账户密码 admin/hertzbeat。 **HAVE FUN** ### 安装包部署常见问题 -1. **按照流程部署,访问 http://ip:1157/console 无界面** +1. **按照流程部署,访问 http://ip:1157/ 无界面** 请参考下面几点排查问题: > 一:依赖服务MYSQL数据库,TDENGINE数据库是否已按照启动成功,对应hertzbeat数据库是否已创建,SQL脚本是否执行 > 二:HertzBeat的配置文件 `hertzbeat/config/application.yml` 里面的依赖服务IP账户密码等配置是否正确 diff --git a/home/docs/start/quickstart.md b/home/docs/start/quickstart.md index 0a83fbec661..4596ae85907 100644 --- a/home/docs/start/quickstart.md +++ b/home/docs/start/quickstart.md @@ -49,7 +49,7 @@ sidebar_label: 快速开始 1. 此为前后端分离项目,本地代码调试需要分别启动后端工程manager和前端工程web-app 2. 后端:需要`maven3+`和`java8+`环境,修改YML配置信息并启动manager服务 3. 前端:需要`nodejs npm angular-cli`环境,待本地后端启动后,在web-app目录下启动 `ng serve --open` -4. 浏览器访问 localhost:4200 即可开始,默认账户密码 admin/admin +4. 浏览器访问 localhost:4200 即可开始,默认账户密码 admin/hertzbeat 详细步骤参考 [参与贡献之本地代码启动](../others/contributing) diff --git a/home/i18n/en/docusaurus-plugin-content-docs/current/start/docker-deploy.md b/home/i18n/en/docusaurus-plugin-content-docs/current/start/docker-deploy.md index bfcd67e85a4..8454a919513 100644 --- a/home/i18n/en/docusaurus-plugin-content-docs/current/start/docker-deploy.md +++ b/home/i18n/en/docusaurus-plugin-content-docs/current/start/docker-deploy.md @@ -40,7 +40,7 @@ sidebar_label: Docker方式部署 ``` 4. 配置用户配置文件(非必须,配置账户需要) - HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili + HertzBeat默认内置三个用户账户,分别为 admin/hertzbeat tom/hertzbeat guest/hertzbeat 若需要新增删除修改账户或密码,可以通过配置 `sureness.yml` 实现,若无此需求可忽略此步骤 在主机目录下创建sureness.yml,eg:/opt/sureness.yml 配置文件内容参考 项目仓库[/script/sureness.yml](https://gitee.com/dromara/hertzbeat/blob/master/script/sureness.yml) @@ -70,8 +70,8 @@ sidebar_label: Docker方式部署 # 用户账户信息 # 下面有 admin tom lili 三个账户 - # eg: admin 拥有[role1,role2]角色,密码为admin - # eg: tom 拥有[role1,role2,role3],密码为tom@123 + # eg: admin 拥有[role1,role2]角色,密码为hertzbeat + # eg: tom 拥有[role1,role2,role3],密码为hertzbeat # eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin @@ -128,7 +128,7 @@ sidebar_label: Docker方式部署 - tancloud/hertzbeat:[版本tag] : 使用拉取的HertzBeat官方发布的应用镜像来启动容器,TAG可查看[官方镜像仓库](https://hub.docker.com/r/tancloud/hertzbeat/tags) 7. 开始探索HertzBeat - 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/admin。 + 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/hertzbeat。 **HAVE FUN** diff --git a/home/i18n/en/docusaurus-plugin-content-docs/current/start/package-deploy.md b/home/i18n/en/docusaurus-plugin-content-docs/current/start/package-deploy.md index e5e50160d0d..d3f4aec8e1f 100644 --- a/home/i18n/en/docusaurus-plugin-content-docs/current/start/package-deploy.md +++ b/home/i18n/en/docusaurus-plugin-content-docs/current/start/package-deploy.md @@ -44,15 +44,15 @@ sidebar_label: 安装包方式部署 ``` 4. 配置用户配置文件(非必须,配置账户需要) - HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili + HertzBeat默认内置三个用户账户,分别为 admin/hertzbeat tom/hertzbeat guest/hertzbeat 若需要新增删除修改账户或密码,可以通过修改位于 `hertzbeat/config/sureness.yml` 的配置文件实现,若无此需求可忽略此步骤 修改sureness.yml的如下**部分参数**:**[注意⚠️sureness配置的其它默认参数需保留]** ```yaml # 用户账户信息 # 下面有 admin tom lili 三个账户 - # eg: admin 拥有[role1,role2]角色,密码为admin - # eg: tom 拥有[role1,role2,role3],密码为tom@123 + # eg: admin 拥有[role1,role2]角色,密码为hertzbeat + # eg: tom 拥有[role1,role2,role3],密码为hertzbeat # eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin @@ -76,7 +76,7 @@ sidebar_label: 安装包方式部署 $ ./startup.sh ``` 6. 开始探索HertzBeat - 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/admin。 + 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警,默认账户密码 admin/hertzbeat。 **HAVE FUN** diff --git a/home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md b/home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md index c0338e75d25..db88f611c8a 100644 --- a/home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md +++ b/home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md @@ -42,7 +42,7 @@ Detailed steps refer to [Install HertzBeat via Docker](https://hertzbeat.com/doc 1. Download the installation package [GITEE Release](https://gitee.com/dromara/hertzbeat/releases) [GITHUB Release](https://github.com/dromara/hertzbeat/releases) 2. Configure the HertzBeat configuration yml file `hertzbeat/config/application.yml` 3. Run shell `$ ./startup.sh ` -4. Access `localhost:1157` to start, default account: `admin/admin` +4. Access `localhost:1157` to start, default account: `admin/hertzbeat` Detailed steps refer to [Install HertzBeat via package](https://hertzbeat.com/docs/start/package-deploy) @@ -50,7 +50,7 @@ Detailed steps refer to [Install HertzBeat via package](https://hertzbeat.com/do 1. Local source code debugging needs to start the back-end project manager and the front-end project web-app. 2. Backend:need `maven3+`, `java8+`, start the manager service. 3. Web:need `nodejs npm angular-cli` environment, Run `ng serve --open` in `web-app` directory after backend startup. -4. Access `localhost:4200` to start, default account: `admin/admin` +4. Access `localhost:4200` to start, default account: `admin/hertzbeat` Detailed steps refer to [CONTRIBUTING](../others/contributing) diff --git a/manager/src/main/java/com/usthe/manager/controller/AccountController.java b/manager/src/main/java/com/usthe/manager/controller/AccountController.java index f24483166ed..c49f2b37cbd 100644 --- a/manager/src/main/java/com/usthe/manager/controller/AccountController.java +++ b/manager/src/main/java/com/usthe/manager/controller/AccountController.java @@ -37,7 +37,7 @@ */ @Api(tags = "Auth Manage API | 认证注册TOKEN管理API") @RestController() -@RequestMapping(value = "/account/auth", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(value = "/api/account/auth", produces = {APPLICATION_JSON_VALUE}) @Slf4j public class AccountController { diff --git a/manager/src/main/java/com/usthe/manager/controller/AppController.java b/manager/src/main/java/com/usthe/manager/controller/AppController.java index 6f4f559838d..81e73b59bc3 100644 --- a/manager/src/main/java/com/usthe/manager/controller/AppController.java +++ b/manager/src/main/java/com/usthe/manager/controller/AppController.java @@ -29,7 +29,7 @@ */ @Api(tags = "Monitor Type Manage API | 监控类型管理API") @RestController -@RequestMapping(path = "/apps", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/apps", produces = {APPLICATION_JSON_VALUE}) public class AppController { @Autowired diff --git a/manager/src/main/java/com/usthe/manager/controller/I18nController.java b/manager/src/main/java/com/usthe/manager/controller/I18nController.java index f1ad8da35b5..abe13e90b41 100644 --- a/manager/src/main/java/com/usthe/manager/controller/I18nController.java +++ b/manager/src/main/java/com/usthe/manager/controller/I18nController.java @@ -25,7 +25,7 @@ */ @Api(tags = "en:I18N API | I18N Internationalization Resource API,zh: I18N API | I18N国际化资源API") @RestController -@RequestMapping(path = "/i18n", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/i18n", produces = {APPLICATION_JSON_VALUE}) public class I18nController { @Autowired diff --git a/manager/src/main/java/com/usthe/manager/controller/MonitorController.java b/manager/src/main/java/com/usthe/manager/controller/MonitorController.java index 3a18e3d41dc..139e055d198 100644 --- a/manager/src/main/java/com/usthe/manager/controller/MonitorController.java +++ b/manager/src/main/java/com/usthe/manager/controller/MonitorController.java @@ -32,7 +32,7 @@ */ @Api(tags = "Monitor Manage API | 监控管理API") @RestController -@RequestMapping(path = "/monitor", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/monitor", produces = {APPLICATION_JSON_VALUE}) public class MonitorController { @Autowired diff --git a/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java b/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java index 7b0723752c5..7effee39375 100644 --- a/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java +++ b/manager/src/main/java/com/usthe/manager/controller/MonitorsController.java @@ -37,7 +37,7 @@ */ @Api(tags = "Monitor Manage Batch API | 监控列表API") @RestController -@RequestMapping(path = "/monitors", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/monitors", produces = {APPLICATION_JSON_VALUE}) public class MonitorsController { private static final byte ALL_MONITOR_STATUS = 9; diff --git a/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java b/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java index 10f902ef5f0..0d067494f6a 100644 --- a/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java +++ b/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java @@ -36,7 +36,7 @@ */ @Api(tags = "Notification Config API | 消息通知配置API") @RestController() -@RequestMapping(value = "/notice", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(value = "/api/notice", produces = {APPLICATION_JSON_VALUE}) public class NoticeConfigController { @Autowired diff --git a/manager/src/main/java/com/usthe/manager/controller/SummaryController.java b/manager/src/main/java/com/usthe/manager/controller/SummaryController.java index 7c0b79ef988..224647c8f1d 100644 --- a/manager/src/main/java/com/usthe/manager/controller/SummaryController.java +++ b/manager/src/main/java/com/usthe/manager/controller/SummaryController.java @@ -26,7 +26,7 @@ */ @Api(tags = "Summary Statistics API | 系统摘要统计API") @RestController -@RequestMapping(path = "/summary", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/summary", produces = {APPLICATION_JSON_VALUE}) public class SummaryController { @Autowired diff --git a/manager/src/main/java/com/usthe/manager/controller/TagController.java b/manager/src/main/java/com/usthe/manager/controller/TagController.java index d923af35d5b..de24411ff4f 100644 --- a/manager/src/main/java/com/usthe/manager/controller/TagController.java +++ b/manager/src/main/java/com/usthe/manager/controller/TagController.java @@ -34,7 +34,7 @@ */ @Api(tags = "Tag Manage API | 标签管理API") @RestController -@RequestMapping(path = "/tag", produces = {APPLICATION_JSON_VALUE}) +@RequestMapping(path = "/api/tag", produces = {APPLICATION_JSON_VALUE}) public class TagController { @Autowired diff --git a/manager/src/main/resources/application.yml b/manager/src/main/resources/application.yml index 664daf935fa..b001678ed54 100644 --- a/manager/src/main/resources/application.yml +++ b/manager/src/main/resources/application.yml @@ -6,7 +6,7 @@ spring: profiles: active: prod mvc: - static-path-pattern: /console/** + static-path-pattern: /** resources: static-locations: - classpath:/dist/ diff --git a/manager/src/main/resources/sureness.yml b/manager/src/main/resources/sureness.yml index 50d0b77ae01..1720d42d520 100644 --- a/manager/src/main/resources/sureness.yml +++ b/manager/src/main/resources/sureness.yml @@ -5,46 +5,51 @@ # eg: /api/v1/source1===get===[admin] 表示 /api/v2/host===post 这条资源支持 admin 这一种角色访问 # eg: /api/v1/source2===get===[] 表示 /api/v1/source2===get 这条资源不支持任何角色访问 resourceRole: - - /account/auth/refresh===post===[admin,user,guest] - - /apps/**===get===[admin,user,guest] - - /monitor/**===get===[admin,user,guest] - - /monitor/**===post===[admin,user] - - /monitor/**===put===[admin,user] - - /monitor/**===delete==[admin] - - /monitors/**===get===[admin,user,guest] - - /monitors/**===post===[admin,user] - - /monitors/**===put===[admin,user] - - /monitors/**===delete===[admin] - - /alert/**===get===[admin,user,guest] - - /alert/**===post===[admin,user] - - /alert/**===put===[admin,user] - - /alert/**===delete===[admin] - - /alerts/**===get===[admin,user,guest] - - /alerts/**===post===[admin,user] - - /alerts/**===put===[admin,user] - - /alerts/**===delete===[admin] - - /notice/**===get===[admin,user,guest] - - /notice/**===post===[admin,user] - - /notice/**===put===[admin,user] - - /notice/**===delete===[admin] - - /tag/**===get===[admin,user,guest] - - /tag/**===post===[admin,user] - - /tag/**===put===[admin,user] - - /tag/**===delete===[admin] - - /summary/**===get===[admin,user,guest] - - /summary/**===post===[admin,user] - - /summary/**===put===[admin,user] - - /summary/**===delete===[admin] + - /api/account/auth/refresh===post===[admin,user,guest] + - /api/apps/**===get===[admin,user,guest] + - /api/monitor/**===get===[admin,user,guest] + - /api/monitor/**===post===[admin,user] + - /api/monitor/**===put===[admin,user] + - /api/monitor/**===delete==[admin] + - /api/monitors/**===get===[admin,user,guest] + - /api/monitors/**===post===[admin,user] + - /api/monitors/**===put===[admin,user] + - /api/monitors/**===delete===[admin] + - /api/alert/**===get===[admin,user,guest] + - /api/alert/**===post===[admin,user] + - /api/alert/**===put===[admin,user] + - /api/alert/**===delete===[admin] + - /api/alerts/**===get===[admin,user,guest] + - /api/alerts/**===post===[admin,user] + - /api/alerts/**===put===[admin,user] + - /api/alerts/**===delete===[admin] + - /api/notice/**===get===[admin,user,guest] + - /api/notice/**===post===[admin,user] + - /api/notice/**===put===[admin,user] + - /api/notice/**===delete===[admin] + - /api/tag/**===get===[admin,user,guest] + - /api/tag/**===post===[admin,user] + - /api/tag/**===put===[admin,user] + - /api/tag/**===delete===[admin] + - /api/summary/**===get===[admin,user,guest] + - /api/summary/**===post===[admin,user] + - /api/summary/**===put===[admin,user] + - /api/summary/**===delete===[admin] # 需要被过滤保护的资源,不认证鉴权直接访问 # /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权 excludedResource: - - /account/auth/**===* + - /api/account/auth/**===* + - /api/i18n/**===get + - /api/apps/hierarchy===get + # web ui 前端静态资源 - /===get - - /i18n/**===get - - /apps/hierarchy===get - # web ui 静态资源 - - /console/**===get + - /dashboard/**===get + - /monitors/**===get + - /alert/**===get + - /account/**===get + - /setting/**===get + - /passport/**===get - /**/*.html===get - /**/*.js===get - /**/*.css===get @@ -52,7 +57,9 @@ excludedResource: - /**/*.ttf===get - /**/*.png===get - /**/*.gif===get - - /**/*.png===* + - /**/*.jpg===get + - /**/*.svg===get + - /**/*.json===get # swagger ui 资源 - /swagger-resources/**===get - /v2/api-docs===get diff --git a/script/application.yml b/script/application.yml index 07e37682bee..802ab6aff94 100644 --- a/script/application.yml +++ b/script/application.yml @@ -6,7 +6,7 @@ spring: profiles: active: prod mvc: - static-path-pattern: /console/** + static-path-pattern: /** resources: static-locations: - classpath:/dist/ diff --git a/script/docker-compose/README.md b/script/docker-compose/README.md index dc42bb0032b..a83d0872576 100644 --- a/script/docker-compose/README.md +++ b/script/docker-compose/README.md @@ -45,7 +45,7 @@ `docker-compose restart hertzbeat` ##### 开始探索HertzBeat - 浏览器访问 http://ip:1157/console 开始使用HertzBeat进行监控告警。 + 浏览器访问 http://ip:1157/ 开始使用HertzBeat进行监控告警。 --- diff --git a/script/docker-compose/conf/application.yml b/script/docker-compose/conf/application.yml index 899924d02da..1f53fb35142 100644 --- a/script/docker-compose/conf/application.yml +++ b/script/docker-compose/conf/application.yml @@ -6,7 +6,7 @@ spring: profiles: active: prod mvc: - static-path-pattern: /console/** + static-path-pattern: /** resources: static-locations: - classpath:/dist/ diff --git a/script/docker-compose/conf/sureness.yml b/script/docker-compose/conf/sureness.yml index 50d0b77ae01..a9c1f82ae62 100644 --- a/script/docker-compose/conf/sureness.yml +++ b/script/docker-compose/conf/sureness.yml @@ -39,12 +39,17 @@ resourceRole: # 需要被过滤保护的资源,不认证鉴权直接访问 # /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权 excludedResource: - - /account/auth/**===* + - /api/account/auth/**===* + - /api/i18n/**===get + - /api/apps/hierarchy===get + # web ui 前端静态资源 - /===get - - /i18n/**===get - - /apps/hierarchy===get - # web ui 静态资源 - - /console/**===get + - /dashboard/**===get + - /monitors/**===get + - /alert/**===get + - /account/**===get + - /setting/**===get + - /passport/**===get - /**/*.html===get - /**/*.js===get - /**/*.css===get @@ -52,7 +57,9 @@ excludedResource: - /**/*.ttf===get - /**/*.png===get - /**/*.gif===get - - /**/*.png===* + - /**/*.jpg===get + - /**/*.svg===get + - /**/*.json===get # swagger ui 资源 - /swagger-resources/**===get - /v2/api-docs===get diff --git a/script/sureness.yml b/script/sureness.yml index 50d0b77ae01..a11a22b73c8 100644 --- a/script/sureness.yml +++ b/script/sureness.yml @@ -39,12 +39,17 @@ resourceRole: # 需要被过滤保护的资源,不认证鉴权直接访问 # /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权 excludedResource: - - /account/auth/**===* + - /api/account/auth/**===* + - /api/i18n/**===get + - /api/apps/hierarchy===get + # web ui 前端静态资源 - /===get - - /i18n/**===get - - /apps/hierarchy===get - # web ui 静态资源 - - /console/**===get + - /dashboard/**===get + - /monitors/**===get + - /alert/**===get + - /account/**===get + - /setting/**===get + - /passport/**===get - /**/*.html===get - /**/*.js===get - /**/*.css===get @@ -52,7 +57,7 @@ excludedResource: - /**/*.ttf===get - /**/*.png===get - /**/*.gif===get - - /**/*.png===* + - /**/*.png===* # swagger ui 资源 - /swagger-resources/**===get - /v2/api-docs===get diff --git a/warehouse/src/main/java/com/usthe/warehouse/controller/MetricsDataController.java b/warehouse/src/main/java/com/usthe/warehouse/controller/MetricsDataController.java index c4fb9884ea7..94f8f3afd4e 100644 --- a/warehouse/src/main/java/com/usthe/warehouse/controller/MetricsDataController.java +++ b/warehouse/src/main/java/com/usthe/warehouse/controller/MetricsDataController.java @@ -45,7 +45,7 @@ public class MetricsDataController { @Autowired private TdEngineDataStorage tdEngineDataStorage; - @GetMapping("/monitor/{monitorId}/metrics/{metrics}") + @GetMapping("/api/monitor/{monitorId}/metrics/{metrics}") @ApiOperation(value = "查询监控指标组的指标数据", notes = "查询监控指标组的指标数据") public ResponseEntity> getMetricsData( @ApiParam(value = "监控ID", example = "343254354") @@ -75,7 +75,7 @@ public ResponseEntity> getMetricsData( } } - @GetMapping("/monitor/{monitorId}/metric/{metricFull}") + @GetMapping("/api/monitor/{monitorId}/metric/{metricFull}") @ApiOperation(value = "查询监控指标组的指定指标的历史数据", notes = "查询监控指标组下的指定指标的历史数据") public ResponseEntity> getMetricHistoryData( @ApiParam(value = "监控ID", example = "343254354") diff --git a/web-app/README.md b/web-app/README.md index 8603a043d01..dc0ca58da76 100644 --- a/web-app/README.md +++ b/web-app/README.md @@ -23,7 +23,7 @@ 1. web-app目录下执行 -```ng build --prod --base-href /console/``` +```ng build --prod``` 2. manager目录下执行 diff --git a/web-app/src/app/layout/basic/widgets/notify.component.ts b/web-app/src/app/layout/basic/widgets/notify.component.ts index d8583b638cb..711aa97a168 100644 --- a/web-app/src/app/layout/basic/widgets/notify.component.ts +++ b/web-app/src/app/layout/basic/widgets/notify.component.ts @@ -85,7 +85,7 @@ export class HeaderNotifyComponent implements OnInit { }, error => { loadAlerts$.unsubscribe(); - console.error(error.msg); + console.error(error); this.loading = false; } ); diff --git a/web-app/src/environments/environment.prod.ts b/web-app/src/environments/environment.prod.ts index a32e33f0b84..514d2400d2b 100644 --- a/web-app/src/environments/environment.prod.ts +++ b/web-app/src/environments/environment.prod.ts @@ -4,7 +4,7 @@ export const environment = { production: true, useHash: false, api: { - baseUrl: '/', + baseUrl: '/api/', refreshTokenEnabled: true } } as Environment; diff --git a/web-app/src/environments/environment.ts b/web-app/src/environments/environment.ts index 7ba92bb7088..d349da713ca 100644 --- a/web-app/src/environments/environment.ts +++ b/web-app/src/environments/environment.ts @@ -11,7 +11,7 @@ export const environment = { production: false, useHash: false, api: { - baseUrl: 'http://localhost:1157/', + baseUrl: 'http://localhost:1157/api/', refreshTokenEnabled: true }, modules: [DelonMockModule.forRoot({ data: MOCK_DATA })] From 759632d75902b714b6d7626ef7873b97436729a5 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Sun, 8 May 2022 07:57:58 +0800 Subject: [PATCH 07/27] [manager,webapp]bugfix: error when tags duplicate in monitor (#116) --- CONTRIBUTING.md | 4 +--- .../com/usthe/manager/service/impl/MonitorServiceImpl.java | 3 +++ .../app/routes/monitor/monitor-edit/monitor-edit.component.ts | 4 +++- .../app/routes/monitor/monitor-new/monitor-new.component.ts | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 84e0996f8a0..5dbcfe1e3e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,8 +9,6 @@ > 提供对监控的管理,监控应用配置的管理,系统用户租户后台管理等。 - **[collector](https://github.com/dromara/hertzbeat/tree/master/collector)** 提供监控数据采集服务 > 使用通用协议远程采集获取对端指标数据。 -- **[scheduler](https://github.com/dromara/hertzbeat/tree/master/scheduler)** 提供监控任务调度服务 -> 采集任务管理,一次性任务和周期性任务的调度分发。 - **[warehouse](https://github.com/dromara/hertzbeat/tree/master/warehouse)** 提供监控数据仓储服务 > 采集指标结果数据管理,数据落盘,查询,计算统计。 - **[alerter](https://github.com/dromara/hertzbeat/tree/master/alerter)** 提供告警服务 @@ -40,7 +38,7 @@ https://gitee.com/dromara/hertzbeat/pulls 1. 部署启动依赖服务`MYSQL`和`TDengine`数据库 2. 需要`maven3+`和`java8+`环境 3. 修改配置文件的依赖服务地址等信息-`manager/src/main/resources/application.yml` -4. 启动`manager`服务 `manager/src/main/java/com/usthe/manager/Manager.java` +4. 启动`springboot manager`服务 `manager/src/main/java/com/usthe/manager/Manager.java` ### 前端启动 diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java index 9a885295613..c4e7e2f8cb2 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MonitorServiceImpl.java @@ -167,6 +167,9 @@ public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgu } } // todo 校验标签 + if (monitor.getTags() != null) { + monitor.setTags(monitor.getTags().stream().distinct().collect(Collectors.toList())); + } // Parameter definition structure verification 参数定义结构校验 List paramDefines = appService.getAppParamDefines(monitorDto.getMonitor().getApp()); diff --git a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts index 0138f84864b..0a9c31c2510 100644 --- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts +++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts @@ -296,7 +296,9 @@ export class MonitorEditComponent implements OnInit { onManageModalOk() { this.isManageModalOkLoading = true; this.checkedTags.forEach(item => { - this.monitor.tags.push(item); + if (this.monitor.tags.find(tag => tag.id == item.id) == undefined) { + this.monitor.tags.push(item); + } }); this.isManageModalOkLoading = false; this.isManageModalVisible = false; diff --git a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts index b9f2dca30ad..b815d9e726c 100644 --- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts +++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts @@ -274,7 +274,9 @@ export class MonitorNewComponent implements OnInit { onManageModalOk() { this.isManageModalOkLoading = true; this.checkedTags.forEach(item => { - this.monitor.tags.push(item); + if (this.monitor.tags.find(tag => tag.id == item.id) == undefined) { + this.monitor.tags.push(item); + } }); this.isManageModalOkLoading = false; this.isManageModalVisible = false; From 22777040d79d24313aceb049b3385b16401e9a21 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Sun, 8 May 2022 18:13:40 +0800 Subject: [PATCH 08/27] [manager,webapp]feature: enable alerter send test msg button (#117) [manager]feature: add alert send test msg [manager]feature: add alert send test msg [monitor]feature: alert test notice Co-authored-by: xgf <13386772885@163.com> --- .../component/alerter/DispatcherAlarm.java | 27 +++++++++++++---- .../impl/EmailAlertNotifyHandlerImpl.java | 2 ++ .../controller/NoticeConfigController.java | 8 +++++ .../manager/service/NoticeConfigService.java | 8 +++++ .../manager/service/impl/MailServiceImpl.java | 8 +++-- .../service/impl/NoticeConfigServiceImpl.java | 28 +++++++++++++++++ .../support/GlobalExceptionHandler.java | 12 ++++++-- .../exception/AlertNoticeException.java | 13 ++++++++ .../alert-notice/alert-notice.component.html | 8 +++++ .../alert-notice/alert-notice.component.ts | 30 ++++++++++++++++++- .../app/service/notice-receiver.service.ts | 6 ++++ web-app/src/assets/i18n/en-US.json | 3 ++ web-app/src/assets/i18n/zh-CN.json | 3 ++ web-app/src/assets/i18n/zh-TW.json | 3 ++ 14 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 manager/src/main/java/com/usthe/manager/support/exception/AlertNoticeException.java diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/DispatcherAlarm.java b/manager/src/main/java/com/usthe/manager/component/alerter/DispatcherAlarm.java index 9067cf4a818..30df98a0f3f 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/DispatcherAlarm.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/DispatcherAlarm.java @@ -53,6 +53,25 @@ public void afterPropertiesSet() throws Exception { } } + /** + * send alert msg to receiver + * @param receiver receiver + * @param alert alert msg + * @return send success or failed + */ + public boolean sendNoticeMsg(NoticeReceiver receiver, Alert alert){ + if(receiver == null || receiver.getType() == null){ + log.warn("DispatcherAlarm-sendNoticeMsg params is empty alert:[{}], receiver:[{}]", alert, receiver); + return false; + } + byte type = receiver.getType(); + if (alertNotifyHandlerMap.containsKey(type)) { + alertNotifyHandlerMap.get(type).send(receiver, alert); + return true; + } + return false; + } + private List matchReceiverByNoticeRules(Alert alert) { // todo use cache 使用缓存 return noticeConfigService.getReceiverFilterRule(alert); @@ -82,13 +101,9 @@ private void sendNotify(Alert alert) { List receivers = matchReceiverByNoticeRules(alert); // todo Send notification here temporarily single thread 发送通知这里暂时单线程 for (NoticeReceiver receiver : receivers) { - byte type = receiver.getType(); - if (alertNotifyHandlerMap.containsKey(type)) { - alertNotifyHandlerMap.get(type).send(receiver, alert); - } - // 暂未支持的通知类型 + sendNoticeMsg(receiver, alert); } } - } + } diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/EmailAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/EmailAlertNotifyHandlerImpl.java index d68eef1a9b6..89e40cf1ba8 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/EmailAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/EmailAlertNotifyHandlerImpl.java @@ -4,6 +4,7 @@ import com.usthe.common.entity.manager.NoticeReceiver; import com.usthe.manager.component.alerter.AlertNotifyHandler; import com.usthe.manager.service.MailService; +import com.usthe.manager.support.exception.AlertNoticeException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -46,6 +47,7 @@ public void send(NoticeReceiver receiver, Alert alert) { javaMailSender.send(mimeMessage); } catch (Exception e) { log.error("[Email Alert] Exception,Exception information={}", e.getMessage()); + throw new AlertNoticeException("[Email Alert] failed: " + e.getMessage()); } } diff --git a/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java b/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java index 0d067494f6a..c3689066df2 100644 --- a/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java +++ b/manager/src/main/java/com/usthe/manager/controller/NoticeConfigController.java @@ -136,4 +136,12 @@ public ResponseEntity>> getRules( return ResponseEntity.ok(message); } + + @PostMapping(path = "/receiver/send-test-msg") + @ApiOperation(value = "Send test msg to receiver", notes = "给指定接收人发送测试消息") + public ResponseEntity> sendTestMsg(@Valid @RequestBody NoticeReceiver noticeReceiver) { + boolean sendFlag = noticeConfigService.sendTestMsg(noticeReceiver); + return ResponseEntity.ok(new Message<>(sendFlag)); + } + } diff --git a/manager/src/main/java/com/usthe/manager/service/NoticeConfigService.java b/manager/src/main/java/com/usthe/manager/service/NoticeConfigService.java index 99db2c65a4b..2f3e9002005 100644 --- a/manager/src/main/java/com/usthe/manager/service/NoticeConfigService.java +++ b/manager/src/main/java/com/usthe/manager/service/NoticeConfigService.java @@ -108,4 +108,12 @@ public interface NoticeConfigService { * @return Notification Rule Entity 通知规则实体 */ NoticeRule getNoticeRulesById(Long ruleId); + + /** + * alert Send test message + * 告警 发送测试消息 + * @param noticeReceiver recipient information 接收人信息 + * @return true send success | false send fail + */ + boolean sendTestMsg(NoticeReceiver noticeReceiver); } diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java index 5cbaad5a5e8..c101de51ef6 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java @@ -31,8 +31,12 @@ public class MailServiceImpl implements MailService { @Override public String buildAlertHtmlTemplate(final Alert alert) { - String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); - String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + String monitorId = null; + String monitorName = null; + if (alert.getTags() != null) { + monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + } monitorId = monitorId == null? "External alarm, no ID" : monitorId; monitorName = monitorName == null? "External alarm, no Name" : monitorName; // Introduce thymeleaf context parameters to render pages diff --git a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java index 604f15ea877..491fca8082d 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java @@ -3,6 +3,8 @@ import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import com.usthe.common.entity.alerter.Alert; +import com.usthe.common.util.CommonConstants; +import com.usthe.manager.component.alerter.DispatcherAlarm; import com.usthe.manager.dao.NoticeReceiverDao; import com.usthe.manager.dao.NoticeRuleDao; import com.usthe.common.entity.manager.NoticeReceiver; @@ -10,6 +12,7 @@ import com.usthe.manager.service.NoticeConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -30,12 +33,20 @@ @Slf4j public class NoticeConfigServiceImpl implements NoticeConfigService { + private static final String ALERT_TEST_TARGET = "Test Target"; + + private static final String ALERT_TEST_CONTENT = "test send msg! \n This is the test data. It is proved that it can be received successfully"; + @Autowired private NoticeReceiverDao noticeReceiverDao; @Autowired private NoticeRuleDao noticeRuleDao; + @Autowired + @Lazy + private DispatcherAlarm dispatcherAlarm; + @Override public List getNoticeReceivers(Specification specification) { return noticeReceiverDao.findAll(specification); @@ -50,6 +61,14 @@ public List getNoticeRules(Specification specification) public void addReceiver(NoticeReceiver noticeReceiver) { noticeReceiverDao.save(noticeReceiver); } + @Override + public boolean sendTestMsg(NoticeReceiver noticeReceiver) { + Alert alert = new Alert(); + alert.setContent(CommonConstants.TEST_SEND_MSG); + alert.setId(0L); + return dispatcherAlarm.sendNoticeMsg(noticeReceiver, alert); + } + @Override public void editReceiver(NoticeReceiver noticeReceiver) { @@ -115,4 +134,13 @@ public NoticeReceiver getReceiverById(Long receiverId) { public NoticeRule getNoticeRulesById(Long ruleId) { return noticeRuleDao.getOne(ruleId); } + + @Override + public boolean sendTestMsg(NoticeReceiver noticeReceiver) { + Alert alert = new Alert(); + alert.setTarget(ALERT_TEST_TARGET); + alert.setContent(ALERT_TEST_CONTENT); + alert.setPriority(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL); + return dispatcherAlarm.sendNoticeMsg(noticeReceiver, alert); + } } diff --git a/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java b/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java index db23f1bb727..1442c8950f5 100644 --- a/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java +++ b/manager/src/main/java/com/usthe/manager/support/GlobalExceptionHandler.java @@ -1,6 +1,7 @@ package com.usthe.manager.support; import com.usthe.common.entity.dto.Message; +import com.usthe.manager.support.exception.AlertNoticeException; import com.usthe.manager.support.exception.MonitorDatabaseException; import com.usthe.manager.support.exception.MonitorDetectException; import lombok.extern.slf4j.Slf4j; @@ -18,9 +19,7 @@ import java.lang.reflect.Field; -import static com.usthe.common.util.CommonConstants.DETECT_FAILED_CODE; -import static com.usthe.common.util.CommonConstants.MONITOR_CONFLICT_CODE; -import static com.usthe.common.util.CommonConstants.PARAM_INVALID_CODE; +import static com.usthe.common.util.CommonConstants.*; /** * controller exception handler @@ -82,6 +81,13 @@ ResponseEntity> handleIllegalArgumentException(IllegalArgumentExce return ResponseEntity.ok(message); } + @ExceptionHandler(AlertNoticeException.class) + @ResponseBody + ResponseEntity> handleAlertNoticeException(AlertNoticeException noticeException) { + Message message = Message.builder().msg(noticeException.getMessage()).code(FAIL_CODE).build(); + return ResponseEntity.ok(message); + } + /** * 处理请求参数错误的失败, 请求参数json映射body时出错 * @param exception 参数映射body异常 diff --git a/manager/src/main/java/com/usthe/manager/support/exception/AlertNoticeException.java b/manager/src/main/java/com/usthe/manager/support/exception/AlertNoticeException.java new file mode 100644 index 00000000000..b083fee31bd --- /dev/null +++ b/manager/src/main/java/com/usthe/manager/support/exception/AlertNoticeException.java @@ -0,0 +1,13 @@ +package com.usthe.manager.support.exception; + +/** + * alert notice send failed + * 告警通知发送异常 + * @author tom + * @date 2022/5/8 17:59 + */ +public class AlertNoticeException extends RuntimeException { + public AlertNoticeException(String message) { + super(message); + } +} diff --git a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html index 20580b8c8c8..c8d0568cd1a 100644 --- a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html +++ b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.html @@ -285,6 +285,14 @@
+ + + + + diff --git a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts index cf6d6c5c6d7..5448695e47c 100644 --- a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts +++ b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts @@ -10,7 +10,6 @@ import { NoticeRule } from '../../../pojo/NoticeRule'; import { NoticeReceiverService } from '../../../service/notice-receiver.service'; import { NoticeRuleService } from '../../../service/notice-rule.service'; import { TagService } from '../../../service/tag.service'; -import { Tag } from '../../../pojo/Tag'; @Component({ selector: 'app-alert-notice', @@ -154,6 +153,7 @@ export class AlertNoticeComponent implements OnInit { isManageReceiverModalVisible: boolean = false; isManageReceiverModalAdd: boolean = true; isManageReceiverModalOkLoading: boolean = false; + isSendTestButtonLoading: boolean = false; receiver!: NoticeReceiver; onNewNoticeReceiver() { @@ -167,6 +167,32 @@ export class AlertNoticeComponent implements OnInit { this.isManageReceiverModalAdd = false; } + onSendAlertTestMsg() { + this.isSendTestButtonLoading = true; + const sendReq$ = this.noticeReceiverSvc + .sendAlertMsgToReceiver(this.receiver) + .pipe( + finalize(() => { + sendReq$.unsubscribe(); + this.isSendTestButtonLoading = false; + }) + ) + .subscribe( + message => { + if (message.code === 0) { + this.isSendTestButtonLoading = false; + this.notifySvc.success(this.i18nSvc.fanyi('alert.notice.send-test.notify.success'), ''); + } else { + this.notifySvc.error(this.i18nSvc.fanyi('alert.notice.send-test.notify.failed'), message.msg); + } + }, + error => { + this.isSendTestButtonLoading = false; + this.notifySvc.error(this.i18nSvc.fanyi('alert.notice.send-test.notify.failed'), error.msg); + } + ); + } + onManageReceiverModalCancel() { this.isManageReceiverModalVisible = false; } @@ -192,6 +218,7 @@ export class AlertNoticeComponent implements OnInit { } }, error => { + this.isManageReceiverModalVisible = false; this.notifySvc.error(this.i18nSvc.fanyi('common.notify.new-fail'), error.msg); } ); @@ -215,6 +242,7 @@ export class AlertNoticeComponent implements OnInit { } }, error => { + this.isManageReceiverModalVisible = false; this.notifySvc.error(this.i18nSvc.fanyi('common.notify.edit-fail'), error.msg); } ); diff --git a/web-app/src/app/service/notice-receiver.service.ts b/web-app/src/app/service/notice-receiver.service.ts index be928997499..9dbb8d353e6 100644 --- a/web-app/src/app/service/notice-receiver.service.ts +++ b/web-app/src/app/service/notice-receiver.service.ts @@ -7,6 +7,7 @@ import { NoticeReceiver } from '../pojo/NoticeReceiver'; const notice_receiver_uri = '/notice/receiver'; const notice_receivers_uri = '/notice/receivers'; +const notice_receiver_send_test_msg_uri = '/notice/receiver/send-test-msg'; @Injectable({ providedIn: 'root' @@ -21,6 +22,7 @@ export class NoticeReceiverService { public editReceiver(body: NoticeReceiver): Observable> { return this.http.put>(notice_receiver_uri, body); } + public deleteReceiver(receiverId: number): Observable> { return this.http.delete>(`${notice_receiver_uri}/${receiverId}`); } @@ -28,4 +30,8 @@ export class NoticeReceiverService { public getReceivers(): Observable> { return this.http.get>(notice_receivers_uri); } + + public sendAlertMsgToReceiver(body: NoticeReceiver): Observable> { + return this.http.post>(notice_receiver_send_test_msg_uri, body); + } } diff --git a/web-app/src/assets/i18n/en-US.json b/web-app/src/assets/i18n/en-US.json index b3ae69fb433..303e98c92bc 100644 --- a/web-app/src/assets/i18n/en-US.json +++ b/web-app/src/assets/i18n/en-US.json @@ -176,6 +176,9 @@ "alert.notice.rule.enable": "Enable", "alert.notice.rule.tag": "Tag Filter", "alert.notice.rule.priority": "Priority Filter", + "alert.notice.send-test": "Send Alert Test Msg", + "alert.notice.send-test.notify.success": "Send Alert Test Success!", + "alert.notice.send-test.notify.failed": "Send Alert Test Failed!", "dashboard.alerts.title": "Recently Alerts List", "dashboard.alerts.title-no": "Recently Pending Alerts", "dashboard.alerts.no": "No Pending Alerts", diff --git a/web-app/src/assets/i18n/zh-CN.json b/web-app/src/assets/i18n/zh-CN.json index 52c797abfcc..e5ede63bdac 100644 --- a/web-app/src/assets/i18n/zh-CN.json +++ b/web-app/src/assets/i18n/zh-CN.json @@ -176,6 +176,9 @@ "alert.notice.rule.enable": "是否启用", "alert.notice.rule.tag": "标签过滤", "alert.notice.rule.priority": "级别过滤", + "alert.notice.send-test": "发送告警测试", + "alert.notice.send-test.notify.success": "触发告警测试成功!", + "alert.notice.send-test.notify.failed": "触发告警测试失败!", "dashboard.alerts.title": "最近告警列表", "dashboard.alerts.title-no": "最近未处理告警", "dashboard.alerts.no": "暂无未处理告警", diff --git a/web-app/src/assets/i18n/zh-TW.json b/web-app/src/assets/i18n/zh-TW.json index 80a627aa03e..3eccf5eb0e2 100644 --- a/web-app/src/assets/i18n/zh-TW.json +++ b/web-app/src/assets/i18n/zh-TW.json @@ -176,6 +176,9 @@ "alert.notice.rule.enable": "是否啓用", "alert.notice.rule.tag": "標簽過濾", "alert.notice.rule.priority": "級別過濾", + "alert.notice.send-test": "發送告警測試", + "alert.notice.send-test.notify.success": "觸發告警測試成功!", + "alert.notice.send-test.notify.failed": "觸發告警測試失敗!", "dashboard.alerts.title": "最近告警列表", "dashboard.alerts.title-no": "最近未處理告警", "dashboard.alerts.no": "暫無未處理告警", From e0da893e104cb5e3599d119c9b9c6b3900e194f8 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Sun, 8 May 2022 21:20:31 +0800 Subject: [PATCH 09/27] [monitor]feature: enable alerter send test msg button (#117) --- .../manager/service/impl/NoticeConfigServiceImpl.java | 8 -------- manager/src/main/resources/sureness.yml | 4 ++-- script/docker-compose/conf/sureness.yml | 4 ++-- script/sureness.yml | 4 ++-- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java index 491fca8082d..c7333c49d6d 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java @@ -61,14 +61,6 @@ public List getNoticeRules(Specification specification) public void addReceiver(NoticeReceiver noticeReceiver) { noticeReceiverDao.save(noticeReceiver); } - @Override - public boolean sendTestMsg(NoticeReceiver noticeReceiver) { - Alert alert = new Alert(); - alert.setContent(CommonConstants.TEST_SEND_MSG); - alert.setId(0L); - return dispatcherAlarm.sendNoticeMsg(noticeReceiver, alert); - } - @Override public void editReceiver(NoticeReceiver noticeReceiver) { diff --git a/manager/src/main/resources/sureness.yml b/manager/src/main/resources/sureness.yml index 1720d42d520..e07e8c5d034 100644 --- a/manager/src/main/resources/sureness.yml +++ b/manager/src/main/resources/sureness.yml @@ -67,8 +67,8 @@ excludedResource: # 用户账户信息 # 下面有 admin tom lili 三个账户 -# eg: admin 拥有[admin,user]角色,密码为admin -# eg: tom 拥有[user],密码为tom@123 +# eg: admin 拥有[admin,user]角色,密码为hertzbeat +# eg: tom 拥有[user],密码为hertzbeat # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin diff --git a/script/docker-compose/conf/sureness.yml b/script/docker-compose/conf/sureness.yml index a9c1f82ae62..45a1dbc2268 100644 --- a/script/docker-compose/conf/sureness.yml +++ b/script/docker-compose/conf/sureness.yml @@ -67,8 +67,8 @@ excludedResource: # 用户账户信息 # 下面有 admin tom lili 三个账户 -# eg: admin 拥有[admin,user]角色,密码为admin -# eg: tom 拥有[user],密码为tom@123 +# eg: admin 拥有[admin,user]角色,密码为hertzbeat +# eg: tom 拥有[user],密码为hertzbeat # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin diff --git a/script/sureness.yml b/script/sureness.yml index a11a22b73c8..53ecb44ee0f 100644 --- a/script/sureness.yml +++ b/script/sureness.yml @@ -65,8 +65,8 @@ excludedResource: # 用户账户信息 # 下面有 admin tom lili 三个账户 -# eg: admin 拥有[admin,user]角色,密码为admin -# eg: tom 拥有[user],密码为tom@123 +# eg: admin 拥有[admin,user]角色,密码为hertzbeat +# eg: tom 拥有[user],密码为hertzbeat # eg: lili 拥有[guest],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 account: - appId: admin From e6f3ac73fa896c0c8f61c541c0a4378e21a6d446 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Sun, 8 May 2022 22:20:58 +0800 Subject: [PATCH 10/27] [manager]feature: support ubuntu linux and centos linux monitoring (#95) --- .../src/main/resources/define/app/centos.yml | 247 ++++++++++++++++++ .../src/main/resources/define/app/ubuntu.yml | 247 ++++++++++++++++++ .../main/resources/define/param/centos.yml | 28 ++ .../main/resources/define/param/ubuntu.yml | 28 ++ 4 files changed, 550 insertions(+) create mode 100644 manager/src/main/resources/define/app/centos.yml create mode 100644 manager/src/main/resources/define/app/ubuntu.yml create mode 100644 manager/src/main/resources/define/param/centos.yml create mode 100644 manager/src/main/resources/define/param/ubuntu.yml diff --git a/manager/src/main/resources/define/app/centos.yml b/manager/src/main/resources/define/app/centos.yml new file mode 100644 index 00000000000..29a86093b68 --- /dev/null +++ b/manager/src/main/resources/define/app/centos.yml @@ -0,0 +1,247 @@ +# 此监控类型所属类别:service-应用服务监控 db-数据库监控 custom-自定义监控 os-操作系统监控 +category: os +# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws... +app: centos +name: + zh-CN: Centos Linux + en-US: Centos Linux +# 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串 +# 强制固定必须参数 - host +configmap: + - key: host + type: 1 + - key: port + type: 0 + - key: username + type: 1 + - key: password + type: 2 + - key: timeout + type: 0 +# 指标组列表 +metrics: +# 第一个监控指标组 basic +# 注意:内置监控指标有 (responseTime - 响应时间) + - name: basic + # 指标组调度优先级(0-127)越小优先级越高,优先级低的指标组会等优先级高的指标组采集完成后才会被调度,相同优先级的指标组会并行调度采集 + # 优先级为0的指标组为可用性指标组,即它会被首先调度,采集成功才会继续调度其它指标组,采集失败则中断调度 + priority: 0 + # 指标组中的具体监控指标 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: hostname + type: 1 + instance: true + - field: version + type: 1 + - field: uptime + type: 1 +# 监控采集使用协议 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: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}' + # 响应数据解析方式:oneRow, multiRow + parseType: multiRow + + - name: cpu + priority: 1 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: info + type: 1 + - field: cores + type: 0 + unit: 核数 + - field: interrupt + type: 0 + unit: 个数 + - field: load + type: 1 + - 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协议时具体的采集配置 + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + 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}'" + parseType: oneRow + + - name: memory + priority: 2 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: total + type: 0 + unit: Mb + - field: used + type: 0 + unit: Mb + - field: free + type: 0 + unit: Mb + - field: buff_cache + type: 0 + unit: Mb + - 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协议时具体的采集配置 + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + username: ^_^username^_^ + password: ^_^password^_^ + timeout: ^_^timeout^_^ + script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}' + parseType: multiRow + + - name: disk + priority: 3 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: disk_num + type: 0 + unit: 块数 + - field: partition_num + type: 0 + unit: 分区数 + - field: block_write + type: 0 + unit: 块数 + - field: block_read + type: 0 + unit: 块数 + - field: write_rate + type: 0 + unit: iops + # 监控采集使用协议 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: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}' + parseType: oneRow + + - name: interface + priority: 4 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: interface_name + type: 1 + instance: true + - field: receive_bytes + type: 0 + unit: byte + - field: transmit_bytes + type: 0 + unit: byte + # 监控采集使用协议 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: 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 \ No newline at end of file diff --git a/manager/src/main/resources/define/app/ubuntu.yml b/manager/src/main/resources/define/app/ubuntu.yml new file mode 100644 index 00000000000..3227ae4b2d8 --- /dev/null +++ b/manager/src/main/resources/define/app/ubuntu.yml @@ -0,0 +1,247 @@ +# 此监控类型所属类别:service-应用服务监控 db-数据库监控 custom-自定义监控 os-操作系统监控 +category: os +# 监控应用类型(与文件名保持一致) eg: linux windows tomcat mysql aws... +app: ubuntu +name: + zh-CN: Ubuntu Linux + en-US: Ubuntu Linux +# 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串 +# 强制固定必须参数 - host +configmap: + - key: host + type: 1 + - key: port + type: 0 + - key: username + type: 1 + - key: password + type: 2 + - key: timeout + type: 0 +# 指标组列表 +metrics: +# 第一个监控指标组 basic +# 注意:内置监控指标有 (responseTime - 响应时间) + - name: basic + # 指标组调度优先级(0-127)越小优先级越高,优先级低的指标组会等优先级高的指标组采集完成后才会被调度,相同优先级的指标组会并行调度采集 + # 优先级为0的指标组为可用性指标组,即它会被首先调度,采集成功才会继续调度其它指标组,采集失败则中断调度 + priority: 0 + # 指标组中的具体监控指标 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: hostname + type: 1 + instance: true + - field: version + type: 1 + - field: uptime + type: 1 +# 监控采集使用协议 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: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}' + # 响应数据解析方式:oneRow, multiRow + parseType: multiRow + + - name: cpu + priority: 1 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: info + type: 1 + - field: cores + type: 0 + unit: 核数 + - field: interrupt + type: 0 + unit: 个数 + - field: load + type: 1 + - 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协议时具体的采集配置 + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + 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}'" + parseType: oneRow + + - name: memory + priority: 2 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: total + type: 0 + unit: Mb + - field: used + type: 0 + unit: Mb + - field: free + type: 0 + unit: Mb + - field: buff_cache + type: 0 + unit: Mb + - 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协议时具体的采集配置 + ssh: + # 主机host: ipv4 ipv6 域名 + host: ^_^host^_^ + # 端口 + port: ^_^port^_^ + username: ^_^username^_^ + password: ^_^password^_^ + timeout: ^_^timeout^_^ + script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}' + parseType: multiRow + + - name: disk + priority: 3 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: disk_num + type: 0 + unit: 块数 + - field: partition_num + type: 0 + unit: 分区数 + - field: block_write + type: 0 + unit: 块数 + - field: block_read + type: 0 + unit: 块数 + - field: write_rate + type: 0 + unit: iops + # 监控采集使用协议 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: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}' + parseType: oneRow + + - name: interface + priority: 4 + fields: + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 + - field: interface_name + type: 1 + instance: true + - field: receive_bytes + type: 0 + unit: byte + - field: transmit_bytes + type: 0 + unit: byte + # 监控采集使用协议 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: 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 \ No newline at end of file diff --git a/manager/src/main/resources/define/param/centos.yml b/manager/src/main/resources/define/param/centos.yml new file mode 100644 index 00000000000..6e2760fe6ad --- /dev/null +++ b/manager/src/main/resources/define/param/centos.yml @@ -0,0 +1,28 @@ +app: centos +param: + - field: host + name: 主机Host + type: host + required: true + - field: port + name: 端口 + type: number + range: '[0,65535]' + required: true + defaultValue: 22 + placeholder: '请输入端口' + - field: timeout + name: 超时时间 + type: number + required: false + defaultValue: 6000 + placeholder: '超时时间' + - field: username + name: 用户名 + type: text + limit: 20 + required: true + - field: password + name: 密码 + type: password + required: false \ No newline at end of file diff --git a/manager/src/main/resources/define/param/ubuntu.yml b/manager/src/main/resources/define/param/ubuntu.yml new file mode 100644 index 00000000000..d2b91a061f3 --- /dev/null +++ b/manager/src/main/resources/define/param/ubuntu.yml @@ -0,0 +1,28 @@ +app: ubuntu +param: + - field: host + name: 主机Host + type: host + required: true + - field: port + name: 端口 + type: number + range: '[0,65535]' + required: true + defaultValue: 22 + placeholder: '请输入端口' + - field: timeout + name: 超时时间 + type: number + required: false + defaultValue: 6000 + placeholder: '超时时间' + - field: username + name: 用户名 + type: text + limit: 20 + required: true + - field: password + name: 密码 + type: password + required: false \ No newline at end of file From 3f7303e828979265c69276670c2a1f9fa933e783 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Tue, 10 May 2022 19:49:34 +0800 Subject: [PATCH 11/27] [manager]bugfix: linux.cpu.interrupt metric value is illegal (#118) --- manager/src/main/resources/define/app/centos.yml | 4 ++-- manager/src/main/resources/define/app/linux.yml | 4 ++-- manager/src/main/resources/define/app/ubuntu.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/manager/src/main/resources/define/app/centos.yml b/manager/src/main/resources/define/app/centos.yml index 29a86093b68..a10ef9606ba 100644 --- a/manager/src/main/resources/define/app/centos.yml +++ b/manager/src/main/resources/define/app/centos.yml @@ -75,8 +75,8 @@ metrics: aliasFields: - info - cores - - interrupt - load + - interrupt - context_switch - idle # (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值 @@ -84,8 +84,8 @@ metrics: calculates: - info=info - cores=cores - - interrupt=interrupt - load=load + - interrupt=interrupt - context_switch=context_switch - usage=100-idle # 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk diff --git a/manager/src/main/resources/define/app/linux.yml b/manager/src/main/resources/define/app/linux.yml index 2030183db4b..fbee5f3c86c 100644 --- a/manager/src/main/resources/define/app/linux.yml +++ b/manager/src/main/resources/define/app/linux.yml @@ -75,8 +75,8 @@ metrics: aliasFields: - info - cores - - interrupt - load + - interrupt - context_switch - idle # (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值 @@ -84,8 +84,8 @@ metrics: calculates: - info=info - cores=cores - - interrupt=interrupt - load=load + - interrupt=interrupt - context_switch=context_switch - usage=100-idle # 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk diff --git a/manager/src/main/resources/define/app/ubuntu.yml b/manager/src/main/resources/define/app/ubuntu.yml index 3227ae4b2d8..82be696b3b2 100644 --- a/manager/src/main/resources/define/app/ubuntu.yml +++ b/manager/src/main/resources/define/app/ubuntu.yml @@ -75,8 +75,8 @@ metrics: aliasFields: - info - cores - - interrupt - load + - interrupt - context_switch - idle # (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值 @@ -84,8 +84,8 @@ metrics: calculates: - info=info - cores=cores - - interrupt=interrupt - load=load + - interrupt=interrupt - context_switch=context_switch - usage=100-idle # 监控采集使用协议 eg: sql, ssh, http, telnet, wmi, snmp, sdk From 6aa0bf365458c8445d6194a799b328bcd1745ad0 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Fri, 13 May 2022 19:13:54 +0800 Subject: [PATCH 12/27] [manager]bugfix: fix no monitorName in tags when send alert --- .../impl/DingTalkRobotAlertNotifyHandlerImpl.java | 8 ++++++-- .../impl/FlyBookAlertNotifyHandlerImpl.java | 14 +++++++++++--- .../impl/WeWorkRobotAlertNotifyHandlerImpl.java | 8 ++++++-- .../impl/WebHookAlertNotifyHandlerImpl.java | 4 +--- .../usthe/manager/controller/TagController.java | 4 +--- .../manager/service/impl/MailServiceImpl.java | 1 + 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java index 6bf69a8a3ce..119b8da22ca 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java @@ -29,8 +29,12 @@ final class DingTalkRobotAlertNotifyHandlerImpl implements AlertNotifyHandler { @Override public void send(NoticeReceiver receiver, Alert alert) { - String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); - String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + String monitorId = null; + String monitorName = null; + if (alert.getTags() != null) { + monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + } DingTalkWebHookDto dingTalkWebHookDto = new DingTalkWebHookDto(); DingTalkWebHookDto.MarkdownDTO markdownDTO = new DingTalkWebHookDto.MarkdownDTO(); StringBuilder contentBuilder = new StringBuilder("#### [TanCloud探云告警通知]\n##### **告警目标对象** : " + diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java index 2ee17843407..eeda70fa83e 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java @@ -1,5 +1,6 @@ package com.usthe.manager.component.alerter.impl; +import com.usthe.alert.AlerterProperties; import com.usthe.common.entity.alerter.Alert; import com.usthe.common.entity.manager.NoticeReceiver; import com.usthe.common.util.CommonConstants; @@ -28,12 +29,19 @@ @RequiredArgsConstructor @Slf4j final class FlyBookAlertNotifyHandlerImpl implements AlertNotifyHandler { + private final RestTemplate restTemplate; + private final AlerterProperties alerterProperties; + @Override public void send(NoticeReceiver receiver, Alert alert) { - String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); - String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + String monitorId = null; + String monitorName = null; + if (alert.getTags() != null) { + monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + } FlyBookWebHookDto flyBookWebHookDto = new FlyBookWebHookDto(); FlyBookWebHookDto.Content content = new FlyBookWebHookDto.Content(); FlyBookWebHookDto.Post post = new FlyBookWebHookDto.Post(); @@ -61,7 +69,7 @@ public void send(NoticeReceiver receiver, Alert alert) { FlyBookWebHookDto.FlyBookContent bookContent = new FlyBookWebHookDto.FlyBookContent(); bookContent.setTag("a"); bookContent.setText("登入控制台"); - bookContent.setHref("https://www.tancloud.cn"); + bookContent.setHref(alerterProperties.getConsoleUrl()); contents1.add(bookContent); contents.add(contents1); zhCn.setTitle("[TanCloud探云告警通知]"); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java index 922ea0695e2..76f52586b45 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java @@ -29,8 +29,12 @@ final class WeWorkRobotAlertNotifyHandlerImpl implements AlertNotifyHandler { @Override public void send(NoticeReceiver receiver, Alert alert) { - String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); - String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + String monitorId = null; + String monitorName = null; + if (alert.getTags() != null) { + monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID); + monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME); + } WeWorkWebHookDto weWorkWebHookDTO = new WeWorkWebHookDto(); WeWorkWebHookDto.MarkdownDTO markdownDTO = new WeWorkWebHookDto.MarkdownDTO(); StringBuilder content = new StringBuilder(); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java index 7b82239d500..7b6d58aadc5 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WebHookAlertNotifyHandlerImpl.java @@ -30,10 +30,8 @@ public void send(NoticeReceiver receiver, Alert alert) { } else { log.warn("Send WebHook: {} Failed", receiver.getHookUrl()); } - } catch (ResourceAccessException e) { - log.warn("Send WebHook: {} Failed: {}.", receiver.getHookUrl(), e.getMessage()); } catch (Exception e) { - log.warn(e.getMessage()); + log.warn("Send WebHook: {} Failed: {}.", receiver.getHookUrl(), e.getMessage()); } } diff --git a/manager/src/main/java/com/usthe/manager/controller/TagController.java b/manager/src/main/java/com/usthe/manager/controller/TagController.java index de24411ff4f..e682eed33e7 100644 --- a/manager/src/main/java/com/usthe/manager/controller/TagController.java +++ b/manager/src/main/java/com/usthe/manager/controller/TagController.java @@ -13,7 +13,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Predicate; import javax.validation.Valid; @@ -47,8 +46,7 @@ public ResponseEntity> addNewTags(@Valid @RequestBody List ta tags = tags.stream().peek(tag -> { tag.setType((byte) 1); tag.setId(null); - }).filter(tag -> tag.getValue() != null) - .distinct().collect(Collectors.toList()); + }).distinct().collect(Collectors.toList()); tagService.addTags(tags); return ResponseEntity.ok(new Message<>("Add success")); } diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java index c101de51ef6..d7db37613e0 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java @@ -26,6 +26,7 @@ public class MailServiceImpl implements MailService { @Resource private TemplateEngine templateEngine; + @Resource private AlerterProperties alerterProperties; From d7bf9b28bb15321422979e9868b37860ebefb79b Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Sun, 15 May 2022 16:51:38 +0800 Subject: [PATCH 13/27] [monitor]feature: support alert nextEvalInterval, triggerTime. Ignore alert when happen again in eval interval. (#123) [monitor]alert msg i18n [monitor]feature: support alert nextEvalInterval, triggerTime. Ignore alert when happen again in eval interval. --- .../com/usthe/alert/AlerterProperties.java | 37 ++++- .../usthe/alert/calculate/CalculateAlarm.java | 131 +++++++++++++----- .../usthe/common/entity/alerter/Alert.java | 37 +++-- .../DingTalkRobotAlertNotifyHandlerImpl.java | 8 ++ .../impl/FlyBookAlertNotifyHandlerImpl.java | 5 + .../WeWorkRobotAlertNotifyHandlerImpl.java | 6 + .../manager/service/impl/MailServiceImpl.java | 5 + .../service/impl/NoticeConfigServiceImpl.java | 3 + .../main/resources/templates/mailAlarm.html | 9 ++ .../core/interceptor/default.interceptor.ts | 12 +- .../layout/basic/widgets/notify.component.ts | 2 +- web-app/src/app/pojo/Alert.ts | 3 + .../alert-center/alert-center.component.html | 2 +- .../alert-center/alert-center.component.ts | 22 +-- .../routes/dashboard/dashboard.component.html | 5 +- 15 files changed, 220 insertions(+), 67 deletions(-) diff --git a/alerter/src/main/java/com/usthe/alert/AlerterProperties.java b/alerter/src/main/java/com/usthe/alert/AlerterProperties.java index e0572c8b7fa..f2a8ce254f3 100644 --- a/alerter/src/main/java/com/usthe/alert/AlerterProperties.java +++ b/alerter/src/main/java/com/usthe/alert/AlerterProperties.java @@ -1,6 +1,5 @@ package com.usthe.alert; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -14,8 +13,26 @@ @ConfigurationProperties(prefix = "alerter") public class AlerterProperties { + /** + * 告警内容控制台链接 + */ private String consoleUrl = "https://console.tancloud.cn"; + /** + * 告警评估时间间隔起始基数 每下一次乘2 + */ + private long alertEvalIntervalBase = 1000 * 60 * 10L; + + /** + * 最大告警评估时间间隔 + */ + private long maxAlertEvalInterval = 1000 * 60 * 60 * 24L; + + /** + * 数据入口配置属性 + */ + private EntranceProperties entrance; + public String getConsoleUrl() { return consoleUrl; } @@ -24,11 +41,21 @@ public void setConsoleUrl(String url) { this.consoleUrl = url; } - /** - * 数据入口配置属性 - */ - private EntranceProperties entrance; + public long getAlertEvalIntervalBase() { + return alertEvalIntervalBase; + } + public void setAlertEvalIntervalBase(long alertEvalIntervalBase) { + this.alertEvalIntervalBase = alertEvalIntervalBase; + } + + public long getMaxAlertEvalInterval() { + return maxAlertEvalInterval; + } + + public void setMaxAlertEvalInterval(long maxAlertEvalInterval) { + this.maxAlertEvalInterval = maxAlertEvalInterval; + } public EntranceProperties getEntrance() { return entrance; diff --git a/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java b/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java index 37cf0f20983..9f07ddc595c 100644 --- a/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java +++ b/alerter/src/main/java/com/usthe/alert/calculate/CalculateAlarm.java @@ -2,6 +2,7 @@ import com.googlecode.aviator.AviatorEvaluator; import com.googlecode.aviator.Expression; +import com.usthe.alert.AlerterProperties; import com.usthe.alert.AlerterWorkerPool; import com.usthe.alert.AlerterDataQueue; import com.usthe.alert.dao.AlertMonitorDao; @@ -36,23 +37,45 @@ public class CalculateAlarm { private AlerterDataQueue dataQueue; private MetricsDataExporter dataExporter; private AlertDefineService alertDefineService; + private AlerterProperties alerterProperties; + /** + * 触发中告警信息 + * key - monitorId+alertDefineId 为普通阈值告警 + * key - monitorId 为监控状态可用性可达性告警 + */ private Map triggeredAlertMap; - private Map triggeredMonitorStateAlertMap; public CalculateAlarm (AlerterWorkerPool workerPool, AlerterDataQueue dataQueue, AlertDefineService alertDefineService, MetricsDataExporter dataExporter, - AlertMonitorDao monitorDao) { + AlertMonitorDao monitorDao, AlerterProperties alerterProperties) { this.workerPool = workerPool; this.dataQueue = dataQueue; this.dataExporter = dataExporter; this.alertDefineService = alertDefineService; + this.alerterProperties = alerterProperties; this.triggeredAlertMap = new ConcurrentHashMap<>(128); - this.triggeredMonitorStateAlertMap = new ConcurrentHashMap<>(128); // 初始化stateAlertMap - List monitors = monitorDao.findMonitorsByStatusIn(Arrays.asList((byte)2, (byte)3)); + List monitors = monitorDao.findMonitorsByStatusIn(Arrays.asList(CommonConstants.UN_AVAILABLE_CODE, + CommonConstants.UN_REACHABLE_CODE)); if (monitors != null) { for (Monitor monitor : monitors) { - this.triggeredMonitorStateAlertMap.put(monitor.getId(), CollectRep.Code.UN_AVAILABLE); + Map tags = new HashMap<>(6); + tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitor.getId())); + tags.put(CommonConstants.TAG_MONITOR_APP, monitor.getApp()); + Alert.AlertBuilder alertBuilder = Alert.builder() + .tags(tags) + .priority(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY) + .status(CommonConstants.ALERT_STATUS_CODE_PENDING) + .target(CommonConstants.AVAILABLE) + .content("Monitoring Availability Emergency Alert: " + CollectRep.Code.UN_AVAILABLE.name()) + .firstTriggerTime(System.currentTimeMillis()) + .lastTriggerTime(System.currentTimeMillis()); + if (monitor.getStatus() == CommonConstants.UN_REACHABLE_CODE) { + alertBuilder + .target(CommonConstants.REACHABLE) + .content("Monitoring Reachability Emergency Alert: " + CollectRep.Code.UN_REACHABLE.name()); + } + this.triggeredAlertMap.put(String.valueOf(monitor.getId()), alertBuilder.build()); } } startCalculate(); @@ -77,6 +100,7 @@ private void startCalculate() { } private void calculate(CollectRep.MetricsData metricsData) { + long currentTimeMilli = System.currentTimeMillis(); long monitorId = metricsData.getId(); String app = metricsData.getApp(); String metrics = metricsData.getMetrics(); @@ -84,54 +108,43 @@ private void calculate(CollectRep.MetricsData metricsData) { if (metricsData.getPriority() == 0) { if (metricsData.getCode() != CollectRep.Code.SUCCESS) { // 采集异常 - Map tags = new HashMap<>(6); - tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitorId)); - tags.put(CommonConstants.TAG_MONITOR_APP, app); - Alert.AlertBuilder alertBuilder = Alert.builder() - .tags(tags) - .priority(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY) - .status(CommonConstants.ALERT_STATUS_CODE_PENDING) - .times(1); if (metricsData.getCode() == CollectRep.Code.UN_AVAILABLE) { - // 采集器不可用 - alertBuilder.target(CommonConstants.AVAILABLE) - .content("监控紧急可用性告警: " + metricsData.getCode().name()); - triggeredMonitorStateAlertMap.put(monitorId, CollectRep.Code.UN_AVAILABLE); - dataQueue.addAlertData(alertBuilder.build()); + // todo 采集器不可用 } else if (metricsData.getCode() == CollectRep.Code.UN_REACHABLE) { // UN_REACHABLE 对端不可达(网络层icmp) - alertBuilder.target(CommonConstants.REACHABLE) - .content("监控紧急可达性告警: " + metricsData.getCode().name()); - triggeredMonitorStateAlertMap.put(monitorId, CollectRep.Code.UN_REACHABLE); - dataQueue.addAlertData(alertBuilder.build()); + handlerMonitorStatusAlert(String.valueOf(monitorId), app, metricsData.getCode()); } else if (metricsData.getCode() == CollectRep.Code.UN_CONNECTABLE) { // UN_CONNECTABLE 对端连接失败(传输层tcp,udp) - alertBuilder.target(CommonConstants.AVAILABLE) - .content("监控紧急可用性告警: " + metricsData.getCode().name()); - triggeredMonitorStateAlertMap.put(monitorId, CollectRep.Code.UN_CONNECTABLE); - dataQueue.addAlertData(alertBuilder.build()); + handlerMonitorStatusAlert(String.valueOf(monitorId), app, metricsData.getCode()); } else { // 其他异常 - alertBuilder.target(CommonConstants.AVAILABLE) - .content("监控可用性告警: " + metricsData.getCode().name() + " : " + metricsData.getMsg()); - triggeredMonitorStateAlertMap.put(monitorId, metricsData.getCode()); - dataQueue.addAlertData(alertBuilder.build()); + handlerMonitorStatusAlert(String.valueOf(monitorId), app, metricsData.getCode()); } return; } else { // 判断关联监控之前是否有可用性或者不可达告警,发送恢复告警进行监控状态恢复 - CollectRep.Code stateCode = triggeredMonitorStateAlertMap.remove(monitorId); - if (stateCode != null) { + Alert preAlert = triggeredAlertMap.get(String.valueOf(monitorId)); + if (preAlert != null) { // 发送告警恢复 Map tags = new HashMap<>(6); tags.put(CommonConstants.TAG_MONITOR_ID, String.valueOf(monitorId)); tags.put(CommonConstants.TAG_MONITOR_APP, app); + String target = CommonConstants.AVAILABLE; + String content = "Availability Alert Resolved, monitoring status has returned to normal"; + if (CommonConstants.REACHABLE.equals(preAlert.getTarget())) { + target = CommonConstants.REACHABLE; + content = "Reachability Alert Resolved, monitoring status has returned to normal"; + } Alert resumeAlert = Alert.builder() .tags(tags) - .target(CommonConstants.AVAILABLE) - .content("告警恢复通知, 此监控状态已恢复正常") + .target(target) + .content(content) .priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING) - .status(CommonConstants.ALERT_STATUS_CODE_RESTORED).build(); + .status(CommonConstants.ALERT_STATUS_CODE_RESTORED) + .firstTriggerTime(currentTimeMilli) + .lastTriggerTime(currentTimeMilli) + .times(1) + .build(); dataQueue.addAlertData(resumeAlert); } } @@ -179,6 +192,7 @@ private void calculate(CollectRep.MetricsData metricsData) { if (triggeredAlert != null) { int times = triggeredAlert.getTimes() + 1; triggeredAlert.setTimes(times); + triggeredAlert.setLastTriggerTime(currentTimeMilli); if (times >= define.getTimes()) { triggeredAlertMap.remove(monitorAlertKey); dataQueue.addAlertData(triggeredAlert); @@ -198,6 +212,8 @@ private void calculate(CollectRep.MetricsData metricsData) { .status(CommonConstants.ALERT_STATUS_CODE_PENDING) .target(app + "." + metrics + "." + define.getField()) .times(times) + .firstTriggerTime(currentTimeMilli) + .lastTriggerTime(currentTimeMilli) // 模板中关键字匹配替换 .content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap)) .build(); @@ -219,4 +235,49 @@ private void calculate(CollectRep.MetricsData metricsData) { } } } + + private void handlerMonitorStatusAlert(String monitorId, String app, CollectRep.Code code) { + Alert preAlert = triggeredAlertMap.get(monitorId); + long currentTimeMill = System.currentTimeMillis(); + if (preAlert == null) { + Map tags = new HashMap<>(6); + tags.put(CommonConstants.TAG_MONITOR_ID, monitorId); + tags.put(CommonConstants.TAG_MONITOR_APP, app); + Alert.AlertBuilder alertBuilder = Alert.builder() + .tags(tags) + .priority(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY) + .status(CommonConstants.ALERT_STATUS_CODE_PENDING) + .target(CommonConstants.AVAILABLE) + .content("Monitoring Availability Emergency Alert: " + code.name()) + .firstTriggerTime(currentTimeMill) + .lastTriggerTime(currentTimeMill) + .nextEvalInterval(alerterProperties.getAlertEvalIntervalBase()) + .times(1); + if (code == CollectRep.Code.UN_REACHABLE) { + alertBuilder + .target(CommonConstants.REACHABLE) + .content("Monitoring Reachability Emergency Alert: " + code.name()); + } + Alert alert = alertBuilder.build(); + dataQueue.addAlertData(alert.clone()); + triggeredAlertMap.put(monitorId, alert); + return; + } + if (preAlert.getLastTriggerTime() + preAlert.getNextEvalInterval() >= currentTimeMill) { + // 还在告警评估时间间隔静默期 + preAlert.setTimes(preAlert.getTimes() + 1); + triggeredAlertMap.put(monitorId, preAlert); + } else { + preAlert.setTimes(preAlert.getTimes() + 1); + preAlert.setLastTriggerTime(currentTimeMill); + long nextEvalInterval = preAlert.getNextEvalInterval() * 2; + if (preAlert.getNextEvalInterval() == 0) { + nextEvalInterval = alerterProperties.getAlertEvalIntervalBase(); + } + nextEvalInterval = Math.min(nextEvalInterval, alerterProperties.getMaxAlertEvalInterval()); + preAlert.setNextEvalInterval(nextEvalInterval); + triggeredAlertMap.put(monitorId, preAlert); + dataQueue.addAlertData(preAlert.clone()); + } + } } diff --git a/common/src/main/java/com/usthe/common/entity/alerter/Alert.java b/common/src/main/java/com/usthe/common/entity/alerter/Alert.java index a00de83a40b..a05d4c88709 100644 --- a/common/src/main/java/com/usthe/common/entity/alerter/Alert.java +++ b/common/src/main/java/com/usthe/common/entity/alerter/Alert.java @@ -1,5 +1,6 @@ package com.usthe.common.entity.alerter; +import com.usthe.common.util.GsonUtil; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -76,27 +77,47 @@ public class Alert { notes = "告警阈值触发次数", example = "3", accessMode = READ_WRITE, position = 8) @Min(0) - @Max(10) private int times; - @ApiModelProperty(value = "告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{key1:value1}", accessMode = READ_WRITE, position = 8) + @ApiModelProperty(value = "Alarm trigger time (timestamp in milliseconds)", + notes = "首次告警触发时间(毫秒时间戳)", + example = "1612198922000", accessMode = READ_ONLY, position = 9) + private long firstTriggerTime; + + @ApiModelProperty(value = "Alarm trigger time (timestamp in milliseconds)", + notes = "最近告警触发时间(毫秒时间戳)", + example = "1612198922000", accessMode = READ_ONLY, position = 9) + private long lastTriggerTime; + + @ApiModelProperty(value = "Alarm evaluation interval (milliseconds)", + notes = "告警评估时间间隔(单位毫秒)", + example = "2000", accessMode = READ_ONLY, position = 10) + private long nextEvalInterval; + + @ApiModelProperty(value = "告警信息标签(monitorId:xxx,monitorName:xxx)", example = "{key1:value1}", accessMode = READ_WRITE, position = 11) @Convert(converter = JsonMapAttributeConverter.class) @SuppressWarnings("JpaAttributeTypeInspection") private Map tags; - @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 8) + @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 12) private String creator; - @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 9) + @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 13) private String modifier; - @ApiModelProperty(value = "Alarm trigger time (timestamp in milliseconds)", - notes = "告警触发时间(毫秒时间戳)", - example = "1612198922000", accessMode = READ_ONLY, position = 9) + @ApiModelProperty(value = "Record the latest creation time (timestamp in milliseconds)", + notes = "记录最新创建时间(毫秒时间戳)", + example = "1612198922000", accessMode = READ_ONLY, position = 14) @Column(insertable = false, updatable = false) private LocalDateTime gmtCreate; - @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 11) + @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 15) @Column(insertable = false, updatable = false) private LocalDateTime gmtUpdate; + + @Override + public Alert clone() { + // deep clone + return GsonUtil.fromJson(GsonUtil.toJson(this), Alert.class); + } } diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java index 119b8da22ca..858afbf1dd9 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/DingTalkRobotAlertNotifyHandlerImpl.java @@ -14,6 +14,9 @@ import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; +import java.text.SimpleDateFormat; +import java.util.Date; + /** * Send alarm information through DingTalk robot * 通过钉钉机器人发送告警信息 @@ -50,6 +53,11 @@ public void send(NoticeReceiver receiver, Alert alert) { contentBuilder.append("##### **告警级别** : ") .append(CommonUtil.transferAlertPriority(alert.getPriority())) .append("\n "); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String triggerTime = simpleDateFormat.format(new Date(alert.getLastTriggerTime())); + contentBuilder.append("##### **告警触发时间** : ") + .append(triggerTime) + .append("\n "); contentBuilder.append("##### **内容详情** : ").append(alert.getContent()); markdownDTO.setText(contentBuilder.toString()); markdownDTO.setTitle("TanCloud探云告警通知"); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java index eeda70fa83e..d791d164dbf 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/FlyBookAlertNotifyHandlerImpl.java @@ -15,7 +15,9 @@ import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -63,6 +65,9 @@ public void send(NoticeReceiver receiver, Alert alert) { } textBuilder.append("\n告警级别 :") .append(CommonUtil.transferAlertPriority(alert.getPriority())); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String triggerTime = simpleDateFormat.format(new Date(alert.getLastTriggerTime())); + textBuilder.append("\n告警触发时间 : ").append(triggerTime); textBuilder.append("\n内容详情 : ").append(alert.getContent()); flyBookContent.setText(textBuilder.toString()); contents1.add(flyBookContent); diff --git a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java index 76f52586b45..8556d8eeacf 100644 --- a/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java +++ b/manager/src/main/java/com/usthe/manager/component/alerter/impl/WeWorkRobotAlertNotifyHandlerImpl.java @@ -14,6 +14,9 @@ import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; +import java.text.SimpleDateFormat; +import java.util.Date; + /** * Send alarm information through enterprise WeChat * 通过企业微信发送告警信息 @@ -53,6 +56,9 @@ public void send(NoticeReceiver receiver, Alert alert) { content.append("告警级别 : ") .append(CommonUtil.transferAlertPriority(alert.getPriority())).append("\n"); } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String triggerTime = simpleDateFormat.format(new Date(alert.getLastTriggerTime())); + content.append("告警触发时间 : ").append(triggerTime).append("\n"); content.append("内容详情 : ").append(alert.getContent()); markdownDTO.setContent(content.toString()); weWorkWebHookDTO.setMarkdown(markdownDTO); diff --git a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java index d7db37613e0..3de5972f40f 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java @@ -11,6 +11,8 @@ import org.thymeleaf.context.Context; import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.util.Date; /** * Mailbox sending service interface implementation class @@ -49,6 +51,9 @@ public String buildAlertHtmlTemplate(final Alert alert) { context.setVariable("priority", CommonUtil.transferAlertPriority(alert.getPriority())); context.setVariable("content", alert.getContent()); context.setVariable("consoleUrl", alerterProperties.getConsoleUrl()); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String triggerTime = simpleDateFormat.format(new Date(alert.getLastTriggerTime())); + context.setVariable("lastTriggerTime", triggerTime); return templateEngine.process("mailAlarm", context); } } diff --git a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java index c7333c49d6d..91367a60094 100644 --- a/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java +++ b/manager/src/main/java/com/usthe/manager/service/impl/NoticeConfigServiceImpl.java @@ -132,6 +132,9 @@ public boolean sendTestMsg(NoticeReceiver noticeReceiver) { Alert alert = new Alert(); alert.setTarget(ALERT_TEST_TARGET); alert.setContent(ALERT_TEST_CONTENT); + alert.setTimes(1); + alert.setFirstTriggerTime(System.currentTimeMillis()); + alert.setLastTriggerTime(System.currentTimeMillis()); alert.setPriority(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL); return dispatcherAlarm.sendNoticeMsg(noticeReceiver, alert); } diff --git a/manager/src/main/resources/templates/mailAlarm.html b/manager/src/main/resources/templates/mailAlarm.html index 6f847b40609..0caeea38ae7 100644 --- a/manager/src/main/resources/templates/mailAlarm.html +++ b/manager/src/main/resources/templates/mailAlarm.html @@ -657,6 +657,15 @@ th:text="${priority}"> + + + 告警触发时间: + + + +
diff --git a/web-app/src/app/core/interceptor/default.interceptor.ts b/web-app/src/app/core/interceptor/default.interceptor.ts index 9c8a5536211..2c7bba656e6 100644 --- a/web-app/src/app/core/interceptor/default.interceptor.ts +++ b/web-app/src/app/core/interceptor/default.interceptor.ts @@ -60,7 +60,7 @@ export class DefaultInterceptor implements HttpInterceptor { } private goTo(url: string): void { - setTimeout(() => { + setTimeout(() => { this.injector.get(Router).navigateByUrl(url); this.notified = false; }); @@ -148,11 +148,11 @@ export class DefaultInterceptor implements HttpInterceptor { } private toLogin(): void { - if (!this.notified) { - this.notified = true; - this.notification.error(`未登录或登录已过期,请重新登录。`, ``); - this.goTo('/passport/login'); - } + if (!this.notified) { + this.notified = true; + this.notification.error(`未登录或登录已过期,请重新登录。`, ``); + this.goTo('/passport/login'); + } } private fillHeaders(headers?: HttpHeaders): { [name: string]: string } { diff --git a/web-app/src/app/layout/basic/widgets/notify.component.ts b/web-app/src/app/layout/basic/widgets/notify.component.ts index 711aa97a168..f0f39ae7ba0 100644 --- a/web-app/src/app/layout/basic/widgets/notify.component.ts +++ b/web-app/src/app/layout/basic/widgets/notify.component.ts @@ -70,7 +70,7 @@ export class HeaderNotifyComponent implements OnInit { id: alert.id, avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', title: `${alert.tags['monitorName']}--${this.i18nSvc.fanyi(`alert.priority.${alert.priority}`)}`, - datetime: alert.gmtCreate, + datetime: new Date(alert.lastTriggerTime).toLocaleString(), color: 'blue', type: this.i18nSvc.fanyi('dashboard.alerts.title-no') }; diff --git a/web-app/src/app/pojo/Alert.ts b/web-app/src/app/pojo/Alert.ts index abf9de0fa16..68f93204b9b 100644 --- a/web-app/src/app/pojo/Alert.ts +++ b/web-app/src/app/pojo/Alert.ts @@ -9,6 +9,9 @@ export class Alert { status!: number; content!: string; times!: number; + firstTriggerTime!: number; + lastTriggerTime!: number; + nextEvalInterval!: number; tags!: Record; gmtCreate!: number; gmtUpdate!: number; diff --git a/web-app/src/app/routes/alert/alert-center/alert-center.component.html b/web-app/src/app/routes/alert/alert-center/alert-center.component.html index 754a4be19fb..d46f83b4fa8 100644 --- a/web-app/src/app/routes/alert/alert-center/alert-center.component.html +++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.html @@ -127,7 +127,7 @@ {{ 'alert.status.' + data.status | i18n }} - {{ data.gmtCreate | date: 'YYYY-MM-dd HH:mm:ss' }} + {{ data.lastTriggerTime | date: 'YYYY-MM-dd HH:mm:ss' }}