mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fb359a428 | ||
|
|
a3e3f754b2 |
+4
-45
@@ -29,14 +29,11 @@ import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.constants.ExportFileConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.common.util.FileUtil;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -51,7 +48,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -62,7 +58,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Alarm definition management interface implementation
|
||||
@@ -74,31 +69,16 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
|
||||
@Autowired
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
@Resource
|
||||
private LabelService labelService;
|
||||
|
||||
@Resource
|
||||
private LabelDao labelDao;
|
||||
|
||||
private final DataSourceService dataSourceService;
|
||||
|
||||
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
|
||||
|
||||
private static final String CONTENT_TYPE = MediaType.APPLICATION_OCTET_STREAM_VALUE + SignConstants.SINGLE_MARK + "charset=" + StandardCharsets.UTF_8;
|
||||
|
||||
private static final Set<String> SYSTEM_BUILT_IN_LABELS = Set.of(
|
||||
CommonConstants.LABEL_INSTANCE,
|
||||
CommonConstants.LABEL_DEFINE_ID,
|
||||
CommonConstants.LABEL_ALERT_NAME,
|
||||
CommonConstants.LABEL_INSTANCE_NAME,
|
||||
CommonConstants.LABEL_ALERT_SEVERITY,
|
||||
CommonConstants.ALERT_MODE_LABEL
|
||||
);
|
||||
|
||||
public AlertDefineServiceImpl(List<AlertDefineImExportService> alertDefineImExportServiceList, DataSourceService dataSourceService) {
|
||||
alertDefineImExportServiceList.forEach(it -> alertDefineImExportServiceMap.put(it.type(), it));
|
||||
this.dataSourceService = dataSourceService;
|
||||
@@ -113,7 +93,7 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
JexlExpressionRunner.compile(alertDefine.getExpr());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("alert expr error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// the name of the alarm rule is unique
|
||||
@@ -127,7 +107,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Override
|
||||
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
saveNewCustomLabel(alertDefine);
|
||||
alertDefine = alertDefineDao.saveAndFlush(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
@@ -135,31 +114,11 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Override
|
||||
public void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
saveNewCustomLabel(alertDefine);
|
||||
alertDefineDao.saveAndFlush(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
private void saveNewCustomLabel(AlertDefine alertDefine) {
|
||||
Map<String, String> labels = alertDefine.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
alertDefine.setLabels(labels);
|
||||
}
|
||||
Map<String, String> customLabels = labels.entrySet().stream()
|
||||
.filter(entry -> !isSystemBuiltInLabel(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
List<Label> addLabels = labelService.determineNewLabels(customLabels.entrySet());
|
||||
if (!addLabels.isEmpty()) {
|
||||
labelDao.saveAll(addLabels);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSystemBuiltInLabel(String labelKey) {
|
||||
return SYSTEM_BUILT_IN_LABELS.contains(labelKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertDefine(long alertId) throws RuntimeException {
|
||||
alertDefineDao.deleteById(alertId);
|
||||
@@ -297,7 +256,7 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
if (!StringUtils.hasText(type)) {
|
||||
throw new IllegalArgumentException("Alert definition type cannot be null or empty");
|
||||
}
|
||||
|
||||
|
||||
switch (type) {
|
||||
case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_REALTIME:
|
||||
case CommonConstants.METRIC_ALERT_THRESHOLD_TYPE_PERIODIC:
|
||||
@@ -308,7 +267,7 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported alert definition type: " + type);
|
||||
}
|
||||
|
||||
|
||||
// Query enabled alert definitions by type
|
||||
return alertDefineDao.findAlertDefinesByTypeAndEnableTrue(type);
|
||||
}
|
||||
|
||||
+34
-44
@@ -21,8 +21,6 @@ import com.google.common.collect.Lists;
|
||||
import org.apache.hertzbeat.alert.calculate.periodic.PeriodicAlertRuleScheduler;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -71,13 +69,7 @@ class AlertDefineServiceTest {
|
||||
|
||||
@Mock
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Mock
|
||||
private LabelService labelService;
|
||||
|
||||
@Mock
|
||||
private LabelDao labelDao;
|
||||
|
||||
|
||||
@Mock
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
@@ -94,8 +86,6 @@ class AlertDefineServiceTest {
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineDao", alertDefineDao);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "periodicAlertRuleScheduler", periodicAlertRuleScheduler);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "labelService", labelService);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "labelDao", labelDao);
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
@@ -197,40 +187,40 @@ class AlertDefineServiceTest {
|
||||
AlertDefine.builder().id(1L).type(METRIC_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build(),
|
||||
AlertDefine.builder().id(2L).type(METRIC_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build()
|
||||
);
|
||||
|
||||
|
||||
try (MockedStatic<CacheFactory> cacheFactoryMock = Mockito.mockStatic(CacheFactory.class)) {
|
||||
// Mock cache hit
|
||||
cacheFactoryMock.when(CacheFactory::getMetricsAlertDefineCache).thenReturn(cachedAlertDefines);
|
||||
|
||||
|
||||
List<AlertDefine> result = alertDefineService.getMetricsRealTimeAlertDefines();
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(1L, result.get(0).getId());
|
||||
assertEquals(2L, result.get(1).getId());
|
||||
|
||||
|
||||
// Verify no database query was called
|
||||
verify(alertDefineDao, times(0)).findAlertDefinesByTypeAndEnableTrue(any());
|
||||
cacheFactoryMock.verify(() -> CacheFactory.setMetricsAlertDefineCache(any()), times(0));
|
||||
}
|
||||
|
||||
|
||||
// Test cache miss scenario
|
||||
List<AlertDefine> dbAlertDefines = Lists.newArrayList(
|
||||
AlertDefine.builder().id(3L).type(METRIC_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build()
|
||||
);
|
||||
|
||||
|
||||
try (MockedStatic<CacheFactory> cacheFactoryMock = Mockito.mockStatic(CacheFactory.class)) {
|
||||
// Mock cache miss
|
||||
cacheFactoryMock.when(CacheFactory::getMetricsAlertDefineCache).thenReturn(null);
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_REALTIME))
|
||||
.thenReturn(dbAlertDefines);
|
||||
|
||||
|
||||
List<AlertDefine> result = alertDefineService.getMetricsRealTimeAlertDefines();
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(3L, result.get(0).getId());
|
||||
|
||||
|
||||
// Verify database query and cache setting were called
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
cacheFactoryMock.verify(() -> CacheFactory.setMetricsAlertDefineCache(dbAlertDefines), times(1));
|
||||
@@ -244,40 +234,40 @@ class AlertDefineServiceTest {
|
||||
AlertDefine.builder().id(4L).type(LOG_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build(),
|
||||
AlertDefine.builder().id(5L).type(LOG_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build()
|
||||
);
|
||||
|
||||
|
||||
try (MockedStatic<CacheFactory> cacheFactoryMock = Mockito.mockStatic(CacheFactory.class)) {
|
||||
// Mock cache hit
|
||||
cacheFactoryMock.when(CacheFactory::getLogAlertDefineCache).thenReturn(cachedAlertDefines);
|
||||
|
||||
|
||||
List<AlertDefine> result = alertDefineService.getLogRealTimeAlertDefines();
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(4L, result.get(0).getId());
|
||||
assertEquals(5L, result.get(1).getId());
|
||||
|
||||
|
||||
// Verify no database query was called
|
||||
verify(alertDefineDao, times(0)).findAlertDefinesByTypeAndEnableTrue(any());
|
||||
cacheFactoryMock.verify(() -> CacheFactory.setLogAlertDefineCache(any()), times(0));
|
||||
}
|
||||
|
||||
|
||||
// Test cache miss scenario
|
||||
List<AlertDefine> dbAlertDefines = Lists.newArrayList(
|
||||
AlertDefine.builder().id(6L).type(LOG_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build()
|
||||
);
|
||||
|
||||
|
||||
try (MockedStatic<CacheFactory> cacheFactoryMock = Mockito.mockStatic(CacheFactory.class)) {
|
||||
// Mock cache miss
|
||||
cacheFactoryMock.when(CacheFactory::getLogAlertDefineCache).thenReturn(null);
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_REALTIME))
|
||||
.thenReturn(dbAlertDefines);
|
||||
|
||||
|
||||
List<AlertDefine> result = alertDefineService.getLogRealTimeAlertDefines();
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(6L, result.get(0).getId());
|
||||
|
||||
|
||||
// Verify database query and cache setting were called
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
cacheFactoryMock.verify(() -> CacheFactory.setLogAlertDefineCache(dbAlertDefines), times(1));
|
||||
@@ -290,58 +280,58 @@ class AlertDefineServiceTest {
|
||||
AlertDefine.builder().id(7L).type(METRIC_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build(),
|
||||
AlertDefine.builder().id(8L).type(METRIC_ALERT_THRESHOLD_TYPE_REALTIME).enable(true).build()
|
||||
);
|
||||
|
||||
|
||||
// Test valid metric realtime alert type
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_REALTIME))
|
||||
.thenReturn(mockAlertDefines);
|
||||
|
||||
|
||||
List<AlertDefine> result = alertDefineService.getAlertDefinesByType(METRIC_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(7L, result.get(0).getId());
|
||||
assertEquals(8L, result.get(1).getId());
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
|
||||
|
||||
// Test valid metric periodic alert type
|
||||
reset(alertDefineDao);
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC))
|
||||
.thenReturn(Lists.newArrayList());
|
||||
|
||||
|
||||
result = alertDefineService.getAlertDefinesByType(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(0, result.size());
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(METRIC_ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
|
||||
|
||||
// Test valid log realtime alert type
|
||||
reset(alertDefineDao);
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_REALTIME))
|
||||
.thenReturn(mockAlertDefines);
|
||||
|
||||
|
||||
result = alertDefineService.getAlertDefinesByType(LOG_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(2, result.size());
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
|
||||
|
||||
// Test valid log periodic alert type
|
||||
reset(alertDefineDao);
|
||||
when(alertDefineDao.findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_PERIODIC))
|
||||
.thenReturn(Lists.newArrayList());
|
||||
|
||||
|
||||
result = alertDefineService.getAlertDefinesByType(LOG_ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(0, result.size());
|
||||
verify(alertDefineDao, times(1)).findAlertDefinesByTypeAndEnableTrue(LOG_ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
|
||||
|
||||
// Test empty string type
|
||||
assertThrows(IllegalArgumentException.class, () -> alertDefineService.getAlertDefinesByType(""));
|
||||
|
||||
|
||||
// Test null type
|
||||
assertThrows(IllegalArgumentException.class, () -> alertDefineService.getAlertDefinesByType(null));
|
||||
|
||||
|
||||
// Test invalid type
|
||||
assertThrows(IllegalArgumentException.class, () -> alertDefineService.getAlertDefinesByType("invalid_type"));
|
||||
}
|
||||
|
||||
@@ -1,50 +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.constants;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Theme Enum
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public enum ThemeEnum {
|
||||
|
||||
COMPACT("compact"), DEFAULT("default"), LIGHT("light"), DARK("dark");
|
||||
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Convert theme to standard theme
|
||||
*
|
||||
* @param theme the input theme
|
||||
* @return the converted theme
|
||||
*/
|
||||
public static String convert(String theme) {
|
||||
if ((theme == null || theme.isEmpty())
|
||||
|| ThemeEnum.COMPACT.getValue().equals(theme)
|
||||
|| ThemeEnum.DEFAULT.getValue().equals(theme)) {
|
||||
return ThemeEnum.LIGHT.getValue();
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -34,7 +34,7 @@ import lombok.NoArgsConstructor;
|
||||
@Entity
|
||||
@Table(name = "hzb_grafana_dashboard")
|
||||
@Data
|
||||
@Builder(toBuilder = true)
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Grafana dashboard entity")
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- hertzbeat base -->
|
||||
<!-- hertzbeat common -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-base</artifactId>
|
||||
<artifactId>hertzbeat-common</artifactId>
|
||||
</dependency>
|
||||
<!-- hertzbeat warehouse -->
|
||||
<dependency>
|
||||
|
||||
+1
-3
@@ -33,8 +33,6 @@ public interface GrafanaConstants {
|
||||
|
||||
String INSTANCE = "&var-instance=";
|
||||
|
||||
String THEME = "&theme=";
|
||||
|
||||
String CREATE_DASHBOARD_API = "/api/dashboards/db";
|
||||
|
||||
String DELETE_DASHBOARD_API = "/api/dashboards/uid/%s";
|
||||
@@ -94,4 +92,4 @@ public interface GrafanaConstants {
|
||||
static String generateUseDatasource(String datasourceName) {
|
||||
return "&var-ds=" + datasourceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-52
@@ -17,15 +17,17 @@
|
||||
|
||||
package org.apache.hertzbeat.grafana.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
import org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum;
|
||||
import org.apache.hertzbeat.common.constants.ThemeEnum;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.CREATE_DASHBOARD_API;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.DELETE_DASHBOARD_API;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.INSTANCE;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.KIOSK;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.REFRESH;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
|
||||
import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.grafana.common.GrafanaConstants;
|
||||
import org.apache.hertzbeat.grafana.config.GrafanaProperties;
|
||||
@@ -40,17 +42,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.CREATE_DASHBOARD_API;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.DELETE_DASHBOARD_API;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.INSTANCE;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.KIOSK;
|
||||
import static org.apache.hertzbeat.grafana.common.GrafanaConstants.REFRESH;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Service for managing Grafana dashboards.
|
||||
@@ -74,11 +66,6 @@ public class DashboardService {
|
||||
@Autowired
|
||||
private DatasourceService datasourceService;
|
||||
|
||||
@Autowired
|
||||
private GeneralConfigDao generalConfigDao;
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Creates or updates a dashboard in Grafana.
|
||||
* The "id" field will be removed from the dashboard JSON before sending
|
||||
@@ -238,16 +225,7 @@ public class DashboardService {
|
||||
* @return GrafanaDashboard object
|
||||
*/
|
||||
public GrafanaDashboard getDashboardByMonitorId(Long monitorId) {
|
||||
GrafanaDashboard dashboard = dashboardDao.findByMonitorId(monitorId);
|
||||
if (null != dashboard
|
||||
&& StringUtils.isNotBlank(dashboard.getUrl())
|
||||
&& !dashboard.getUrl().contains(GrafanaConstants.THEME)) {
|
||||
String theme = loadThemeConfig();
|
||||
return dashboard.toBuilder()
|
||||
.url(dashboard.getUrl() + GrafanaConstants.THEME + theme)
|
||||
.build();
|
||||
}
|
||||
return dashboard;
|
||||
return dashboardDao.findByMonitorId(monitorId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,21 +248,4 @@ public class DashboardService {
|
||||
log.warn("No Grafana dashboard record found for monitorId {} to disable.", monitorId);
|
||||
}
|
||||
}
|
||||
|
||||
private String loadThemeConfig() {
|
||||
try {
|
||||
GeneralConfig config = generalConfigDao.findByType(GeneralConfigTypeEnum.system.name());
|
||||
if (config != null && config.getContent() != null) {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(config.getContent());
|
||||
JsonNode node = root.get("theme");
|
||||
if (node != null && !node.isNull()) {
|
||||
return ThemeEnum.convert(node.asText());
|
||||
}
|
||||
return ThemeEnum.LIGHT.getValue();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to load database theme configuration", e);
|
||||
}
|
||||
return ThemeEnum.LIGHT.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-45
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.hertzbeat.grafana.service;
|
||||
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
|
||||
import org.apache.hertzbeat.grafana.config.GrafanaProperties;
|
||||
import org.apache.hertzbeat.grafana.dao.DashboardDao;
|
||||
@@ -35,12 +34,10 @@ import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
|
||||
/**
|
||||
* Test case for {@link DashboardService}
|
||||
@@ -63,9 +60,6 @@ public class DashboardServiceTest {
|
||||
@Mock
|
||||
private DatasourceService datasourceService;
|
||||
|
||||
@Mock
|
||||
private GeneralConfigDao generalConfigDao;
|
||||
|
||||
@InjectMocks
|
||||
private DashboardService dashboardService;
|
||||
|
||||
@@ -79,27 +73,25 @@ public class DashboardServiceTest {
|
||||
+ " \"version\": 3"
|
||||
+ "}";
|
||||
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lenient().when(datasourceService.getCurrentDatasourceName()).thenReturn("hertzbeat-vm-localhost-8428");
|
||||
lenient().when(grafanaProperties.enabled()).thenReturn(Boolean.TRUE);
|
||||
lenient().when(grafanaProperties.getPrefix()).thenReturn("");
|
||||
lenient().when(grafanaProperties.getUrl()).thenReturn("http://127.0.0.1:3000");
|
||||
lenient().when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000");
|
||||
lenient().when(serviceAccountService.getToken()).thenReturn("test-token");
|
||||
when(datasourceService.getCurrentDatasourceName()).thenReturn("hertzbeat-vm-localhost-8428");
|
||||
when(grafanaProperties.enabled()).thenReturn(Boolean.TRUE);
|
||||
when(grafanaProperties.getPrefix()).thenReturn("");
|
||||
when(grafanaProperties.getUrl()).thenReturn("http://127.0.0.1:3000");
|
||||
when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000");
|
||||
when(serviceAccountService.getToken()).thenReturn("test-token");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testCreateOrUpdateDashboard() {
|
||||
ResponseEntity<String> responseEntity = new ResponseEntity<>(GRAFANA_API_RESULT, HttpStatus.OK);
|
||||
|
||||
|
||||
when(restTemplate.postForEntity(
|
||||
eq("http://127.0.0.1:3000/api/dashboards/db"), any(HttpEntity.class), eq(String.class)
|
||||
)).thenReturn(responseEntity);
|
||||
|
||||
|
||||
ArgumentCaptor<GrafanaDashboard> dashboardCaptor = ArgumentCaptor.forClass(GrafanaDashboard.class);
|
||||
dashboardService.createOrUpdateDashboard("{\"id\":11}", 1L);
|
||||
verify(dashboardDao).save(dashboardCaptor.capture());
|
||||
@@ -117,7 +109,7 @@ public class DashboardServiceTest {
|
||||
assertTrue(savedDashboard.getUrl().contains("refresh=15s"), "URL should contain refresh parameter");
|
||||
assertTrue(savedDashboard.getUrl().contains("var-instance=1"), "URL should contain instance parameter");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testCreateOrUpdateDashboardWithTrailingSlash() {
|
||||
when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000/");
|
||||
@@ -146,31 +138,4 @@ public class DashboardServiceTest {
|
||||
assertTrue(savedDashboard.getUrl().contains("var-instance=1"), "URL should contain instance parameter");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testGetDashboardByMonitorId() {
|
||||
Long monitorId = 1L;
|
||||
String originalUrl = "http://localhost:3000/d/123/my-dashboard?kiosk";
|
||||
GrafanaDashboard originalDashboard = GrafanaDashboard.builder()
|
||||
.monitorId(monitorId)
|
||||
.url(originalUrl)
|
||||
.build();
|
||||
|
||||
when(dashboardDao.findByMonitorId(eq(monitorId))).thenReturn(originalDashboard);
|
||||
|
||||
String themeConfigJson = "{\"theme\":\"light\"}";
|
||||
org.apache.hertzbeat.common.entity.manager.GeneralConfig runConfig = new org.apache.hertzbeat.common.entity.manager.GeneralConfig();
|
||||
runConfig.setContent(themeConfigJson);
|
||||
|
||||
when(generalConfigDao.findByType(eq(org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum.system.name())))
|
||||
.thenReturn(runConfig);
|
||||
|
||||
GrafanaDashboard resultDashboard = dashboardService.getDashboardByMonitorId(monitorId);
|
||||
|
||||
assertNotNull(resultDashboard);
|
||||
assertTrue(resultDashboard.getUrl().contains("&theme=light"));
|
||||
assertTrue(originalDashboard.getUrl().endsWith("?kiosk"));
|
||||
assertFalse(originalDashboard.getUrl().contains("&theme="));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -26,7 +26,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.service.LabelService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -46,7 +46,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/label", produces = {APPLICATION_JSON_VALUE})
|
||||
public class LabelController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private LabelService labelService;
|
||||
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.base.dao;
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -33,7 +33,7 @@ public interface LabelDao extends JpaRepository<Label, Long>, JpaSpecificationEx
|
||||
* @param ids id list
|
||||
*/
|
||||
void deleteLabelsByIdIn(Set<Long> ids);
|
||||
|
||||
|
||||
/**
|
||||
* find Label by name and value
|
||||
* @param name Label name
|
||||
@@ -41,5 +41,5 @@ public interface LabelDao extends JpaRepository<Label, Long>, JpaSpecificationEx
|
||||
* @return Label
|
||||
*/
|
||||
Optional<Label> findLabelByNameAndTagValue(String name, String value);
|
||||
|
||||
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.base.service;
|
||||
package org.apache.hertzbeat.manager.service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
+1
-1
@@ -31,7 +31,7 @@ import org.apache.hertzbeat.manager.config.ManagerSseManager;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.MonitorDto;
|
||||
import org.apache.hertzbeat.manager.service.ImExportService;
|
||||
import org.apache.hertzbeat.manager.service.MonitorService;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.service.LabelService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.service.LabelService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ import org.apache.hertzbeat.grafana.service.DashboardService;
|
||||
import org.apache.hertzbeat.manager.component.validator.ParamValidatorManager;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorDao;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorDao;
|
||||
import org.apache.hertzbeat.manager.dao.ParamDao;
|
||||
@@ -59,7 +59,7 @@ import org.apache.hertzbeat.manager.pojo.dto.MetricsInfo;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.MonitorDto;
|
||||
import org.apache.hertzbeat.manager.scheduler.CollectJobScheduling;
|
||||
import org.apache.hertzbeat.manager.service.AppService;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.service.MetricsFavoriteService;
|
||||
import org.apache.hertzbeat.manager.service.MonitorService;
|
||||
import org.apache.hertzbeat.manager.service.helper.MonitorImExportHelper;
|
||||
|
||||
@@ -25,7 +25,7 @@ name:
|
||||
# The description and help of this monitoring type
|
||||
help:
|
||||
zh-CN: HertzBeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jdbc'> JDBC 协议</a> 通过配置 SQL 对 Mysql 数据库的通用性能指标(系统信息、性能状态、Innodb、缓存、事物、用户线程、慢SQL等)进行采集监控,支持版本为 Mysql5+。<br>您可以点击“<i>新建 Mysql 数据库</i>”并进行配置,或者选择“<i>更多操作</i>”,导入已有配置。
|
||||
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jdbc'>JDBC Protocol</a> to collect general performance metrics of MySQL databases (system information, performance status, InnoDB, cache, transactions, user threads, slow SQL, etc.) by configuring SQL. Supported versions include MySQL 5.0 and above. <br>You can click "<i>New MySQL Database</i>" to configure it, or select "<i>More Operations</i>" to import an existing configuration.
|
||||
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jdbc'> JDBC Protocol</a> to configure SQL for collecting general metrics of Mysql database (system information, performance status, Innodb, cache, things, user threads, slow SQL, etc.). Supported version is Mysql5+. <br>You can click "<i>New Mysql Database</i>" and configure it, or select "<i>More Operations</i>" to import the existing configuration.
|
||||
zh-TW: HertzBeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jdbc'> JDBC 協議</a> 通過配置 SQL 對 Mysql 數據庫的通用性能指標(系統信息、性能狀態、Innodb、緩存、事物、用戶線程、慢SQL等)進行采集監控,支持版本爲 Mysql5+。<br>您可以點擊“<i>新建 Mysql 數據庫</i>”並進行配置,或者選擇“<i>更多操作</i>”,導入已有配置。
|
||||
ja-JP: HertzBeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-jdbc'> JDBC プロトコル</a> を介して Mysql データベースの一般的なパフォーマンスのメトリクス(システム情報、パフォーマンスステータス、Innodb、キャッシュ、オブジェクト、ユーザースレッド、遅いSQLなど)収集監視を行う,サポートバージョンは Mysql5+。<br>クリックすることができます“<i>新規 Mysql データベース</i>”および構成,または“<i>その他の操作</i>”,既存の構成のインポートすることができます。
|
||||
helpLink:
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.hertzbeat.manager.service;
|
||||
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -35,7 +34,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.service.impl.LabelServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
|
||||
import org.apache.hertzbeat.base.service.LabelService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.base.dao.LabelDao;
|
||||
import org.apache.hertzbeat.manager.dao.LabelDao;
|
||||
import org.apache.hertzbeat.startup.AbstractSpringIntegrationTest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
+3
-18
@@ -49,8 +49,6 @@ import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* data storage by duckdb
|
||||
@@ -66,9 +64,6 @@ public class DuckdbDatabaseDataStorage extends AbstractHistoryDataStorage {
|
||||
// Ideal number of data points for charting (avoids frontend lag)
|
||||
private static final int TARGET_CHART_POINTS = 800;
|
||||
|
||||
// Regex to strictly match days format, e.g., "90d", "7D". Group 1 captures the digits.
|
||||
private static final Pattern DAY_PATTERN = Pattern.compile("^(\\d+)[dD]$");
|
||||
|
||||
private final String expireTimeStr;
|
||||
private final String dbPath;
|
||||
private HikariDataSource dataSource;
|
||||
@@ -141,21 +136,11 @@ public class DuckdbDatabaseDataStorage extends AbstractHistoryDataStorage {
|
||||
log.info("[duckdb] start data cleaner and checkpoint...");
|
||||
long expireTime;
|
||||
try {
|
||||
// Ensure no whitespace issues
|
||||
String cleanExpireStr = expireTimeStr == null ? "" : expireTimeStr.trim();
|
||||
Matcher dayMatcher = DAY_PATTERN.matcher(cleanExpireStr);
|
||||
|
||||
if (NumberUtils.isParsable(cleanExpireStr)) {
|
||||
expireTime = NumberUtils.toLong(cleanExpireStr);
|
||||
if (NumberUtils.isParsable(expireTimeStr)) {
|
||||
expireTime = NumberUtils.toLong(expireTimeStr);
|
||||
expireTime = (ZonedDateTime.now().toEpochSecond() - expireTime) * 1000L;
|
||||
} else if (dayMatcher.matches()) {
|
||||
// Strictly matched "90d" or "90D" format
|
||||
long days = Long.parseLong(dayMatcher.group(1));
|
||||
ZonedDateTime dateTime = ZonedDateTime.now().minus(Duration.ofDays(days));
|
||||
expireTime = dateTime.toEpochSecond() * 1000L;
|
||||
} else {
|
||||
// Fallback to existing utility for other units (h, m, s, etc.)
|
||||
TemporalAmount temporalAmount = TimePeriodUtil.parseTokenTime(cleanExpireStr);
|
||||
TemporalAmount temporalAmount = TimePeriodUtil.parseTokenTime(expireTimeStr);
|
||||
ZonedDateTime dateTime = ZonedDateTime.now().minus(temporalAmount);
|
||||
expireTime = dateTime.toEpochSecond() * 1000L;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# you need to remember that it will be used in subsequent steps.
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Please enter this passphrase to │
|
||||
@@ -146,7 +146,7 @@ $ svn co https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
$ cd svn/dev/hertzbeat
|
||||
|
||||
# Append the KEY you generated to the file KEYS, and check if it is added correctly
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -161,7 +161,7 @@ $ svn co https://dist.apache.org/repos/dist/release/hertzbeat
|
||||
$ cd svn/release/hertzbeat
|
||||
|
||||
# Append the KEY you generated to the file KEYS, and check if it is added correctly
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -310,15 +310,15 @@ apache-hertzbeat-collector-1.7.3-bin.tar.gz: OK
|
||||
|
||||
```shell
|
||||
# Check out the dev directory of the Apache SVN to the svn/dev directory under dist in the root directory of the Apache HertzBeat™ project
|
||||
svn co https://dist.apache.org/repos/dist/dev/hertzbeat svn/dev
|
||||
svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat svn/dev
|
||||
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/incubator/hertzbeat
|
||||
```
|
||||
|
||||
- Copy the material package to the dev directory
|
||||
|
||||
Create a version number directory and name it in the form of ${release_version}-${RC_version}. RC_version starts from 1, that is, the candidate version starts from RC1. During the release process, there is a problem that causes the vote to fail.
|
||||
If it needs to be corrected, it needs to iterate the RC version , the RC version number needs to be +1. For example: Vote for version 1.7.3-RC1. If the vote passes without any problems, the RC1 version material will be released as the final version material.
|
||||
Create a version number directory and name it in the form of ${release_version}-${RC_version}. RC_version starts from 1, that is, the candidate version starts from RC1. During the release process, there is a problem that causes the vote to fail.
|
||||
If it needs to be corrected, it needs to iterate the RC version , the RC version number needs to be +1. For example: Vote for version 1.7.3-RC1. If the vote passes without any problems, the RC1 version material will be released as the final version material.
|
||||
If there is a problem (when the hertzbeat/incubator community votes, the voters will strictly check various release requirements and compliance issues) and need to be corrected, then re-initiate the vote after the correction, and the candidate version for the next vote is 1.7.3- RC2.
|
||||
|
||||
```shell
|
||||
@@ -368,13 +368,13 @@ Release notes:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.7.3-rc1
|
||||
|
||||
The release candidates:
|
||||
https://dist.apache.org/repos/dist/dev/hertzbeat/1.7.3-RC1/
|
||||
https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.7.3-RC1/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.7.3-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -492,17 +492,17 @@ The rename the release-1.7.3-rc1 branch to release-1.7.3.
|
||||
Hi Community,
|
||||
|
||||
We are glad to announce the release of Apache HertzBeat™ 1.7.3.
|
||||
Thanks again for your help.
|
||||
Thanks again for your help.
|
||||
|
||||
Apache HertzBeat™ (https://hertzbeat.apache.org/) - A real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
Download Link:
|
||||
Download Link:
|
||||
https://hertzbeat.apache.org/docs/download/
|
||||
|
||||
Release Note:
|
||||
Release Note:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.7.3
|
||||
|
||||
Website:
|
||||
Website:
|
||||
https://hertzbeat.apache.org/
|
||||
|
||||
HertzBeat Resources:
|
||||
|
||||
@@ -22,9 +22,9 @@ If the network is poor, downloading may be time-consuming. The download is compl
|
||||
|
||||
```shell
|
||||
#If there is svn locally, you can clone to the local
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/
|
||||
#or download the material file directly
|
||||
$ wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
$ wget https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
```
|
||||
|
||||
### 2. Verify that the uploaded version is compliant
|
||||
@@ -47,7 +47,7 @@ First import the publisher's public key. Import KEYS from the svn repository to
|
||||
##### 2.2.1 Import public key
|
||||
|
||||
```shell
|
||||
-curl https://downloads.apache.org/hertzbeat/KEYS > KEYS # Download KEYS
|
||||
curl https://downloads.apache.org/incubator/hertzbeat/KEYS > KEYS # Download KEYS
|
||||
gpg --import KEYS # Import KEYS to local
|
||||
```
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ community members are welcome to comment and modify it.
|
||||
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **RE10** | Releases consist of source code, distributed using standard and open archive formats that are expected to stay readable in the long term. | **YES** Source release is distributed via [dist.apache.org](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/) and linked from [download page](https://hertzbeat.apache.org/docs/download). |
|
||||
| **RE20** | The project's PPMC (Project Management Committee, see CS10) approves each software release in order to make the release an act of the Foundation. | **YES** All releases have been voted at <dev@hertzbeat.apache.org> and <general@incubator.apache.org>, and have at least 3 PPMC member's votes. |
|
||||
| **RE30** | Releases are signed and/or distributed along with digests that anyone can reliably use to validate the downloaded archives. | **YES** All releases are signed, and the [KEYS](https://dist.apache.org/repos/dist/release/hertzbeat/KEYS) are available. |
|
||||
| **RE30** | Releases are signed and/or distributed along with digests that anyone can reliably use to validate the downloaded archives. | **YES** All releases are signed, and the [KEYS](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/KEYS) are available. |
|
||||
| **RE40** | The project can distribute convenience binaries alongside source code, but they are not Apache Releases, they are provided with no guarantee. | **YES** User can easily build binaries from source code, and we do not provide binaries as Apache Releases. |
|
||||
| **RE50** | The project documents a repeatable release process so that someone new to the project can independently generate the complete set of artifacts required for a release. | **YES** We can follow the [Release guide](https://hertzbeat.apache.org/docs/community/how_to_release) to make a new Apache HertzBeat release, and so far we had 4 different release managers. |
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: docker-compose-deploy
|
||||
title: Install HertzBeat via Docker Compose
|
||||
title: Install HertzBeat via Docker Compose
|
||||
sidebar_label: Install via Docker Compose
|
||||
---
|
||||
|
||||
@@ -21,7 +21,7 @@ Run the `docker compose version` command to check if you have a Docker Compose e
|
||||
|
||||
:::tip
|
||||
|
||||
- `apache-hertzbeat-${version}-docker-compose.tar.gz` contains multiple deployment solutions after decompression. Here we recommend choosing the `hertzbeat-postgresql-victoria-metrics` solution.
|
||||
- `apache-hertzbeat-xxx-docker-compose.tar.gz` contains multiple deployment solutions after decompression. Here we recommend choosing the `hertzbeat-postgresql-victoria-metrics` solution.
|
||||
- Other deployment methods, please read the README.md file of each deployment solution in detail. The MySQL solution requires you to prepare the MySQL driver package yourself.
|
||||
|
||||
:::
|
||||
@@ -29,13 +29,13 @@ Run the `docker compose version` command to check if you have a Docker Compose e
|
||||
- Unzip the script package
|
||||
|
||||
```shell
|
||||
tar zxvf apache-hertzbeat-${version}-docker-compose.tar.gz
|
||||
tar zxvf apache-hertzbeat-1.6.0-docker-compose.tar.gz
|
||||
```
|
||||
|
||||
- Enter the decompression directory and select `HertzBeat + PostgreSQL + VictoriaMetrics` for one-click deployment
|
||||
|
||||
```shell
|
||||
cd apache-hertzbeat-${version}-docker-compose
|
||||
cd apache-hertzbeat-1.6.0-docker-compose
|
||||
cd hertzbeat-postgresql-victoria-metrics
|
||||
```
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ module.exports = {
|
||||
},
|
||||
copyright:
|
||||
`
|
||||
<div style="text-align: left;margin-top:20px">
|
||||
<div style="text-align: left;margin-top:20px">
|
||||
<a href="https://apache.org/" style="display: flex; align-items: center; justify-content:center">
|
||||
<img src="/img/icons/asf_logo.svg" alt="Apache logo" style="width:auto;height:140px">
|
||||
</a>
|
||||
@@ -399,15 +399,5 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
],
|
||||
themes: ['@docusaurus/theme-live-codeblock'],
|
||||
scripts: [
|
||||
{
|
||||
src: "https://widget.kapa.ai/kapa-widget.bundle.js",
|
||||
"data-website-id": "cb9dd0e9-5736-4572-be75-fd2ebe7443b0",
|
||||
"data-project-name": "Apache HertzBeat",
|
||||
"data-project-color": "#444FD9",
|
||||
"data-project-logo": "https://hertzbeat.apache.org/zh-cn/assets/files/hertzbeat-logo-f92c500146ae43b9cb901600cd274d67.png",
|
||||
async: true,
|
||||
},
|
||||
],
|
||||
themes: ['@docusaurus/theme-live-codeblock']
|
||||
}
|
||||
|
||||
@@ -200,13 +200,13 @@
|
||||
"description": "The live editor label of the live codeblocks"
|
||||
},
|
||||
"convenient": {
|
||||
"message": "Easy to Use"
|
||||
"message": "Convenient Used"
|
||||
},
|
||||
"custom-multi-support": {
|
||||
"message": "High Performance & Extensible"
|
||||
"message": "High Performance and Custom"
|
||||
},
|
||||
"opensource": {
|
||||
"message": "Open Source & Community Driven"
|
||||
"message": "Opensource Friendly"
|
||||
},
|
||||
"theme.ErrorPageContent.title": {
|
||||
"message": "This page crashed.",
|
||||
@@ -257,13 +257,13 @@
|
||||
"description": "The label used by the button on the collapsible TOC component"
|
||||
},
|
||||
"convenient-content": {
|
||||
"message": "{docker} {br} All-in-one monitoring, alerting, and notification system. Supports web services, databases, caching, OS, middleware, big data, cloud-native, and network monitoring. {br} Agentless with full web-based operations, simply point-and-click to get started. {br} Enterprise-grade security with end-to-end encryption for all sensitive data."
|
||||
"message": "{docker} {br} Monitoring-alarm-notify all in one. Support monitoring web service, database, cache, os, web-server, middleware, bigdata, cloud-native, network etc. {br} Easy to use and agentless, full web-based operations with just a click of a mouse. {br} Security is the most important, encrypt the whole secret data."
|
||||
},
|
||||
"custom-multi-support-content": {
|
||||
"message": "Configurable protocols including HTTP, JMX, SSH, SNMP, and JDBC. Define custom metrics with simple online YAML configuration. {br} High-performance architecture with horizontal scaling for collector clusters, multi-network isolation, and cloud-edge collaboration. {br} Flexible alerting rules with timely notifications via Discord, Slack, email, Webhook, and more."
|
||||
"message": "Makes protocols such as Http, Jmx, Ssh, Snmp, Jdbc configurable, you can collect any metrics by simply configuring the yml online. {br} High performance, supports horizontal expansion of multi-collector clusters, multi-isolated network monitoring and cloud-edge collaboration. {br} Flexible threshold rules and timely notifications delivered via discord slack email webhook more."
|
||||
},
|
||||
"opensource-content": {
|
||||
"message": "Apache HertzBeat™ is open source with an inclusive community. We welcome contributions of all sizes—code, documentation, or bug reports. {br} Respect every contributor and grow together. {br} Deployed on thousands of servers worldwide."
|
||||
"message": "Apache HertzBeat™ is open source, has an inclusive and open community. Unlimited and anyone who are interested in it are very welcome to contribute. No matter how small the contribution is, whether it is a code document or a typo, respect everyone and grow together. {br} Our code is being deployed on thousands of machines worldwide."
|
||||
},
|
||||
"slogan": {
|
||||
"message": "AI-powered Open Source Observability System"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"convenient": {
|
||||
"message": "简单易用"
|
||||
"message": "开箱即用"
|
||||
},
|
||||
"custom-multi-support": {
|
||||
"message": "高性能可扩展"
|
||||
"message": "高性能与自定义"
|
||||
},
|
||||
"opensource": {
|
||||
"message": "开源社区"
|
||||
"message": "拥抱开源"
|
||||
},
|
||||
"theme.ErrorPageContent.title": {
|
||||
"message": "页面已崩溃。",
|
||||
@@ -431,13 +431,13 @@
|
||||
"description": "The live editor label of the live codeblocks"
|
||||
},
|
||||
"convenient-content": {
|
||||
"message": "{docker} {br}集监控、告警、通知于一体,支持 Web 服务、数据库、缓存、操作系统、中间件、大数据、云原生、网络等全方位监控。{br}无需 Agent,全 Web 可视化操作,点点鼠标即可上手。{br}企业级安全保障,敏感数据全链路加密。"
|
||||
"message": "{docker} {br}集监控-告警-通知为一体,支持应用服务,Web,数据库,缓存,操作系统,中间件,大数据,云原生,网络等监控阈值告警通知一步到位。{br} 易用友好,无需Agent,全WEB页面操作,鼠标点一点就能监控告警,无需学习成本。{br}安全是最重要的,数据密钥全链路加密。"
|
||||
},
|
||||
"custom-multi-support-content": {
|
||||
"message": "支持 HTTP、JMX、SSH、SNMP、JDBC 等协议可配置化,在线配置 YAML 即可自定义监控指标。{br}高性能架构,支持多采集器集群横向扩展、多网络隔离监控、云边协同。{br}灵活的告警规则,支持邮箱、短信、钉钉、企业微信、飞书、Webhook 等多种通知方式。"
|
||||
"message": "将 Http,Jmx,Ssh,Snmp,Jdbc 等协议规范可配置模板化,只需在线配置YML就可自定义监控指标。{br} 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。{br}灵活的告警阈值规则,邮箱,短信,钉钉,企业微信,飞书,Webhook等消息及时送达。{br} 您相信只需配置下就能适配新K8s监控类型吗?"
|
||||
},
|
||||
"opensource-content": {
|
||||
"message": "Apache HertzBeat™ 是开源项目,拥有开放包容的社区。{br}欢迎任何形式的贡献,无论是代码、文档还是问题反馈,尊重每位贡献者,共同成长。{br}已部署至全球数千台服务器。"
|
||||
"message": "Apache HertzBeat™ 是开源的,拥有一个包容开放的社区。{br}欢迎任何对此有兴趣的同学参与其中,无论是代码文档或者错别字,尊重社区的每一位,一起进步成长。{br}我们的代码正被部署到全球成千上万机器上。"
|
||||
},
|
||||
"slogan": {
|
||||
"message": "AI 驱动的下一代开源观测系统"
|
||||
|
||||
@@ -71,7 +71,7 @@ some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# you need to remember that it will be used in subsequent steps.
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Please enter this passphrase to │
|
||||
@@ -146,7 +146,7 @@ $ svn co https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
$ cd svn/dev/hertzbeat
|
||||
|
||||
# 将生成的KEY追加到KEYS文件中,检查是否添加正确
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -161,7 +161,7 @@ $ svn co https://dist.apache.org/repos/dist/release/hertzbeat
|
||||
$ cd svn/release/hertzbeat
|
||||
|
||||
# 将生成的KEY追加到KEYS文件中,检查是否添加正确
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -374,7 +374,7 @@ Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.7.3-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -492,17 +492,17 @@ release note: xxx
|
||||
Hi Community,
|
||||
|
||||
We are glad to announce the release of Apache HertzBeat 1.7.3.
|
||||
Thanks again for your help.
|
||||
Thanks again for your help.
|
||||
|
||||
Apache HertzBeat (https://hertzbeat.apache.org/) - a real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
Download Link:
|
||||
Download Link:
|
||||
https://hertzbeat.apache.org/docs/download/
|
||||
|
||||
Release Note:
|
||||
Release Note:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.7.3
|
||||
|
||||
Website:
|
||||
Website:
|
||||
https://hertzbeat.apache.org/
|
||||
|
||||
HertzBeat Resources:
|
||||
|
||||
@@ -16,9 +16,9 @@ sidebar_position: 4
|
||||
|
||||
```shell
|
||||
#如果本地有svn,可以clone到本地
|
||||
svn co https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/
|
||||
svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/
|
||||
#或者 直接下载物料文件
|
||||
wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
wget https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
|
||||
```
|
||||
|
||||
@@ -42,7 +42,7 @@ wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_ve
|
||||
##### 2.2.1 导入公钥
|
||||
|
||||
```shell
|
||||
curl https://downloads.apache.org/hertzbeat/KEYS > KEYS # 下载KEYS
|
||||
curl https://downloads.apache.org/incubator/hertzbeat/KEYS > KEYS # 下载KEYS
|
||||
gpg --import KEYS # 导入KEYS到本地
|
||||
```
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ The following table is filled according to the [Apache Maturity Model](https://c
|
||||
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **RE10** | Releases consist of source code, distributed using standard and open archive formats that are expected to stay readable in the long term. | **YES** Source release is distributed via [dist.apache.org](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/) and linked from [download page](https://hertzbeat.apache.org/docs/download). |
|
||||
| **RE20** | The project's PPMC (Project Management Committee, see CS10) approves each software release in order to make the release an act of the Foundation. | **YES** All releases have been voted at <dev@hertzbeat.apache.org> and <general@incubator.apache.org>, and have at least 3 PPMC member's votes. |
|
||||
| **RE30** | Releases are signed and/or distributed along with digests that anyone can reliably use to validate the downloaded archives. | **YES** All releases are signed, and the [KEYS](https://dist.apache.org/repos/dist/release/hertzbeat/KEYS) are available. |
|
||||
| **RE30** | Releases are signed and/or distributed along with digests that anyone can reliably use to validate the downloaded archives. | **YES** All releases are signed, and the [KEYS](https://dist.apache.org/repos/dist/release/incubator/hertzbeat/KEYS) are available. |
|
||||
| **RE40** | The project can distribute convenience binaries alongside source code, but they are not Apache Releases, they are provided with no guarantee. | **YES** User can easily build binaries from source code, and we do not provide binaries as Apache Releases. |
|
||||
| **RE50** | The project documents a repeatable release process so that someone new to the project can independently generate the complete set of artifacts required for a release. | **YES** We can follow the [Release guide](https://hertzbeat.apache.org/docs/community/how_to_release) to make a new Apache HertzBeat release, and so far we had 4 different release managers. |
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
|
||||
```shell
|
||||
./configure --prefix=/usr/local/nginx --with-http_stub_status_module
|
||||
|
||||
|
||||
make && make install
|
||||
```
|
||||
|
||||
@@ -66,15 +66,15 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
```shell
|
||||
# install `ngx_http_reqstat_module`
|
||||
wget https://github.com/zls0424/ngx_req_status/archive/master.zip -O ngx_req_status.zip
|
||||
|
||||
|
||||
unzip ngx_req_status.zip
|
||||
|
||||
|
||||
patch -p1 < ../ngx_req_status-master/write_filter.patch
|
||||
|
||||
|
||||
./configure --prefix=/usr/local/nginx --add-module=/path/to/ngx_req_status-master
|
||||
|
||||
|
||||
make -j2
|
||||
|
||||
|
||||
make install
|
||||
```
|
||||
|
||||
@@ -87,9 +87,9 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
http {
|
||||
req_status_zone server_name $server_name 256k;
|
||||
req_status_zone server_addr $server_addr 256k;
|
||||
|
||||
|
||||
req_status server_name server_addr;
|
||||
|
||||
|
||||
server {
|
||||
location /req-status {
|
||||
req_status_show on;
|
||||
@@ -108,7 +108,6 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
|
||||
4. 在浏览器访问 `http://localhost/req-status` 即可查看 Nginx 监控状态信息。
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
**参考文档: <https://blog.csdn.net/weixin_55985097/article/details/116722309>**
|
||||
|
||||
**⚠️注意监控模块的端点路径为 `/nginx-status` `/req-status`**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: tomcat
|
||||
title: 监控:Tomcat监控
|
||||
id: tomcat
|
||||
title: 监控:Tomcat监控
|
||||
sidebar_label: Tomcat监控
|
||||
keywords: [开源监控系统, 开源网站监控, Tomcat监控]
|
||||
---
|
||||
@@ -72,5 +72,4 @@ keywords: [开源监控系统, 开源网站监控, Tomcat监控]
|
||||
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=10.1.1.52 -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
|
||||
```
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
参考: <https://blog.csdn.net/weixin_41924764/article/details/108694239>
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ sidebar_label: Docker Compose方式安装
|
||||
|
||||
:::tip
|
||||
|
||||
- `apache-hertzbeat-${version}-docker-compose.tar.gz` 解压后包含多个部署方案,这里我们推荐选择 `hertzbeat-postgresql-victoria-metrics` 方案。
|
||||
- `apache-hertzbeat-xxx-docker-compose.tar.gz` 解压后包含多个部署方案,这里我们推荐选择 `hertzbeat-postgresql-victoria-metrics` 方案。
|
||||
- 其它部署方式请详细阅读各个部署方案的 README.md 文件, MySQL 方案需要自行准备 MySQL 驱动包。
|
||||
|
||||
:::
|
||||
@@ -29,13 +29,13 @@ sidebar_label: Docker Compose方式安装
|
||||
- 解压脚本包
|
||||
|
||||
```shell
|
||||
tar zxvf apache-hertzbeat-${version}-docker-compose.tar.gz
|
||||
tar zxvf apache-hertzbeat-1.6.0-docker-compose.tar.gz
|
||||
```
|
||||
|
||||
- 进入解压目录, 选择 `HertzBeat + PostgreSQL + VictoriaMetrics` 一键部署
|
||||
|
||||
```shell
|
||||
cd apache-hertzbeat-${version}-docker-compose
|
||||
cd apache-hertzbeat-1.6.0-docker-compose
|
||||
cd hertzbeat-postgresql-victoria-metrics
|
||||
```
|
||||
|
||||
|
||||
+15
-15
@@ -71,7 +71,7 @@ some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# you need to remember that it will be used in subsequent steps.
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Please enter this passphrase to │
|
||||
@@ -142,11 +142,11 @@ gpg: unchanged: 1
|
||||
$ mkdir -p svn/dev
|
||||
$ cd svn/dev
|
||||
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat
|
||||
$ cd svn/dev/hertzbeat
|
||||
|
||||
# 将生成的KEY追加到KEYS文件中,检查是否添加正确
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -161,7 +161,7 @@ $ svn co https://dist.apache.org/repos/dist/release/incubator/hertzbeat
|
||||
$ cd svn/release/hertzbeat
|
||||
|
||||
# 将生成的KEY追加到KEYS文件中,检查是否添加正确
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -308,9 +308,9 @@ apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz: OK
|
||||
|
||||
```shell
|
||||
# 检出 Apache SVN 仓库中的 dev/incubator/hertzbeat 目录到 Apache HertzBeat™ 项目根目录下的 svn/dev 目录下
|
||||
svn co https://dist.apache.org/repos/dist/dev/hertzbeat svn/dev
|
||||
svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat svn/dev
|
||||
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/incubator/hertzbeat
|
||||
```
|
||||
|
||||
- 复制物料包到 SVN 仓库
|
||||
@@ -364,13 +364,13 @@ Release notes:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The release candidates:
|
||||
https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1/
|
||||
https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -460,13 +460,13 @@ Vote result thread:
|
||||
https://lists.apache.org/thread/t5z58mvrs1drgzfyc48c9lhmd8skswn7
|
||||
|
||||
The release candidate:
|
||||
https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1/
|
||||
https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -542,7 +542,7 @@ ChunJin Mu
|
||||
### 4.1 迁移源代码和二进制包
|
||||
|
||||
```shell
|
||||
svn mv https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1 https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.0 -m "transfer packages for 1.6.0-RC1"
|
||||
svn mv https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1 https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.0 -m "transfer packages for 1.6.0-RC1"
|
||||
```
|
||||
|
||||
### 4.2 添加新版本下载地址到官网
|
||||
@@ -592,17 +592,17 @@ release note: xxx
|
||||
Hi Community,
|
||||
|
||||
We are glad to announce the release of Apache HertzBeat 1.6.0.
|
||||
Thanks again for your help.
|
||||
Thanks again for your help.
|
||||
|
||||
Apache HertzBeat (https://hertzbeat.apache.org/) - a real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
Download Link:
|
||||
Download Link:
|
||||
https://hertzbeat.apache.org/docs/download/
|
||||
|
||||
Release Note:
|
||||
Release Note:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0
|
||||
|
||||
Website:
|
||||
Website:
|
||||
https://hertzbeat.apache.org/
|
||||
|
||||
HertzBeat Resources:
|
||||
|
||||
+9
-9
@@ -15,10 +15,10 @@ sidebar_position: 4
|
||||
> 需要依赖gpg工具,如果没有,建议安装gpg2
|
||||
|
||||
```shell
|
||||
#如果本地有svn,可以clone到本地
|
||||
svn co https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/
|
||||
#如果本地有svn,可以clone到本地
|
||||
svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/
|
||||
#或者 直接下载物料文件
|
||||
wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
wget https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
|
||||
```
|
||||
|
||||
@@ -42,7 +42,7 @@ wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_ve
|
||||
##### 2.2.1 导入公钥
|
||||
|
||||
```shell
|
||||
curl https://downloads.apache.org/hertzbeat/KEYS > KEYS # 下载KEYS
|
||||
curl https://downloads.apache.org/incubator/hertzbeat/KEYS > KEYS # 下载KEYS
|
||||
gpg --import KEYS # 导入KEYS到本地
|
||||
```
|
||||
|
||||
@@ -70,9 +70,9 @@ Please decide how far you trust this user to correctly verify other users' keys
|
||||
|
||||
Your decision? 5 #选择5
|
||||
Do you really want to set this key to ultimate trust? (y/N) y #选择y
|
||||
|
||||
gpg>
|
||||
|
||||
|
||||
gpg>
|
||||
|
||||
```
|
||||
|
||||
##### 2.2.3 检查签名
|
||||
@@ -174,7 +174,7 @@ I checked:
|
||||
1. All download links are valid
|
||||
2. Checksum and signature are OK
|
||||
3. LICENSE and NOTICE are exist
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
5. ....
|
||||
```
|
||||
|
||||
@@ -187,7 +187,7 @@ I checked:
|
||||
1. All download links are valid
|
||||
2. Checksum and signature are OK
|
||||
3. LICENSE and NOTICE are exist
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
5. ....
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ sidebar_label: Download
|
||||
|
||||
:::tip
|
||||
|
||||
- 验证下载版本,请使用相应的哈希(sha512)、签名和[项目发布KEYS](https://downloads.apache.org/hertzbeat/KEYS)。
|
||||
- 验证下载版本,请使用相应的哈希(sha512)、签名和[项目发布KEYS](https://downloads.apache.org/incubator/hertzbeat/KEYS)。
|
||||
- 检查哈希和签名的方法参考 [如何验证](https://www.apache.org/dyn/closer.cgi#verify)。
|
||||
|
||||
:::
|
||||
|
||||
@@ -29,7 +29,7 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
|
||||
```shell
|
||||
./configure --prefix=/usr/local/nginx --with-http_stub_status_module
|
||||
|
||||
|
||||
make && make install
|
||||
```
|
||||
|
||||
@@ -66,15 +66,15 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
```shell
|
||||
# install `ngx_http_reqstat_module`
|
||||
wget https://github.com/zls0424/ngx_req_status/archive/master.zip -O ngx_req_status.zip
|
||||
|
||||
|
||||
unzip ngx_req_status.zip
|
||||
|
||||
|
||||
patch -p1 < ../ngx_req_status-master/write_filter.patch
|
||||
|
||||
|
||||
./configure --prefix=/usr/local/nginx --add-module=/path/to/ngx_req_status-master
|
||||
|
||||
|
||||
make -j2
|
||||
|
||||
|
||||
make install
|
||||
```
|
||||
|
||||
@@ -87,9 +87,9 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
http {
|
||||
req_status_zone server_name $server_name 256k;
|
||||
req_status_zone server_addr $server_addr 256k;
|
||||
|
||||
|
||||
req_status server_name server_addr;
|
||||
|
||||
|
||||
server {
|
||||
location /req-status {
|
||||
req_status_show on;
|
||||
@@ -108,7 +108,6 @@ keywords: [开源监控工具, 开源Java监控工具, 监控Nginx指标]
|
||||
|
||||
4. 在浏览器访问 `http://localhost/req-status` 即可查看 Nginx 监控状态信息。
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
**参考文档: <https://blog.csdn.net/weixin_55985097/article/details/116722309>**
|
||||
|
||||
**⚠️注意监控模块的端点路径为 `/nginx-status` `/req-status`**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: tomcat
|
||||
title: 监控:Tomcat监控
|
||||
id: tomcat
|
||||
title: 监控:Tomcat监控
|
||||
sidebar_label: Tomcat监控
|
||||
keywords: [开源监控系统, 开源网站监控, Tomcat监控]
|
||||
---
|
||||
@@ -72,5 +72,4 @@ keywords: [开源监控系统, 开源网站监控, Tomcat监控]
|
||||
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=10.1.1.52 -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
|
||||
```
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
参考: <https://blog.csdn.net/weixin_41924764/article/details/108694239>
|
||||
|
||||
+8
-131
@@ -1,73 +1,24 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
|
||||
@import url(navbar.css);
|
||||
@import url(hero.css);
|
||||
|
||||
:root {
|
||||
--md-primary: #6750A4;
|
||||
--md-on-primary: #FFFFFF;
|
||||
--md-primary-container: #EADDFF;
|
||||
--md-on-primary-container: #21005D;
|
||||
--md-secondary: #625B71;
|
||||
--md-secondary-container: #E8DEF8;
|
||||
--md-on-secondary-container: #1D192B;
|
||||
--md-tertiary: #7D5260;
|
||||
--md-tertiary-container: #FFD8E4;
|
||||
--md-surface: #FFFBFE;
|
||||
--md-surface-dim: #DDD8E1;
|
||||
--md-surface-bright: #FFFBFE;
|
||||
--md-surface-container-low: #F7F2FA;
|
||||
--md-surface-container: #F3EDF7;
|
||||
--md-surface-container-high: #ECE6F0;
|
||||
--md-on-surface: #1C1B1F;
|
||||
--md-on-surface-variant: #49454F;
|
||||
--md-outline: #79747E;
|
||||
--md-outline-variant: #CAC4D0;
|
||||
|
||||
--ifm-color-primary: #7228B5;
|
||||
--ifm-color-primary-dark: #6228B5;
|
||||
--ifm-color-primary-darker: #5228B5;
|
||||
--ifm-color-primary-darkest: #5128B5;
|
||||
--ifm-color-primary-light: #8228C5;
|
||||
--ifm-color-primary-lighter: #9228D5;
|
||||
--ifm-color-primary-lightest: #A228E5;
|
||||
--ifm-color-primary-light: #7228B5;
|
||||
--ifm-color-primary-lighter: #8228B5;
|
||||
--ifm-color-primary-lightest: #9228B5;
|
||||
--ifm-navbar-link-hover-color: var(--ifm-color-primary);
|
||||
--ifm-code-font-size: 95%;
|
||||
|
||||
--ifm-font-family-base: 'Roboto', Verdana, Arial, Helvetica, sans-serif;
|
||||
--ifm-font-family-monospace: 'Courier New', monospace;
|
||||
|
||||
--ifm-button-padding-horizontal: 1.5rem;
|
||||
--ifm-button-padding-vertical: 0.75rem;
|
||||
--ifm-button-border-radius: 9999px;
|
||||
--ifm-button-border-width: 0;
|
||||
--ifm-font-family-base: Verdana, Arial, Helvetica, sans-serif;
|
||||
--ifm-font-family-monospace: sans-serif;
|
||||
--ifm-button-padding-horizontal: 0.55rem;
|
||||
--ifm-button-padding-vertical: .7rem;
|
||||
--ifm-navbar-height: 85px;
|
||||
|
||||
--md-easing: cubic-bezier(0.2, 0, 0, 1);
|
||||
--md-transition-fast: 200ms var(--md-easing);
|
||||
--md-transition-medium: 300ms var(--md-easing);
|
||||
--ifm-button-border-width: 0;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
--md-primary: #D0BCFF;
|
||||
--md-on-primary: #381E72;
|
||||
--md-primary-container: #4F378B;
|
||||
--md-on-primary-container: #EADDFF;
|
||||
--md-secondary: #CCC2DC;
|
||||
--md-secondary-container: #4A4458;
|
||||
--md-on-secondary-container: #E8DEF8;
|
||||
--md-tertiary: #EFB8C8;
|
||||
--md-tertiary-container: #633B48;
|
||||
--md-surface: #141218;
|
||||
--md-surface-dim: #141218;
|
||||
--md-surface-bright: #3B383E;
|
||||
--md-surface-container-low: #1D1B20;
|
||||
--md-surface-container: #211F26;
|
||||
--md-surface-container-high: #2B2930;
|
||||
--md-on-surface: #E6E1E5;
|
||||
--md-on-surface-variant: #CAC4D0;
|
||||
--md-outline: #938F99;
|
||||
--md-outline-variant: #49454F;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-weight: 460;
|
||||
}
|
||||
@@ -214,77 +165,3 @@ article thead {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--md-surface);
|
||||
color: var(--md-on-surface);
|
||||
}
|
||||
|
||||
.button {
|
||||
border-radius: 9999px !important;
|
||||
transition: all var(--md-transition-medium) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
background-color: var(--ifm-color-primary) !important;
|
||||
color: var(--md-on-primary) !important;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.button--primary:hover {
|
||||
background-color: rgba(114, 40, 181, 0.9) !important;
|
||||
box-shadow: 0 4px 12px rgba(114, 40, 181, 0.3) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.button--primary:active {
|
||||
transform: scale(0.95) translateY(0);
|
||||
box-shadow: 0 2px 6px rgba(114, 40, 181, 0.2) !important;
|
||||
}
|
||||
|
||||
.button:focus-visible {
|
||||
outline: 2px solid var(--md-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 500;
|
||||
color: var(--md-on-surface);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
a, button {
|
||||
transition: color var(--md-transition-fast),
|
||||
background-color var(--md-transition-fast),
|
||||
border-color var(--md-transition-fast),
|
||||
box-shadow var(--md-transition-medium),
|
||||
transform var(--md-transition-medium);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--md-surface-container);
|
||||
border-radius: 24px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
transition: all var(--md-transition-medium);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
+14
-161
@@ -1,181 +1,34 @@
|
||||
.hero--primary {
|
||||
--ifm-hero-background-color: var(--md-surface);
|
||||
--ifm-hero-text-color: var(--md-on-surface);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 600px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
--ifm-hero-background-color: #fff;
|
||||
--ifm-hero-text-color: #000;
|
||||
}
|
||||
|
||||
html[data-theme=dark] .hero--primary {
|
||||
--ifm-hero-background-color: #121212;
|
||||
--ifm-hero-text-color: var(--md-surface);
|
||||
}
|
||||
|
||||
.hero--primary::before,
|
||||
.hero--primary::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
opacity: 0.25;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero--primary::before {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: var(--md-primary);
|
||||
top: -100px;
|
||||
right: -150px;
|
||||
animation: float 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.hero--primary::after {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: var(--md-secondary-container);
|
||||
bottom: -50px;
|
||||
left: -100px;
|
||||
animation: float 10s ease-in-out infinite reverse;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
50% {
|
||||
transform: translate(20px, -20px);
|
||||
}
|
||||
}
|
||||
|
||||
.hero--primary .container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
--ifm-hero-background-color: #0c0c0c;
|
||||
--ifm-hero-text-color: #fff;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
background-size: 110%;
|
||||
color: transparent;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
height: auto;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero__title img {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
height: auto;
|
||||
margin-top: 0;
|
||||
filter: drop-shadow(0 4px 20px rgba(103, 80, 164, 0.15));
|
||||
transition: transform var(--md-transition-medium);
|
||||
}
|
||||
|
||||
.hero__title img:hover {
|
||||
transform: scale(1.02);
|
||||
margin: 0 auto;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.hero__subtitle {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background: linear-gradient(135deg, var(--md-primary) 0%, #A228E5 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-size: 3em;
|
||||
font-weight: bolder;
|
||||
font-family: monospace;
|
||||
color: #d97700;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
margin: 1.5rem auto 2rem;
|
||||
text-align: center;
|
||||
letter-spacing: -0.02em;
|
||||
white-space: nowrap;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.hero--primary {
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.hero--primary::before {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
top: -50px;
|
||||
right: -100px;
|
||||
}
|
||||
|
||||
.hero--primary::after {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
bottom: -30px;
|
||||
left: -80px;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
max-width: 500px;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.hero__subtitle {
|
||||
font-size: 1.8rem;
|
||||
padding: 0 1rem;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.hero--primary {
|
||||
min-height: 450px;
|
||||
padding: 2rem 1rem 4rem;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
max-width: 350px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.hero__subtitle {
|
||||
font-size: 1.4rem;
|
||||
margin: 1rem auto 1.5rem;
|
||||
}
|
||||
|
||||
.hero--primary::before,
|
||||
.hero--primary::after {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin: 2rem 0 0;
|
||||
}
|
||||
|
||||
.buttons > a {
|
||||
margin: 0;
|
||||
min-width: 160px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.buttons {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin: 1.5rem auto 0;
|
||||
}
|
||||
|
||||
.buttons > a {
|
||||
width: 100%;
|
||||
margin: 0.25rem 0;
|
||||
font-size: 1.3em;
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,6 @@
|
||||
.header-discord-link {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.header-twitter-link,
|
||||
.header-youtube-link,
|
||||
.header-discord-link {
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-github-link:before {
|
||||
background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import styles from '../styles.module.css'
|
||||
|
||||
export default function Feature({ title, description, index = 0 }) {
|
||||
export default function Feature({ title, description }) {
|
||||
return (
|
||||
<div
|
||||
className={clsx('featureCard', styles.featureCard, styles.animateFadeInUp)}
|
||||
style={{
|
||||
animationDelay: `${index * 0.1}s`
|
||||
}}
|
||||
>
|
||||
<h3 className={styles.featureTitle}>{title}</h3>
|
||||
<p className={styles.featureDescription}>{description}</p>
|
||||
<div className={clsx('col col--4')}>
|
||||
<h3 style={{textAlign: 'center', fontSize: 'x-large'}}>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import React from 'react'
|
||||
import clsx from 'clsx'
|
||||
import styles from '../styles.module.css'
|
||||
|
||||
export default function Section({ children, className }) {
|
||||
export default function Section({children }) {
|
||||
return (
|
||||
<section className={clsx(styles.section, className)}>
|
||||
<section style={{padding: '4rem 0', width: '100%' }}>
|
||||
<div className="container">
|
||||
<div className={styles.sectionRow}>
|
||||
<div className="row">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+77
-63
@@ -7,14 +7,16 @@ import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
import Translate from '@docusaurus/Translate'
|
||||
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import { Autoplay } from 'swiper'
|
||||
import { EffectFade, Navigation, Autoplay } from 'swiper'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/pagination'
|
||||
|
||||
import Feature from './components/Feature'
|
||||
import Section from './components/Section'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
import { features } from '../constants'
|
||||
import {features} from '../constants'
|
||||
|
||||
function Home() {
|
||||
const context = useDocusaurusContext()
|
||||
@@ -26,13 +28,13 @@ function Home() {
|
||||
title={`${siteConfig.title} · ${siteConfig.tagline}`}
|
||||
description={`${siteConfig.tagline}`}
|
||||
>
|
||||
{/* Hero Section */}
|
||||
<header className={clsx('hero--primary', styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">
|
||||
<img
|
||||
style={{ width: '500px', marginTop: '100px' }}
|
||||
src={'/img/hertzbeat-brand.svg'}
|
||||
alt={'HertzBeat Logo'}
|
||||
alt={'#'}
|
||||
/>
|
||||
</h1>
|
||||
<p className="hero__subtitle">
|
||||
@@ -50,67 +52,79 @@ function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{/* Features Section */}
|
||||
{features && features.length > 0 && (
|
||||
<section className={styles.featuresSection}>
|
||||
<div className="container">
|
||||
<div className={styles.featuresRow}>
|
||||
{features.map((props, idx) => (
|
||||
<Feature key={idx} {...props} index={idx} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Swiper Carousel Section */}
|
||||
<div className={styles.swiperContainer}>
|
||||
<div className={styles.swiperInner}>
|
||||
<Swiper
|
||||
modules={[Autoplay]}
|
||||
grabCursor
|
||||
loop={true}
|
||||
speed={600}
|
||||
autoplay={{
|
||||
delay: 6000,
|
||||
disableOnInteraction: false,
|
||||
}}
|
||||
>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
src={useBaseUrl('/img/docs/hertzbeat-arch.png')}
|
||||
alt="HertzBeat Architecture"
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
src={useBaseUrl('/img/home/status.png')}
|
||||
alt="Monitoring Status"
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
src={useBaseUrl('/img/home/0.png')}
|
||||
alt="Dashboard Overview"
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
src={useBaseUrl('/img/home/1.png')}
|
||||
alt="Feature Demo"
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
src={useBaseUrl('/img/home/9.png')}
|
||||
alt="Advanced Features"
|
||||
/>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
<div>
|
||||
<Swiper
|
||||
modules={[Autoplay, EffectFade, Navigation]}
|
||||
watchSlidesProgress={true}
|
||||
navigation={{
|
||||
nextEl: '.user-swiper-button-next',
|
||||
prevEl: '.user-swiper-button-prev',
|
||||
}}
|
||||
grabCursor
|
||||
// effect will disable when auto scroll
|
||||
// effect={'fade'}
|
||||
// fadeEffect={{
|
||||
// crossFade: true
|
||||
// }}
|
||||
// slidesPerView={1}
|
||||
// auto scroll
|
||||
loop={true}
|
||||
speed={0}
|
||||
autoplay={{
|
||||
delay: 6000,
|
||||
disableOnInteraction: false,
|
||||
waitForTransition: false,
|
||||
}}
|
||||
>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
style={{ width: '1400px', display: 'block', margin: '0 auto' }}
|
||||
src={useBaseUrl('/img/docs/hertzbeat-arch.png')}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
style={{ width: '1400px', display: 'block', margin: '0 auto' }}
|
||||
src={useBaseUrl('/img/home/status.png')}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
style={{ width: '1400px', display: 'block', margin: '0 auto' }}
|
||||
src={useBaseUrl('/img/home/0.png')}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
style={{ width: '1400px', display: 'block', margin: '0 auto' }}
|
||||
src={useBaseUrl('/img/home/1.png')}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
<SwiperSlide>
|
||||
<img
|
||||
style={{ width: '1400px', display: 'block', margin: '0 auto' }}
|
||||
src={useBaseUrl('/img/home/9.png')}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="swiper-button-prev user-swiper-button-prev"
|
||||
style={{ top: '880px', left: '50px', color: '#000033' }}
|
||||
/>
|
||||
<div
|
||||
className="swiper-button-next user-swiper-button-next"
|
||||
style={{ top: '880px', right: '50px', color: '#000033' }}
|
||||
/>
|
||||
{features && features.length > 0 && (
|
||||
<Section>
|
||||
{features.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</main>
|
||||
</Layout>
|
||||
</>
|
||||
|
||||
@@ -5,20 +5,8 @@
|
||||
* and scoped locally.
|
||||
*/
|
||||
|
||||
.section {
|
||||
padding: 4rem 0;
|
||||
width: 100%;
|
||||
background: var(--md-surface);
|
||||
}
|
||||
|
||||
.sectionRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.heroBanner {
|
||||
padding: 3rem 0 5rem;
|
||||
padding: 1rem 0 5rem;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -26,214 +14,28 @@
|
||||
|
||||
@media screen and (max-width: 966px) {
|
||||
.heroBanner {
|
||||
padding: 2rem 1rem 4rem;
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.swiperContainer {
|
||||
position: relative;
|
||||
background: linear-gradient(180deg,
|
||||
var(--md-surface) 0%,
|
||||
var(--md-surface-container) 100%);
|
||||
padding: 3rem 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.swiperInner {
|
||||
width: 100%;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.swiperContainer::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: var(--md-secondary-container);
|
||||
border-radius: 50%;
|
||||
filter: blur(100px);
|
||||
opacity: 0.2;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.swiperContainer .swiper {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.swiperContainer .swiper-slide {
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
margin: 30px 0 0;
|
||||
}
|
||||
|
||||
.swiperContainer .swiper-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.swiperContainer .swiper-slide img {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
height: auto;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
transition: all var(--md-transition-medium);
|
||||
}
|
||||
|
||||
.swiperContainer .swiper-slide img:hover {
|
||||
box-shadow: 0 8px 30px rgba(103, 80, 164, 0.15);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.swiperContainer {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.featuresSection {
|
||||
background: var(--md-surface);
|
||||
padding: 5rem 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.featuresSection::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: var(--md-tertiary-container);
|
||||
border-radius: 50%;
|
||||
filter: blur(90px);
|
||||
opacity: 0.15;
|
||||
top: 20%;
|
||||
right: -100px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.featuresRow {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.featuresRow {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.buttons > a {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.featuresRow {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.featureCard {
|
||||
background: var(--md-surface-container);
|
||||
border-radius: 24px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
transition: all var(--md-transition-medium);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.featureCard::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg,
|
||||
var(--md-primary) 0%,
|
||||
var(--md-tertiary) 100%);
|
||||
opacity: 0;
|
||||
transition: opacity var(--md-transition-medium);
|
||||
}
|
||||
|
||||
.featureCard:hover {
|
||||
box-shadow: 0 8px 24px rgba(103, 80, 164, 0.15);
|
||||
transform: translateY(-4px) scale(1.02);
|
||||
background: var(--md-surface-container-low);
|
||||
}
|
||||
|
||||
.featureCard:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.featureCard:active {
|
||||
transform: translateY(-2px) scale(1.01);
|
||||
}
|
||||
|
||||
.featureTitle {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 600;
|
||||
color: var(--md-on-surface);
|
||||
margin: 0 0 0.75rem 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.featureDescription {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: var(--md-on-surface-variant);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.featureDescription code {
|
||||
background: var(--md-surface-container-high);
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9em;
|
||||
color: var(--md-primary);
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.featureCard {
|
||||
padding: 1.5rem;
|
||||
border-radius: 20px;
|
||||
.buttons > a {
|
||||
margin: 10px 5px;
|
||||
}
|
||||
|
||||
.featureTitle {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.featureDescription {
|
||||
font-size: 0.95rem;
|
||||
.frameworkLogos img {
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# CSP permissions for hertzbeat.apache.org - Adding 3rd party service Algolia & Kapa.ai
|
||||
SetEnv CSP_PROJECT_DOMAINS "https://*.algolia.net/ https://*.algolianet.com/ https://*.algolia.io/ https://widget.kapa.ai https://proxy.kapa.ai https://kapa-widget-proxy-la7dkmplpq-uc.a.run.app https://metrics.kapa.ai https://www.google.com/recaptcha/ https://hcaptcha.com https://*.hcaptcha.com"
|
||||
@@ -71,7 +71,7 @@ some other action (type on the keyboard, move the mouse, utilize the
|
||||
disks) during the prime generation; this gives the random number
|
||||
generator a better chance to gain enough entropy.
|
||||
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# At this time, a dialog box will pop up, asking you to enter the key for this gpg.
|
||||
# you need to remember that it will be used in subsequent steps.
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Please enter this passphrase to │
|
||||
@@ -142,11 +142,11 @@ Or enter <https://keyserver.ubuntu.com/> address in the browser, enter the name
|
||||
$ mkdir -p svn/dev
|
||||
$ cd svn/dev
|
||||
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat
|
||||
$ cd svn/dev/hertzbeat
|
||||
|
||||
# Append the KEY you generated to the file KEYS, and check if it is added correctly
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -161,7 +161,7 @@ $ svn co https://dist.apache.org/repos/dist/release/incubator/hertzbeat
|
||||
$ cd svn/release/hertzbeat
|
||||
|
||||
# Append the KEY you generated to the file KEYS, and check if it is added correctly
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
$ (gpg --list-sigs muchunjin@apache.org && gpg --export --armor muchunjin@apache.org) >> KEYS
|
||||
|
||||
$ svn ci -m "add gpg key for muchunjin"
|
||||
```
|
||||
@@ -306,15 +306,15 @@ apache-hertzbeat-collector-1.6.0-incubating-bin.tar.gz: OK
|
||||
|
||||
```shell
|
||||
# Check out the dev directory of the Apache SVN to the svn/dev directory under dist in the root directory of the Apache HertzBeat project
|
||||
svn co https://dist.apache.org/repos/dist/dev/hertzbeat svn/dev
|
||||
svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat svn/dev
|
||||
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/hertzbeat
|
||||
svn co --depth empty https://dist.apache.org/repos/dist/dev/incubator/hertzbeat
|
||||
```
|
||||
|
||||
- Copy the material package to the dev directory
|
||||
|
||||
Create a version number directory and name it in the form of ${release_version}-${RC_version}. RC_version starts from 1, that is, the candidate version starts from RC1. During the release process, there is a problem that causes the vote to fail.
|
||||
If it needs to be corrected, it needs to iterate the RC version , the RC version number needs to be +1. For example: Vote for version 1.6.0-RC1. If the vote passes without any problems, the RC1 version material will be released as the final version material.
|
||||
Create a version number directory and name it in the form of ${release_version}-${RC_version}. RC_version starts from 1, that is, the candidate version starts from RC1. During the release process, there is a problem that causes the vote to fail.
|
||||
If it needs to be corrected, it needs to iterate the RC version , the RC version number needs to be +1. For example: Vote for version 1.6.0-RC1. If the vote passes without any problems, the RC1 version material will be released as the final version material.
|
||||
If there is a problem (when the hertzbeat/incubator community votes, the voters will strictly check various release requirements and compliance issues) and need to be corrected, then re-initiate the vote after the correction, and the candidate version for the next vote is 1.6.0- RC2.
|
||||
|
||||
```shell
|
||||
@@ -364,13 +364,13 @@ Release notes:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The release candidates:
|
||||
https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1/
|
||||
https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -460,13 +460,13 @@ Vote result thread:
|
||||
https://lists.apache.org/thread/t5z58mvrs1drgzfyc48c9lhmd8skswn7
|
||||
|
||||
The release candidate:
|
||||
https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1/
|
||||
https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0-rc1
|
||||
|
||||
The artifacts signed with PGP key [33545C76], corresponding to [muchunjin@apache.org], that can be found in keys file:
|
||||
https://downloads.apache.org/hertzbeat/KEYS
|
||||
https://downloads.apache.org/incubator/hertzbeat/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the necessary number of votes are reached.
|
||||
|
||||
@@ -542,7 +542,7 @@ Wait a day to see if the tutor has any other comments, if not, send the followin
|
||||
### 5.1 Migrating source and binary packages
|
||||
|
||||
```shell
|
||||
svn mv https://dist.apache.org/repos/dist/dev/hertzbeat/1.6.0-RC1 https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.0 -m "transfer packages for 1.6.0-RC1"
|
||||
svn mv https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/1.6.0-RC1 https://dist.apache.org/repos/dist/release/incubator/hertzbeat/1.6.0 -m "transfer packages for 1.6.0-RC1"
|
||||
```
|
||||
|
||||
#### 4.2 Add the new version download address to the official website
|
||||
@@ -592,17 +592,17 @@ The rename the release-1.6.0-rc1 branch to release-1.6.0.
|
||||
Hi Community,
|
||||
|
||||
We are glad to announce the release of Apache HertzBeat 1.6.0.
|
||||
Thanks again for your help.
|
||||
Thanks again for your help.
|
||||
|
||||
Apache HertzBeat (https://hertzbeat.apache.org/) - a real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
Download Link:
|
||||
Download Link:
|
||||
https://hertzbeat.apache.org/docs/download/
|
||||
|
||||
Release Note:
|
||||
Release Note:
|
||||
https://github.com/apache/hertzbeat/releases/tag/v1.6.0
|
||||
|
||||
Website:
|
||||
Website:
|
||||
https://hertzbeat.apache.org/
|
||||
|
||||
HertzBeat Resources:
|
||||
|
||||
@@ -22,9 +22,9 @@ If the network is poor, downloading may be time-consuming. The download is compl
|
||||
|
||||
```shell
|
||||
#If there is svn locally, you can clone to the local
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/
|
||||
$ svn co https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/
|
||||
#or download the material file directly
|
||||
$ wget https://dist.apache.org/repos/dist/dev/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
$ wget https://dist.apache.org/repos/dist/dev/incubator/hertzbeat/${release_version}-${rc_version}/xxx.xxx
|
||||
```
|
||||
|
||||
### 2. Verify that the uploaded version is compliant
|
||||
@@ -47,7 +47,7 @@ First import the publisher's public key. Import KEYS from the svn repository to
|
||||
##### 2.2.1 Import public key
|
||||
|
||||
```shell
|
||||
-curl https://downloads.apache.org/hertzbeat/KEYS > KEYS # Download KEYS
|
||||
curl https://downloads.apache.org/incubator/hertzbeat/KEYS > KEYS # Download KEYS
|
||||
gpg --import KEYS # Import KEYS to local
|
||||
```
|
||||
|
||||
@@ -75,9 +75,9 @@ Please decide how far you trust this user to correctly verify other users' keys
|
||||
|
||||
Your decision? 5 #choose 5
|
||||
Do you really want to set this key to ultimate trust? (y/N) y #choose y
|
||||
|
||||
|
||||
gpg>
|
||||
|
||||
|
||||
```
|
||||
|
||||
##### 2.2.3 Check the gpg signature
|
||||
@@ -177,7 +177,7 @@ I checked:
|
||||
2. Checksum and signature are OK
|
||||
3. LICENSE and NOTICE are exist
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
5.
|
||||
5.
|
||||
```
|
||||
|
||||
PPMC/IPMC member:
|
||||
@@ -190,7 +190,7 @@ I checked:
|
||||
2. Checksum and signature are OK
|
||||
3. LICENSE and NOTICE are exist
|
||||
4. Build successfully on macOS(Big Sur)
|
||||
5.
|
||||
5.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ sidebar_label: Download
|
||||
|
||||
:::tip
|
||||
|
||||
- Please verify the release with corresponding hashes(sha512), signatures and [project release KEYS](https://downloads.apache.org/hertzbeat/KEYS).
|
||||
- Please verify the release with corresponding hashes(sha512), signatures and [project release KEYS](https://downloads.apache.org/incubator/hertzbeat/KEYS).
|
||||
- Refer to [How to Verify](https://www.apache.org/dyn/closer.cgi#verify) for how to check the hashes and signatures.
|
||||
|
||||
:::
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<tr>
|
||||
<th nzAlign="center" nzLeft nzWidth="3%" [(nzChecked)]="checkedAll" (nzCheckedChange)="onAllChecked($event)"></th>
|
||||
<th nzAlign="center" nzWidth="12%">{{ 'alert.setting.name' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="10%">{{ 'alert.setting.type' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="8%">{{ 'alert.setting.type' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="15%">{{ 'alert.setting.expr' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="15%">{{ 'alert.setting.template' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="15%">{{ 'label.bind' | i18n }}</th>
|
||||
@@ -910,9 +910,6 @@
|
||||
field: 'labels',
|
||||
type: 'label-selector'
|
||||
}"
|
||||
[extra]="{
|
||||
labelIsCustom: labelIsCustom
|
||||
}"
|
||||
[name]="'labels'"
|
||||
[(ngModel)]="define.labels"
|
||||
/>
|
||||
|
||||
@@ -577,7 +577,6 @@ export class AlertSettingComponent implements OnInit {
|
||||
userExpr!: string;
|
||||
severity!: string;
|
||||
alertMode!: string;
|
||||
labelIsCustom: boolean = true;
|
||||
logFields: any[] = [];
|
||||
|
||||
editAlertDefine(alertDefineId: number) {
|
||||
|
||||
@@ -402,7 +402,7 @@
|
||||
</span>
|
||||
</ng-template>
|
||||
<nz-card-tab>
|
||||
<nz-tabset nzSize="small" [(nzSelectedIndex)]="collectorsTabSelectedIndexes[i]">
|
||||
<nz-tabset nzSize="small" [(nzSelectedIndex)]="collectorsTabSelectedIndex">
|
||||
<nz-tab [nzTitle]="'collector.status' | i18n" />
|
||||
<nz-tab [nzTitle]="'collector.task' | i18n" />
|
||||
<nz-tab [nzTitle]="'collector.start-time' | i18n" />
|
||||
@@ -410,7 +410,7 @@
|
||||
<nz-tab [nzTitle]="'collector.node' | i18n" />
|
||||
</nz-tabset>
|
||||
</nz-card-tab>
|
||||
<ng-container *ngIf="collectorsTabSelectedIndexes[i] === 0">
|
||||
<ng-container *ngIf="collectorsTabSelectedIndex === 0">
|
||||
<div class="rounded-lg" style="text-align: center; width: 100%; background-color: transparent; padding: 2%">
|
||||
<span [style]="'font-size: x-large; font-weight: bolder;' + (item.collector.status == 0 ? 'color: green' : 'color: red')">
|
||||
{{
|
||||
@@ -419,7 +419,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="collectorsTabSelectedIndexes[i] === 1">
|
||||
<ng-container *ngIf="collectorsTabSelectedIndex === 1">
|
||||
<div class="rounded-lg" style="text-align: center; width: 100%; background-color: transparent; padding: 2%">
|
||||
<span style="font-size: xxx-large; font-weight: bolder">
|
||||
{{ item.pinMonitorNum + item.dispatchMonitorNum }}
|
||||
@@ -431,19 +431,19 @@
|
||||
>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="collectorsTabSelectedIndexes[i] === 2">
|
||||
<ng-container *ngIf="collectorsTabSelectedIndex === 2">
|
||||
<div class="rounded-lg" style="text-align: center; width: 100%; background-color: transparent; padding: 2%">
|
||||
<span style="font-size: x-large; font-weight: bolder">{{
|
||||
(item.collector.gmtUpdate | date : 'YYYY-MM-dd HH:mm:ss')?.trim()
|
||||
}}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="collectorsTabSelectedIndexes[i] === 3">
|
||||
<ng-container *ngIf="collectorsTabSelectedIndex === 3">
|
||||
<div class="rounded-lg" style="text-align: center; width: 100%; background-color: transparent; padding: 2%">
|
||||
<span style="font-size: x-large; font-weight: bolder">{{ item.collector.ip }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="collectorsTabSelectedIndexes[i] === 4">
|
||||
<ng-container *ngIf="collectorsTabSelectedIndex === 4">
|
||||
<div class="rounded-lg" style="text-align: center; width: 100%; background-color: transparent; padding: 2%">
|
||||
<span style="font-size: x-large; font-weight: bolder">{{ item.collector.name }}</span>
|
||||
</div>
|
||||
|
||||
@@ -174,7 +174,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
// collector list
|
||||
collectorsLoading: boolean = false;
|
||||
collectors!: CollectorSummary[];
|
||||
collectorsTabSelectedIndexes: { [key: number]: number } = {};
|
||||
collectorsTabSelectedIndex = 0;
|
||||
|
||||
// alert list
|
||||
alerts!: SingleAlert[];
|
||||
|
||||
@@ -201,12 +201,7 @@ export class MonitorDataChartComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
extraCssText: 'max-width: 580px; max-height: 400px; overflow-y: auto;',
|
||||
enterable: true,
|
||||
renderMode: 'html',
|
||||
appendToBody: true,
|
||||
confine: false
|
||||
trigger: 'axis'
|
||||
},
|
||||
grid: {
|
||||
left: '2',
|
||||
@@ -227,7 +222,6 @@ export class MonitorDataChartComponent implements OnInit, OnDestroy {
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
scale: true,
|
||||
splitLine: {
|
||||
show: true
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<th nzAlign="center" nzLeft nzWidth="3%" [(nzChecked)]="checkedAll" (nzCheckedChange)="onAllChecked($event)"></th>
|
||||
<th nzAlign="center" nzWidth="14%">{{ 'collector' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="9%">{{ 'collector.status' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="10%">{{ 'collector.mode' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="9%">{{ 'collector.mode' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="9%">{{ 'collector.task' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="9%">{{ 'collector.pinned' | i18n }}</th>
|
||||
<th nzAlign="center" nzWidth="9%">{{ 'collector.dispatched' | i18n }}</th>
|
||||
|
||||
@@ -147,14 +147,14 @@
|
||||
<div *nzModalContent class="-inner-content">
|
||||
<form [formGroup]="pluginForm" nz-form>
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="8" nzRequired="true" nzFor="name">{{ 'plugin.name' | i18n }}</nz-form-label>
|
||||
<nz-form-label [nzSpan]="6" nzRequired="true" nzFor="name">{{ 'plugin.name' | i18n }}</nz-form-label>
|
||||
<nz-form-control [nzSpan]="14" [nzErrorTip]="'validation.required' | i18n">
|
||||
<input nz-input formControlName="name" id="name" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="8" nzRequired="true" nzFor="jarFile">{{ 'plugin.jar.file' | i18n }}</nz-form-label>
|
||||
<nz-form-label [nzSpan]="6" nzRequired="true" nzFor="jarFile">{{ 'plugin.jar.file' | i18n }}</nz-form-label>
|
||||
<nz-form-control [nzSpan]="14" [nzErrorTip]="'validation.required' | i18n">
|
||||
<nz-upload
|
||||
nzAccept=".jar"
|
||||
@@ -175,7 +175,7 @@
|
||||
</nz-form-item>
|
||||
|
||||
<nz-form-item>
|
||||
<nz-form-label [nzSpan]="8" nzRequired="true" nzFor="enableStatus">{{ 'plugin.status' | i18n }}</nz-form-label>
|
||||
<nz-form-label [nzSpan]="6" nzRequired="true" nzFor="enableStatus">{{ 'plugin.status' | i18n }}</nz-form-label>
|
||||
<nz-form-control [nzSpan]="14">
|
||||
<nz-switch formControlName="enableStatus" required></nz-switch>
|
||||
</nz-form-control>
|
||||
|
||||
@@ -32,13 +32,7 @@
|
||||
>
|
||||
<nz-option *ngFor="let keyOption of labelKeys" [nzLabel]="keyOption" [nzValue]="keyOption"></nz-option>
|
||||
<nz-option
|
||||
*ngIf="
|
||||
labelIsCustom &&
|
||||
activeSearchIndex === i &&
|
||||
activeSearchType === 'key' &&
|
||||
customInputKey &&
|
||||
!hasMatchingOption(customInputKey, labelKeys)
|
||||
"
|
||||
*ngIf="labelIsCustom && customInputKey && !hasMatchingOption(customInputKey, labelKeys)"
|
||||
[nzLabel]="customInputKey"
|
||||
[nzValue]="customInputKey"
|
||||
></nz-option>
|
||||
@@ -59,13 +53,7 @@
|
||||
>
|
||||
<nz-option *ngFor="let valueOption of getLabelValues(value[i].key)" [nzLabel]="valueOption" [nzValue]="valueOption"></nz-option>
|
||||
<nz-option
|
||||
*ngIf="
|
||||
labelIsCustom &&
|
||||
activeSearchIndex === i &&
|
||||
activeSearchType === 'value' &&
|
||||
customInputValue &&
|
||||
!hasMatchingOption(customInputValue, getLabelValues(value[i].key))
|
||||
"
|
||||
*ngIf="labelIsCustom && customInputValue && !hasMatchingOption(customInputValue, getLabelValues(value[i].key))"
|
||||
[nzLabel]="customInputValue"
|
||||
[nzValue]="customInputValue"
|
||||
></nz-option>
|
||||
|
||||
@@ -46,105 +46,32 @@ export class LabelSelectorComponent implements ControlValueAccessor, OnInit {
|
||||
value: Array<{ key: string; value: string }> = [];
|
||||
customInputKey: string = '';
|
||||
customInputValue: string = '';
|
||||
// Track the active search input to prevent cross-row pollution of custom options
|
||||
activeSearchIndex: number | null = null;
|
||||
activeSearchType: 'key' | 'value' | null = null;
|
||||
|
||||
_onChange = (_: any) => {};
|
||||
_onTouched = () => {};
|
||||
|
||||
writeValue(value: any): void {
|
||||
// Validate and initialize: use valid array if provided, otherwise initialize with empty key-value pair
|
||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||
this.value = [{ key: '', value: '' }];
|
||||
return;
|
||||
}
|
||||
this.value = value;
|
||||
// Build labelKeys and labelMap for autocomplete from the provided values
|
||||
this.value.forEach(item => {
|
||||
// Skip invalid items
|
||||
if (!item || typeof item !== 'object' || !item.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = String(item.key).trim();
|
||||
const val = item.value ? String(item.value).trim() : '';
|
||||
// Skip empty keys
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
// Add new key to labelKeys
|
||||
if (!this.labelKeys.includes(key)) {
|
||||
this.labelKeys.push(key);
|
||||
}
|
||||
// Initialize value array for this key
|
||||
if (!this.labelMap[key]) {
|
||||
this.labelMap[key] = [];
|
||||
}
|
||||
// Add value to labelMap (with deduplication)
|
||||
if (val && !this.labelMap[key].includes(val)) {
|
||||
this.labelMap[key].push(val);
|
||||
}
|
||||
});
|
||||
this.value = value && value.length > 0 ? value : [{ key: '', value: '' }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle key change: clear the corresponding value and add new key to labelKeys for reuse
|
||||
*/
|
||||
onKeyChange(index: number, value: string) {
|
||||
// Clear the value when key changes
|
||||
this.value[index].value = '';
|
||||
|
||||
// Add new key to labelKeys so other rows can see this custom key
|
||||
if (value && !this.labelKeys.includes(value)) {
|
||||
this.labelKeys.push(value);
|
||||
}
|
||||
|
||||
// Initialize value array for this key
|
||||
if (value && !this.labelMap[value]) {
|
||||
this.labelMap[value] = [];
|
||||
}
|
||||
|
||||
this._onChange(this.value);
|
||||
this._onTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle value change: add new value to the corresponding key's labelMap for reuse
|
||||
*/
|
||||
onValueChange(index: number, value: string) {
|
||||
const key = this.value[index].key;
|
||||
|
||||
// Add new value to labelMap so other rows can see this custom value
|
||||
if (key && value && this.labelMap[key] && !this.labelMap[key].includes(value)) {
|
||||
this.labelMap[key].push(value);
|
||||
}
|
||||
|
||||
this._onChange(this.value);
|
||||
this._onTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle search event: track the active input to ensure custom options only show in the corresponding field
|
||||
*/
|
||||
onSearch(value: string, index: number, type: 'key' | 'value'): void {
|
||||
// Track the currently active search input
|
||||
this.activeSearchIndex = index;
|
||||
this.activeSearchType = type;
|
||||
|
||||
if (value) {
|
||||
if (type === 'key') {
|
||||
this.customInputKey = value;
|
||||
} else {
|
||||
this.customInputValue = value;
|
||||
}
|
||||
} else {
|
||||
// Clear custom input value when search is empty
|
||||
if (type === 'key') {
|
||||
this.customInputKey = '';
|
||||
} else {
|
||||
this.customInputValue = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user