Compare commits

...
Author SHA1 Message Date
tomsun28 0db72b0f02 update 2025-05-04 11:43:51 +08:00
tomsun28 6c7cb62fd8 [refactor] update
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-04 11:26:21 +08:00
tomsun28andCopilot a5b8a95991 Update hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertTemplateUtil.java
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-04 09:58:09 +08:00
tomsun28 e5693476f2 Merge branch 'master' into encoding-file-default 2025-05-04 00:07:41 +08:00
tomsun28 f05e91247a [refactor] refactor auto discovery http sd (#3300)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-04 00:07:06 +08:00
tomsun28 650d63d6eb [refactor] update
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-02 16:30:53 +08:00
tomsun28 1f46260469 [refactor] update job
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-02 16:27:20 +08:00
tomsun28 c73e47711e [refactor] update alert fingerprints column max length
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-02 16:06:39 +08:00
tomsun28 826e7b1dd9 [refactor] update alert fingerprints column max length
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-02 15:47:54 +08:00
tomsun28 ecb7f82321 [doc] update blog 2025-05-01 20:31:29 +08:00
tomsun28 5ac13827fb [refactor] set default encoding charset utf8
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-01 20:23:58 +08:00
tomsun28 383ba7d976 [refactor] set default encoding charset utf8
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-05-01 19:16:09 +08:00
60 changed files with 1088 additions and 1272 deletions
@@ -52,86 +52,84 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
log.error("The Group Alerts is empty, ignore store");
return groupAlert;
}
// 1. Find existing alert group
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
// 2. Process individual alerts
// Process individual alerts
Set<String> alertFingerprints = new HashSet<>(8);
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
List<SingleAlert> newAlerts = new ArrayList<>();
for (SingleAlert singleAlert : originalAlerts) {
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
if (existAlert != null) {
// Update the existing alert with the ID and creation time from the database
singleAlert.setId(existAlert.getId());
singleAlert.setGmtCreate(existAlert.getGmtCreate());
// Status transition logic
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
synchronized (singleAlert.getFingerprint().intern()) {
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
if (existAlert != null) {
// Update the existing alert with the ID and creation time from the database
singleAlert.setId(existAlert.getId());
singleAlert.setGmtCreate(existAlert.getGmtCreate());
// Status transition logic
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
singleAlert.setStartAt(existAlert.getStartAt());
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
singleAlert.setTriggerTimes(triggerTimes);
}
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
if (singleAlert.getEndAt() == null) {
singleAlert.setEndAt(System.currentTimeMillis());
}
singleAlert.setStartAt(existAlert.getStartAt());
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
singleAlert.setTriggerTimes(triggerTimes);
singleAlert.setActiveAt(existAlert.getActiveAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
}
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
if (singleAlert.getEndAt() == null) {
singleAlert.setEndAt(System.currentTimeMillis());
}
singleAlert.setStartAt(existAlert.getStartAt());
singleAlert.setActiveAt(existAlert.getActiveAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
}
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
newAlerts.add(savedSingleAlert);
alertFingerprints.add(savedSingleAlert.getFingerprint());
}
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
newAlerts.add(savedSingleAlert);
alertFingerprints.add(savedSingleAlert.getFingerprint());
}
groupAlert.setAlerts(newAlerts);
// 3. Process resolved alerts
if (existGroupAlert != null) {
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
if (existFingerprints != null) {
alertFingerprints.addAll(existFingerprints);
}
// Merge group information
groupAlert.setId(existGroupAlert.getId());
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
// Merge other historical information to preserve
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
if (existCommonLabels != null) {
Map<String, String> commonLabels = groupAlert.getCommonLabels();
if (commonLabels != null) {
// filter common label in commonLabels and existCommonLabels
commonLabels = commonLabels.entrySet().stream()
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonLabels(commonLabels);
// Find existing alert group
synchronized (groupAlert.getGroupKey().intern()) {
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
// Process resolved alerts
if (existGroupAlert != null) {
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
if (existFingerprints != null) {
alertFingerprints.addAll(existFingerprints);
}
}
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
if (existCommonAnnotations != null) {
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
if (commonAnnotations != null) {
// filter common annotation in commonAnnotations and existCommonAnnotations
commonAnnotations = commonAnnotations.entrySet().stream()
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonAnnotations(commonAnnotations);
// Merge group information
groupAlert.setId(existGroupAlert.getId());
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
// Merge other historical information to preserve
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
if (existCommonLabels != null) {
Map<String, String> commonLabels = groupAlert.getCommonLabels();
if (commonLabels != null) {
// filter common label in commonLabels and existCommonLabels
commonLabels = commonLabels.entrySet().stream()
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonLabels(commonLabels);
}
}
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
if (existCommonAnnotations != null) {
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
if (commonAnnotations != null) {
// filter common annotation in commonAnnotations and existCommonAnnotations
commonAnnotations = commonAnnotations.entrySet().stream()
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
groupAlert.setCommonAnnotations(commonAnnotations);
}
}
}
// Save alert group
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
savedGroupAlert.setAlerts(groupAlert.getAlerts());
return savedGroupAlert;
}
// 4. Save alert group
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
savedGroupAlert.setAlerts(groupAlert.getAlerts());
return savedGroupAlert;
}
}
@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
/**
* Alarm template keyword matching replacement engine tool
@@ -38,11 +39,11 @@ public final class AlertTemplateUtil {
private static final Pattern PATTERN = Pattern.compile("\\$\\{(\\w+)\\}");
public static String render(String template, Map<String, Object> replaceData) {
if (template == null) {
return null;
if (!StringUtils.hasText(template)) {
return template;
}
if (replaceData == null) {
log.warn("The replaceData is null.");
log.warn("The render replace data is null.");
return template;
}
try {
@@ -50,7 +51,7 @@ public final class AlertTemplateUtil {
StringBuilder builder = new StringBuilder();
while (matcher.find()) {
Object objectValue = replaceData.getOrDefault(matcher.group(1), "NullValue");
String value = objectValue.toString();
String value = objectValue != null ? objectValue.toString() : "NullValue";
matcher.appendReplacement(builder, Matcher.quoteReplacement(value));
}
matcher.appendTail(builder);
@@ -57,6 +57,7 @@ class DbAlertStoreHandlerImplTest {
public void setUp() {
groupAlert = new GroupAlert();
singleAlert = new SingleAlert();
singleAlert.setFingerprint("test-fingerprint");
List<SingleAlert> alerts = new ArrayList<>();
alerts.add(singleAlert);
groupAlert.setAlerts(alerts);
@@ -77,11 +78,13 @@ class DbAlertStoreHandlerImplTest {
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(null);
SingleAlert savedSingleAlert = new SingleAlert();
savedSingleAlert.setFingerprint("test-finger");
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(savedSingleAlert);
GroupAlert savedGroupAlert = new GroupAlert();
savedGroupAlert.setGroupKey(groupKey);
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(savedGroupAlert);
dbAlertStoreHandler.store(groupAlert);
verify(singleAlertDao).save(any(SingleAlert.class));
@@ -52,7 +52,7 @@ public class HttpSdCollectImpl extends AbstractCollect {
@Override
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
List<ConnectionConfig> configList = Lists.newArrayList();
HttpUriRequest request = RequestBuilder.get().setUri(metrics.getSdProtocol().getSdSource()).build();
HttpUriRequest request = RequestBuilder.get().setUri(metrics.getHttp_sd().getUrl()).build();
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request)) {
int statusCode = response.getStatusLine().getStatusCode();
@@ -281,7 +281,8 @@ public class CommonDispatcher implements MetricsTaskDispatch, CollectDataDispatc
}
// If it is an asynchronous periodic cyclic task, directly response the collected data
if (job.isSd()) {
commonDataQueue.sendServiceDiscoveryData(metricsData);
CollectRep.MetricsData sdMetricsData = CollectRep.MetricsData.newBuilder(metricsData).build();
commonDataQueue.sendServiceDiscoveryData(sdMetricsData);
}
commonDataQueue.sendMetricsData(metricsData);
} else {
@@ -126,7 +126,7 @@ public interface DispatchConstants {
/**
* protocol http sd
*/
String PROTOCOL_HTTP_SD = "httpsd";
String PROTOCOL_HTTP_SD = "http_sd";
/**
* protocol redfish
*/
@@ -17,10 +17,10 @@
package org.apache.hertzbeat.collector.dispatch.timer;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
@@ -87,7 +87,7 @@ public class TimerDispatcher implements TimerDispatch, DisposableBean {
for (Metrics metric : addJob.getMetrics()) {
metric.setInterval(0L);
}
addJob.setIntervals(new LinkedList<>(List.of(0L)));
addJob.setIntervals(new ConcurrentLinkedDeque<>(List.of(0L)));
Timeout timeout = wheelTimer.newTimeout(timerJob, addJob.getInterval(), TimeUnit.SECONDS);
currentTempTaskMap.put(addJob.getId(), timeout);
eventListeners.put(addJob.getId(), eventListener);
@@ -73,14 +73,14 @@ public interface CommonConstants {
byte MONITOR_DOWN_CODE = 0x02;
/**
* Monitor bind type, 0: monitor that auto-created by sd
* scrape type static
*/
byte MONITOR_BIND_TYPE_SD_SUB_MONITOR = 0x00;
String SCRAPE_STATIC = "static";
/**
* Monitor bind type, 1: the main monitor of sd
* scrape type
*/
byte MONITOR_BIND_TYPE_SD_MAIN_MONITOR = 0x01;
String SCRAPE_HTTP_SD = "http_sd";
/**
* label key: instance
@@ -192,11 +192,6 @@ public interface CommonConstants {
*/
String PROM_VALUE = "value";
/**
* Monitor total availability metrics
*/
String AVAILABILITY = "availability";
/**
* Parameter Type Number
*/
@@ -222,104 +217,6 @@ public interface CommonConstants {
*/
byte PARAM_TYPE_ARRAY = 4;
/**
* Authentication type Account password
*/
byte AUTH_TYPE_PASSWORD = 1;
/**
* Authentication type GitHub three-party login
*/
byte AUTH_TYPE_GITHUB = 2;
/**
* Authentication type WeChat three-party login
*/
byte AUTH_TYPE_WEIXIN = 3;
/**
* Authentication type GITEE three-party login
*/
byte AUTH_TYPE_GITEE = 5;
/**
* inner default label key __instance__
*/
String LABEL_INNER_KEY_INSTANCE = "__instance__";
/**
* Inside the tag: monitorId Monitor task ID
*/
String TAG_MONITOR_ID = "monitorId";
/**
* Inside the tag: monitorName Task name
*/
String TAG_MONITOR_NAME = "monitorName";
/**
* Inside the tag: indicate that this monitor is auto-created by main monitor
*/
String TAG_AUTO_CREATED = "autoCreated";
/**
* Inside the tag: indicate that this monitor is a main monitor which provides service discovery
*/
String TAG_SD_MAIN_MONITOR = "sdMainMonitor";
/**
* Inside the tag: monitorHost Task host
*/
String TAG_MONITOR_HOST = "monitorHost";
/**
* Inside the tag: policyId Alarm threshold rule ID
*/
String TAG_THRESHOLD_ID = "thresholdId";
/**
* Inside the tag: app Type of monitoring
*/
String TAG_MONITOR_APP = "app";
/**
* Inside the tag: metrics
*/
String TAG_METRICS = "metrics";
/**
* Inside the tag: metric
*/
String TAG_METRIC = "metric";
/**
* Inside the tag: code
*/
String TAG_CODE = "code";
/**
* Tag Type: Auto-generated
*/
byte TAG_TYPE_AUTO_GENERATE = 0;
/**
* Tag Type: User-generated
*/
byte TAG_TYPE_USER_GENERATE = 1;
/**
* Tag Type: System preset
*/
byte TAG_TYPE_SYSTEM_PRESET = 2;
/**
* notice_period type Type field, daily type
*/
int NOTICE_PERIOD_DAILY = 0;
/**
* key is receiver.id, value is noticePeriod cache key prefix
*/
String RECEIVER_NOTICE_PERIOD_CACHE_PREFIX = "receiver_notice_period:";
/**
* cache key notice_rule
*/
@@ -86,7 +86,7 @@ public class GroupAlert {
@Schema(title = "Alert Fingerprints", example = "[\"dxsdfdsf\"]")
@Convert(converter = JsonStringListAttributeConverter.class)
@Column(length = 2048)
@Column(length = 8192)
private List<String> alertFingerprints;
@Schema(title = "The creator of this record", example = "tom")
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -114,7 +115,7 @@ public class Job {
/**
* Refresh time list for one cycle of the job
*/
private LinkedList<Long> intervals;
private ConcurrentLinkedDeque<Long> intervals;
/**
* Whether it is a recurring periodic task true is yes, false is no
*/
@@ -328,7 +329,7 @@ public class Job {
* @param metricsIntervals A unique list composed of intervals for all metrics
* Generate a list of refresh intervals for metric collection
*/
public void generateMetricsIntervals(List<Long> metricsIntervals) {
public synchronized void generateMetricsIntervals(List<Long> metricsIntervals) {
// 1. To find the least common multiple (LCM) of all metric refresh intervals
long lcm = lcm(metricsIntervals);
List<Long> refreshTimes = new LinkedList<>();
@@ -348,14 +349,16 @@ public class Job {
for (int i = 1; i < refreshTimes.size(); i++) {
intervals.add(refreshTimes.get(i) - refreshTimes.get(i - 1));
}
setIntervals(intervals);
setIntervals(new ConcurrentLinkedDeque<>(intervals));
}
public long getInterval() {
if (!CollectionUtils.isEmpty(getIntervals())) {
long interval = getIntervals().remove();
getIntervals().add(interval);
return interval;
public synchronized long getInterval() {
if (!CollectionUtils.isEmpty(this.intervals)) {
Long interval = this.intervals.removeFirst();
if (interval != null) {
this.intervals.addLast(interval);
return interval;
}
}
return getDefaultInterval();
}
@@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.common.entity.job.protocol.DnsProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.FtpProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.HttpSdProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.ModbusProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.PlcProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
@@ -63,7 +64,6 @@ import org.apache.hertzbeat.common.entity.job.protocol.TelnetProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.UdpProtocol;
import org.apache.hertzbeat.common.entity.job.protocol.WebsocketProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
/**
* Details of the monitoring metrics collected
@@ -74,6 +74,7 @@ import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
@NoArgsConstructor
@Builder
@Slf4j
@SuppressWarnings("checkstyle:MemberName")
public class Metrics {
/**
@@ -260,9 +261,9 @@ public class Metrics {
*/
private KafkaProtocol kclient;
/**
* Collect sd data protocol
* http sd protocol
*/
private ServiceDiscoveryProtocol sdProtocol;
private HttpSdProtocol http_sd;
/**
* Monitoring configuration information using the public plc protocol
*/
@@ -21,7 +21,8 @@ package org.apache.hertzbeat.common.entity.job.protocol;
* Define common field method for each protocol in {@link org.apache.hertzbeat.common.entity.job.Metrics}
*/
public interface CommonRequestProtocol {
void setHost(String host);
void setPort(String port);
}
}
@@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
package org.apache.hertzbeat.common.entity.job.protocol;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -23,15 +23,13 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* sd monitor param
* Dns protocol
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SdMonitorParam {
private String detectedHost;
private String detectedPort;
private Param sdParam;
private Long bizId;
}
public class HttpSdProtocol implements Protocol {
private String url;
}
@@ -77,6 +77,10 @@ public class Monitor {
@Size(max = 100)
private String app;
@Schema(title = "Scrape type: static | http_sd", example = "static", accessMode = READ_WRITE)
@Size(max = 100)
private String scrape;
@Schema(title = "peer host: ipv4, ipv6, domain name", example = "192.167.25.11", accessMode = READ_WRITE)
@Size(max = 100)
@HostValid
@@ -17,7 +17,6 @@
package org.apache.hertzbeat.common.entity.manager;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -27,8 +26,6 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -60,18 +57,16 @@ public class MonitorBind {
@Schema(title = "primary id", example = "23")
private Long id;
@Schema(title = "collector name", example = "87432674384")
@Schema(title = "key string: ip:port")
private String keyStr;
@Schema(title = "connect bind id", example = "87432674384")
private Long bizId;
@Schema(title = "monitor ID", example = "87432674336")
@Column(name = "monitor_id")
private Long monitorId;
@Schema(title = "Bind type 0: sd", accessMode = READ_WRITE)
@Min(0)
@Max(3)
private byte type;
@Schema(title = "The creator of this record", example = "tom")
@CreatedBy
private String creator;
@@ -87,4 +82,4 @@ public class MonitorBind {
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
@LastModifiedDate
private LocalDateTime gmtUpdate;
}
}
@@ -1,67 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.sd;
import java.util.Arrays;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* service discovery common field
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServiceDiscoveryProtocol {
private Long id;
private List<Long> jobIdList;
/**
* sd type
*/
private Type type;
/**
* Target sd sources by which collector updates SDCache.
*/
private String sdSource;
/**
* sd Type
*/
public enum Type {
HTTP_SD("httpsd"),
;
private final String protocolName;
Type(String protocolName) {
this.protocolName = protocolName;
}
public static Type getType(String str) {
return Arrays.stream(Type.values()).filter(t -> t.toString().equalsIgnoreCase(str)).findFirst().orElse(null);
}
public String getProtocolName() {
return protocolName;
}
}
}
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.common.support.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.springframework.util.StringUtils;
/**
* Host Param Validator
@@ -33,10 +34,13 @@ public class HostParamValidator implements ConstraintValidator<HostValid, String
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value != null && value.toLowerCase().contains(HTTP)){
if (!StringUtils.hasText(value)) {
return true;
}
if (value.toLowerCase().contains(HTTP)){
value = value.replaceFirst(PATTERN_HTTP, BLANK);
}
if (value != null && value.toLowerCase().contains(HTTPS)){
if (value.toLowerCase().contains(HTTPS)){
value = value.replaceFirst(PATTERN_HTTPS, BLANK);
}
@@ -1,151 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.util;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.job.Configmap;
import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
import org.apache.hertzbeat.common.entity.manager.Param;
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
import org.apache.hertzbeat.common.entity.manager.Label;
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
/**
* Operator for service discovery
*/
public class SdMonitorOperator {
public static Optional<Param> getSdParam(List<Param> params) {
return params.stream()
.filter(param -> StringUtils.isNoneBlank(param.getField(), param.getParamValue()))
.filter(param -> Objects.nonNull(ServiceDiscoveryProtocol.Type.getType(param.getField())))
.findFirst();
}
public static List<Param> removeSdParam(List<Param> params) {
return params.stream()
.filter(param -> Objects.isNull(ServiceDiscoveryProtocol.Type.getType(param.getField())))
.collect(Collectors.toList());
}
public static List<Param> cloneParamList(List<Param> params) {
return params.stream()
.map(Param::clone)
.peek(param -> param.setId(null))
.collect(Collectors.toList());
}
public static MonitorBind buildSdSubMonitorBind(SdMonitorParam sdMonitorParam, long monitorId, Map<String, String> labels) {
if (Objects.isNull(sdMonitorParam.getBizId())) {
return null;
}
labels.put(CommonConstants.TAG_AUTO_CREATED, String.valueOf(sdMonitorParam.getBizId()));
return MonitorBind.builder()
.id(SnowFlakeIdGenerator.generateId())
.bizId(sdMonitorParam.getBizId())
.monitorId(monitorId)
.type(CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR)
.build();
}
public static MonitorBind buildSdMainMonitorBind(SdMonitorParam sdMonitorParam, long monitorId) {
if (Objects.isNull(sdMonitorParam.getSdParam())) {
return null;
}
return MonitorBind.builder()
.id(SnowFlakeIdGenerator.generateId())
.bizId(sdMonitorParam.getBizId())
.monitorId(monitorId)
.type(CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR)
.build();
}
public static Job constructSdJobAndTag(SdMonitorParam sdMonitorParam, Map<String, String> labels, Job appDefine) {
if (Objects.isNull(sdMonitorParam.getSdParam())) {
return appDefine;
}
labels.put(CommonConstants.TAG_SD_MAIN_MONITOR, ServiceDiscoveryProtocol.Type.getType(sdMonitorParam.getSdParam().getField()).name());
return constructSdJob(appDefine, sdMonitorParam.getSdParam());
}
public static Job constructSdJob(Job appDefine, Param sdParam) {
final Job sdJob = appDefine.clone();
final ServiceDiscoveryProtocol.Type sdType = ServiceDiscoveryProtocol.Type.getType(sdParam.getField());
List<Metrics> metricsList = Lists.newArrayList();
Map<String, String> i18n = Maps.newHashMap();
i18n.put("zh-CN", "监控目标");
i18n.put("en-US", "Monitor Target");
List<Metrics.Field> fields = Lists.newArrayList();
fields.add(Metrics.Field.builder()
.field("host")
.type(CommonConstants.TYPE_STRING)
.i18n(constructSdFieldI18n("主机", "host"))
.build());
fields.add(Metrics.Field.builder()
.field("port")
.type(CommonConstants.TYPE_STRING)
.i18n(constructSdFieldI18n("端口", "port"))
.build());
metricsList.add(Metrics.builder()
.name("target")
.fields(fields)
.i18n(i18n)
.protocol(sdType.getProtocolName())
.sdProtocol(ServiceDiscoveryProtocol.builder()
.sdSource(sdParam.getParamValue())
.type(sdType)
.build())
.build());
sdJob.setSd(Boolean.TRUE);
sdJob.setMetrics(metricsList);
sdJob.setConfigmap(Lists.newArrayList(new Configmap(sdParam.getField(), sdParam.getParamValue(), sdParam.getType())));
return sdJob;
}
public static List<Label> addMainMonitorTag(List<Label> tagList, Param sdParam) {
tagList = tagList.stream()
.filter(tag -> !StringUtils.equals(tag.getName(), CommonConstants.TAG_SD_MAIN_MONITOR))
.filter(tag -> !StringUtils.equals(tag.getName(), CommonConstants.TAG_AUTO_CREATED))
.collect(Collectors.toList());
tagList.add(Label.builder().name(CommonConstants.TAG_SD_MAIN_MONITOR)
.tagValue(ServiceDiscoveryProtocol.Type.getType(sdParam.getField()).name())
.type(CommonConstants.TAG_TYPE_AUTO_GENERATE)
.build());
return tagList;
}
private static Map<String, String> constructSdFieldI18n(String zh, String en) {
Map<String, String> i18n = Maps.newHashMap();
i18n.put("zh-CN", zh);
i18n.put("en-US", en);
return i18n;
}
}
@@ -53,13 +53,13 @@ class HostParamValidatorTest {
@Test
public void testIsValid() {
boolean result = hostParamValidator.isValid(null, context);
assertFalse(result);
assertTrue(result);
result = hostParamValidator.isValid("", context);
assertFalse(result);
assertTrue(result);
result = hostParamValidator.isValid(" ", context);
assertFalse(result);
assertTrue(result);
result = hostParamValidator.isValid("192.168.1.1", context);
assertTrue(result);
@@ -18,25 +18,22 @@
package org.apache.hertzbeat.manager.component.sd;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
import org.apache.hertzbeat.common.entity.manager.Param;
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.util.SdMonitorOperator;
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
import org.apache.hertzbeat.manager.dao.MonitorDao;
@@ -45,7 +42,6 @@ import org.apache.hertzbeat.manager.scheduler.ManagerWorkerPool;
import org.apache.hertzbeat.manager.service.MonitorService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* Service Discovery Worker
@@ -53,6 +49,9 @@ import org.springframework.util.CollectionUtils;
@Slf4j
@Component
public class ServiceDiscoveryWorker implements InitializingBean {
private static final String FILED_HOST = "host";
private static final String FILED_PORT = "port";
private final MonitorService monitorService;
private final ParamDao paramDao;
private final MonitorDao monitorDao;
@@ -83,74 +82,73 @@ public class ServiceDiscoveryWorker implements InitializingBean {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try (final CollectRep.MetricsData metricsData = dataQueue.pollServiceDiscoveryData()) {
Long monitorId = metricsData.getId();
final Monitor mainMonitor = monitorDao.findMonitorsByIdIn(Sets.newHashSet(monitorId)).get(0);
mainMonitor.getLabels().remove(CommonConstants.TAG_SD_MAIN_MONITOR);
final Monitor mainMonitor = monitorDao.findById(monitorId).orElse(null);
if (mainMonitor == null) {
log.warn("No monitor found for id {}", monitorId);
continue;
}
// collector
final Optional<CollectorMonitorBind> collectorBind = collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(mainMonitor.getId());
final Optional<CollectorMonitorBind> collectorBind = collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(monitorId);
String collector = collectorBind.map(CollectorMonitorBind::getCollector).orElse(null);
// param
List<Param> mainMonitorParamList = paramDao.findParamsByMonitorId(mainMonitor.getId());
mainMonitorParamList = SdMonitorOperator.removeSdParam(mainMonitorParamList);
final Set<Long> subMonitorIdSet = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR)
.stream()
.map(MonitorBind::getMonitorId)
.collect(Collectors.toSet());
final Map<String, List<Monitor>> hostMonitorMap = CollectionUtils.isEmpty(subMonitorIdSet)
? Maps.newHashMap()
: monitorDao.findMonitorsByIdIn(subMonitorIdSet).stream().collect(Collectors.groupingBy(Monitor::getHost));
// params
List<Param> mainMonitorParams = paramDao.findParamsByMonitorId(monitorId);
final Map<String, MonitorBind> subMonitorBindMap = monitorBindDao.findMonitorBindsByBizId(monitorId)
.stream().collect(Collectors.toMap(MonitorBind::getKeyStr, item -> item));
RowWrapper rowWrapper = metricsData.readRow();
Map<String, String> fieldsValue = Maps.newHashMapWithExpectedSize(8);
while (rowWrapper.hasNextRow()) {
rowWrapper = rowWrapper.nextRow();
final String host = rowWrapper.nextCell().getValue();
final String port = rowWrapper.nextCell().getValue();
final List<Monitor> monitorList = hostMonitorMap.get(host);
if (CollectionUtils.isEmpty(monitorList)) {
monitorService.addAndSaveMonitorJob(mainMonitor.clone(), SdMonitorOperator.cloneParamList(mainMonitorParamList), collector,
SdMonitorParam.builder()
.detectedHost(host)
.detectedPort(port)
.bizId(mainMonitor.getId())
.build(), null);
return;
fieldsValue.clear();
rowWrapper.cellStream().forEach(cell -> {
String value = cell.getValue();
fieldsValue.put(cell.getField().getName(), value);
});
final String host = fieldsValue.get(FILED_HOST);
final String port = fieldsValue.get(FILED_PORT);
final String keyStr = host + ":" + port;
if (subMonitorBindMap.containsKey(keyStr)) {
subMonitorBindMap.remove(keyStr);
continue;
}
for (Monitor monitor : monitorList) {
// make sure monitor that has the same host and port is not existed.
final Optional<Param> samePortParam = paramDao.findParamsByMonitorId(monitor.getId()).stream()
.filter(param -> StringUtils.equals(param.getField(), "port"))
.filter(param -> StringUtils.equals(param.getParamValue(), port))
.findFirst();
if (samePortParam.isEmpty()) {
monitorService.addAndSaveMonitorJob(mainMonitor.clone(), SdMonitorOperator.cloneParamList(mainMonitorParamList), collector,
SdMonitorParam.builder()
.detectedHost(host)
.detectedPort(port)
.bizId(mainMonitor.getId())
.build(), null);
} else {
monitorService.enableManageMonitors(Sets.newHashSet(monitor.getId()));
Monitor newMonitor = mainMonitor.clone();
newMonitor.setId(null);
newMonitor.setHost(host);
newMonitor.setName(newMonitor.getName() + "-" + host + ":" + port);
newMonitor.setScrape(CommonConstants.SCRAPE_STATIC);
newMonitor.setGmtCreate(LocalDateTime.now());
newMonitor.setGmtUpdate(LocalDateTime.now());
// replace host port
List<Param> newParams = new LinkedList<>();
for (Param param : mainMonitorParams) {
Param newParam = param.clone();
newParam.setId(null);
newParam.setGmtUpdate(null);
newParam.setGmtCreate(null);
if (FILED_HOST.equals(newParam.getField())) {
newParam.setParamValue(host);
} else if (FILED_PORT.equals(newParam.getField())) {
newParam.setParamValue(port);
}
newParams.add(newParam);
}
// make sure hostMonitorMap contains monitors that have not judged yet.
hostMonitorMap.remove(host);
monitorService.addMonitor(newMonitor, newParams, collector, null);
MonitorBind monitorBind = MonitorBind.builder()
.bizId(monitorId)
.monitorId(newMonitor.getId())
.keyStr(keyStr)
.build();
monitorBindDao.save(monitorBind);
}
// hostMonitorMap only contains monitors which are already existed but not in service discovery data
// due to monitors that coincide with service discovery data are removed.
// Thus, all monitors still in hostMonitorMap need to be cancelled.
final HashSet<Long> needCancelMonitorIdSet = Sets.newHashSet();
hostMonitorMap.forEach((key, value) -> needCancelMonitorIdSet.addAll(value.stream()
.map(Monitor::getId)
.collect(Collectors.toSet())));
final Set<Long> needCancelMonitorIdSet = subMonitorBindMap.values().stream()
.map(MonitorBind::getMonitorId).collect(Collectors.toSet());
monitorService.cancelManageMonitors(needCancelMonitorIdSet);
for (Long id : needCancelMonitorIdSet) {
monitorBindDao.deleteMonitorBindByBizIdAndMonitorId(monitorId, id);
}
} catch (Exception exception) {
log.error(exception.getMessage(), exception);
}
@@ -127,16 +127,4 @@ public class MonitorsController {
monitorService.importConfig(file);
return ResponseEntity.ok(Message.success("Import success"));
}
@PostMapping("/copy")
@Operation(summary = "copy monitors by ids", description = "copy monitors by ids")
public ResponseEntity<Message<Void>> duplicateMonitors(
@Parameter(description = "Monitor ID List", example = "6565463543") @RequestParam List<Long> ids
) {
if (ids != null && !ids.isEmpty()) {
monitorService.copyMonitors(ids);
}
return ResponseEntity.ok(Message.success("copy success"));
}
}
@@ -18,22 +18,20 @@
package org.apache.hertzbeat.manager.dao;
import java.util.List;
import java.util.Set;
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
/**
* AuthResources database operation
* MonitorBind database operation
*/
public interface MonitorBindDao extends JpaRepository<MonitorBind, Long>, JpaSpecificationExecutor<MonitorBind> {
List<MonitorBind> findAllByBizIdInAndType(Set<Long> bizIdList, byte type);
List<MonitorBind> findMonitorBindByBizIdAndType(Long bizId, byte type);
MonitorBind findMonitorBindByBizIdAndMonitorIdAndType(Long bizId, Long monitorId, byte type);
List<MonitorBind> findAllByType(byte type);
List<MonitorBind> findMonitorBindsByBizId(Long bizId);
void deleteByMonitorId(Long monitorId);
}
@Modifying
void deleteMonitorBindByBizIdAndMonitorId(Long bizId, Long monitorId);
}
@@ -36,12 +36,12 @@ import org.apache.hertzbeat.common.entity.manager.Param;
@Schema(description = "Monitoring information entities")
public class MonitorDto {
@Schema(description = "Monitor Content", accessMode = READ_WRITE)
@Schema(description = "monitor content", accessMode = READ_WRITE)
@NotNull
@Valid
private Monitor monitor;
@Schema(description = "Monitor Params", accessMode = READ_WRITE)
@Schema(description = "monitor params", accessMode = READ_WRITE)
@NotEmpty
@Valid
private List<Param> params;
@@ -19,10 +19,8 @@ package org.apache.hertzbeat.manager.scheduler;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.dto.CollectorInfo;
import org.apache.hertzbeat.common.entity.job.Configmap;
@@ -30,13 +28,10 @@ import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.manager.Collector;
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
import org.apache.hertzbeat.common.entity.manager.Param;
import org.apache.hertzbeat.common.entity.manager.ParamDefine;
import org.apache.hertzbeat.common.util.SdMonitorOperator;
import org.apache.hertzbeat.manager.dao.CollectorDao;
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
import org.apache.hertzbeat.manager.dao.MonitorDao;
import org.apache.hertzbeat.manager.dao.ParamDao;
import org.apache.hertzbeat.manager.service.AppService;
@@ -45,6 +40,7 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
/**
* scheduler init
@@ -77,9 +73,6 @@ public class SchedulerInit implements CommandLineRunner {
@Autowired
private CollectorMonitorBindDao collectorMonitorBindDao;
@Autowired
private MonitorBindDao monitorBindDao;
@Override
public void run(String... args) throws Exception {
@@ -98,20 +91,18 @@ public class SchedulerInit implements CommandLineRunner {
// init jobs
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndJobIdNotNull(List.of(CommonConstants.MONITOR_PAUSED_CODE));
List<CollectorMonitorBind> monitorBinds = collectorMonitorBindDao.findAll();
final Set<Long> sdMonitorIds = monitorBindDao.findAllByType(CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR).stream()
.map(MonitorBind::getBizId)
.collect(Collectors.toSet());
Map<Long, String> monitorIdCollectorMap = monitorBinds.stream().collect(
Collectors.toMap(CollectorMonitorBind::getMonitorId, CollectorMonitorBind::getCollector));
for (Monitor monitor : monitors) {
try {
// build collect job entity
Job appDefine = appService.getAppDefine(monitor.getApp());
List<Param> params = paramDao.findParamsByMonitorId(monitor.getId());
if (sdMonitorIds.contains(monitor.getId())) {
appDefine = SdMonitorOperator.constructSdJob(appDefine, SdMonitorOperator.getSdParam(params).get());
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
String app = isStatic ? monitor.getApp() : monitor.getScrape();
Job appDefine = appService.getAppDefine(app);
if (!isStatic) {
appDefine.setSd(true);
}
List<Param> params = paramDao.findParamsByMonitorId(monitor.getId());
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
}
@@ -129,7 +120,7 @@ public class SchedulerInit implements CommandLineRunner {
.map(param -> new Configmap(param.getField(), param.getParamValue(),
param.getType())).collect(Collectors.toList());
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
.toList();
paramDefaultValue.forEach(defaultVar -> {
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
@@ -22,14 +22,12 @@ import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.manager.Param;
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
import org.apache.hertzbeat.manager.pojo.dto.AppCount;
import org.apache.hertzbeat.manager.pojo.dto.MonitorDto;
import org.apache.hertzbeat.manager.support.exception.MonitorDetectException;
import org.springframework.data.domain.Page;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -125,14 +123,14 @@ public interface MonitorService {
*
* @param ids Monitoring ID List
*/
void cancelManageMonitors(HashSet<Long> ids);
void cancelManageMonitors(Set<Long> ids);
/**
* Start the managed monitoring items in batches according to the monitoring ID list
*
* @param ids Monitoring ID List
*/
void enableManageMonitors(HashSet<Long> ids);
void enableManageMonitors(Set<Long> ids);
/**
* Query the monitoring category and its corresponding monitoring quantity
@@ -183,22 +181,13 @@ public interface MonitorService {
*/
void importConfig(MultipartFile file) throws Exception;
/**
* Copy monitor in batches based on the id
*
* @param ids monitor id
*/
void copyMonitors(List<Long> ids);
/**
* update app collect job by app
*
* @param job job content
*/
void updateAppCollectJob(Job job);
void addAndSaveMonitorJob(Monitor monitor, List<Param> params, String collector, SdMonitorParam sdMonitorParam, GrafanaDashboard grafanaDashboard);
/**
* Copy monitor by id
*
@@ -270,10 +270,6 @@ public class AppServiceImpl implements AppService, InitializingBean {
public List<Hierarchy> getAllAppHierarchy(String lang) {
LinkedList<Hierarchy> hierarchies = new LinkedList<>();
for (var job : appDefines.values()) {
// TODO temporarily filter out push to solve the front-end problem, and open it after the subsequent design optimization
if (DispatchConstants.PROTOCOL_PUSH.equalsIgnoreCase(job.getApp())) {
continue;
}
queryAppHierarchy(lang, hierarchies, job);
}
return hierarchies;
@@ -23,7 +23,6 @@ import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.common.constants.CommonConstants;
@@ -34,23 +33,19 @@ import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
import org.apache.hertzbeat.common.entity.job.Configmap;
import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.CommonRequestProtocol;
import org.apache.hertzbeat.common.entity.manager.Collector;
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
import org.apache.hertzbeat.common.entity.manager.Param;
import org.apache.hertzbeat.common.entity.manager.ParamDefine;
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
import org.apache.hertzbeat.common.support.event.MonitorDeletedEvent;
import org.apache.hertzbeat.common.util.AesUtil;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.FileUtil;
import org.apache.hertzbeat.common.util.IntervalExpressionUtil;
import org.apache.hertzbeat.common.util.IpDomainUtil;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.SdMonitorOperator;
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
import org.apache.hertzbeat.grafana.service.DashboardService;
import org.apache.hertzbeat.manager.config.ManagerSseManager;
@@ -65,7 +60,6 @@ import org.apache.hertzbeat.manager.scheduler.CollectJobScheduling;
import org.apache.hertzbeat.manager.service.AppService;
import org.apache.hertzbeat.manager.service.ImExportService;
import org.apache.hertzbeat.manager.service.MonitorService;
import org.apache.hertzbeat.manager.service.LabelService;
import org.apache.hertzbeat.manager.support.exception.MonitorDatabaseException;
import org.apache.hertzbeat.manager.support.exception.MonitorDetectException;
import org.apache.hertzbeat.warehouse.service.WarehouseService;
@@ -81,15 +75,14 @@ import org.springframework.http.MediaType;
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 java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -109,15 +102,11 @@ public class MonitorServiceImpl implements MonitorService {
private static final Long MONITOR_ID_TMP = 1000000000L;
private static final byte ALL_MONITOR_STATUS = 9;
private static final int TAG_LENGTH = 2;
private static final String CONTENT_VALUE = MediaType.APPLICATION_OCTET_STREAM_VALUE + SignConstants.SINGLE_MARK + "charset=" + StandardCharsets.UTF_8;
private final Map<String, ImExportService> imExportServiceMap = new HashMap<>();
@Autowired
private AppService appService;
@Autowired
private LabelService tagService;
@Autowired
private CollectJobScheduling collectJobScheduling;
@Autowired
private MonitorDao monitorDao;
@@ -147,25 +136,74 @@ public class MonitorServiceImpl implements MonitorService {
@Override
@Transactional(readOnly = true)
public void detectMonitor(Monitor monitor, List<Param> params, String collector) throws MonitorDetectException {
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
if (sdParam.isPresent()) {
collectOneTimeSdData(monitor, collector, sdParam.get());
} else {
if (CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape())) {
detectMonitorDirectly(monitor, params, collector);
} else {
detectSdMonitor(monitor, params, collector);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addMonitor(Monitor monitor, List<Param> params, String collector, GrafanaDashboard grafanaDashboard) throws RuntimeException {
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
if (sdParam.isPresent()) {
addAndSaveMonitorJob(monitor.clone(), params, collector, SdMonitorParam.builder().sdParam(sdParam.get()).build(), grafanaDashboard);
return;
// Apply for monitor id
long monitorId = SnowFlakeIdGenerator.generateId();
Map<String, String> labels = monitor.getLabels();
if (labels == null) {
labels = new HashMap<>(8);
monitor.setLabels(labels);
}
// Construct the collection task Job entity
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
String app = isStatic ? monitor.getApp() : monitor.getScrape();
Job appDefine = appService.getAppDefine(app);
if (!isStatic) {
appDefine.setSd(true);
}
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
}
appDefine.setMonitorId(monitorId);
appDefine.setDefaultInterval(monitor.getIntervals());
appDefine.setCyclic(true);
appDefine.setTimestamp(System.currentTimeMillis());
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
appDefine.setMetadata(metadata);
appDefine.setLabels(monitor.getLabels());
appDefine.setAnnotations(monitor.getAnnotations());
List<Configmap> configmaps = params.stream().map(param -> {
param.setMonitorId(monitorId);
return new Configmap(param.getField(), param.getParamValue(), param.getType());
}).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
long jobId = collector == null ? collectJobScheduling.addAsyncCollectJob(appDefine, null) :
collectJobScheduling.addAsyncCollectJob(appDefine, collector);
try {
detectMonitor(monitor, params, collector);
} catch (Exception ignored) {}
addAndSaveMonitorJob(monitor, params, collector, null, grafanaDashboard);
try {
if (collector != null) {
CollectorMonitorBind collectorMonitorBind = CollectorMonitorBind.builder()
.collector(collector)
.monitorId(monitorId)
.build();
collectorMonitorBindDao.save(collectorMonitorBind);
}
monitor.setId(monitorId);
monitor.setJobId(jobId);
// create grafana dashboard
if (monitor.getApp().equals(CommonConstants.PROMETHEUS) && grafanaDashboard != null && grafanaDashboard.isEnabled()) {
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(), monitorId);
}
monitorDao.save(monitor);
paramDao.saveAll(params);
} catch (Exception e) {
log.error("Error while adding monitor: {}", e.getMessage(), e);
collectJobScheduling.cancelAsyncCollectJob(jobId);
throw new MonitorDatabaseException(e.getMessage());
}
}
@Override
@@ -233,7 +271,7 @@ public class MonitorServiceImpl implements MonitorService {
}
}
// the dispatch collector must exist if pin
if (StringUtils.isNotBlank(monitorDto.getCollector())) {
if (StringUtils.hasText(monitorDto.getCollector())) {
Optional<Collector> optionalCollector = collectorDao.findCollectorByName(monitorDto.getCollector());
if (optionalCollector.isEmpty()) {
throw new IllegalArgumentException("The pinned collector does not exist.");
@@ -250,7 +288,7 @@ public class MonitorServiceImpl implements MonitorService {
if (paramDefine.isRequired() && (param == null || param.getParamValue() == null)) {
throw new IllegalArgumentException("Params field " + field + " is required.");
}
if (param != null && param.getParamValue() != null && StringUtils.isNotBlank(param.getParamValue())) {
if (param != null && StringUtils.hasText(param.getParamValue())) {
switch (paramDefine.getType()) {
case "number":
double doubleValue;
@@ -376,7 +414,6 @@ public class MonitorServiceImpl implements MonitorService {
@Override
@Transactional(rollbackFor = Exception.class)
public void modifyMonitor(Monitor monitor, List<Param> params, String collector, GrafanaDashboard grafanaDashboard) throws RuntimeException {
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
long monitorId = monitor.getId();
// Check to determine whether the monitor corresponding to the monitor id exists
Optional<Monitor> queryOption = monitorDao.findById(monitorId);
@@ -393,36 +430,14 @@ public class MonitorServiceImpl implements MonitorService {
labels = new HashMap<>(8);
monitor.setLabels(labels);
}
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
if (preMonitor.getStatus() != CommonConstants.MONITOR_PAUSED_CODE) {
// Construct the collection task Job entity
Job appDefine = appService.getAppDefine(monitor.getApp());
if (sdParam.isPresent()) {
// not allow to modify a sub monitor to main monitor
labels.entrySet()
.stream()
.filter(label -> org.apache.commons.lang3.StringUtils.equals(label.getKey(), CommonConstants.TAG_AUTO_CREATED))
.findFirst()
.ifPresent(label -> {
final MonitorBind isSubMonitorBefore = monitorBindDao.findMonitorBindByBizIdAndMonitorIdAndType(Long.valueOf(label.getValue()),
monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
if (Objects.nonNull(isSubMonitorBefore)) {
throw new UnsupportedOperationException("Can not change a auto-created monitor to sd! ");
}
});
// create main monitor bind
final List<MonitorBind> mainMonitorBind = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR);
if (CollectionUtils.isEmpty(mainMonitorBind)) {
monitorBindDao.save(Objects.requireNonNull(
SdMonitorOperator.buildSdMainMonitorBind(
SdMonitorParam.builder().sdParam(sdParam.get()).build(), monitorId
)));
}
appDefine = SdMonitorOperator.constructSdJob(appDefine, sdParam.get());
labels.put(CommonConstants.TAG_SD_MAIN_MONITOR, ServiceDiscoveryProtocol.Type.getType(sdParam.get().getField()).name());
String app = isStatic ? monitor.getApp() : monitor.getScrape();
Job appDefine = appService.getAppDefine(app);
if (!isStatic) {
appDefine.setSd(true);
}
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
}
@@ -450,8 +465,7 @@ public class MonitorServiceImpl implements MonitorService {
// execute only in non paused status
try {
detectMonitor(monitor, params, collector);
} catch (Exception ignored) {
}
} catch (Exception ignored) {}
}
// After the update is successfully released, refresh the database
@@ -474,12 +488,6 @@ public class MonitorServiceImpl implements MonitorService {
}
}
monitorDao.save(monitor);
// delete all sub monitor if sd param is removed
final List<MonitorBind> subMonitorBindList = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
if (!CollectionUtils.isEmpty(subMonitorBindList) && sdParam.isEmpty()) {
deleteMonitors(subMonitorBindList.stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet()));
}
paramDao.saveAll(params);
} catch (Exception e) {
log.error(e.getMessage(), e);
@@ -501,12 +509,6 @@ public class MonitorServiceImpl implements MonitorService {
if (CollectionUtils.isEmpty(ids)) {
return;
}
final List<MonitorBind> allSubMonitorBind = monitorBindDao.findAllByBizIdInAndType(ids, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
if (!CollectionUtils.isEmpty(allSubMonitorBind)) {
ids.addAll(allSubMonitorBind.stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet()));
}
List<Monitor> monitors = monitorDao.findMonitorsByIdIn(ids);
if (!monitors.isEmpty()) {
monitorDao.deleteAll(monitors);
@@ -537,7 +539,9 @@ public class MonitorServiceImpl implements MonitorService {
monitorDto.setMetrics(metrics);
monitorDto.setGrafanaDashboard(dashboardService.getDashboardByMonitorId(id));
} else {
Job job = appService.getAppDefine(monitor.getApp());
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
String type = isStatic ? monitor.getApp() : monitor.getScrape();
Job job = appService.getAppDefine(type);
List<String> metrics = job.getMetrics().stream()
.filter(Metrics::isVisible)
.map(Metrics::getName).collect(Collectors.toList());
@@ -563,7 +567,7 @@ public class MonitorServiceImpl implements MonitorService {
}
andList.add(inPredicate);
}
if (StringUtils.isNotBlank(app)) {
if (StringUtils.hasText(app)) {
Predicate predicateApp = criteriaBuilder.equal(root.get("app"), app);
andList.add(predicateApp);
}
@@ -575,17 +579,17 @@ public class MonitorServiceImpl implements MonitorService {
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
List<Predicate> orList = new ArrayList<>();
if (StringUtils.isNotBlank(search)) {
if (StringUtils.hasText(search)) {
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + search + "%");
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
if (StringUtils.isNumeric(search)){
if (CommonUtil.isNumeric(search)){
Predicate predicateId = criteriaBuilder.equal(root.get("id"), Long.parseLong(search));
orList.add(predicateId);
}
orList.add(predicateHost);
orList.add(predicateName);
}
if (StringUtils.isNotBlank(labels)) {
if (StringUtils.hasText(labels)) {
String[] labelAres = labels.split(",");
for (String label : labelAres) {
String[] labelArr = label.split(":");
@@ -620,18 +624,10 @@ public class MonitorServiceImpl implements MonitorService {
}
@Override
public void cancelManageMonitors(HashSet<Long> ids) {
public void cancelManageMonitors(Set<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
final Set<Long> subMonitorIds = monitorBindDao.findAllByBizIdInAndType(ids, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR).stream()
.map(MonitorBind::getMonitorId)
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(subMonitorIds)) {
ids.addAll(subMonitorIds);
}
// Update monitoring status Delete corresponding monitoring periodic task
// The jobId is not deleted, and the jobId is reused again after the management is started.
List<Monitor> managedMonitors = monitorDao.findMonitorsByIdIn(ids)
@@ -648,7 +644,7 @@ public class MonitorServiceImpl implements MonitorService {
}
@Override
public void enableManageMonitors(HashSet<Long> ids) {
public void enableManageMonitors(Set<Long> ids) {
// Update monitoring status Add corresponding monitoring periodic task
List<Monitor> unManagedMonitors = monitorDao.findMonitorsByIdIn(ids)
.stream().filter(monitor ->
@@ -662,20 +658,12 @@ public class MonitorServiceImpl implements MonitorService {
for (Monitor monitor : unManagedMonitors) {
// Construct the collection task Job entity
List<Param> params = paramDao.findParamsByMonitorId(monitor.getId());
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
Job appDefine = appService.getAppDefine(monitor.getApp());
if (sdParam.isPresent()) {
final List<MonitorBind> mainMonitorBind = monitorBindDao.findMonitorBindByBizIdAndType(monitor.getId(), CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR);
if (CollectionUtils.isEmpty(mainMonitorBind)) {
monitorBindDao.save(Objects.requireNonNull(
SdMonitorOperator.buildSdMainMonitorBind(
SdMonitorParam.builder().sdParam(sdParam.get()).build(), monitor.getId()
)));
}
appDefine = SdMonitorOperator.constructSdJob(appDefine, sdParam.get());
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
String app = isStatic ? monitor.getApp() : monitor.getScrape();
Job appDefine = appService.getAppDefine(app);
if (!isStatic) {
appDefine.setSd(true);
}
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
}
@@ -691,7 +679,7 @@ public class MonitorServiceImpl implements MonitorService {
List<Configmap> configmaps = params.stream().map(param ->
new Configmap(param.getField(), param.getParamValue(), param.getType())).collect(Collectors.toList());
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
.toList();
paramDefaultValue.forEach(defaultVar -> {
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
@@ -752,25 +740,6 @@ public class MonitorServiceImpl implements MonitorService {
}).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void copyMonitors(List<Long> ids) {
ids.stream().parallel().forEach(id -> {
// get monitor and Params according id
Optional<Monitor> monitorOpt = monitorDao.findById(id);
List<Param> params = paramDao.findParamsByMonitorId(id);
monitorOpt.ifPresentOrElse(monitor -> {
// deep copy original monitor to achieve persist in JPA
Monitor newMonitor = JsonUtil.fromJson(JsonUtil.toJson(monitor), Monitor.class);
if (newMonitor != null) {
copyMonitor(newMonitor, params);
}
}, () -> log.warn("can not find the monitor for id {}", id));
});
}
@Override
public void updateAppCollectJob(Job job) {
List<Monitor> monitors = monitorDao.findMonitorsByAppEquals(job.getApp())
@@ -807,7 +776,7 @@ public class MonitorServiceImpl implements MonitorService {
List<Configmap> configmaps = params.stream().map(param -> new Configmap(param.getField(),
param.getParamValue(), param.getType())).collect(Collectors.toList());
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
.toList();
paramDefaultValue.forEach(defaultVar -> {
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
@@ -827,80 +796,7 @@ public class MonitorServiceImpl implements MonitorService {
}
}
}
@Override
public void addAndSaveMonitorJob(Monitor monitor, List<Param> params, String collector, SdMonitorParam sdMonitorParam, GrafanaDashboard grafanaDashboard) {
// Apply for monitor id
long monitorId = SnowFlakeIdGenerator.generateId();
Map<String, String> labels = monitor.getLabels();
if (labels == null) {
labels = new HashMap<>(8);
monitor.setLabels(labels);
}
MonitorBind monitorBind = null;
// Construct the collection task Job entity
Job appDefine = appService.getAppDefine(monitor.getApp());
if (Objects.nonNull(sdMonitorParam)) {
monitorBind = Objects.isNull(sdMonitorParam.getSdParam())
? SdMonitorOperator.buildSdSubMonitorBind(sdMonitorParam, monitorId, labels)
: SdMonitorOperator.buildSdMainMonitorBind(sdMonitorParam, monitorId);
appDefine = SdMonitorOperator.constructSdJobAndTag(sdMonitorParam, labels, appDefine);
}
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
}
appDefine.setMonitorId(monitorId);
appDefine.setDefaultInterval(monitor.getIntervals());
appDefine.setCyclic(true);
appDefine.setTimestamp(System.currentTimeMillis());
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
appDefine.setMetadata(metadata);
appDefine.setLabels(monitor.getLabels());
appDefine.setAnnotations(monitor.getAnnotations());
resetMetricsCommonField(monitor, appDefine, sdMonitorParam);
List<Configmap> configmaps = params.stream().map(param -> {
param.setMonitorId(monitorId);
return new Configmap(param.getField(), param.getParamValue(), param.getType());
}).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
long jobId = collector == null ? collectJobScheduling.addAsyncCollectJob(appDefine, null) :
collectJobScheduling.addAsyncCollectJob(appDefine, collector);
try {
detectMonitor(monitor, params, collector);
} catch (Exception ignored) {
}
try {
if (collector != null) {
CollectorMonitorBind collectorMonitorBind = CollectorMonitorBind.builder()
.collector(collector)
.monitorId(monitorId)
.build();
collectorMonitorBindDao.save(collectorMonitorBind);
}
monitor.setId(monitorId);
monitor.setJobId(jobId);
// create grafana dashboard
if (monitor.getApp().equals(CommonConstants.PROMETHEUS) && grafanaDashboard != null && grafanaDashboard.isEnabled()) {
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(), monitorId);
}
monitorDao.save(monitor);
if (Objects.nonNull(monitorBind)) {
monitorBindDao.save(monitorBind);
}
paramDao.saveAll(params);
} catch (Exception e) {
log.error("Error while adding monitor: {}", e.getMessage(), e);
collectJobScheduling.cancelAsyncCollectJob(jobId);
throw new MonitorDatabaseException(e.getMessage());
}
}
@Override
public Monitor getMonitor(Long monitorId) {
return monitorDao.findById(monitorId).orElse(null);
@@ -916,73 +812,69 @@ public class MonitorServiceImpl implements MonitorService {
return monitorDao.findMonitorsByAppEquals(app);
}
private void resetMetricsCommonField(Monitor monitor, Job appDefine, SdMonitorParam sdMonitorParam) {
if (Objects.isNull(sdMonitorParam)
|| org.apache.commons.lang3.StringUtils.isAnyBlank(sdMonitorParam.getDetectedHost(), sdMonitorParam.getDetectedPort())) {
return;
@Override
@Transactional(rollbackFor = Exception.class)
public void copyMonitor(Long id) {
// Get the source monitor information
Optional<Monitor> monitorOptional = monitorDao.findById(id);
if (monitorOptional.isEmpty()) {
throw new IllegalArgumentException("Monitor not found: " + id);
}
for (Metrics metric : appDefine.getMetrics()) {
Arrays.stream(metric.getClass().getDeclaredFields())
.filter(field -> metric.getProtocol().equalsIgnoreCase(field.getName()))
.findFirst()
.ifPresent(field -> {
field.setAccessible(true);
final Object obj;
try {
obj = field.get(metric);
if (obj instanceof CommonRequestProtocol protocol) {
protocol.setHost(sdMonitorParam.getDetectedHost());
protocol.setPort(sdMonitorParam.getDetectedPort());
}
} catch (IllegalAccessException exception) {
log.warn("Not such field {}", field.getName());
}
});
Monitor sourceMonitor = monitorOptional.get();
// Get the parameters of source monitor
List<Param> sourceParams = paramDao.findParamsByMonitorId(id);
// Create new monitor object
Monitor newMonitor = new Monitor();
// Copy basic properties, exclude ID, jobId and status
BeanUtils.copyProperties(sourceMonitor, newMonitor, "id", "jobId", "status");
// Set new name
newMonitor.setName(sourceMonitor.getName() + "_copy");
// Set initial status
newMonitor.setStatus(CommonConstants.MONITOR_UP_CODE);
// Set create and update time
newMonitor.setGmtCreate(LocalDateTime.now());
newMonitor.setGmtUpdate(LocalDateTime.now());
// Copy parameters
List<Param> newParams = new ArrayList<>();
if (!sourceParams.isEmpty()) {
for (Param sourceParam : sourceParams) {
Param newParam = new Param();
BeanUtils.copyProperties(sourceParam, newParam, "id");
newParam.setMonitorId(newMonitor.getId());
newParams.add(newParam);
}
}
monitor.setHost(sdMonitorParam.getDetectedHost());
Map<String, String> labels = monitor.getLabels();
if (labels == null) {
labels = new HashMap<>(8);
monitor.setLabels(labels);
}
labels.put(CommonConstants.TAG_MONITOR_HOST, sdMonitorParam.getDetectedHost());
monitor.setName(monitor.getApp().toUpperCase() + CommonConstants.LOCALE_SEPARATOR + sdMonitorParam.getDetectedHost()
+ CommonConstants.LOCALE_SEPARATOR + SnowFlakeIdGenerator.generateId());
addMonitor(newMonitor, newParams, null, null);
}
private void copyMonitor(Monitor monitor, List<Param> params) {
monitor.setName(String.format("%s - copy", monitor.getName()));
addMonitor(monitor, params, null, null);
}
private void collectOneTimeSdData(Monitor monitor, String collector, Param sdParam) {
private void detectSdMonitor(Monitor monitor, List<Param> params, String collector) {
Long monitorId = monitor.getId();
if (monitorId == null || monitorId == 0) {
monitorId = MONITOR_ID_TMP;
}
Job appDefine = appService.getAppDefine(monitor.getApp());
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
if (monitor.getScrape() == null || params == null || params.isEmpty()) {
throw new IllegalArgumentException("scrape params is null or empty!");
}
Job appDefine = appService.getAppDefine(monitor.getScrape());
appDefine.setMonitorId(monitorId);
appDefine.setCyclic(false);
appDefine.setTimestamp(System.currentTimeMillis());
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName());
appDefine.setMetadata(metadata);
appDefine.setLabels(monitor.getLabels());
appDefine.setAnnotations(monitor.getAnnotations());
final Job sdJob = SdMonitorOperator.constructSdJob(appDefine, sdParam);
List<Configmap> configmaps = params.stream().map(param ->
new Configmap(param.getField(), param.getParamValue(), param.getType())).collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
appDefine.setSd(true);
List<CollectRep.MetricsData> collectRep;
if (collector != null) {
collectRep = collectJobScheduling.collectSyncJobData(sdJob, collector);
collectRep = collectJobScheduling.collectSyncJobData(appDefine, collector);
} else {
collectRep = collectJobScheduling.collectSyncJobData(sdJob);
collectRep = collectJobScheduling.collectSyncJobData(appDefine);
}
monitor.setStatus(CommonConstants.MONITOR_UP_CODE);
// If the detection result fails, a detection exception is thrown
if (collectRep == null || collectRep.isEmpty()) {
monitor.setStatus(CommonConstants.MONITOR_DOWN_CODE);
@@ -1038,95 +930,4 @@ public class MonitorServiceImpl implements MonitorService {
}
collectRep.forEach(CollectRep.MetricsData::close);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void copyMonitor(Long id) {
// Get the source monitor information
Optional<Monitor> monitorOptional = monitorDao.findById(id);
if (monitorOptional.isEmpty()) {
throw new IllegalArgumentException("Monitor not found: " + id);
}
Monitor sourceMonitor = monitorOptional.get();
// Get the parameters of source monitor
List<Param> sourceParams = paramDao.findParamsByMonitorId(id);
// Create new monitor object
Monitor newMonitor = new Monitor();
// Copy basic properties, exclude ID, jobId and status
BeanUtils.copyProperties(sourceMonitor, newMonitor, "id", "jobId", "status");
// Set new name
newMonitor.setName(sourceMonitor.getName() + "_copy");
// Set initial status
newMonitor.setStatus(CommonConstants.MONITOR_UP_CODE);
// Set create and update time
newMonitor.setGmtCreate(LocalDateTime.now());
newMonitor.setGmtUpdate(LocalDateTime.now());
// Generate new ID using snowflake algorithm
newMonitor.setId(SnowFlakeIdGenerator.generateId());
// Save new monitor
newMonitor = monitorDao.save(newMonitor);
// Ensure ID is set
if (newMonitor.getId() == null) {
throw new RuntimeException("Failed to generate monitor ID");
}
// Copy parameters
if (!sourceParams.isEmpty()) {
List<Param> newParams = new ArrayList<>();
for (Param sourceParam : sourceParams) {
Param newParam = new Param();
BeanUtils.copyProperties(sourceParam, newParam, "id");
newParam.setMonitorId(newMonitor.getId());
newParams.add(newParam);
}
paramDao.saveAll(newParams);
}
try {
// Build collect job
Job appDefine = appService.getAppDefine(newMonitor.getApp());
if (CommonConstants.PROMETHEUS.equals(newMonitor.getApp())) {
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + newMonitor.getName());
}
// Ensure using correct monitor ID
appDefine.setMonitorId(newMonitor.getId());
appDefine.setDefaultInterval(newMonitor.getIntervals());
appDefine.setCyclic(true);
appDefine.setTimestamp(System.currentTimeMillis());
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, newMonitor.getName(),
CommonConstants.LABEL_INSTANCE_HOST, newMonitor.getHost());
appDefine.setMetadata(metadata);
appDefine.setLabels(newMonitor.getLabels());
appDefine.setAnnotations(newMonitor.getAnnotations());
List<Configmap> configmaps = sourceParams.stream()
.map(param -> new Configmap(param.getField(), param.getParamValue(), param.getType()))
.collect(Collectors.toList());
appDefine.setConfigmap(configmaps);
// Get collector configuration from source monitor
Optional<CollectorMonitorBind> bindOptional =
collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(sourceMonitor.getId());
String collector = bindOptional.map(CollectorMonitorBind::getCollector).orElse(null);
// Dispatch collect job
long jobId = collectJobScheduling.addAsyncCollectJob(appDefine, collector);
newMonitor.setJobId(jobId);
monitorDao.save(newMonitor);
// Copy collector binding if exists
if (collector != null) {
CollectorMonitorBind newBind = CollectorMonitorBind.builder()
.collector(collector)
.monitorId(newMonitor.getId())
.build();
collectorMonitorBindDao.save(newBind);
}
} catch (Exception e) {
log.error("Create collect job error: {}", e.getMessage(), e);
throw new RuntimeException("Create collect job failed: " + e.getMessage());
}
}
}
@@ -0,0 +1,62 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The monitoring type categoryservice-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
category: __system__
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
app: http_sd
# The app api i18n name
name:
zh-CN: Http Service Discovery
en-US: Http Service Discovery
# Input params define for app api(render web ui by the definition)
params:
# field-param field key
- field: __sd_url__
# name-param field display i18n name
name:
zh-CN: 服务发现地址
en-US: Service Discovery Url
# type-param field type(most mapping the html input type)
type: text
# required-true or false
required: true
metrics:
- name: target
i18n:
zh-CN: 监控目标
en-US: Monitor Target
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
priority: 0
# collect metrics content
fields:
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: host
type: 1
i18n:
zh-CN: Host
en-US: Host
- field: port
type: 1
i18n:
zh-CN: Port
en-US: Port
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http_sd
# the config content when protocol is http_sd
http_sd:
url: ^_^__sd_url__^_^
@@ -14,7 +14,7 @@
# limitations under the License.
# The monitoring type categoryservice-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
category: service
category: __system__
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
app: push
# The app api i18n name
@@ -145,15 +145,4 @@ class MonitorsControllerTest {
.andExpect(jsonPath("$.code").value("0"))
.andExpect(jsonPath("$.msg").value("Import success"));
}
@Test
void duplicateMonitors() throws Exception {
// Mock the behavior of monitorService.copyMonitors
doNothing().when(monitorService).copyMonitors(List.of(6565463543L));
// Perform the POST request and verify the response
this.mockMvc.perform(MockMvcRequestBuilders.post("/api/monitors/copy")
.param("ids", "6565463543"))
.andExpect(status().isOk());
}
}
@@ -744,7 +744,7 @@ class MonitorServiceTest {
List<Param> params = Collections.singletonList(new Param());
when(monitorDao.findById(1L)).thenReturn(Optional.of(monitor));
when(paramDao.findParamsByMonitorId(1L)).thenReturn(params);
assertDoesNotThrow(() -> monitorService.copyMonitors(List.of(1L)));
assertDoesNotThrow(() -> monitorService.copyMonitor(1L));
}
}
@@ -1,103 +1,94 @@
# HertzBeat 升级指导-(Docker Mode)
# HertzBeat Upgrade Guide (Docker Mode)
## Docker 方式升级 - Mysql 数据库
## Docker-based Upgrade
1. 数据备份
### 1. Data Backup
- 备份数据库,将 mysql 数据手动做备份,按需备份
- **Back up the database**: Manually back up MySQL data as needed.
```bash
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <库名> hertzbeat_backup-`date +%Y-%m-%d`.sql #单库备份
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" > hertzbeat_backup-`date +%Y-%m-%d`.sql # 整库备份
```
```bash
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <DB_NAME> > hertzbeat_backup-`date +%Y-%m-%d`.sql # Single database
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" --all-databases > hertzbeat_backup-`date +%Y-%m-%d`.sql # Full database
```
- 备份配置文件及数据目录
- **Back up configuration files and data directory**:
```bash
mv application.yml application-bak.yml && mv sureness-bak.yml
cp -R data data-`date +%Y-%m-%d`. bak
```
```bash
mv application.yml application-bak.yml && mv sureness.yml sureness-bak.yml
cp -R data data-`date +%Y-%m-%d`.bak
```
2. 关闭 并移除 HertzBeat 容器
### 2. Stop and Remove the HertzBeat Container
```shell
docker stop hertzbeat && docker rm hertzbeat
```
```bash
docker stop hertzbeat && docker rm hertzbeat
```
3. 升级数据库脚本
### 3. Upgrade Database Schema
打开[https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration) 选择你使用的数据库的目录下相应的 `V160__update_column.sql`文件在 Mysql 执行升级 sql。
Navigate to [HertzBeat GitHub Migration Scripts](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration), select the appropriate `V160__update_column.sql` file under your database type (e.g., MySQL), and execute it in MySQL.
4. 更换镜像重新启动 HertzBeat 容器
### 4. Restart HertzBeat with the New Image
```bash
$ docker run -d -p 1157:1157 -p 1158:1158 \
-v $(pwd)/data:/opt/hertzbeat/data \
-v $(pwd)/logs:/opt/hertzbeat/logs \
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
--restart=always \
--name hertzbeat apache/hertzbeat:v1.7.0
```
```bash
docker run -d -p 1157:1157 -p 1158:1158 \
-v $(pwd)/data:/opt/hertzbeat/data \
-v $(pwd)/logs:/opt/hertzbeat/logs \
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
--restart=always \
--name hertzbeat apache/hertzbeat:v1.7.0
```
5. 升级配置文件
### 5. Update Configuration Files
参考备份配置根据自己的需求基础上进行修改。
Modify the backup configurations as needed:
- `application.yml`一般需要修改以下部分
- **`application.yml`** (Typical modifications):
默认为:
```yaml
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
hikari:
max-lifetime: 120000
jpa:
show-sql: false
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
database: mysql
properties:
eclipselink:
logging:
level: SEVERE
```
```yaml
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
hikari:
max-lifetime: 120000
- **`sureness.yml`** (Optional, modify for account/password changes):
jpa:
show-sql: false
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
database: mysql
properties:
eclipselink:
logging:
level: SEVERE
```
```yaml
account:
- appId: admin
credential: hertzbeat
role: [admin]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
- appId: lili
credential: 94C6B34E7A199A9F9D4E1F208093B489
salt: 123
role: [user]
```
- `sureness.yml`修改是可选的,一般在你需要修改账号密码时
### 6. Add Database Drivers
```yaml
# account info config
# eg: admin has role [admin,user], password is hertzbeat
# eg: tom has role [user], password is hertzbeat
# eg: lili has role [guest], plain password is lili, salt is 123, salted password is 1A676730B0C7F54654B0E09184448289
account:
- appId: admin
credential: hertzbeat
role: [admin]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
- appId: lili
# credential = MD5(password + salt)
# plain password: hertzbeat
# attention: digest authentication does not support salted encrypted password accounts
credential: 94C6B34E7A199A9F9D4E1F208093B489
salt: 123
role: [user]
```
Due to Apache Foundations license compliance requirements, HertzBeat cannot include GPL-licensed dependencies (e.g., MySQL, Oracle). Users must manually download drivers and place them in the `ext-lib` directory, then mount it to `/opt/hertzbeat/ext-lib`:
6. 添加相应的数据库驱动
由于 apache 基金会对于 license 合规的要求,HertzBeat 的安装包不能包含 mysqloracle 等 gpl 许可的依赖,需要用户自行添加,用户可通过以下链接自行下载驱动 jar 放到本地 `ext-lib`目录下,然后启动时将`ext-lib`挂载到容器的 `/opt/hertzbeat/ext-lib`目录。
mysql[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
oracle(如果你要监控 oracle,这两个驱动是必须的):
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
- **MySQL Driver**: [Download MySQL Connector/J 8.0.25](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip)
- **Oracle Driver** (Required for Oracle monitoring):
- [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
- [orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
@@ -1,43 +1,41 @@
# HertzBeat 从 1.6.1 版本升级到 1.7.0 版本指引-(Helm Mode)
# HertzBeat Upgrade Guide from v1.6.1 to v1.7.0 (Helm Mode)
## 1. 前置准备
1. 确保已安装以下工具:
## 1. Prerequisites
1. Ensure the following tools are installed:
- Helm 3.x
- kubectl
- Git (可选)
- Git (optional)
2. 确认当前部署信息:
2. Verify current deployment information:
```bash
helm list -n <your-namespace>
# 如果你老版本的chart包不见了,可以使用以下命令导出values.yaml文件
# If the old chart package is missing, export values.yaml with:
helm get values hertzbeat -n <your-namespace> > hertzbeat-1.6.1-values.yaml
```
3. 数据备份
3. Data Backup:
> 1. 若使用了自定义监控模版
> **For custom monitoring templates:**
>
> - 需要备份 `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define` 当前运行 pod里面的 `/opt/hertzbeat/define` 目录到当前主机下,如果做了持久化 请拷贝持久化目录
> - `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define`
> - Backup `/opt/hertzbeat/define` from the running pod:
>
> 2. 若使用外置关系型数据库 Mysql, PostgreSQL数据
> ```bash
> kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define
> ```
>
> - 一般使用helm部署部署都做了持久化,可以选择拷贝持久化目录,也可以通过mysqldump、pgdump等工具完成备份
> **For external databases (MySQL/PostgreSQL):**
>
> ```bash
> kubectl get pvc -n hertzbeat
> NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
> hertzbeat-database Bound pvc-c63cf479-0033-423b-8466-eb00aa181657 4Gi RWO standard 68d
> hertzbeat-ext-lib Bound pvc-31fee163-1211-424f-8966-e3e805c23ff5 1Gi RWX standard 68d
> hertzbeat-tsdb Bound pvc-4f2ef614-0302-4a4c-8dd4-e68b34e9061c 4Gi RWO standard 68d
> ```
> - Use `mysqldump`/`pg_dump` or copy PVC directories:
>
> ```bash
> kubectl get pvc -n hertzbeat
> ```
## 2. 升级步骤
## 2. Upgrade Steps
### 1. 拉取最新Chart到本地
### 1. Pull the latest Chart
```bash
helm repo update
@@ -45,33 +43,32 @@ helm pull hertzbeat/hertzbeat --version 1.7.0 --untar
cd hertzbeat
```
或者从GitHub仓库获取(按需修改Chart):
Or clone from GitHub:
```bash
git clone https://github.com/hertzbeat/helm-charts.git
cd helm-charts/charts/hertzbeat
```
### 2. 修改values.yaml
### 2. Update values.yaml
比较并合并您的自定义配置到新版本values.yaml:
Compare and merge configurations:
```bash
# 使用diff工具比较新旧values文件
diff -u ../hertzbeat-1.6.1-values.yaml values.yaml
vimdiff 对着修改,改完继续diff,无输出则正常
# Use vimdiff to compare and merge changes
```
常见需要关注的配置项:
Key configurations to check:
- 镜像版本: `image.tag`
- 资源限制: `resources`
- 持久化配置: `persistence`
- 服务类型: `service.type`
- Ingress配置
- 数据库配置(如果使用外部数据库)
- `image.tag`
- `resources`
- `persistence`
- `service.type`
- Ingress settings
- External database configurations
### 3. 测试升级(干运行)
### 3. Dry-run Upgrade
```bash
helm upgrade hertzbeat . -n <your-namespace> \
@@ -80,24 +77,19 @@ helm upgrade hertzbeat . -n <your-namespace> \
--debug
```
### 4. 执行升级
### 4. Execute Upgrade
```bash
helm upgrade hertzbeat . -n <your-namespace> \
--values values.yaml \
--atomic \ # 升级失败自动回滚
--timeout 10m # 设置超时时间
--atomic \ # Auto-rollback on failure
--timeout 10m # Set timeout
```
### 5. 验证升级
### 5. Verify Upgrade
```bash
# 检查发布状态
helm status hertzbeat -n <your-namespace>
# 检查Pod状态
kubectl get pods -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat
# 检查日志
kubectl logs -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat --tail=100
```
@@ -1,94 +0,0 @@
# HertzBeat Upgrade Guide (Docker Mode)
## Docker-based Upgrade
### 1. Data Backup
- **Back up the database**: Manually back up MySQL data as needed.
```bash
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <DB_NAME> > hertzbeat_backup-`date +%Y-%m-%d`.sql # Single database
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" --all-databases > hertzbeat_backup-`date +%Y-%m-%d`.sql # Full database
```
- **Back up configuration files and data directory**:
```bash
mv application.yml application-bak.yml && mv sureness.yml sureness-bak.yml
cp -R data data-`date +%Y-%m-%d`.bak
```
### 2. Stop and Remove the HertzBeat Container
```bash
docker stop hertzbeat && docker rm hertzbeat
```
### 3. Upgrade Database Schema
Navigate to [HertzBeat GitHub Migration Scripts](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration), select the appropriate `V160__update_column.sql` file under your database type (e.g., MySQL), and execute it in MySQL.
### 4. Restart HertzBeat with the New Image
```bash
docker run -d -p 1157:1157 -p 1158:1158 \
-v $(pwd)/data:/opt/hertzbeat/data \
-v $(pwd)/logs:/opt/hertzbeat/logs \
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
--restart=always \
--name hertzbeat apache/hertzbeat:v1.7.0
```
### 5. Update Configuration Files
Modify the backup configurations as needed:
- **`application.yml`** (Typical modifications):
```yaml
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
hikari:
max-lifetime: 120000
jpa:
show-sql: false
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
database: mysql
properties:
eclipselink:
logging:
level: SEVERE
```
- **`sureness.yml`** (Optional, modify for account/password changes):
```yaml
account:
- appId: admin
credential: hertzbeat
role: [admin]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
- appId: lili
credential: 94C6B34E7A199A9F9D4E1F208093B489
salt: 123
role: [user]
```
### 6. Add Database Drivers
Due to Apache Foundations license compliance requirements, HertzBeat cannot include GPL-licensed dependencies (e.g., MySQL, Oracle). Users must manually download drivers and place them in the `ext-lib` directory, then mount it to `/opt/hertzbeat/ext-lib`:
- **MySQL Driver**: [Download MySQL Connector/J 8.0.25](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip)
- **Oracle Driver** (Required for Oracle monitoring):
- [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
- [orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
@@ -1,95 +0,0 @@
# HertzBeat Upgrade Guide from v1.6.1 to v1.7.0 (Helm Mode)
## 1. Prerequisites
1. Ensure the following tools are installed:
- Helm 3.x
- kubectl
- Git (optional)
2. Verify current deployment information:
```bash
helm list -n <your-namespace>
# If the old chart package is missing, export values.yaml with:
helm get values hertzbeat -n <your-namespace> > hertzbeat-1.6.1-values.yaml
```
3. Data Backup:
> **For custom monitoring templates:**
>
> - Backup `/opt/hertzbeat/define` from the running pod:
>
> ```bash
> kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define
> ```
>
> **For external databases (MySQL/PostgreSQL):**
>
> - Use `mysqldump`/`pg_dump` or copy PVC directories:
>
> ```bash
> kubectl get pvc -n hertzbeat
> ```
## 2. Upgrade Steps
### 1. Pull the latest Chart
```bash
helm repo update
helm pull hertzbeat/hertzbeat --version 1.7.0 --untar
cd hertzbeat
```
Or clone from GitHub:
```bash
git clone https://github.com/hertzbeat/helm-charts.git
cd helm-charts/charts/hertzbeat
```
### 2. Update values.yaml
Compare and merge configurations:
```bash
diff -u ../hertzbeat-1.6.1-values.yaml values.yaml
# Use vimdiff to compare and merge changes
```
Key configurations to check:
- `image.tag`
- `resources`
- `persistence`
- `service.type`
- Ingress settings
- External database configurations
### 3. Dry-run Upgrade
```bash
helm upgrade hertzbeat . -n <your-namespace> \
--values values.yaml \
--dry-run \
--debug
```
### 4. Execute Upgrade
```bash
helm upgrade hertzbeat . -n <your-namespace> \
--values values.yaml \
--atomic \ # Auto-rollback on failure
--timeout 10m # Set timeout
```
### 5. Verify Upgrade
```bash
helm status hertzbeat -n <your-namespace>
kubectl get pods -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat
kubectl logs -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat --tail=100
```
@@ -0,0 +1,103 @@
# HertzBeat 升级指导-(Docker Mode)
## Docker 方式升级 - Mysql 数据库
1. 数据备份
- 备份数据库,将 mysql 数据手动做备份,按需备份
```bash
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <库名> hertzbeat_backup-`date +%Y-%m-%d`.sql #单库备份
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" > hertzbeat_backup-`date +%Y-%m-%d`.sql # 整库备份
```
- 备份配置文件及数据目录
```bash
mv application.yml application-bak.yml && mv sureness-bak.yml
cp -R data data-`date +%Y-%m-%d`. bak
```
2. 关闭 并移除 HertzBeat 容器
```shell
docker stop hertzbeat && docker rm hertzbeat
```
3. 升级数据库脚本
打开[https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration) 选择你使用的数据库的目录下相应的 `V160__update_column.sql`文件在 Mysql 执行升级 sql。
4. 更换镜像重新启动 HertzBeat 容器
```bash
$ docker run -d -p 1157:1157 -p 1158:1158 \
-v $(pwd)/data:/opt/hertzbeat/data \
-v $(pwd)/logs:/opt/hertzbeat/logs \
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
--restart=always \
--name hertzbeat apache/hertzbeat:v1.7.0
```
5. 升级配置文件
参考备份配置根据自己的需求基础上进行修改。
- `application.yml`一般需要修改以下部分
默认为:
```yaml
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
hikari:
max-lifetime: 120000
jpa:
show-sql: false
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
database: mysql
properties:
eclipselink:
logging:
level: SEVERE
```
- `sureness.yml`修改是可选的,一般在你需要修改账号密码时
```yaml
# account info config
# eg: admin has role [admin,user], password is hertzbeat
# eg: tom has role [user], password is hertzbeat
# eg: lili has role [guest], plain password is lili, salt is 123, salted password is 1A676730B0C7F54654B0E09184448289
account:
- appId: admin
credential: hertzbeat
role: [admin]
- appId: tom
credential: hertzbeat
role: [user]
- appId: guest
credential: hertzbeat
role: [guest]
- appId: lili
# credential = MD5(password + salt)
# plain password: hertzbeat
# attention: digest authentication does not support salted encrypted password accounts
credential: 94C6B34E7A199A9F9D4E1F208093B489
salt: 123
role: [user]
```
6. 添加相应的数据库驱动
由于 apache 基金会对于 license 合规的要求,HertzBeat 的安装包不能包含 mysqloracle 等 gpl 许可的依赖,需要用户自行添加,用户可通过以下链接自行下载驱动 jar 放到本地 `ext-lib`目录下,然后启动时将`ext-lib`挂载到容器的 `/opt/hertzbeat/ext-lib`目录。
mysql[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
oracle(如果你要监控 oracle,这两个驱动是必须的):
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
@@ -0,0 +1,103 @@
# HertzBeat 从 1.6.1 版本升级到 1.7.0 版本指引-(Helm Mode)
## 1. 前置准备
1. 确保已安装以下工具:
- Helm 3.x
- kubectl
- Git (可选)
2. 确认当前部署信息:
```bash
helm list -n <your-namespace>
# 如果你老版本的chart包不见了,可以使用以下命令导出values.yaml文件
helm get values hertzbeat -n <your-namespace> > hertzbeat-1.6.1-values.yaml
```
3. 数据备份
> 1. 若使用了自定义监控模版
>
> - 需要备份 `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define` 当前运行 pod里面的 `/opt/hertzbeat/define` 目录到当前主机下,如果做了持久化 请拷贝持久化目录
> - `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define`
>
> 2. 若使用外置关系型数据库 Mysql, PostgreSQL数据
>
> - 一般使用helm部署部署都做了持久化,可以选择拷贝持久化目录,也可以通过mysqldump、pgdump等工具完成备份
>
> ```bash
> kubectl get pvc -n hertzbeat
> NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
> hertzbeat-database Bound pvc-c63cf479-0033-423b-8466-eb00aa181657 4Gi RWO standard 68d
> hertzbeat-ext-lib Bound pvc-31fee163-1211-424f-8966-e3e805c23ff5 1Gi RWX standard 68d
> hertzbeat-tsdb Bound pvc-4f2ef614-0302-4a4c-8dd4-e68b34e9061c 4Gi RWO standard 68d
> ```
## 2. 升级步骤
### 1. 拉取最新Chart到本地
```bash
helm repo update
helm pull hertzbeat/hertzbeat --version 1.7.0 --untar
cd hertzbeat
```
或者从GitHub仓库获取(按需修改Chart):
```bash
git clone https://github.com/hertzbeat/helm-charts.git
cd helm-charts/charts/hertzbeat
```
### 2. 修改values.yaml
比较并合并您的自定义配置到新版本values.yaml:
```bash
# 使用diff工具比较新旧values文件
diff -u ../hertzbeat-1.6.1-values.yaml values.yaml
vimdiff 对着修改,改完继续diff,无输出则正常
```
常见需要关注的配置项:
- 镜像版本: `image.tag`
- 资源限制: `resources`
- 持久化配置: `persistence`
- 服务类型: `service.type`
- Ingress配置
- 数据库配置(如果使用外部数据库)
### 3. 测试升级(干运行)
```bash
helm upgrade hertzbeat . -n <your-namespace> \
--values values.yaml \
--dry-run \
--debug
```
### 4. 执行升级
```bash
helm upgrade hertzbeat . -n <your-namespace> \
--values values.yaml \
--atomic \ # 升级失败自动回滚
--timeout 10m # 设置超时时间
```
### 5. 验证升级
```bash
# 检查发布状态
helm status hertzbeat -n <your-namespace>
# 检查Pod状态
kubectl get pods -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat
# 检查日志
kubectl logs -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat --tail=100
```
+1 -1
View File
@@ -44,7 +44,7 @@ if [ ! -d $LOGS_DIR ]; then
fi
# JVM Configuration
JAVA_OPTS=" -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_OPTS=" -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
# JVM Configuration
JAVA_MEM_OPTS=" -server -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOGS_DIR"
+1 -1
View File
@@ -51,7 +51,7 @@ if not exist %LOGS_DIR% (
)
rem JVM Configuration
set JAVA_OPTS= -Duser.timezone=Asia/Shanghai -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED
set JAVA_OPTS= -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED
set JAVA_MEM_OPTS= -server -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%LOGS_DIR%
+1 -1
View File
@@ -82,7 +82,7 @@ if [ ! -d $LOGS_DIR ]; then
fi
# JVM Configuration
JAVA_OPTS=" -Duser.timezone=Asia/Shanghai -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_OPTS=" -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_MEM_OPTS=" -server -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOGS_DIR"
+2 -2
View File
@@ -47,9 +47,9 @@ fi
# JVM Configuration
if [ -z "$JAVA_OPTS" ]; then
JAVA_OPTS=" -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_OPTS=" -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
else
JAVA_OPTS="${JAVA_OPTS} -Doracle.jdbc.timezoneAsRegion=false"
JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false"
fi
# JVM Configuration
+1 -1
View File
@@ -52,7 +52,7 @@ if not exist %LOGS_DIR% (
rem JVM Configuration
set JAVA_OPTS= -Duser.timezone=Asia/Shanghai -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED
set JAVA_OPTS= -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED
set JAVA_MEM_OPTS= -server -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%LOGS_DIR%
+1 -1
View File
@@ -86,7 +86,7 @@ fi
# JVM Configuration
JAVA_OPTS=" -Duser.timezone=Asia/Shanghai -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_OPTS=" -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8 -Doracle.jdbc.timezoneAsRegion=false --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED"
JAVA_MEM_OPTS=" -server -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOGS_DIR"
+1
View File
@@ -21,6 +21,7 @@ export class Monitor {
id!: number;
name!: string;
app!: string;
scrape!: string;
host!: string;
intervals: number = 60;
// Monitoring status 0: Paused, 1: Up, 2: Down
@@ -145,6 +145,9 @@ export class MonitorDetailComponent implements OnInit, OnDestroy {
if (message.code === 0) {
this.monitor = message.data.monitor;
this.app = this.monitor?.app;
if (this.monitor.scrape && this.monitor.scrape != 'static') {
this.app = this.monitor.scrape;
}
let params: Param[] = message.data.params;
params.forEach(param => {
if (param.field === 'port') {
@@ -39,6 +39,9 @@
[paramValueMap]="paramValueMap"
[collector]="collector"
[collectors]="collectors"
[sdParams]="sdParams"
[sdDefines]="sdDefines"
(scrapeChange)="onScrapeChange($event)"
(formCancel)="onCancel()"
(formSubmit)="onSubmit($event)"
(formDetect)="onDetect($event)"
@@ -24,8 +24,8 @@ import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN, TitleService } from '@delon/theme';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { NzUploadFile } from 'ng-zorro-antd/upload';
import { throwError } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { of, throwError } from 'rxjs';
import { finalize, switchMap } from 'rxjs/operators';
import { Collector } from '../../../pojo/Collector';
import { GrafanaDashboard } from '../../../pojo/GrafanaDashboard';
@@ -54,6 +54,8 @@ export class MonitorEditComponent implements OnInit {
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService
) {}
sdDefines: ParamDefine[] = [];
sdParams: Param[] = [];
paramDefines!: ParamDefine[];
hostName!: string;
params!: Param[];
@@ -83,6 +85,9 @@ export class MonitorEditComponent implements OnInit {
if (message.code === 0) {
let paramValueMap = new Map<String, Param>();
this.monitor = message.data.monitor;
if (this.monitor.scrape == null || this.monitor.scrape == undefined) {
this.monitor.scrape = 'static';
}
this.grafanaDashboard = message.data.grafanaDashboard != undefined ? message.data.grafanaDashboard : new GrafanaDashboard();
this.collector = message.data.collector == null ? '' : message.data.collector;
this.titleSvc.setTitleByI18n(`monitor.app.${this.monitor.app}`);
@@ -172,27 +177,142 @@ export class MonitorEditComponent implements OnInit {
return this.collectorSvc.getCollectors();
})
)
.pipe(
switchMap((message: any) => {
if (message.code === 0) {
this.collectors = message.data.content?.map((item: { collector: any }) => item.collector);
} else {
console.warn(message.msg);
}
if (this.monitor.scrape == 'static') {
return of({ code: 0, data: [], msg: '' });
} else {
return this.appDefineSvc.getAppParamsDefine(this.monitor.scrape);
}
})
)
.subscribe(
message => {
if (message.code === 0) {
this.collectors = message.data.content?.map(item => item.collector);
if (message.data.length > 0) {
let params: Param[] = [];
let paramDefines: ParamDefine[] = [];
message.data.forEach(define => {
let param = this.paramValueMap.get(define.field);
if (param === undefined) {
param = new Param();
param.field = define.field;
if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else if (define.type === 'array') {
param.type = 4;
} else {
param.type = 1;
}
if (define.type === 'boolean') {
param.paramValue = define.defaultValue == 'true';
} else if (param.field === 'host') {
param.paramValue = this.monitor.host;
} else if (define.defaultValue != undefined) {
if (define.type === 'number') {
param.paramValue = Number(define.defaultValue);
} else if (define.type === 'boolean') {
param.paramValue = define.defaultValue.toLowerCase() == 'true';
} else {
param.paramValue = define.defaultValue;
}
}
} else {
if (define.type === 'boolean') {
if (param.paramValue != null) {
param.paramValue = param.paramValue.toLowerCase() == 'true';
} else {
param.paramValue = false;
}
}
}
define.name = this.i18nSvc.fanyi(`monitor.app.${this.monitor.scrape}.param.${define.field}`);
if (define.placeholder == null && this.i18nSvc.fanyi(`monitor.${define.field}.tip`) != `monitor.${define.field}.tip`) {
define.placeholder = this.i18nSvc.fanyi(`monitor.${define.field}.tip`);
}
params.push(param);
paramDefines.push(define);
});
this.sdParams = [...params];
this.sdDefines = [...paramDefines];
}
} else {
console.warn(message.msg);
}
this.isSpinning = false;
},
error => {
console.error(error);
this.isSpinning = false;
}
);
}
onScrapeChange(scrapeValue: string) {
this.monitor.scrape = scrapeValue;
if (this.monitor.scrape !== 'static') {
this.isSpinning = true;
let queryScrapeDefine$ = this.appDefineSvc
.getAppParamsDefine(this.monitor.scrape)
.pipe(
finalize(() => {
queryScrapeDefine$.unsubscribe();
this.isSpinning = false;
})
)
.subscribe(message => {
if (message.code === 0) {
let params: Param[] = [];
let paramDefines: ParamDefine[] = [];
message.data.forEach(define => {
let param = new Param();
param.field = define.field;
if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else if (define.type === 'array') {
param.type = 4;
} else {
param.type = 1;
}
if (define.type === 'boolean') {
param.paramValue = false;
}
if (define.defaultValue != undefined) {
if (define.type === 'number') {
param.paramValue = Number(define.defaultValue);
} else if (define.type === 'boolean') {
param.paramValue = define.defaultValue.toLowerCase() == 'true';
} else {
param.paramValue = define.defaultValue;
}
}
define.name = this.i18nSvc.fanyi(`monitor.app.${this.monitor.scrape}.param.${define.field}`);
if (define.placeholder == null && this.i18nSvc.fanyi(`monitor.${define.field}.tip`) != `monitor.${define.field}.tip`) {
define.placeholder = this.i18nSvc.fanyi(`monitor.${define.field}.tip`);
}
params.push(param);
paramDefines.push(define);
});
this.sdParams = [...params];
this.sdDefines = [...paramDefines];
}
});
}
}
onSubmit(info: any) {
let addMonitor = {
monitor: info.monitor,
collector: info.collector,
params: info.params.concat(info.advancedParams),
params: info.params.concat(info.advancedParams).concat(info.sdParams),
grafanaDashboard: info.grafanaDashboard
};
this.spinningTip = 'Loading...';
@@ -218,7 +338,7 @@ export class MonitorEditComponent implements OnInit {
let detectMonitor = {
monitor: info.monitor,
collector: info.collector,
params: info.params.concat(info.advancedParams)
params: info.params.concat(info.advancedParams).concat(info.sdParams)
};
this.spinningTip = this.i18nSvc.fanyi('monitor.spinning-tip.detecting');
this.isSpinning = true;
@@ -21,6 +21,45 @@
<div class="-inner-content">
<form nz-form #form="ngForm">
<nz-form-item>
<nz-form-control nzSpan="4" nzOffset="9" [nzErrorTip]="'validation.required' | i18n">
<nz-select
style="text-align: center"
[nzDropdownStyle]="{ textAlign: 'center' }"
[(ngModel)]="monitor.scrape"
name="scrape"
(ngModelChange)="onScrapeChange($event)"
>
<nz-option nzValue="static" [nzLabel]="'monitor.scrape.type.static' | i18n"></nz-option>
<nz-option nzValue="http_sd" [nzLabel]="'monitor.scrape.type.http_sd' | i18n"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<ng-container *ngIf="monitor.scrape != 'static'">
<ng-container *ngFor="let sdPramDefine of sdDefines; let i = index">
<nz-form-item *ngIf="sdParams[i].display !== false">
<nz-form-label nzSpan="7" [nzRequired]="sdPramDefine.required" [nzFor]="sdPramDefine.field"
>{{ sdPramDefine.name }}
</nz-form-label>
<nz-form-control nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-form-field
[item]="sdPramDefine"
[name]="sdPramDefine.field"
[(ngModel)]="sdParams[i].paramValue"
(ngModelChange)="
sdPramDefine.type === 'boolean'
? onParamBooleanChanged($event, sdPramDefine.field)
: sdPramDefine.type === 'radio'
? onDependChanged($event, sdPramDefine.field)
: null
"
/>
</nz-form-control>
</nz-form-item>
</ng-container>
</ng-container>
<nz-form-item *ngIf="monitor.scrape == 'static'">
<nz-form-label nzSpan="7" [nzRequired]="true" [nzFor]="'host'">{{ hostName ? hostName : ('monitor.host' | i18n) }} </nz-form-label>
<nz-form-control nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-form-field
@@ -53,6 +92,8 @@
</nz-form-control>
</nz-form-item>
<nz-divider></nz-divider>
<ng-container *ngFor="let paramDefine of paramDefines; let i = index">
<nz-form-item *ngIf="params[i].display !== false && paramDefine.field !== 'host'">
<nz-form-label nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field">{{ paramDefine.name }} </nz-form-label>
@@ -46,11 +46,14 @@ export class MonitorFormComponent implements OnChanges {
@Input() paramDefines!: ParamDefine[];
@Input() advancedParamDefines!: ParamDefine[];
@Input() paramValueMap!: Map<String, Param>;
@Input() sdDefines!: ParamDefine[];
@Input() sdParams!: Param[];
@Output() readonly formSubmit = new EventEmitter<any>();
@Output() readonly formCancel = new EventEmitter<any>();
@Output() readonly formDetect = new EventEmitter<any>();
@Output() readonly hostChange = new EventEmitter<string>();
@Output() readonly scrapeChange = new EventEmitter<string>();
@Output() readonly collectorChange = new EventEmitter<string>();
hasAdvancedParams: boolean = false;
@@ -87,7 +90,7 @@ export class MonitorFormComponent implements OnChanges {
});
return;
}
this.monitor.host = this.monitor.host.trim();
this.monitor.host = this.monitor.host ? this.monitor.host.trim() : '';
this.monitor.name = this.monitor.name.trim();
// todo Set the host property value separately for now
this.params.forEach(param => {
@@ -103,7 +106,18 @@ export class MonitorFormComponent implements OnChanges {
param.paramValue = (param.paramValue as string).trim();
}
});
this.formDetect.emit({ monitor: this.monitor, params: this.params, advancedParams: this.advancedParams, collector: this.collector });
this.sdParams.forEach(param => {
if (param.paramValue != null && typeof param.paramValue == 'string') {
param.paramValue = (param.paramValue as string).trim();
}
});
this.formDetect.emit({
monitor: this.monitor,
sdParams: this.sdParams,
params: this.params,
advancedParams: this.advancedParams,
collector: this.collector
});
}
onSubmit(formGroup: FormGroup) {
@@ -132,8 +146,14 @@ export class MonitorFormComponent implements OnChanges {
param.paramValue = (param.paramValue as string).trim();
}
});
this.sdParams.forEach(param => {
if (param.paramValue != null && typeof param.paramValue == 'string') {
param.paramValue = (param.paramValue as string).trim();
}
});
this.formSubmit.emit({
monitor: this.monitor,
sdParams: this.sdParams,
params: this.params,
advancedParams: this.advancedParams,
collector: this.collector,
@@ -145,6 +165,10 @@ export class MonitorFormComponent implements OnChanges {
this.formCancel.emit();
}
onScrapeChange(scrape: string) {
this.scrapeChange.emit(scrape);
}
onHostChange(host: string) {
this.hostChange.emit(host);
}
@@ -193,6 +217,17 @@ export class MonitorFormComponent implements OnChanges {
}
}
});
this.sdDefines.forEach((paramDefine, index) => {
if (paramDefine.depend) {
let fieldValues = new Map(Object.entries(paramDefine.depend)).get(dependField);
if (fieldValues) {
this.sdParams[index].display = false;
if (fieldValues.map(String).includes(dependValue)) {
this.sdParams[index].display = true;
}
}
}
});
this.advancedParamDefines.forEach((advancedParamDefine, index) => {
if (advancedParamDefine.depend) {
let fieldValues = new Map(Object.entries(advancedParamDefine.depend)).get(dependField);
@@ -174,6 +174,7 @@
<div class="monitor-card-info">
<div class="monitor-card-info-item host-item">
<button
*ngIf="data.scrape == 'static' || !data.scrape"
nz-button
nzType="text"
[cdkCopyToClipboard]="data.host"
@@ -184,6 +185,10 @@
<i nz-icon nzType="global"></i>
{{ data.host }}
</button>
<button nz-button nzType="text" *ngIf="data.scrape && data.scrape != 'static'">
<i nz-icon nzType="partition"></i>
{{ 'monitor.scrape.type.' + data.scrape | i18n }}
</button>
<button nz-button nz-dropdown [nzDropdownMenu]="actionMenu" class="action-button">
<span nz-icon nzType="ellipsis"></span>
@@ -28,7 +28,6 @@ import { NzUploadChangeParam } from 'ng-zorro-antd/upload';
import { finalize } from 'rxjs/operators';
import { Monitor } from '../../../pojo/Monitor';
import { AppDefineService } from '../../../service/app-define.service';
import { MemoryStorageService } from '../../../service/memory-storage.service';
import { MonitorService } from '../../../service/monitor.service';
import { findDeepestSelected, renderLabelColor } from '../../../shared/utils/common-util';
@@ -46,7 +45,6 @@ export class MonitorListComponent implements OnInit, OnDestroy {
private notifySvc: NzNotificationService,
private monitorSvc: MonitorService,
private storageSvc: MemoryStorageService,
private appDefineSvc: AppDefineService,
private menuService: MenuService,
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService
) {}
@@ -506,43 +504,29 @@ export class MonitorListComponent implements OnInit, OnDestroy {
onAppSwitchModalOpen() {
this.appSwitchModalVisible = true;
this.appSearchLoading = true;
const getHierarchy$ = this.appDefineSvc
.getAppHierarchy(this.i18nSvc.defaultLang)
.pipe(
finalize(() => {
getHierarchy$.unsubscribe();
this.appSearchLoading = false;
})
)
.subscribe(
message => {
if (message.code === 0) {
let appMenus: Record<string, any> = {};
message.data.forEach((app: any) => {
let menus = appMenus[app.category];
app.categoryLabel = this.i18nSvc.fanyi(`menu.monitor.${app.category}`);
if (app.categoryLabel == `menu.monitor.${app.category}`) {
app.categoryLabel = app.category.toUpperCase();
}
if (menus == undefined) {
menus = { label: app.categoryLabel, child: [app] };
} else {
menus.child.push(app);
}
appMenus[app.category] = menus;
});
this.appSearchOrigin = Object.entries(appMenus);
this.appSearchOrigin.sort((a, b) => {
return b[1].length - a[1].length;
});
} else {
console.warn(message.msg);
}
},
error => {
console.warn(error.msg);
}
);
let appMenus: Record<string, any> = {};
let hierarchy: any[] = this.storageSvc.getData('hierarchy');
hierarchy.forEach((app: any) => {
if (app.category == '__system__') {
return;
}
let menus = appMenus[app.category];
app.categoryLabel = this.i18nSvc.fanyi(`menu.monitor.${app.category}`);
if (app.categoryLabel == `menu.monitor.${app.category}`) {
app.categoryLabel = app.category.toUpperCase();
}
if (menus == undefined) {
menus = { label: app.categoryLabel, child: [app] };
} else {
menus.child.push(app);
}
appMenus[app.category] = menus;
});
this.appSearchOrigin = Object.entries(appMenus);
this.appSearchOrigin.sort((a, b) => {
return b[1].length - a[1].length;
});
this.appSearchLoading = false;
}
onAppSwitchModalCancel() {
@@ -38,6 +38,9 @@
[advancedParamDefines]="advancedParamDefines"
[collector]="collector"
[collectors]="collectors"
[sdParams]="sdParams"
[sdDefines]="sdDefines"
(scrapeChange)="onScrapeChange($event)"
(hostChange)="onHostChange($event)"
(formCancel)="onCancel()"
(formSubmit)="onSubmit($event)"
@@ -22,7 +22,8 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN, TitleService } from '@delon/theme';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import { finalize, switchMap } from 'rxjs/operators';
import { Collector } from '../../../pojo/Collector';
import { GrafanaDashboard } from '../../../pojo/GrafanaDashboard';
@@ -41,6 +42,8 @@ import { generateReadableRandomString } from '../../../shared/utils/common-util'
styles: []
})
export class MonitorNewComponent implements OnInit {
sdDefines: ParamDefine[] = [];
sdParams: Param[] = [];
paramDefines!: ParamDefine[];
hostName!: string;
params!: Param[];
@@ -75,6 +78,7 @@ export class MonitorNewComponent implements OnInit {
if (this.monitor.app == '') {
this.router.navigateByUrl('/monitors/new?app=website');
}
this.monitor.scrape = paramMap.get('scrape') || 'static';
this.titleSvc.setTitleByI18n(`monitor.app.${this.monitor.app}`);
this.isSpinning = true;
return this.appDefineSvc.getAppParamsDefine(this.monitor.app);
@@ -140,21 +144,125 @@ export class MonitorNewComponent implements OnInit {
return this.collectorSvc.getCollectors();
})
)
.pipe(
switchMap((message: any) => {
if (message.code === 0) {
this.collectors = message.data.content?.map((item: { collector: any }) => item.collector);
} else {
console.warn(message.msg);
}
if (this.monitor.scrape == 'static') {
return of({ code: 0, data: [], msg: '' });
} else {
return this.appDefineSvc.getAppParamsDefine(this.monitor.scrape);
}
})
)
.subscribe(
message => {
if (message.code === 0) {
this.collectors = message.data.content?.map(item => item.collector);
if (message.data.length > 0) {
let params: Param[] = [];
let paramDefines: ParamDefine[] = [];
message.data.forEach(define => {
let param = new Param();
param.field = define.field;
if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else if (define.type === 'array') {
param.type = 4;
} else {
param.type = 1;
}
if (define.type === 'boolean') {
param.paramValue = false;
}
if (define.defaultValue != undefined) {
if (define.type === 'number') {
param.paramValue = Number(define.defaultValue);
} else if (define.type === 'boolean') {
param.paramValue = define.defaultValue.toLowerCase() == 'true';
} else {
param.paramValue = define.defaultValue;
}
}
define.name = this.i18nSvc.fanyi(`monitor.app.${this.monitor.scrape}.param.${define.field}`);
if (define.placeholder == null && this.i18nSvc.fanyi(`monitor.${define.field}.tip`) != `monitor.${define.field}.tip`) {
define.placeholder = this.i18nSvc.fanyi(`monitor.${define.field}.tip`);
}
params.push(param);
paramDefines.push(define);
});
this.sdParams = [...params];
this.sdDefines = [...paramDefines];
}
} else {
console.warn(message.msg);
}
this.isSpinning = false;
},
error => {
this.isSpinning = true;
this.isSpinning = false;
}
);
}
onScrapeChange(scrapeValue: string) {
this.monitor.scrape = scrapeValue;
if (this.monitor.scrape !== 'static') {
this.isSpinning = true;
let queryScrapeDefine$ = this.appDefineSvc
.getAppParamsDefine(this.monitor.scrape)
.pipe(
finalize(() => {
queryScrapeDefine$.unsubscribe();
this.isSpinning = false;
})
)
.subscribe(message => {
if (message.code === 0) {
let params: Param[] = [];
let paramDefines: ParamDefine[] = [];
message.data.forEach(define => {
let param = new Param();
param.field = define.field;
if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else if (define.type === 'array') {
param.type = 4;
} else {
param.type = 1;
}
if (define.type === 'boolean') {
param.paramValue = false;
}
if (define.defaultValue != undefined) {
if (define.type === 'number') {
param.paramValue = Number(define.defaultValue);
} else if (define.type === 'boolean') {
param.paramValue = define.defaultValue.toLowerCase() == 'true';
} else {
param.paramValue = define.defaultValue;
}
}
define.name = this.i18nSvc.fanyi(`monitor.app.${this.monitor.scrape}.param.${define.field}`);
if (define.placeholder == null && this.i18nSvc.fanyi(`monitor.${define.field}.tip`) != `monitor.${define.field}.tip`) {
define.placeholder = this.i18nSvc.fanyi(`monitor.${define.field}.tip`);
}
params.push(param);
paramDefines.push(define);
});
this.sdParams = [...params];
this.sdDefines = [...paramDefines];
}
});
}
}
onHostChange(hostValue: string) {
if (this.monitor.name == undefined || this.monitor.name == '') {
this.monitor.name = generateReadableRandomString();
@@ -165,7 +273,7 @@ export class MonitorNewComponent implements OnInit {
let addMonitor = {
monitor: info.monitor,
collector: info.collector,
params: info.params.concat(info.advancedParams),
params: info.params.concat(info.advancedParams).concat(info.sdParams),
grafanaDashboard: info.grafanaDashboard
};
this.spinningTip = 'Loading...';
@@ -191,7 +299,7 @@ export class MonitorNewComponent implements OnInit {
let detectMonitor = {
monitor: info.monitor,
collector: info.collector,
params: info.params.concat(info.advancedParams)
params: info.params.concat(info.advancedParams).concat(info.sdParams)
};
this.spinningTip = this.i18nSvc.fanyi('monitor.spinning-tip.detecting');
this.isSpinning = true;
@@ -94,6 +94,9 @@ export class DefineComponent implements OnInit {
if (app.value == 'prometheus') {
return;
}
if (app.category == '__system__') {
return;
}
this.appLabel[app.value] = app.label;
let menus = appMenus[app.category];
if (menus == undefined) {
+5 -3
View File
@@ -612,7 +612,7 @@
"menu.monitor.service": "Service Monitor",
"menu.monitor.webserver": "Webserver Monitor",
"menu.more": "More",
"menu.search.placeholder": "Search Monitoring Task Name、Host etc",
"menu.search.placeholder": "Search Task Name、Host etc",
"monitor": "Monitor",
"monitor.advanced": "Advanced",
"monitor.advanced.tip": "Advanced Setting Params",
@@ -709,7 +709,7 @@
"monitor.headerValue.tip": "Header Value",
"monitor.holdingRegisterAddresses.tip": "Input RegisterAddress",
"monitor.host": "Target Host",
"monitor.host.tip": "The monitored peer ip or domain name",
"monitor.host.tip": "The target peer ip or domain name",
"monitor.icon.bigdata": "dot-chart",
"monitor.icon.cache": "group",
"monitor.icon.center": "laptop",
@@ -728,7 +728,7 @@
"monitor.keyword.tip": "Enter keyword which occurrences need to be monitored",
"monitor.list": "Monitor List",
"monitor.name": "Task Name",
"monitor.name.tip": "Monitoring task name",
"monitor.name.tip": "The unique name that identifies the task",
"monitor.new": "New",
"monitor.new-monitor": "New Monitor",
"monitor.new.failed": "New Monitor Failed",
@@ -759,6 +759,8 @@
"monitor.sshPort.tip": "Required When Enabling SSH Tunnel",
"monitor.sshUsername.tip": "Required When Enabling SSH Tunnel",
"monitor.sshPrivateKey.tip": "BEGIN RSA PRIVATE KEY",
"monitor.scrape.type.static": "Static Scrape",
"monitor.scrape.type.http_sd": "Http Service Discovery",
"placeholder.key": "Key",
"placeholder.value": "Value",
"plugin.delete": "Delete Plugin",
+2
View File
@@ -759,6 +759,8 @@
"monitor.sshPort.tip": "SSHトンネルオープン時に必要",
"monitor.sshUsername.tip": "SSHトンネルオープン時に必要",
"monitor.sshPrivateKey.tip": "RSA秘密鍵の起動",
"monitor.scrape.type.static": "静的",
"monitor.scrape.type.http_sd": "Http Service Discovery",
"placeholder.key": "キー",
"placeholder.value": "値",
"plugin.delete": "プラグインを削除",
+2
View File
@@ -571,6 +571,8 @@
"monitor.sshPort.tip": "Obrigatório quando túnel SSH está ativo",
"monitor.sshUsername.tip": "Obrigatório quando túnel SSH está ativo",
"monitor.sshPrivateKey.tip": "Chave privada RSA",
"monitor.scrape.type.static": "Estatico",
"monitor.scrape.type.http_sd": "Http Service Discovery",
"common.name": "Nome da Métrica",
"common.value": "Valor da Métrica",
"common.search": "Pesquisar",
+2
View File
@@ -759,6 +759,8 @@
"monitor.sshPort.tip": "SSH隧道开启时必填",
"monitor.sshUsername.tip": "SSH隧道开启时必填",
"monitor.sshPrivateKey.tip": "启动RSA私钥",
"monitor.scrape.type.static": "静态配置",
"monitor.scrape.type.http_sd": "Http 服务发现",
"placeholder.key": "键",
"placeholder.value": "值",
"plugin.delete": "刪除插件",
+2
View File
@@ -758,6 +758,8 @@
"monitor.sshPort.tip": "SSH隧道開啓時必填",
"monitor.sshUsername.tip": "SSH隧道開啓時必填",
"monitor.sshPrivateKey.tip": "啟動RSA私鑰",
"monitor.scrape.type.static": "靜態配置",
"monitor.scrape.type.http_sd": "Http 服務發現",
"placeholder.key": "鍵",
"placeholder.value": "值",
"plugin.delete": "刪除插件",