diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorController.java b/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorController.java index 9634461ca95..0300e97c266 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorController.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorController.java @@ -27,15 +27,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; 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.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.IOException; import java.util.List; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -43,6 +40,7 @@ /** * Monitoring management API * 监控管理API + * * @author tomsun28 * @date 2021/11/14 10:57 */ @@ -118,21 +116,22 @@ public ResponseEntity> detectMonitor(@Valid @RequestBody MonitorDt } @PostMapping("/optional") - @Operation(summary = "Add a monitor that can select metrics",description = "新增一个可选指标的监控器") - public ResponseEntity> addNewMonitorOptionalMetrics(@Valid @RequestBody MonitorDto monitorDto){ + @Operation(summary = "Add a monitor that can select metrics", description = "新增一个可选指标的监控器") + public ResponseEntity> addNewMonitorOptionalMetrics(@Valid @RequestBody MonitorDto monitorDto) { monitorService.validate(monitorDto, false); if (monitorDto.isDetected()) { monitorService.detectMonitor(monitorDto.getMonitor(), monitorDto.getParams()); } - monitorService.addNewMonitorOptionalMetrics(monitorDto.getMetrics(),monitorDto.getMonitor(),monitorDto.getParams()); + monitorService.addNewMonitorOptionalMetrics(monitorDto.getMetrics(), monitorDto.getMonitor(), monitorDto.getParams()); return ResponseEntity.ok(new Message<>("Add success")); } - @GetMapping(value = {"/metric/{app}","/metric"}) + @GetMapping(value = {"/metric/{app}", "/metric"}) @Operation(summary = "get app metric", description = "根据app名称获取该app可监控指标,不传为获取全部指标") public ResponseEntity>> getMonitorMetrics( - @PathVariable(value = "app",required = false) String app) { + @PathVariable(value = "app", required = false) String app) { List metricNames = monitorService.getMonitorMetrics(app); return ResponseEntity.ok(new Message<>(metricNames)); } + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorsController.java b/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorsController.java index be76d877908..833fdb3c7e5 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorsController.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/controller/MonitorsController.java @@ -17,12 +17,12 @@ package org.dromara.hertzbeat.manager.controller; -import org.dromara.hertzbeat.common.entity.dto.Message; -import org.dromara.hertzbeat.common.entity.manager.Monitor; -import org.dromara.hertzbeat.manager.service.MonitorService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import org.dromara.hertzbeat.common.entity.dto.Message; +import org.dromara.hertzbeat.common.entity.manager.Monitor; +import org.dromara.hertzbeat.manager.service.MonitorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -30,22 +30,22 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; -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.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Predicate; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; + import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /** * Monitor and manage batch API * 监控管理批量API + * * @author tom * @date 2021/12/1 20:43 */ @@ -170,4 +170,19 @@ public ResponseEntity> enableManageMonitors( Message message = new Message<>(); return ResponseEntity.ok(message); } + + @GetMapping("/export") + @Operation(summary = "export monitor config", description = "导出监控配置") + public void export(@RequestParam List ids, @RequestParam(defaultValue = "JSON") String type, + HttpServletResponse res) throws IOException { + monitorService.export(ids, type, res); + } + + @PostMapping("/import") + @Operation(summary = "import monitor config", description = "导入监控配置") + public ResponseEntity> export(MultipartFile file) throws IOException { + monitorService.importConfig(file); + return ResponseEntity.ok(new Message<>("Import success")); + } + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/dao/TagDao.java b/manager/src/main/java/org/dromara/hertzbeat/manager/dao/TagDao.java index ede108eb786..006f3ea58ed 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/dao/TagDao.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/dao/TagDao.java @@ -21,10 +21,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; import java.util.Set; /** * tag repository + * * @author tom * @date 2022/5/1 13:55 */ @@ -32,7 +34,17 @@ public interface TagDao extends JpaRepository, JpaSpecificationExecut /** * delete tags by tag id + * * @param ids id list */ void deleteTagsByIdIn(Set ids); + + /** + * find tags by tag id + * + * @param ids id list + * @return tag list + */ + List findByIdIn(Set ids); + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/ImExportService.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/ImExportService.java new file mode 100644 index 00000000000..1f830d26bc9 --- /dev/null +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/ImExportService.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hertzbeat.manager.service; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +/** + * Configuration Import Export + * 配置导入导出 + * + * @author gcdd1993 + * Created by gcdd1993 on 2023/3/31 + */ +public interface ImExportService { + + /** + * Import Configuration + * 导入配置 + * + * @param is 输入流 + */ + void importConfig(InputStream is); + + /** + * Export Configuration + * 导出配置 + * + * @param os 输出流 + * @param configList 配置列表 + */ + void exportConfig(OutputStream os, List configList); + + /** + * Export file type + * 导出文件类型 + * + * @return 文件类型 + */ + String type(); + + /** + * Get Export File Name + * 获取导出文件名 + * + * @return 文件名 + */ + String getFileName(); + +} diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/MonitorService.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/MonitorService.java index 5573b294f95..3d629eb9670 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/MonitorService.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/MonitorService.java @@ -17,15 +17,18 @@ package org.dromara.hertzbeat.manager.service; -import org.dromara.hertzbeat.manager.pojo.dto.AppCount; -import org.dromara.hertzbeat.manager.pojo.dto.MonitorDto; import org.dromara.hertzbeat.common.entity.manager.Monitor; import org.dromara.hertzbeat.common.entity.manager.Param; +import org.dromara.hertzbeat.manager.pojo.dto.AppCount; +import org.dromara.hertzbeat.manager.pojo.dto.MonitorDto; import org.dromara.hertzbeat.manager.support.exception.MonitorDetectException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -169,17 +172,39 @@ public interface MonitorService { /** * 新增一个可选指标的监控 - * @param metrics 用户指标 - * @param monitor 监控提示 - * @param params 配置参数 + * + * @param metrics 用户指标 + * @param monitor 监控提示 + * @param params 配置参数 */ void addNewMonitorOptionalMetrics(List metrics, Monitor monitor, List params); /** * 根据App名称获取可监控指标,不传为获取全部指标 + * * @param app app name * @return metrics */ List getMonitorMetrics(String app); + /** + * Export Monitoring Configuration + * 导出监控配置 + * + * @param ids 监控配置ID列表 + * @param type 文件类型 + * @param res response + * @throws IOException This exception will be thrown if the export fails + */ + void export(List ids, String type, HttpServletResponse res) throws IOException; + + /** + * Import Monitoring Configuration + * 导入监控配置 + * + * @param file 配置文件 + * @throws IOException This exception will be thrown if the export fails + */ + void importConfig(MultipartFile file) throws IOException; + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/TagService.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/TagService.java index c7b2bdb0f27..40c867aa195 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/TagService.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/TagService.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; +import java.util.Set; /** * 标签服务 @@ -35,27 +36,40 @@ 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 分页条件 + * @param pageRequest 分页条件 * @return Tags */ Page getTags(Specification specification, PageRequest pageRequest); /** * delete tags + * * @param ids tag id list */ void deleteTags(HashSet ids); + + /** + * list tags + * + * @param ids tag id list + * @return tag list + */ + List listTag(Set ids); + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java new file mode 100644 index 00000000000..090935b614a --- /dev/null +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hertzbeat.manager.service.impl; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import org.dromara.hertzbeat.common.entity.manager.Monitor; +import org.dromara.hertzbeat.common.entity.manager.Param; +import org.dromara.hertzbeat.common.entity.manager.Tag; +import org.dromara.hertzbeat.manager.pojo.dto.MonitorDto; +import org.dromara.hertzbeat.manager.service.ImExportService; +import org.dromara.hertzbeat.manager.service.MonitorService; +import org.dromara.hertzbeat.manager.service.TagService; +import org.springframework.beans.BeanUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.io.OutputStream; +import java.time.LocalDate; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author gcdd1993 + * Created by gcdd1993 on 2023/3/31 + */ +abstract class AbstractImExportServiceImpl implements ImExportService { + + @Resource + @Lazy + private MonitorService monitorService; + + @Resource + private TagService tagService; + + @Override + public void importConfig(InputStream is) { + var formList = parseImport(is) + .stream() + .map(this::convert) + .collect(Collectors.toUnmodifiableList()); + if (!CollectionUtils.isEmpty(formList)) { + formList.forEach(it -> { + monitorService.validate(it, false); + if (it.isDetected()) { + monitorService.detectMonitor(it.getMonitor(), it.getParams()); + } + monitorService.addMonitor(it.getMonitor(), it.getParams()); + }); + } + } + + @Override + public void exportConfig(OutputStream os, List configList) { + var monitorList = configList.stream() + .map(it -> monitorService.getMonitorDto(it)) + .map(this::convert) + .collect(Collectors.toUnmodifiableList()); + writeOs(monitorList, os); + } + + /** + * Parsing an input stream into a form + * 将输入流解析为表单 + * + * @param is 输入流 + * @return 表单 + */ + abstract List parseImport(InputStream is); + + /** + * Export Configuration to Output Stream + * 导出配置到输出流 + * + * @param monitorList 配置列表 + * @param os 输出流 + */ + abstract void writeOs(List monitorList, OutputStream os); + + private ExportMonitorDTO convert(MonitorDto dto) { + var exportMonitor = new ExportMonitorDTO(); + var monitor = new MonitorDTO(); + BeanUtils.copyProperties(dto.getMonitor(), monitor); + if (!CollectionUtils.isEmpty(dto.getMonitor().getTags())) { + monitor.setTags(dto.getMonitor().getTags().stream() + .map(Tag::getId).collect(Collectors.toUnmodifiableList())); + } + exportMonitor.setMonitor(monitor); + exportMonitor.setParams(dto.getParams().stream() + .map(it -> { + var param = new ParamDTO(); + BeanUtils.copyProperties(it, param); + return param; + }) + .collect(Collectors.toUnmodifiableList())); + exportMonitor.setMetrics(dto.getMetrics()); + return exportMonitor; + } + + private MonitorDto convert(ExportMonitorDTO exportMonitor) { + var monitorDto = new MonitorDto(); + monitorDto.setDetected(true); + var monitor = new Monitor(); + BeanUtils.copyProperties(exportMonitor.monitor, monitor); + monitor.setTags(tagService.listTag(new HashSet<>(exportMonitor.monitor.tags))); + monitorDto.setMonitor(monitor); + monitorDto.setMetrics(exportMonitor.metrics); + monitorDto.setParams(exportMonitor.params.stream() + .map(it -> { + var param = new Param(); + BeanUtils.copyProperties(it, param); + return param; + }) + .collect(Collectors.toUnmodifiableList())); + return monitorDto; + } + + protected String fileNamePrefix() { + return "hertzbeat_monitor_" + LocalDate.now(); + } + + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + protected static class ExportMonitorDTO { + private MonitorDTO monitor; + private List params; + private List metrics; + } + + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + protected static class MonitorDTO { + private String name; + private String app; + private String host; + private Integer intervals; + private Byte status; + private String description; + private List tags; + } + + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + protected static class ParamDTO { + private String field; + private String value; + private Byte type; + } + +} diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/JsonImExportServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/JsonImExportServiceImpl.java new file mode 100644 index 00000000000..b3db0afd43e --- /dev/null +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/JsonImExportServiceImpl.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hertzbeat.manager.service.impl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +/** + * Configure the import and export JSON format + * 配置导入导出 JSON格式 + * + * @author gcdd1993 + * Created by gcdd1993 on 2023/3/31 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class JsonImExportServiceImpl extends AbstractImExportServiceImpl { + public static final String TYPE = "JSON"; + public static final String FILE_SUFFIX = ".json"; + + private final ObjectMapper objectMapper; + + @Override + List parseImport(InputStream is) { + try { + return objectMapper.readValue(is, new TypeReference<>() { + }); + } catch (IOException ex) { + log.error("import monitor failed.", ex); + throw new RuntimeException("import monitor failed"); + } + } + + @Override + void writeOs(List monitorList, OutputStream os) { + try { + objectMapper.writeValue(os, monitorList); + } catch (IOException ex) { + log.error("export monitor failed.", ex); + throw new RuntimeException("export monitor failed"); + } + } + + @Override + public String type() { + return TYPE; + } + + @Override + public String getFileName() { + return fileNamePrefix() + ".json"; + } + +} diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MonitorServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MonitorServiceImpl.java index 070f84871e4..cd1abb58c8e 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MonitorServiceImpl.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/MonitorServiceImpl.java @@ -17,12 +17,17 @@ package org.dromara.hertzbeat.manager.service.impl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.dromara.hertzbeat.alert.calculate.CalculateAlarm; import org.dromara.hertzbeat.alert.dao.AlertDefineBindDao; import org.dromara.hertzbeat.collector.dispatch.entrance.internal.CollectJobService; import org.dromara.hertzbeat.common.entity.job.Configmap; import org.dromara.hertzbeat.common.entity.job.Job; import org.dromara.hertzbeat.common.entity.job.Metrics; +import org.dromara.hertzbeat.common.entity.manager.Monitor; +import org.dromara.hertzbeat.common.entity.manager.Param; +import org.dromara.hertzbeat.common.entity.manager.ParamDefine; import org.dromara.hertzbeat.common.entity.manager.Tag; import org.dromara.hertzbeat.common.entity.message.CollectRep; import org.dromara.hertzbeat.common.util.*; @@ -30,24 +35,27 @@ import org.dromara.hertzbeat.manager.dao.ParamDao; import org.dromara.hertzbeat.manager.pojo.dto.AppCount; import org.dromara.hertzbeat.manager.pojo.dto.MonitorDto; -import org.dromara.hertzbeat.common.entity.manager.Monitor; -import org.dromara.hertzbeat.common.entity.manager.Param; -import org.dromara.hertzbeat.common.entity.manager.ParamDefine; import org.dromara.hertzbeat.manager.service.AppService; +import org.dromara.hertzbeat.manager.service.ImExportService; import org.dromara.hertzbeat.manager.service.MonitorService; import org.dromara.hertzbeat.manager.support.exception.MonitorDatabaseException; import org.dromara.hertzbeat.manager.support.exception.MonitorDetectException; import org.dromara.hertzbeat.manager.support.exception.MonitorMetricsException; -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.http.HttpHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -60,6 +68,7 @@ */ @Service @Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor @Slf4j public class MonitorServiceImpl implements MonitorService { @@ -83,6 +92,12 @@ public class MonitorServiceImpl implements MonitorService { @Autowired private CalculateAlarm calculateAlarm; + private final Map imExportServiceMap = new HashMap<>(); + + public MonitorServiceImpl(List imExportServiceList) { + imExportServiceList.forEach(it -> imExportServiceMap.put(it.type(), it)); + } + @Override @Transactional(readOnly = true) public void detectMonitor(Monitor monitor, List params) throws MonitorDetectException { @@ -171,7 +186,7 @@ public void addNewMonitorOptionalMetrics(List metrics, Monitor monitor, //设置用户可选指标 List metricsDefine = appDefine.getMetrics(); List metricsDefineNames = metricsDefine.stream().map(Metrics::getName).collect(Collectors.toList()); - if (CollectionUtils.isEmpty(metrics) || !metricsDefineNames.containsAll(metrics)){ + if (CollectionUtils.isEmpty(metrics) || !metricsDefineNames.containsAll(metrics)) { throw new MonitorMetricsException("no select metrics or select illegal metrics"); } List realMetrics = metricsDefine.stream().filter(m -> metrics.contains(m.getName())).collect(Collectors.toList()); @@ -210,6 +225,34 @@ public List getMonitorMetrics(String app) { return appService.getAppDefineMetricNames(app); } + @Override + public void export(List ids, String type, HttpServletResponse res) throws IOException { + var imExportService = imExportServiceMap.get(type); + var fileName = imExportService.getFileName(); + res.setHeader("content-type", "application/octet-stream;charset=UTF-8"); + res.setContentType("application/octet-stream;charset=UTF-8"); + res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8)); + res.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); + imExportService.exportConfig(res.getOutputStream(), ids); + } + + @Override + public void importConfig(MultipartFile file) throws IOException { + var fileName = file.getOriginalFilename(); + if (!StringUtils.hasText(fileName)) { + return; + } + var type = ""; + if (fileName.toLowerCase().endsWith(JsonImExportServiceImpl.FILE_SUFFIX)) { + type = JsonImExportServiceImpl.TYPE; + } + if (!imExportServiceMap.containsKey(type)) { + throw new RuntimeException("file " + fileName + " is not supported."); + } + var imExportService = imExportServiceMap.get(type); + imExportService.importConfig(file.getInputStream()); + } + @Override @Transactional(readOnly = true) @@ -274,10 +317,10 @@ public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgu param.setType(CommonConstants.PARAM_TYPE_NUMBER); break; case "textarea": - if (StringUtils.hasText(param.getValue())){ - throw new IllegalArgumentException("Params field " + field + " type " - + paramDefine.getType() + " over limit " + param.getValue()); - } + if (StringUtils.hasText(param.getValue())) { + throw new IllegalArgumentException("Params field " + field + " type " + + paramDefine.getType() + " over limit " + param.getValue()); + } break; case "text": Short limit = paramDefine.getLimit(); @@ -343,15 +386,15 @@ public void validate(MonitorDto monitorDto, Boolean isModify) throws IllegalArgu } } } - if (checkboxInvalid){ + if (checkboxInvalid) { throw new IllegalArgumentException("Params field " + field + " value " - + param.getValue() + " is invalid checkbox value"); + + param.getValue() + " is invalid checkbox value"); } break; case "key-value": - try{ + try { JsonUtil.toJson(param.getValue()); - } catch (Exception e){ + } catch (Exception e) { throw new IllegalArgumentException("Params field " + field + " value " + param.getValue() + " is invalid key-value value"); } @@ -596,4 +639,5 @@ public void updateMonitorStatus(Long monitorId, byte status) { public List getAppMonitors(String app) { return monitorDao.findMonitorsByAppEquals(app); } + } diff --git a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/TagServiceImpl.java b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/TagServiceImpl.java index 8261a5ef70f..ab05d1e8e1f 100644 --- a/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/TagServiceImpl.java +++ b/manager/src/main/java/org/dromara/hertzbeat/manager/service/impl/TagServiceImpl.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; /** * @author tom @@ -68,4 +69,11 @@ public Page getTags(Specification specification, PageRequest pageReque public void deleteTags(HashSet ids) { tagDao.deleteTagsByIdIn(ids); } + + @Override + public List listTag(Set ids) { + return tagDao.findByIdIn(ids); + } + + } diff --git a/manager/src/test/java/org/dromara/hertzbeat/manager/service/MonitorServiceTest.java b/manager/src/test/java/org/dromara/hertzbeat/manager/service/MonitorServiceTest.java index 0c5ea4764f9..4b3401b7203 100644 --- a/manager/src/test/java/org/dromara/hertzbeat/manager/service/MonitorServiceTest.java +++ b/manager/src/test/java/org/dromara/hertzbeat/manager/service/MonitorServiceTest.java @@ -42,27 +42,28 @@ * newBranch feature-clickhouse#179 * 配置带密码的clickhouse * https://www.cnblogs.com/it1042290135/p/16202478.html - - 9363是promethus的http端口(在config.xml里面打开), http://clickhouse:9363/metrics - docker run -d --name some-clickhouse-server -p 8123:8123 -p 9009:9009 -p 9090:9000 -p 9363:9363 --ulimit nofile=262144:262144 --volume=/opt/clickhouse/data:/var/lib/clickhouse --volume=/opt/clickhouse/log:/var/log/clickhouse-server --volume=/opt/clickhouse/conf/config.xml:/etc/clickhouse-server/config.xml --volume=/opt/clickhouse/conf/users.xml:/etc/clickhouse-server/users.xml clickhouse/clickhouse-server - - * + *

+ * 9363是promethus的http端口(在config.xml里面打开), http://clickhouse:9363/metrics + * docker run -d --name some-clickhouse-server -p 8123:8123 -p 9009:9009 -p 9090:9000 -p 9363:9363 --ulimit nofile=262144:262144 --volume=/opt/clickhouse/data:/var/lib/clickhouse --volume=/opt/clickhouse/log:/var/log/clickhouse-server --volume=/opt/clickhouse/conf/config.xml:/etc/clickhouse-server/config.xml --volume=/opt/clickhouse/conf/users.xml:/etc/clickhouse-server/users.xml clickhouse/clickhouse-server + *

+ *

* https://hub.docker.com/r/clickhouse/clickhouse-server/ * docker run -d -p 18123:8123 -p19000:9000 --name some-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server * curl 'http://localhost:18123/' * web UI * http://localhost:18123/play - * + *

* 明文密码linux可以登录了,但是navicat还是无法登录 * clickhouse client -h 127.0.0.1 -d default -m -u default --password 123456 * Test case for {@link MonitorService} + * * @see TagServiceTest */ @ExtendWith(MockitoExtension.class) class MonitorServiceTest { @InjectMocks - private MonitorServiceImpl monitorService; + private MonitorServiceImpl monitorService = new MonitorServiceImpl(List.of()); // @Mock(lenient = true) @Mock @@ -109,7 +110,7 @@ void detectMonitorEmpty() { when(collectJobService.collectSyncJobData(job)).thenReturn(collectRep); List params = Collections.singletonList(new Param()); - assertThrows(MonitorDetectException.class,() -> monitorService.detectMonitor(monitor,params)); + assertThrows(MonitorDetectException.class, () -> monitorService.detectMonitor(monitor, params)); } /** @@ -135,7 +136,7 @@ void detectMonitorFail() { when(collectJobService.collectSyncJobData(job)).thenReturn(collectRep); List params = Collections.singletonList(new Param()); - assertThrows(MonitorDetectException.class,() -> monitorService.detectMonitor(monitor,params)); + assertThrows(MonitorDetectException.class, () -> monitorService.detectMonitor(monitor, params)); } @Test @@ -151,8 +152,9 @@ void addMonitorSuccess() { when(monitorDao.save(monitor)).thenReturn(monitor); List params = Collections.singletonList(new Param()); when(paramDao.saveAll(params)).thenReturn(params); - assertDoesNotThrow(() -> monitorService.addMonitor(monitor,params)); + assertDoesNotThrow(() -> monitorService.addMonitor(monitor, params)); } + @Test void addMonitorException() { Monitor monitor = Monitor.builder() @@ -165,7 +167,7 @@ void addMonitorException() { when(collectJobService.addAsyncCollectJob(job)).thenReturn(1L); List params = Collections.singletonList(new Param()); when(monitorDao.save(monitor)).thenThrow(RuntimeException.class); - assertThrows(MonitorDatabaseException.class,() -> monitorService.addMonitor(monitor,params)); + assertThrows(MonitorDatabaseException.class, () -> monitorService.addMonitor(monitor, params)); } /** @@ -182,11 +184,12 @@ void validateMonitorName() { Monitor existMonitor = Monitor.builder().name("memory").host("host").id(2L).build(); when(monitorDao.findMonitorByNameEquals(monitor.getName())).thenReturn(Optional.of(existMonitor)); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - assertEquals("监控名称不能重复!",e.getMessage()); + assertEquals("监控名称不能重复!", e.getMessage()); } } + /** * 参数校验-为必填的参数没有填 */ @@ -214,11 +217,12 @@ void validateRequireMonitorParams() { paramDefines.add(pd); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - assertEquals("Params field " + field + " is required.",e.getMessage()); + assertEquals("Params field " + field + " is required.", e.getMessage()); } } + /** * 参数校验-为必填的参数类型错误 */ @@ -248,12 +252,13 @@ void validateMonitorParamsType() { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { assertEquals("Params field " + field + " type " - + paramDefine.getType() + " is invalid.",e.getMessage()); + + paramDefine.getType() + " is invalid.", e.getMessage()); } } + /** * 参数校验-为必填的-整形参数范围 */ @@ -283,12 +288,13 @@ void validateMonitorParamsRange() { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { assertEquals("Params field " + field + " type " - + paramDefine.getType() + " over range " + paramDefine.getRange(),e.getMessage()); + + paramDefine.getType() + " over range " + paramDefine.getRange(), e.getMessage()); } } + /** * 参数校验-为必填的-文本参数长度 */ @@ -319,12 +325,13 @@ void validateMonitorParamsTextLimit() { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { assertEquals("Params field " + field + " type " - + paramDefine.getType() + " over limit " + limit,e.getMessage()); + + paramDefine.getType() + " over limit " + limit, e.getMessage()); } } + /** * 参数校验-主机IP参数格式 */ @@ -362,13 +369,14 @@ void validateMonitorParamsHost(ArgumentsAccessor arguments) { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - if (checkException){ - assertEquals("Params field " + field + " value " + value + " is invalid host value.",e.getMessage()); + if (checkException) { + assertEquals("Params field " + field + " value " + value + " is invalid host value.", e.getMessage()); } } } + /** * 参数校验-布尔类型 */ @@ -407,14 +415,15 @@ void validateMonitorParamsBoolean(ArgumentsAccessor arguments) { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - if (checkException){ + if (checkException) { assertEquals("Params field " + field + " value " - + value + " is invalid boolean value.",e.getMessage()); + + value + " is invalid boolean value.", e.getMessage()); } } } + /** * 参数校验-布尔类型 */ @@ -446,7 +455,7 @@ void validateMonitorParamsRadio(ArgumentsAccessor arguments) { String type = "radio"; List options = new ArrayList<>(); - options.add(new ParamDefine.Option("language","zh")); + options.add(new ParamDefine.Option("language", "zh")); ParamDefine paramDefine = ParamDefine.builder() .required(true) .type(type) @@ -457,14 +466,15 @@ void validateMonitorParamsRadio(ArgumentsAccessor arguments) { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - if (checkException){ + if (checkException) { assertEquals("Params field " + field + " value " - + param.getValue() + " is invalid option value",e.getMessage()); + + param.getValue() + " is invalid option value", e.getMessage()); } } } + /** * 参数校验-没有定义的类型 */ @@ -496,7 +506,7 @@ void validateMonitorParamsNone(ArgumentsAccessor arguments) { String type = "none"; List options = new ArrayList<>(); - options.add(new ParamDefine.Option("language","zh")); + options.add(new ParamDefine.Option("language", "zh")); ParamDefine paramDefine = ParamDefine.builder() .required(true) .type(type) @@ -507,10 +517,10 @@ void validateMonitorParamsNone(ArgumentsAccessor arguments) { paramDefines.add(paramDefine); when(appService.getAppParamDefines(monitor.getApp())).thenReturn(paramDefines); try { - monitorService.validate(dto,isModify); + monitorService.validate(dto, isModify); } catch (IllegalArgumentException e) { - if (checkException){ - assertEquals("ParamDefine type " + paramDefine.getType() + " is invalid.",e.getMessage()); + if (checkException) { + assertEquals("ParamDefine type " + paramDefine.getType() + " is invalid.", e.getMessage()); } } } @@ -536,7 +546,7 @@ void modifyMonitor() { dto.setMonitor(monitor); when(monitorDao.findById(monitorId)).thenReturn(Optional.empty()); try { - monitorService.modifyMonitor(dto.getMonitor(),dto.getParams()); + monitorService.modifyMonitor(dto.getMonitor(), dto.getParams()); } catch (IllegalArgumentException e) { assertEquals("The Monitor " + monitorId + " not exists", e.getMessage()); } @@ -547,7 +557,7 @@ void modifyMonitor() { Monitor existErrorMonitor = Monitor.builder().app("app2").name("memory").host("host").id(monitorId).build(); when(monitorDao.findById(monitorId)).thenReturn(Optional.of(existErrorMonitor)); try { - monitorService.modifyMonitor(dto.getMonitor(),dto.getParams()); + monitorService.modifyMonitor(dto.getMonitor(), dto.getParams()); } catch (IllegalArgumentException e) { assertEquals("Can not modify monitor's app type", e.getMessage()); } @@ -556,7 +566,7 @@ void modifyMonitor() { when(monitorDao.findById(monitorId)).thenReturn(Optional.of(existOKMonitor)); when(monitorDao.save(monitor)).thenThrow(RuntimeException.class); - assertThrows(MonitorDatabaseException.class,()->monitorService.modifyMonitor(dto.getMonitor(),dto.getParams())); + assertThrows(MonitorDatabaseException.class, () -> monitorService.modifyMonitor(dto.getMonitor(), dto.getParams())); } @Test @@ -565,7 +575,7 @@ void deleteMonitor() { Monitor existOKMonitor = Monitor.builder().jobId(id).intervals(1).app("app").name("memory").host("host").id(id).build(); when(monitorDao.findById(id)).thenReturn(Optional.of(existOKMonitor)); doNothing().when(alertDefineBindDao).deleteAlertDefineMonitorBindsByMonitorIdEquals(id); - assertDoesNotThrow(()-> monitorService.deleteMonitor(id)); + assertDoesNotThrow(() -> monitorService.deleteMonitor(id)); } @Test @@ -575,12 +585,12 @@ void deleteMonitors() { ids.add(2L); List monitors = new ArrayList<>(); - for(Long id : ids){ + for (Long id : ids) { Monitor monitor = Monitor.builder().jobId(id).intervals(1).app("app").name("memory").host("host").id(id).build(); monitors.add(monitor); } when(monitorDao.findMonitorsByIdIn(ids)).thenReturn(monitors); - assertDoesNotThrow(()-> monitorService.deleteMonitors(ids)); + assertDoesNotThrow(() -> monitorService.deleteMonitors(ids)); } @Test @@ -611,12 +621,12 @@ void cancelManageMonitors() { ids.add(2L); List monitors = new ArrayList<>(); - for(Long id : ids){ + for (Long id : ids) { Monitor monitor = Monitor.builder().jobId(id).intervals(1).app("app").name("memory").host("host").id(id).build(); monitors.add(monitor); } when(monitorDao.findMonitorsByIdIn(ids)).thenReturn(monitors); - assertDoesNotThrow(()-> monitorService.cancelManageMonitors(ids)); + assertDoesNotThrow(() -> monitorService.cancelManageMonitors(ids)); } @Test @@ -626,7 +636,7 @@ void enableManageMonitors() { ids.add(2L); List monitors = new ArrayList<>(); - for(Long id : ids){ + for (Long id : ids) { Monitor monitor = Monitor.builder().jobId(id).intervals(1).app("app").name("memory").host("host").id(id).build(); monitor.setStatus(CommonConstants.UN_MANAGE_CODE); monitors.add(monitor); @@ -637,7 +647,7 @@ void enableManageMonitors() { when(appService.getAppDefine(monitors.get(0).getApp())).thenReturn(job); List params = Collections.singletonList(new Param()); when(paramDao.findParamsByMonitorId(monitors.get(0).getId())).thenReturn(params); - assertDoesNotThrow(()-> monitorService.enableManageMonitors(ids)); + assertDoesNotThrow(() -> monitorService.enableManageMonitors(ids)); } @Test @@ -655,24 +665,24 @@ void getAllAppMonitorsCount() { job.setMetrics(new ArrayList<>()); when(appService.getAppDefine(appCounts.get(0).getApp())).thenReturn(job); - assertDoesNotThrow(()-> monitorService.getAllAppMonitorsCount()); + assertDoesNotThrow(() -> monitorService.getAllAppMonitorsCount()); } @Test void getMonitor() { long monitorId = 1L; when(monitorDao.findById(monitorId)).thenReturn(Optional.empty()); - assertDoesNotThrow(()-> monitorService.getMonitor(monitorId)); + assertDoesNotThrow(() -> monitorService.getMonitor(monitorId)); } @Test void updateMonitorStatus() { - assertDoesNotThrow(()-> monitorService.updateMonitorStatus(1L,CommonConstants.AVAILABLE_CODE)); + assertDoesNotThrow(() -> monitorService.updateMonitorStatus(1L, CommonConstants.AVAILABLE_CODE)); } @Test void getAppMonitors() { - assertDoesNotThrow(()-> monitorDao.findMonitorsByAppEquals("test")); + assertDoesNotThrow(() -> monitorDao.findMonitorsByAppEquals("test")); } @Test @@ -690,9 +700,9 @@ void addNewMonitorOptionalMetrics() { List params = Collections.singletonList(new Param()); List metrics = Arrays.asList(); try { - monitorService.addNewMonitorOptionalMetrics(metrics,monitor,params); - }catch (MonitorMetricsException e){ - assertEquals("no select metrics or select illegal metrics",e.getMessage()); + monitorService.addNewMonitorOptionalMetrics(metrics, monitor, params); + } catch (MonitorMetricsException e) { + assertEquals("no select metrics or select illegal metrics", e.getMessage()); } reset(); when(monitorDao.save(monitor)).thenThrow(RuntimeException.class); @@ -703,12 +713,12 @@ void addNewMonitorOptionalMetrics() { metricsDefine.add(e); job.setMetrics(metricsDefine); List finalMetrics = metrics; - assertThrows(MonitorDatabaseException.class,() -> monitorService.addNewMonitorOptionalMetrics(finalMetrics,monitor,params)); + assertThrows(MonitorDatabaseException.class, () -> monitorService.addNewMonitorOptionalMetrics(finalMetrics, monitor, params)); } @Test void getMonitorMetrics() { - Assertions.assertDoesNotThrow(()-> appService.getAppDefineMetricNames("test")); + Assertions.assertDoesNotThrow(() -> appService.getAppDefineMetricNames("test")); } } diff --git a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html index c6dcc37805c..5c66057b8f4 100644 --- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html +++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html @@ -46,8 +46,30 @@ + + +

    +
  • + +
  • +
  • + + + +
  • +
+