Compare commits

..
Author SHA1 Message Date
Jast aeee629c5f Merge branch 'master' into traces 2025-05-09 13:16:04 +08:00
tomsun28andshown d0b7e38537 [bugfix] fix the calculator expr exist not work (#3314)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
2025-05-09 13:15:46 +08:00
Logic cc4b63f9ad Merge branch 'master' into traces 2025-05-07 22:52:23 +08:00
096153b3a4 [license] update the license file, to add the plc4j-driver-s7 license (#3318)
Co-authored-by: cuijianzhong <shangyuancjz@126.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: aias00 <liuhongyu@apache.org>
2025-05-07 22:00:05 +08:00
Logic 93a22c3b22 style(hertzbeat-otel): format OpenTelemetryConfig and OpenTelemetryLogbackAppenderInstaller
- Update code formatting in OpenTelemetryConfig.java and OpenTelemetryLogbackAppenderInstaller.java
- Adjust indentation and line breaks for better readability
- Remove unnecessary spaces in method implementations
2025-05-07 18:17:45 +08:00
Logic 8954775632 Merge branch 'master' into traces 2025-05-07 17:30:16 +08:00
Logic 59781ad673 feat(otel): integrate OpenTelemetry for GreptimeDB logs and traces- Rename hertzbeat-log to hertzbeat-otel
- Update dependencies to use OpenTelemetry instrumentation
- Refactor OpenTelemetry configuration for GreptimeDB integration
- Add OpenTelemetryLogbackAppenderInstaller for Logback integration
- Update Maven pom files to reflect new dependencies and structure
2025-05-07 17:26:19 +08:00
zhangyaxi b313db36c6 [feature] Support Zabbix alert source (#3317)
Signed-off-by: zhangyaxi <yaxi.zhang950408@outlook.com>
2025-05-07 08:54:23 +08:00
Logic ce61c7d456 Merge branch 'master' of github.com:apache/hertzbeat 2025-05-06 10:47:33 +08:00
tomsun28andCopilot 47298033dc [refactor] set default encoding charset utf8 (#3315)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-05 23:30:30 +08:00
Logic 320766a48e Merge branch 'master' of github.com:apache/hertzbeat 2025-04-17 16:57:54 +08:00
Logic efb2ac02df Merge branch 'master' of github.com:apache/hertzbeat 2025-04-07 10:14:36 +08:00
Logic 06c939c810 Merge branch 'master' of github.com:apache/hertzbeat 2025-04-03 15:57:12 +08:00
Logic e56823b240 Merge branch 'master' of github.com:apache/hertzbeat 2025-03-27 10:20:45 +08:00
Logic 3b18d1ddaf Merge branch 'master' of github.com:apache/hertzbeat 2025-03-06 16:21:20 +08:00
Logic d44fe43eb0 Merge branch 'master' of github.com:apache/hertzbeat 2025-01-24 08:12:57 +08:00
Logic a221ddfba2 Merge branch 'master' of github.com:apache/hertzbeat 2025-01-23 15:29:18 +08:00
Logic 8e31a3147b Merge branch 'master' of github.com:apache/hertzbeat 2025-01-10 08:47:21 +08:00
Logic d2f9a56fe7 Merge branch 'master' of github.com:apache/hertzbeat 2025-01-08 10:33:11 +08:00
Logic a0b41dc7bb Merge branch 'master' of github.com:apache/hertzbeat 2024-12-27 08:24:42 +08:00
Logic 3f572a1ddc Merge branch 'master' of github.com:apache/hertzbeat 2024-11-26 11:22:35 +08:00
Logic 767deccee5 Merge branch 'master' of github.com:apache/hertzbeat 2024-10-26 10:42:23 +08:00
Logic 9221e2f6c1 Merge branch 'master' of github.com:apache/hertzbeat 2024-10-22 17:29:48 +08:00
Logic 0a36b3df90 Merge branch 'master' of github.com:apache/hertzbeat 2024-09-24 11:20:58 +08:00
zqr10159 f3202133d8 Merge branch 'master' of github.com:apache/hertzbeat 2024-09-12 15:48:50 +08:00
zqr10159 6ee458c521 Merge branch 'master' of github.com:apache/hertzbeat 2024-09-11 15:04:01 +08:00
zqr10159 cbc1caf4ac Merge branch 'master' of github.com:apache/hertzbeat 2024-09-06 08:55:12 +08:00
zqr10159 86db630fc7 Merge branch 'master' of github.com:apache/hertzbeat 2024-09-04 08:25:56 +08:00
zqr10159 145c4d1d5e Merge branch 'master' of github.com:apache/hertzbeat 2024-08-31 09:05:14 +08:00
zqr10159 0217cbb7f9 Merge branch 'master' of github.com:apache/hertzbeat 2024-08-30 08:54:44 +08:00
zqr10159 9f5df95f29 Merge branch 'master' of github.com:apache/hertzbeat 2024-08-29 11:35:20 +08:00
zqr10159 0f955019bf Merge branch 'master' of github.com:apache/hertzbeat 2024-08-29 08:36:55 +08:00
zqr10159 129b6282d3 Merge branch 'master' of github.com:apache/hertzbeat 2024-08-23 10:28:34 +08:00
zqr10159 36c5d8f518 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-17 14:55:40 +08:00
zqr10159 de8a473e24 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-16 09:09:47 +08:00
zqr10159 ffc4641dc9 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-09 15:45:37 +08:00
zqr10159 d0cb9f8bf3 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-05 11:36:50 +08:00
zqr10159 0d4ff33f19 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-05 11:15:02 +08:00
zqr10159 86f40e5e15 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-03 08:06:25 +08:00
zqr10159 dbbbc045d4 Merge branch 'master' of github.com:apache/hertzbeat 2024-07-02 15:41:38 +08:00
zqr10159 b0fdd5e11d Merge branch 'master' of github.com:apache/hertzbeat 2024-06-18 11:30:05 +08:00
zqr10159 9c5ea20b15 [doc]HertzBeat 1.6.0 Upgrade Guide. 2024-06-11 14:57:35 +08:00
zqr10159 7a74f80906 Merge branch 'master' of github.com:apache/hertzbeat 2024-06-11 14:36:25 +08:00
zqr10159 ee32a890eb Merge branch 'master' of github.com:dromara/hertzbeat 2024-06-01 10:08:28 +08:00
zqr10159 ec1c04194b Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-18 15:52:12 +08:00
zqr10159 55d8dfdede Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-14 08:15:58 +08:00
zqr10159 918073dca7 Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-11 08:45:53 +08:00
zqr10159 2defaecd93 Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-10 08:39:59 +08:00
zqr10159 641ba47f4c Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-09 17:08:47 +08:00
zqr10159 ccbc2151ae Merge branch 'master' of github.com:dromara/hertzbeat 2024-05-07 09:05:40 +08:00
zqr10159 1cb919a301 Merge branch 'master' of github.com:dromara/hertzbeat 2024-04-29 10:47:25 +08:00
zqr10159 7351a03c81 Merge branch 'master' of github.com:dromara/hertzbeat 2024-04-28 11:47:27 +08:00
zqr10159 26c3a802d1 Merge branch 'master' of github.com:dromara/hertzbeat 2024-04-25 09:17:04 +08:00
zqr10159 47dbe2fd2a Merge branch 'master' of github.com:dromara/hertzbeat 2024-04-24 08:13:26 +08:00
zqr10159 f4943e9d89 Merge branch 'master' of github.com:dromara/hertzbeat 2024-04-23 14:53:04 +08:00
zqr10159 1f6614d1a1 [remove]Remove optional comments. 2024-04-23 10:36:58 +08:00
zqr10159 4a96c02b9a [remove]Remove optional comments. 2024-04-23 10:35:21 +08:00
26 changed files with 946 additions and 177 deletions
@@ -220,11 +220,11 @@ public class RealTimeAlertCalculator {
fingerPrints.putAll(commonFingerPrints);
for (int index = 0; index < valueRow.getColumnsList().size(); index++) {
String valueStr = valueRow.getColumns(index);
final CollectRep.Field field = fields.get(index);
if (CommonConstants.NULL_VALUE.equals(valueStr)) {
fieldValueMap.put(field.getName(), null);
continue;
}
final CollectRep.Field field = fields.get(index);
final int fieldType = field.getType();
if (fieldType == CommonConstants.TYPE_NUMBER) {
@@ -0,0 +1,52 @@
/*
* 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.alert.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.ExternAlertService;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* zabbix external alarm service impl
*/
@Slf4j
@Service
public class ZabbixExternAlertServiceImpl implements ExternAlertService {
@Autowired
private AlarmCommonReduce alarmCommonReduce;
@Override
public void addExternAlert(String content) {
SingleAlert alert = JsonUtil.fromJson(content, SingleAlert.class);
if (alert == null) {
log.warn("parse extern alert content failed! content: {}", content);
return;
}
alarmCommonReduce.reduceAndSendAlarm(alert);
}
@Override
public String supportSource() {
return "zabbix";
}
}
@@ -1,117 +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.log.config;
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import jakarta.annotation.PostConstruct;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
/**
* OpenTelemetryConfig is responsible for initializing OpenTelemetry with the specified service name and GrepTimeDB endpoint.
* It ensures that the initialization is done in a thread-safe manner and includes authentication for GrepTimeDB.
*/
@Configuration
@Slf4j
public class OpenTelemetryConfig {
@Autowired
private GreptimeProperties greptimeProperties;
/**
* Initializes OpenTelemetry with the given service name and GrepTimeDB endpoint.
* Includes authentication if configured in GreptimeProperties.
*/
@PostConstruct
public void initializeOpenTelemetry() {
if (greptimeProperties == null || !greptimeProperties.enabled()) {
log.info("GrepTimeDB logging is disabled, skipping OpenTelemetry configuration.");
return;
}
try {
Resource resource = Resource.getDefault()
.merge(Resource.builder()
.put(SERVICE_NAME, "HertzBeat")
.build());
Map<String, String> headers = new HashMap<>();
headers.put("X-Greptime-DB-Name", "public");
headers.put("X-Greptime-Log-Table-Name", "hzb_log");
addAuthenticationHeaders(headers);
OtlpHttpLogRecordExporter logExporter = OtlpHttpLogRecordExporter.builder()
.setEndpoint(greptimeProperties.httpEndpoint() + "/v1/otlp/v1/logs")
.setHeaders(()-> headers)
.setTimeout(10, TimeUnit.SECONDS)
.build();
SdkLoggerProvider loggerProvider = SdkLoggerProvider.builder()
.setResource(resource)
.addLogRecordProcessor(
BatchLogRecordProcessor.builder(logExporter)
.setScheduleDelay(1000, TimeUnit.MILLISECONDS)
.setMaxExportBatchSize(512)
.build())
.build();
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
.setLoggerProvider(loggerProvider)
.build();
OpenTelemetryAppender.install(openTelemetry);
log.info("OpenTelemetry successfully configured with GrepTimeDB exporter.");
} catch (Exception e) {
log.error("Failed to initialize OpenTelemetry with GrepTimeDB", e);
}
}
/**
* Adds authentication headers to the provided map if username and password are configured.
*
* @param headers the map to which authentication headers will be added
*/
private void addAuthenticationHeaders(Map<String, String> headers) {
if (StringUtils.isNotBlank(greptimeProperties.username())
&& StringUtils.isNotBlank(greptimeProperties.password())) {
String credentials = greptimeProperties.username() + ":" + greptimeProperties.password();
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
headers.put("Authorization", "Basic " + encodedCredentials);
}
}
}
+1 -1
View File
@@ -92,7 +92,7 @@
<!-- log -->
<dependency>
<groupId>org.apache.hertzbeat</groupId>
<artifactId>hertzbeat-log</artifactId>
<artifactId>hertzbeat-otel</artifactId>
</dependency>
<!-- spring -->
<dependency>
@@ -35,7 +35,7 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
JavaTimeModule javaTimeModule = new JavaTimeModule();
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
@@ -207,12 +207,12 @@ metrics:
# collect metrics content
fields:
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
- field: Content-Type
- field: content_type
type: 1
i18n:
zh-CN: Content Type
en-US: Content Type
- field: Content-Length
- field: content_length
type: 1
i18n:
zh-CN: 响应内容长度
@@ -24,7 +24,7 @@
<version>2.0-SNAPSHOT</version>
</parent>
<artifactId>hertzbeat-log</artifactId>
<artifactId>hertzbeat-otel</artifactId>
<name>${project.artifactId}</name>
<properties>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
@@ -43,25 +43,14 @@
<artifactId>hertzbeat-warehouse</artifactId>
</dependency>
<!-- OpenTelemetry -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-logs</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-logback-appender-1.0</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
@@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.hertzbeat.log.config;
package org.apache.hertzbeat.otel.config;
import org.apache.hertzbeat.common.constants.ConfigConstants;
import org.apache.hertzbeat.common.constants.SignConstants;
@@ -0,0 +1,154 @@
/*
* 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.otel.config;
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* OpenTelemetryConfig provides customizations for the auto-configured OpenTelemetry SDK,
* specifically for integrating with GrepTimeDB for logs and traces.
*/
@Configuration
@Slf4j
public class OpenTelemetryConfig {
private static final String HERTZBEAT_SERVICE_NAME = "HertzBeat";
private static final String DEFAULT_GREPTIME_DB_NAME = "public";
private static final String DEFAULT_LOGS_TABLE_NAME = "hzb_logs";
private static final String DEFAULT_TRACES_TABLE_NAME = "hzb_traces";
private static final String GREPTIME_DB_NAME_HEADER = "X-Greptime-DB-Name";
private static final String GREPTIME_LOG_TABLE_NAME_HEADER = "X-Greptime-Log-Table-Name";
private static final String GREPTIME_TRACE_TABLE_NAME_HEADER = "X-Greptime-Trace-Table-Name";
private static final String GREPTIME_PIPELINE_NAME_HEADER = "X-Greptime-Pipeline-Name";
/**
* Adds authentication headers if credentials are provided.
*/
private void addAuthenticationHeaders(Map<String, String> headers, GreptimeProperties greptimeProps) {
if (greptimeProps != null && StringUtils.isNotBlank(greptimeProps.username())
&& StringUtils.isNotBlank(greptimeProps.password())) {
String credentials = greptimeProps.username() + ":" + greptimeProps.password();
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
headers.put("Authorization", "Basic " + encodedCredentials);
log.debug("Added Basic Authentication header for GreptimeDB.");
} else {
log.debug("GreptimeDB username/password not configured, skipping Authentication header.");
}
}
/**
* Builds HTTP Log headers for OTLP communication with GreptimeDB.
*/
private Map<String, String> buildGreptimeOtlpLogHeaders(GreptimeProperties greptimeProps) {
Map<String, String> headers = new HashMap<>();
headers.put(GREPTIME_DB_NAME_HEADER, DEFAULT_GREPTIME_DB_NAME);
headers.put(GREPTIME_LOG_TABLE_NAME_HEADER, DEFAULT_LOGS_TABLE_NAME);
addAuthenticationHeaders(headers, greptimeProps);
return Collections.unmodifiableMap(headers);
}
/**
* Builds HTTP Trace headers for OTLP communication with GreptimeDB.
*/
private Map<String, String> buildGreptimeOtlpTraceHeaders(GreptimeProperties greptimeProps) {
Map<String, String> headers = new HashMap<>();
headers.put(GREPTIME_DB_NAME_HEADER, DEFAULT_GREPTIME_DB_NAME);
headers.put(GREPTIME_TRACE_TABLE_NAME_HEADER, DEFAULT_TRACES_TABLE_NAME);
headers.put(CONTENT_TYPE, "application/x-protobuf");
headers.put(GREPTIME_PIPELINE_NAME_HEADER, "greptime_trace_v1");
addAuthenticationHeaders(headers, greptimeProps);
return Collections.unmodifiableMap(headers);
}
/**
* Provides an AutoConfigurationCustomizerProvider to tailor the auto-configured OpenTelemetry SDK.
* This includes setting up GrepTimeDB exporters for logs and traces, and customizing the resource.
* Active only if 'greptime.enabled' is true.
*
* @param greptimeProperties Configuration for GrepTimeDB.
* @return AutoConfigurationCustomizerProvider instance.
*/
@Bean
@ConditionalOnProperty(name = "warehouse.store.greptime.enabled", havingValue = "true")
public AutoConfigurationCustomizerProvider greptimeOtelCustomizer(GreptimeProperties greptimeProperties) {
log.info("GreptimeDB is enabled. Applying OpenTelemetry SDK customizations.");
return providerCustomizer -> providerCustomizer
.addPropertiesCustomizer(sdkConfigProperties -> {
Map<String, String> newProperties = new HashMap<>();
newProperties.put("otel.metrics.exporter", "none");
newProperties.put("otel.traces.exporter", "otlp");
newProperties.put("otel.logs.exporter", "none");
return newProperties;
})
.addResourceCustomizer((resource, configProperties) -> {
log.info("Customizing auto-configured OpenTelemetry Resource with service name: {}.", HERTZBEAT_SERVICE_NAME);
return resource.merge(Resource.builder().put(SERVICE_NAME, HERTZBEAT_SERVICE_NAME).build());
})
.addSpanExporterCustomizer((originalSpanExporter, configProperties) -> {
String traceEndpoint = greptimeProperties.httpEndpoint() + "/v1/otlp/v1/traces";
log.info("Programmatically configuring OtlpHttpSpanExporter for GreptimeDB traces. Endpoint: {}", traceEndpoint);
Map<String, String> traceHeaders = buildGreptimeOtlpTraceHeaders(greptimeProperties);
log.info("Trace Headers for GreptimeDB (programmatic HTTP config): {}", traceHeaders);
OtlpHttpSpanExporterBuilder httpExporterBuilder = OtlpHttpSpanExporter.builder()
.setEndpoint(traceEndpoint)
.setHeaders(() -> traceHeaders)
.setTimeout(10000, TimeUnit.MILLISECONDS);
return httpExporterBuilder.build();
})
.addLoggerProviderCustomizer((sdkLoggerProviderBuilder, configProperties) -> {
log.info("Customizing auto-configured SdkLoggerProviderBuilder for GrepTimeDB logs.");
OtlpHttpLogRecordExporter logExporter = OtlpHttpLogRecordExporter.builder()
.setEndpoint(greptimeProperties.httpEndpoint() + "/v1/otlp/v1/logs")
.setHeaders(() -> buildGreptimeOtlpLogHeaders(greptimeProperties))
.setTimeout(10000, TimeUnit.MILLISECONDS)
.build();
BatchLogRecordProcessor batchLogProcessor = BatchLogRecordProcessor.builder(logExporter)
.setScheduleDelay(1000, TimeUnit.MILLISECONDS)
.setMaxExportBatchSize(512)
.build();
return sdkLoggerProviderBuilder.addLogRecordProcessor(batchLogProcessor);
});
}
}
@@ -0,0 +1,58 @@
/*
* 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.otel.config;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* Installs the OpenTelemetryAppender for Logback once the auto-configured
* OpenTelemetry SDK is available and GrepTimeDB integration is enabled.
*/
@Component
@ConditionalOnProperty(name = "warehouse.store.greptime.enabled", havingValue = "true")
@Slf4j
public class OpenTelemetryLogbackAppenderInstaller implements InitializingBean {
private final OpenTelemetry openTelemetry;
@Autowired
public OpenTelemetryLogbackAppenderInstaller(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}
@Override
public void afterPropertiesSet() {
if (this.openTelemetry instanceof OpenTelemetrySdk) {
log.info("Auto-configured OpenTelemetry SDK detected. Installing OpenTelemetryAppender for Logback.");
OpenTelemetryAppender.install(this.openTelemetry);
} else {
log.warn("OpenTelemetry SDK is not an instance of OpenTelemetrySdk (type: {}). "
+ "OpenTelemetryAppender for Logback will not be installed. "
+ "Ensure OpenTelemetry auto-configuration is active and correctly providing an SDK.",
this.openTelemetry != null ? this.openTelemetry.getClass().getName() : "null");
}
}
}
@@ -14,4 +14,4 @@
# limitations under the License.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.hertzbeat.log.config.LogAutoConfiguration
org.apache.hertzbeat.otel.config.LogAutoConfiguration
+2 -4
View File
@@ -358,6 +358,7 @@ The text of each license is the standard Apache 2.0 license.
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-api/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-modbus/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-s7/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.attoparser/attoparser/2.0.7.RELEASE Apache-2.0
https://mvnrepository.com/artifact/org.freemarker/freemarker/2.3.32 Apache-2.0
https://mvnrepository.com/artifact/org.flywaydb/flyway-core/10.11.1
@@ -434,10 +435,7 @@ The text of each license is the standard Apache 2.0 license.
https://mvnrepository.com/artifact/org.webjars/swagger-ui/5.10.3 Apache-2.0
https://mvnrepository.com/artifact/com.google.flatbuffers/flatbuffers-java/1.12.0 Apache-2.0
https://mvnrepository.com/artifact/com.vesoft/client/3.6.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-api/1.49.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-sdk/1.49.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-sdk-logs/1.49.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-exporter-otlp/1.49.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry.instrumentation/opentelemetry-spring-boot-starter-2.15.0 Apache-2.0
https://mvnrepository.com/artifact/io.opentelemetry.instrumentation/opentelemetry-logback-appender-1.0 Apache-2.0
========================================================================
+1
View File
@@ -358,6 +358,7 @@ The text of each license is the standard Apache 2.0 license.
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-api/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-modbus/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-s7/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.attoparser/attoparser/2.0.7.RELEASE Apache-2.0
https://mvnrepository.com/artifact/org.freemarker/freemarker/2.3.32 Apache-2.0
https://mvnrepository.com/artifact/org.flywaydb/flyway-core/10.11.1
+1
View File
@@ -291,6 +291,7 @@ The text of each license is the standard Apache 2.0 license.
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-api/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-modbus/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-driver-s7/0.12.0 Apache-2.0
https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator/8.0.1.Final Apache-2.0
https://mvnrepository.com/artifact/org.lz4/lz4-java/1.8.0 Apache-2.0
https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-core/4.6.1 Apache-2.0
+10 -25
View File
@@ -88,7 +88,7 @@
<module>hertzbeat-push</module>
<module>hertzbeat-plugin</module>
<module>hertzbeat-grafana</module>
<module>hertzbeat-log</module>
<module>hertzbeat-otel</module>
<module>hertzbeat-e2e</module>
<module>hertzbeat-base</module>
<module>hertzbeat-mcp</module>
@@ -175,7 +175,7 @@
<arrow.version>18.1.0</arrow.version>
<snappy-java.version>1.1.10.7</snappy-java.version>
<sshd-sftp.version>2.13.1</sshd-sftp.version>
<opentelemetry-api.version>1.43.0</opentelemetry-api.version>
<opentelemetry-starter.version>2.15.0</opentelemetry-starter.version>
<opentelemetry-logback.version>2.14.0-alpha</opentelemetry-logback.version>
</properties>
@@ -238,7 +238,7 @@
<!-- log -->
<dependency>
<groupId>org.apache.hertzbeat</groupId>
<artifactId>hertzbeat-log</artifactId>
<artifactId>hertzbeat-otel</artifactId>
<version>${hertzbeat.version}</version>
</dependency>
<!-- collector-basic -->
@@ -477,33 +477,18 @@
<version>${sshd-sftp.version}</version>
</dependency>
<!-- OpenTelemetry -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${opentelemetry-api.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${opentelemetry-api.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-logs</artifactId>
<version>${opentelemetry-api.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>${opentelemetry-api.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-logback-appender-1.0</artifactId>
<version>${opentelemetry-logback.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-bom</artifactId>
<version>${opentelemetry-starter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -68,15 +68,20 @@ export class AlertIntegrationComponent implements OnInit {
name: this.i18nSvc.fanyi('alert.integration.source.skywalking'),
icon: 'assets/img/integration/skywalking.svg'
},
{
id: 'tencent',
name: this.i18nSvc.fanyi('alert.integration.source.tencent'),
icon: 'assets/img/integration/tencent.svg'
},
{
id: 'uptime-kuma',
name: this.i18nSvc.fanyi('alert.integration.source.uptime-kuma'),
icon: 'assets/img/integration/uptime-kuma.svg'
},
{
id: 'zabbix',
name: this.i18nSvc.fanyi('alert.integration.source.zabbix'),
icon: 'assets/img/integration/zabbix.svg'
},
{
id: 'tencent',
name: this.i18nSvc.fanyi('alert.integration.source.tencent'),
icon: 'assets/img/integration/tencent.svg'
}
];
@@ -0,0 +1,201 @@
Send Zabbix alerts to the HertzBeat alert platform via Webhook.
### Step 1: Create Custom Webhook Media Type in Zabbix
1. Log in to Zabbix Web interface
2. Go to **Administration** > **Media types** > **Create media type**
3. Configure basic information
- Name: HertzBeat Webhook
- Type: Webhook
4. Add the following fields in the **Parameters** section
| Name | Value |
|-----|-----|
| URL | http://your-hertzbeat-server:1157/api/report/zabbix |
| AlertName | {TRIGGER.NAME} |
| AlertId | {EVENT.ID} |
| HostName | {HOST.NAME} |
| HostIp | {HOST.IP} |
| TriggerDescription | {TRIGGER.DESCRIPTION} |
| TriggerSeverity | {TRIGGER.SEVERITY} |
| TriggerStatus | {EVENT.STATUS} |
| ItemName | {ITEM.NAME} |
| ItemValue | {ITEM.VALUE} |
| ItemLastValue | {ITEM.LASTVALUE} |
| EventDate | {EVENT.DATE} |
| EventTime | {EVENT.TIME} |
| EventTags | {EVENT.TAGS} |
| EventRecoveryDate | {EVENT.RECOVERY.DATE} |
| EventRecoveryTime | {EVENT.RECOVERY.TIME} |
5. Add the following JavaScript code in the **Script** section
```javascript
var Hertzbeat = {
sendMessage: function(url, alert) {
request = new HttpRequest();
request.addHeader('Content-Type: application/json');
data = JSON.stringify(alert);
Zabbix.Log(4, '[Hertzbeat Webhook] params: ' + data);
// Push alert message
response = request.post(url, data);
Zabbix.Log(4, '[Hertzbeat Webhook] HTTP code: ' + request.Status());
Zabbix.Log(4, '[Hertzbeat Webhook] response: ' + response);
// Format the returned result and make a judgment, throw an exception if there is an exception.
try {
response = JSON.parse(response);
} catch (error) {
response = null;
Zabbix.Log(4, '[Hertzbeat Webhook] response parse error');
}
if (request.Status() !== 200 || response.errcode !== 0 || response.errmsg !== 'ok') {
if (typeof response.errmsg === 'string') {
throw response.errmsg;
}
else {
throw 'Unknown error. Check debug log for more information.'
}
}
}
}
try {
var params = JSON.parse(value);
// Check if Webhook_url parameter is defined, throw an error if not defined.
if (typeof params.URL === 'undefined') {
throw 'Incorrect value is given for parameter "Webhook_url": parameter is missing';
}
var currentTimestamp = Date.now();
// Convert Zabbix severity to HertzBeat priority
function convertSeverity(severity) {
var severityMap = {
"信息": "info",
"Information": "info",
"警告": "warning",
"Warning": "warning",
"一般严重": "error",
"Average": "error",
"严重": "critical",
"High": "critical",
"灾难": "emergency",
"Disaster": "emergency"
};
return severityMap[severity] || "error";
}
// Build fingerprint unique identifier
var fingerprint = "zabbix-event:" + params.AlertId + "-" + params.HostName;
// Build labels
var labels = {
"alertname": params.AlertName,
"source": "zabbix",
"severity": convertSeverity(params.TriggerSeverity),
"host": params.HostName,
"hostip": params.HostIp,
"itemname": params.ItemName
};
// Parse event tags
if (params.EventTags) {
var tags = params.EventTags.split(',');
for (var i = 0; i < tags.length; i++) {
var tagParts = tags[i].split(':');
if (tagParts.length == 2) {
var key = tagParts[0].trim();
var value = tagParts[1].trim();
labels[key] = value;
}
}
}
// Build annotations
var annotations = {
"summary": params.AlertName,
"description": params.TriggerDescription,
"value": params.ItemValue,
"lastValue": params.ItemLastValue
};
// Build alert content
var content = "Host: " + params.HostName +
"\nIP: " + params.HostIp +
"\nAlert: " + params.AlertName +
"\nLevel: " + params.TriggerSeverity +
"\nDescription: " + params.TriggerDescription +
"\nItem: " + params.ItemName +
"\nCurrent Value: " + params.ItemValue +
"\nLast Value: " + params.ItemLastValue +
"\nTime: " + params.EventDate + " " + params.EventTime;
// Determine status
var status = params.TriggerStatus === "RESOLVED" ? "resolved" : "firing";
// Calculate timestamp
var startAt = currentTimestamp;
var endAt = null;
// If it's a recovery event, calculate end time
if (status === "resolved" && params.EventRecoveryDate && params.EventRecoveryTime) {
endAt = currentTimestamp;
}
// Build payload to send to HertzBeat
var hertzbeatAlert = {
"fingerprint": fingerprint,
"labels": labels,
"annotations": annotations,
"content": content,
"status": status,
"triggerTimes": 1,
"startAt": startAt,
"activeAt": startAt,
"endAt": endAt
};
// Log
Zabbix.Log(4, "HertzBeat webhook payload: " + JSON.stringify(hertzbeatAlert));
// Execute message push function
Hertzbeat.sendMessage(params.URL, hertzbeatAlert);
// Return to Zabbix, 'OK' will be used to identify successful execution in Zabbix actions.
return 'OK';
} catch (error) {
Zabbix.Log(4, '[Hertzbeat Webhook] notification failed: ' + error);
throw 'Sending failed: ' + error + '.';
}
```
7. Click the **Add** button to save the media type
### Step 2: Configure Media for Users
1. Go to **Administration** > **Users** > **Select the user who will receive alerts** (you can create a dedicated user for alerts)
2. Select the **Media** tab > **Add**
3. Select **HertzBeat Webhook** type
4. Choose the time period and alert severity as needed, ensure the status is enabled
5. Click the **Add** button to save the media
### Step 3: Configure Alert Actions
1. Go to **Configuration** > **Actions** > **Trigger actions** > **Create action**
2. Configure the Action tab information
- Name: HertzBeat Webhook
- Conditions: Configure trigger conditions as needed
3. Configure Operations tab information
- Operation step duration: Set as needed
- In the **Operations** section, add and configure users or user groups, select the user configured with HertzBeat Webhook media, select **HertzBeat Webhook** as the media type, check custom message content, ensure all macros are correctly passed
- **Recovery operations** and **Update operations** can be configured similarly
4. Click the **Add** button to save the alert action
### Common Issues
#### Alerts Not Received
- Ensure the Webhook URL is accessible by the Zabbix server
- Check server logs for request records
#### Alerts Not Triggered
- Ensure alert policy conditions are correct and notifications are bound
For more information, refer to [Zabbix Webhook](https://www.zabbix.com/documentation/current/manual/config/notifications/webhook) and [Zabbix Macros](https://www.zabbix.com/documentation/current/en/manual/appendix/macros)
```
@@ -0,0 +1,201 @@
>将 Zabbix 的告警通过 Webhook 方式发送到 HertzBeat 告警平台。
### 步骤一: 在 Zabbix 创建自定义 Webhook 媒介类型
1. 登录 Zabbix Web 界面
2. 进入 **告警** > **媒介类型** > **创建媒介类型**
3. 配置基本信息
- 名称: HertzBeat Webhook
- 类型: Webhook
4.**参数** 部分添加以下字段
| 名称 | 值 |
|-----|-----|
| URL | http://your-hertzbeat-server:1157/api/report/zabbix |
| AlertName | {TRIGGER.NAME} |
| AlertId | {EVENT.ID} |
| HostName | {HOST.NAME} |
| HostIp | {HOST.IP} |
| TriggerDescription | {TRIGGER.DESCRIPTION} |
| TriggerSeverity | {TRIGGER.SEVERITY} |
| TriggerStatus | {EVENT.STATUS} |
| ItemName | {ITEM.NAME} |
| ItemValue | {ITEM.VALUE} |
| ItemLastValue | {ITEM.LASTVALUE} |
| EventDate | {EVENT.DATE} |
| EventTime | {EVENT.TIME} |
| EventTags | {EVENT.TAGS} |
| EventRecoveryDate | {EVENT.RECOVERY.DATE} |
| EventRecoveryTime | {EVENT.RECOVERY.TIME} |
5.**脚本** 部分添加以下 JavaScript 代码
```javascript
var Hertzbeat = {
sendMessage: function(url, alert) {
request = new HttpRequest();
request.addHeader('Content-Type: application/json');
data = JSON.stringify(alert);
Zabbix.Log(4, '[Hertzbeat Webhook] params: ' + data);
// 推送告警消息
response = request.post(url, data);
Zabbix.Log(4, '[Hertzbeat Webhook] HTTP code: ' + request.Status());
Zabbix.Log(4, '[Hertzbeat Webhook] response: ' + response);
// 格式化返回的结果并做出判断,有异常则抛出异常。
try {
response = JSON.parse(response);
} catch (error) {
response = null;
Zabbix.Log(4, '[Hertzbeat Webhook] response parse error');
}
if (request.Status() !== 200 || response.errcode !== 0 || response.errmsg !== 'ok') {
if (typeof response.errmsg === 'string') {
throw response.errmsg;
}
else {
throw 'Unknown error. Check debug log for more information.'
}
}
}
}
try {
var params = JSON.parse(value);
// 判断Webhook_url参数是否定义,未定义抛出错误。
if (typeof params.URL === 'undefined') {
throw 'Incorrect value is given for parameter "Webhook_url": parameter is missing';
}
var currentTimestamp = Date.now();
// 转换 zabbix 严重性到 HertzBeat 优先级
function convertSeverity(severity) {
var severityMap = {
"信息": "info",
"Information": "info",
"警告": "warning",
"Warning": "warning",
"一般严重": "error",
"Average": "error",
"严重": "critical",
"High": "critical",
"灾难": "emergency",
"Disaster": "emergency"
};
return severityMap[severity] || "error";
}
// 构建指纹唯一标识
var fingerprint = "zabbix-event:" + params.AlertId + "-" + params.HostName;
// 构建 labels
var labels = {
"alertname": params.AlertName,
"source": "zabbix",
"severity": convertSeverity(params.TriggerSeverity),
"host": params.HostName,
"hostip": params.HostIp,
"itemname": params.ItemName
};
// 解析事件标签
if (params.EventTags) {
var tags = params.EventTags.split(',');
for (var i = 0; i < tags.length; i++) {
var tagParts = tags[i].split(':');
if (tagParts.length == 2) {
var key = tagParts[0].trim();
var value = tagParts[1].trim();
labels[key] = value;
}
}
}
// 构建注解信息
var annotations = {
"summary": params.AlertName,
"description": params.TriggerDescription,
"value": params.ItemValue,
"lastValue": params.ItemLastValue
};
// 构建告警内容
var content = "主机: " + params.HostName +
"\nIP: " + params.HostIp +
"\n告警: " + params.AlertName +
"\n级别: " + params.TriggerSeverity +
"\n描述: " + params.TriggerDescription +
"\n监控项: " + params.ItemName +
"\n当前值: " + params.ItemValue +
"\n上次值: " + params.ItemLastValue +
"\n时间: " + params.EventDate + " " + params.EventTime;
// 确定状态
var status = params.TriggerStatus === "RESOLVED" ? "resolved" : "firing";
// 计算时间戳
var startAt = currentTimestamp;
var endAt = null;
// 如果是恢复事件,计算结束时间
if (status === "resolved" && params.EventRecoveryDate && params.EventRecoveryTime) {
endAt = currentTimestamp;
}
// 构建发送到 HertzBeat 的 payload
var hertzbeatAlert = {
"fingerprint": fingerprint,
"labels": labels,
"annotations": annotations,
"content": content,
"status": status,
"triggerTimes": 1,
"startAt": startAt,
"activeAt": startAt,
"endAt": endAt
};
// 记录日志
Zabbix.Log(4, "HertzBeat webhook payload: " + JSON.stringify(hertzbeatAlert));
// 执行消息推送函数
Hertzbeat.sendMessage(params.URL, hertzbeatAlert);
// 返回给zabbixok 在 Zabbix 动作中会被用来标识成功执行。
return 'OK';
} catch (error) {
Zabbix.Log(4, '[Hertzbeat Webhook] notification failed: ' + error);
throw 'Sending failed: ' + error + '.';
}
```
7. 点击 **添加** 按钮保存媒介类型
### 步骤二: 为用户配置媒介
1. 进入 **用户** > **用户** > **选择要接收告警的用户** (可以创建一个专门用于告警的用户)
2. 选择 **报警媒介** 选项卡 > **添加**
3. 选择 **HertzBeat Webhook** 类型
4. 启用时间段按需选择,报警严重性按需选择,确保状态为启用
5. 点击 **添加** 按钮保存媒介
### 步骤三: 配置告警动作
1. 进入 **告警** > **动作** > **触发器动作** > **创建动作**
2. 配置动作选项卡信息
- 名称: HertzBeat Webhook
- 条件: 根据需要配置触发条件
3. 配置操作选项卡信息
- 操作步骤持续时间: 根据需要进行设置
- 在**操作**部分添加,配置用户或用户组,选择之前配置 HertzBeat Webhook 媒介的用户,发送至媒体类型选择 **HertzBeat Webhook**,选中自定义消息内容,确保所有宏都被正确传递
- **恢复操作**以及**更新操作**可以根据上述进行类似配置
4. 点击 **添加** 按钮保存告警动作
### 常见问题
#### 未收到告警
- 确保 Webhook URL 可以被 zabbix 服务访问
- 检查服务器日志是否有请求记录
#### 告警未触发
- 确保告警策略的条件正确,并已绑定通知
更多信息请参考 [Zabbix Webhook](https://www.zabbix.com/documentation/current/manual/config/notifications/webhook) 以及 [Zabbix 宏](https://www.zabbix.com/documentation/current/zh/manual/appendix/macros)
@@ -0,0 +1,201 @@
將 Zabbix 的告警通過 Webhook 方式發送到 HertzBeat 告警平臺。
### 步驟一: 在 Zabbix 創建自定義 Webhook 媒介類型
1. 登錄 Zabbix Web 界面
2. 進入 **告警** > **媒介類型** > **創建媒介類型**
3. 配置基本信息
- 名稱: HertzBeat Webhook
- 類型: Webhook
4.**參數** 部分添加以下字段
| 名稱 | 值 |
|-----|-----|
| URL | http://your-hertzbeat-server:1157/api/report/zabbix |
| AlertName | {TRIGGER.NAME} |
| AlertId | {EVENT.ID} |
| HostName | {HOST.NAME} |
| HostIp | {HOST.IP} |
| TriggerDescription | {TRIGGER.DESCRIPTION} |
| TriggerSeverity | {TRIGGER.SEVERITY} |
| TriggerStatus | {EVENT.STATUS} |
| ItemName | {ITEM.NAME} |
| ItemValue | {ITEM.VALUE} |
| ItemLastValue | {ITEM.LASTVALUE} |
| EventDate | {EVENT.DATE} |
| EventTime | {EVENT.TIME} |
| EventTags | {EVENT.TAGS} |
| EventRecoveryDate | {EVENT.RECOVERY.DATE} |
| EventRecoveryTime | {EVENT.RECOVERY.TIME} |
5.**腳本** 部分添加以下 JavaScript 代碼
```javascript
var Hertzbeat = {
sendMessage: function(url, alert) {
request = new HttpRequest();
request.addHeader('Content-Type: application/json');
data = JSON.stringify(alert);
Zabbix.Log(4, '[Hertzbeat Webhook] params: ' + data);
// 推送告警消息
response = request.post(url, data);
Zabbix.Log(4, '[Hertzbeat Webhook] HTTP code: ' + request.Status());
Zabbix.Log(4, '[Hertzbeat Webhook] response: ' + response);
// 格式化返回的結果並做出判斷,有異常則拋出異常。
try {
response = JSON.parse(response);
} catch (error) {
response = null;
Zabbix.Log(4, '[Hertzbeat Webhook] response parse error');
}
if (request.Status() !== 200 || response.errcode !== 0 || response.errmsg !== 'ok') {
if (typeof response.errmsg === 'string') {
throw response.errmsg;
}
else {
throw 'Unknown error. Check debug log for more information.'
}
}
}
}
try {
var params = JSON.parse(value);
// 判斷Webhook_url參數是否定義,未定義拋出錯誤。
if (typeof params.URL === 'undefined') {
throw 'Incorrect value is given for parameter "Webhook_url": parameter is missing';
}
var currentTimestamp = Date.now();
// 轉換 zabbix 嚴重性到 HertzBeat 優先級
function convertSeverity(severity) {
var severityMap = {
"信息": "info",
"Information": "info",
"警告": "warning",
"Warning": "warning",
"一般嚴重": "error",
"Average": "error",
"嚴重": "critical",
"High": "critical",
"災難": "emergency",
"Disaster": "emergency"
};
return severityMap[severity] || "error";
}
// 構建指紋唯一標識
var fingerprint = "zabbix-event:" + params.AlertId + "-" + params.HostName;
// 構建 labels
var labels = {
"alertname": params.AlertName,
"source": "zabbix",
"severity": convertSeverity(params.TriggerSeverity),
"host": params.HostName,
"hostip": params.HostIp,
"itemname": params.ItemName
};
// 解析事件標籤
if (params.EventTags) {
var tags = params.EventTags.split(',');
for (var i = 0; i < tags.length; i++) {
var tagParts = tags[i].split(':');
if (tagParts.length == 2) {
var key = tagParts[0].trim();
var value = tagParts[1].trim();
labels[key] = value;
}
}
}
// 構建註解信息
var annotations = {
"summary": params.AlertName,
"description": params.TriggerDescription,
"value": params.ItemValue,
"lastValue": params.ItemLastValue
};
// 構建告警內容
var content = "主機: " + params.HostName +
"\nIP: " + params.HostIp +
"\n告警: " + params.AlertName +
"\n級別: " + params.TriggerSeverity +
"\n描述: " + params.TriggerDescription +
"\n監控項: " + params.ItemName +
"\n當前值: " + params.ItemValue +
"\n上次值: " + params.ItemLastValue +
"\n時間: " + params.EventDate + " " + params.EventTime;
// 確定狀態
var status = params.TriggerStatus === "RESOLVED" ? "resolved" : "firing";
// 計算時間戳
var startAt = currentTimestamp;
var endAt = null;
// 如果是恢復事件,計算結束時間
if (status === "resolved" && params.EventRecoveryDate && params.EventRecoveryTime) {
endAt = currentTimestamp;
}
// 構建發送到 HertzBeat 的 payload
var hertzbeatAlert = {
"fingerprint": fingerprint,
"labels": labels,
"annotations": annotations,
"content": content,
"status": status,
"triggerTimes": 1,
"startAt": startAt,
"activeAt": startAt,
"endAt": endAt
};
// 記錄日誌
Zabbix.Log(4, "HertzBeat webhook payload: " + JSON.stringify(hertzbeatAlert));
// 執行消息推送函數
Hertzbeat.sendMessage(params.URL, hertzbeatAlert);
// 返回給zabbixok 在 Zabbix 動作中會被用來標識成功執行。
return 'OK';
} catch (error) {
Zabbix.Log(4, '[Hertzbeat Webhook] notification failed: ' + error);
throw 'Sending failed: ' + error + '.';
}
```
7. 點擊 **添加** 按鈕保存媒介類型
### 步驟二: 為用戶配置媒介
1. 進入 **用戶** > **用戶** > **選擇要接收告警的用戶** (可以創建一個專門用於告警的用戶)
2. 選擇 **報警媒介** 選項卡 > **添加**
3. 選擇 **HertzBeat Webhook** 類型
4. 啟用時間段按需選擇,報警嚴重性按需選擇,確保狀態為啟用
5. 點擊 **添加** 按鈕保存媒介
### 步驟三: 配置告警動作
1. 進入 **告警** > **動作** > **觸發器動作** > **創建動作**
2. 配置動作選項卡信息
- 名稱: HertzBeat Webhook
- 條件: 根據需要配置觸發條件
3. 配置操作選項卡信息
- 操作步驟持續時間: 根據需要進行設置
- 在**操作**部分添加,配置用戶或用戶組,選擇之前配置 HertzBeat Webhook 媒介的用戶,發送至媒體類型選擇 **HertzBeat Webhook**,選中自定義消息內容,確保所有宏都被正確傳遞
- **恢復操作**以及**更新操作**可以根據上述進行類似配置
4. 點擊 **添加** 按鈕保存告警動作
### 常見問題
#### 未收到告警
- 確保 Webhook URL 可以被 zabbix 服務訪問
- 檢查服務器日誌是否有請求記錄
#### 告警未觸發
- 確保告警策略的條件正確,並已綁定通知
更多信息請參考 [Zabbix Webhook](https://www.zabbix.com/documentation/current/manual/config/notifications/webhook) 以及 [Zabbix 宏](https://www.zabbix.com/documentation/current/zh/manual/appendix/macros)
```
+1
View File
@@ -99,6 +99,7 @@
"alert.integration.source.webhook": "Default Webhook",
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.token.desc": "Token you generated that can be used to access the HertzBeat API.",
"alert.integration.token.new": "Click to Generate Token",
"alert.integration.token.notice": "Token only be displayed once. Please keep your token secure. Do not share it with others.",
+1
View File
@@ -99,6 +99,7 @@
"alert.integration.source.webhook": "デフォルトWebhook",
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.token.desc": "HertzBeat APIにアクセスするために生成したトークン。",
"alert.integration.token.new": "トークンを生成するにはクリック",
"alert.integration.token.notice": "トークンは一度だけ表示されます。トークンを安全に保管し、他人と共有しないでください。",
+1
View File
@@ -1126,6 +1126,7 @@
"alert.export.use-type": "Exportar regras no formato de arquivo {{type}}",
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"dashboard.alerts.title": "Lista de Alarmes Recentes",
"dashboard.alerts.title-no": "Alarmes Pendentes Recentes",
"dashboard.alerts.no": "Nenhum Alarme Pendente",
+1
View File
@@ -99,6 +99,7 @@
"alert.integration.source.webhook": "默认Webhook",
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.token.desc": "生成的 Token 可用于访问 HertzBeat API",
"alert.integration.token.new": "点击生成 Token",
"alert.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人",
+1
View File
@@ -99,6 +99,7 @@
"alert.integration.source.webhook": "默认Webhook",
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.token.desc": "生成的 Token 可用于访问 HertzBeat API",
"alert.integration.token.new": "点击生成 Token",
"alert.integration.token.notice": "此内容只会展示一次,请妥善保管您的 Token,不要泄露给他人",
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="600px"
viewBox="0 0 168 44" style="enable-background:new 0 0 168 44;" xml:space="preserve">
<style type="text/css">
.st0{clip-path:url(#SVGID_2_);fill:#D40000;}
.st1{clip-path:url(#SVGID_2_);fill:#FFFFFF;}
</style>
<g>
<g>
<defs>
<rect id="SVGID_1_" y="0" width="168" height="44"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
</clipPath>
<rect x="0" y="0" class="st0" width="168" height="44"/>
<polygon class="st1" points="7.2,6.7 31.5,6.7 31.5,9.9 11.9,33.8 32,33.8 32,37.3 6.7,37.3 6.7,34.1 26.3,10.2 7.2,10.2 "/>
<path class="st1" d="M48.4,10.8L42.7,26h11.4L48.4,10.8z M46,6.7h4.7l11.8,30.6h-4.3l-2.8-7.8H41.4l-2.8,7.8h-4.4L46,6.7z"/>
<path class="st1" d="M71.2,22.7v11.2h6.7c2.3,0,3.9-0.5,5-1.4c1.1-0.9,1.6-2.3,1.6-4.2c0-1.9-0.5-3.3-1.6-4.2
c-1.1-0.9-2.8-1.4-5-1.4H71.2z M71.2,10.1v9.2h6.2c2,0,3.6-0.4,4.6-1.1c1-0.8,1.5-1.9,1.5-3.5c0-1.5-0.5-2.7-1.5-3.5
c-1-0.8-2.5-1.1-4.6-1.1H71.2z M67,6.7h10.7c3.2,0,5.6,0.7,7.4,2c1.7,1.3,2.6,3.2,2.6,5.6c0,1.9-0.4,3.4-1.3,4.5
c-0.9,1.1-2.2,1.8-3.9,2.1c2.1,0.4,3.7,1.3,4.8,2.7c1.1,1.4,1.7,3.1,1.7,5.2c0,2.7-0.9,4.8-2.8,6.3c-1.9,1.5-4.5,2.2-8,2.2H67V6.7
z"/>
<path class="st1" d="M99.9,22.7v11.2h6.7c2.2,0,3.9-0.5,5-1.4c1.1-0.9,1.6-2.3,1.6-4.2c0-1.9-0.5-3.3-1.6-4.2
c-1.1-0.9-2.8-1.4-5-1.4H99.9z M99.9,10.1v9.2h6.2c2,0,3.6-0.4,4.6-1.1c1-0.8,1.5-1.9,1.5-3.5c0-1.5-0.5-2.7-1.5-3.5
c-1-0.8-2.5-1.1-4.6-1.1H99.9z M95.7,6.7h10.7c3.2,0,5.6,0.7,7.4,2c1.7,1.3,2.6,3.2,2.6,5.6c0,1.9-0.4,3.4-1.3,4.5
c-0.9,1.1-2.2,1.8-3.9,2.1c2.1,0.4,3.7,1.3,4.8,2.7c1.1,1.4,1.7,3.1,1.7,5.2c0,2.7-0.9,4.8-2.8,6.3c-1.9,1.5-4.5,2.2-8,2.2H95.7
V6.7z"/>
<polygon class="st1" points="136.4,6.7 141,6.7 148.3,17.7 155.7,6.7 160.1,6.7 150.5,21 161.3,37.3 156.7,37.3 148.2,24.5
139.7,37.3 135.2,37.3 146,21.1 "/>
<rect x="124.3" y="6.7" class="st1" width="4.2" height="30.6"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB