This is an automated email from the ASF dual-hosted git repository.
zhaoqingran pushed a commit to branch self-monitor
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/self-monitor by this push:
new 06e6300693 feat(otel): replace OpenTelemetry with Micrometer for
metrics
06e6300693 is described below
commit 06e6300693ee240feb94dbba93591bb3c8794ad6
Author: Logic <[email protected]>
AuthorDate: Mon Jul 14 17:23:53 2025 +0800
feat(otel): replace OpenTelemetry with Micrometer for metrics
- Remove OpenTelemetry metrics-related code and configuration
- Add Micrometer dependencies and configure with Prometheus registry
- Update MetricsService to use Micrometer for recording metrics- Expose
Prometheus metrics via Spring Boot Actuator endpoint
---
.../hertzbeat-collector-collector/pom.xml | 4 +
.../collector/dispatch/CommonDispatcher.java | 57 ++++++++--
hertzbeat-manager/pom.xml | 4 +
.../src/main/resources/application.yml | 12 ++
hertzbeat-manager/src/main/resources/sureness.yml | 3 +
hertzbeat-otel/pom.xml | 10 +-
.../hertzbeat/otel/config/OpenTelemetryConfig.java | 91 ++--------------
.../controller/PrometheusMetricsController.java | 58 ----------
.../hertzbeat/otel/service/MetricsService.java | 121 +++++++++------------
9 files changed, 134 insertions(+), 226 deletions(-)
diff --git a/hertzbeat-collector/hertzbeat-collector-collector/pom.xml
b/hertzbeat-collector/hertzbeat-collector-collector/pom.xml
index 172d3af836..0584537b77 100644
--- a/hertzbeat-collector/hertzbeat-collector-collector/pom.xml
+++ b/hertzbeat-collector/hertzbeat-collector-collector/pom.xml
@@ -87,6 +87,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.hertzbeat</groupId>
+ <artifactId>hertzbeat-otel</artifactId>
+ </dependency>
</dependencies>
diff --git
a/hertzbeat-collector/hertzbeat-collector-collector/src/main/java/org/apache/hertzbeat/collector/dispatch/CommonDispatcher.java
b/hertzbeat-collector/hertzbeat-collector-collector/src/main/java/org/apache/hertzbeat/collector/dispatch/CommonDispatcher.java
index d58d53cdac..a49726aa1b 100644
---
a/hertzbeat-collector/hertzbeat-collector-collector/src/main/java/org/apache/hertzbeat/collector/dispatch/CommonDispatcher.java
+++
b/hertzbeat-collector/hertzbeat-collector-collector/src/main/java/org/apache/hertzbeat/collector/dispatch/CommonDispatcher.java
@@ -33,6 +33,8 @@ import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
+import org.apache.hertzbeat.otel.service.MetricsService; // 新增
+import org.springframework.beans.factory.annotation.Autowired; // 新增
import org.springframework.stereotype.Component;
import java.util.HashMap;
@@ -90,6 +92,9 @@ public class CommonDispatcher implements MetricsTaskDispatch,
CollectDataDispatc
private final String collectorIdentity;
+ @Autowired
+ private MetricsService metricsService; // 新增注入
+
public CommonDispatcher(MetricsCollectorQueue jobRequestQueue,
TimerDispatch timerDispatch,
CommonDataQueue commonDataQueue,
@@ -153,12 +158,23 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
for (Map.Entry<String, MetricsTime> entry :
metricsTimeoutMonitorMap.entrySet()) {
MetricsTime metricsTime = entry.getValue();
if (metricsTime.getStartTime() < deadline) {
- // Metrics collection timeout
+ // Metrics collection timeout
+ MetricsTime removedMetricsTime =
metricsTimeoutMonitorMap.remove(entry.getKey());
+ if (removedMetricsTime == null) {
+ continue;
+ }
WheelTimerTask timerJob = (WheelTimerTask)
metricsTime.getTimeout().task();
+ Job job = timerJob.getJob();
+ // ========================> 超时埋点 <========================
+ if (metricsService != null) {
+ long duration = System.currentTimeMillis() -
removedMetricsTime.getStartTime();
+ metricsService.recordCollectMetrics(job, duration,
"timeout");
+ }
+ // ========================> 埋点结束 <========================
CollectRep.MetricsData metricsData =
CollectRep.MetricsData.newBuilder()
- .setId(timerJob.getJob().getMonitorId())
- .setTenantId(timerJob.getJob().getTenantId())
- .setApp(timerJob.getJob().getApp())
+ .setId(job.getMonitorId())
+ .setTenantId(job.getTenantId())
+ .setApp(job.getApp())
.setMetrics(metricsTime.getMetrics().getName())
.setPriority(metricsTime.getMetrics().getPriority())
.setTime(System.currentTimeMillis())
@@ -167,7 +183,6 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
if (metricsData.getPriority() == 0) {
dispatchCollectData(metricsTime.timeout,
metricsTime.getMetrics(), metricsData);
}
- metricsTimeoutMonitorMap.remove(entry.getKey());
}
}
} catch (Exception e) {
@@ -177,7 +192,7 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
@Override
public void dispatchMetricsTask(Timeout timeout) {
- // Divide the collection task of a single application into
corresponding collection tasks of the metrics according to the metrics under it.
+ // Divide the collection task of a single application into
corresponding collection tasks of the metrics under it.
// Put each collect task into the thread pool for scheduling
WheelTimerTask timerTask = (WheelTimerTask) timeout.task();
Job job = timerTask.getJob();
@@ -201,16 +216,27 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
public void dispatchCollectData(Timeout timeout, Metrics metrics,
CollectRep.MetricsData metricsData) {
WheelTimerTask timerJob = (WheelTimerTask) timeout.task();
Job job = timerJob.getJob();
+ String monitorKey;
+ if (metrics.isHasSubTask()) {
+ monitorKey = job.getId() + "-" + metrics.getName() + "-sub-" +
metrics.getSubTaskId();
+ } else {
+ monitorKey = job.getId() + "-" + metrics.getName();
+ }
+ MetricsTime metricsTime = metricsTimeoutMonitorMap.remove(monitorKey);
+ // ========================> 任务完成埋点 <========================
+ if (metricsTime != null && metricsService != null) {
+ long duration = System.currentTimeMillis() -
metricsTime.getStartTime();
+ String status = metricsData.getCode() == CollectRep.Code.SUCCESS ?
"success" : "fail";
+ metricsService.recordCollectMetrics(job, duration, status);
+ }
+ // ========================> 埋点结束 <========================
if (metrics.isHasSubTask()) {
- metricsTimeoutMonitorMap.remove(job.getId() + "-" +
metrics.getName() + "-sub-" + metrics.getSubTaskId());
boolean isLastTask = metrics.consumeSubTaskResponse(metricsData);
if (isLastTask) {
metricsData = metrics.getSubTaskDataRef().get().build();
} else {
return;
}
- } else {
- metricsTimeoutMonitorMap.remove(job.getId() + "-" +
metrics.getName());
}
Set<Metrics> metricsSet = job.getNextCollectMetrics(metrics, false);
if (job.isCyclic()) {
@@ -322,7 +348,15 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
public void dispatchCollectData(Timeout timeout, Metrics metrics,
List<CollectRep.MetricsData> metricsDataList) {
WheelTimerTask timerJob = (WheelTimerTask) timeout.task();
Job job = timerJob.getJob();
- metricsTimeoutMonitorMap.remove(String.valueOf(job.getId()));
+ MetricsTime metricsTime =
metricsTimeoutMonitorMap.remove(String.valueOf(job.getId()));
+ // ========================> Prometheus任务完成埋点 <========================
+ if (metricsTime != null && metricsService != null) {
+ long duration = System.currentTimeMillis() -
metricsTime.getStartTime();
+ // For a list, we consider it a success if at least one item is
successful.
+ boolean isSuccess = metricsDataList.stream().anyMatch(item ->
item.getCode() == CollectRep.Code.SUCCESS);
+ metricsService.recordCollectMetrics(job, duration, isSuccess ?
"success" : "fail");
+ }
+ // ========================> 埋点结束 <========================
if (job.isCyclic()) {
// The collection and execution of all task of this job are
completed.
// The periodic task pushes the task to the time wheel again.
@@ -340,7 +374,6 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
// and the result listener is notified of the combination of all
metrics data
timerDispatch.responseSyncJobData(job.getId(), metricsDataList);
}
-
}
@@ -354,4 +387,4 @@ public class CommonDispatcher implements
MetricsTaskDispatch, CollectDataDispatc
private Metrics metrics;
private Timeout timeout;
}
-}
+}
\ No newline at end of file
diff --git a/hertzbeat-manager/pom.xml b/hertzbeat-manager/pom.xml
index 7351b3fc15..b9065fdc56 100644
--- a/hertzbeat-manager/pom.xml
+++ b/hertzbeat-manager/pom.xml
@@ -210,6 +210,10 @@
<groupId>org.apache.arrow</groupId>
<artifactId>arrow-memory-netty</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-registry-prometheus</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/hertzbeat-manager/src/main/resources/application.yml
b/hertzbeat-manager/src/main/resources/application.yml
index dcd30990fb..3ec3b9567e 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -48,7 +48,19 @@ management:
include:
- 'metrics'
- 'health'
+ - 'prometheus'
enabled-by-default: on
+ endpoint:
+ prometheus:
+ access: read_only
+ metrics:
+ tags:
+ application: ${spring.application.name}
+ environment: ${spring.profiles.active}
+ prometheus:
+ metrics:
+ export:
+ enabled: true
sureness:
container: jakarta_servlet
diff --git a/hertzbeat-manager/src/main/resources/sureness.yml
b/hertzbeat-manager/src/main/resources/sureness.yml
index d846c6a616..ae1296d4d2 100644
--- a/hertzbeat-manager/src/main/resources/sureness.yml
+++ b/hertzbeat-manager/src/main/resources/sureness.yml
@@ -107,6 +107,9 @@ excludedResource:
- /v3/api-docs===get
# h2 database
- /h2-console/**===*
+ # Prometheus endpoint
+ - /actuator/prometheus/**===get
+
# account info config
# eg: admin has role [admin,user], password is hertzbeat
diff --git a/hertzbeat-otel/pom.xml b/hertzbeat-otel/pom.xml
index bcf36c4bd4..c6fbab8d2e 100644
--- a/hertzbeat-otel/pom.xml
+++ b/hertzbeat-otel/pom.xml
@@ -56,10 +56,12 @@
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
<dependency>
- <groupId>io.opentelemetry</groupId>
- <artifactId>opentelemetry-exporter-prometheus</artifactId>
- <version>1.52.0-alpha</version>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
-
</dependencies>
</project>
diff --git
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/config/OpenTelemetryConfig.java
b/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/config/OpenTelemetryConfig.java
index 6ec278f5f2..2144a92c4b 100644
---
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/config/OpenTelemetryConfig.java
+++
b/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/config/OpenTelemetryConfig.java
@@ -1,33 +1,13 @@
-/*
- * 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.api.metrics.Meter;
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.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Base64;
@@ -46,6 +26,7 @@ import org.springframework.context.annotation.Configuration;
/**
* OpenTelemetryConfig provides customizations for the auto-configured
OpenTelemetry SDK,
* specifically for integrating with GrepTimeDB for logs and traces.
+ * Metrics are handled by Micrometer and exposed via Spring Boot Actuator.
*/
@Configuration
@Slf4j
@@ -60,9 +41,6 @@ public class OpenTelemetryConfig {
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())) {
@@ -75,9 +53,6 @@ public class OpenTelemetryConfig {
}
}
- /**
- * 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);
@@ -86,9 +61,6 @@ public class OpenTelemetryConfig {
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);
@@ -99,33 +71,29 @@ public class OpenTelemetryConfig {
return Collections.unmodifiableMap(headers);
}
- /**
- * Provides default OpenTelemetry configuration that always executes.
- */
@Bean
public AutoConfigurationCustomizerProvider defaultOtelCustomizer() {
- log.info("Applying default OpenTelemetry SDK customizations.");
+ log.info("Applying default OpenTelemetry SDK customizations (logs &
traces only).");
return providerCustomizer -> providerCustomizer
.addPropertiesCustomizer(sdkConfigProperties -> {
Map<String, String> newProperties = new HashMap<>();
+ // Disable all built-in exporters - we use Micrometer for
metrics
newProperties.put("otel.metrics.exporter", "none");
newProperties.put("otel.traces.exporter", "none");
newProperties.put("otel.logs.exporter", "none");
+ log.info("OpenTelemetry exporters disabled. Metrics
handled by Micrometer.");
return newProperties;
})
.addResourceCustomizer((resource, configProperties) -> {
- log.info("Customizing auto-configured OpenTelemetry
Resource with service name: {}.", HERTZBEAT_SERVICE_NAME);
+ log.info("Customizing OpenTelemetry Resource with service
name: {}.", HERTZBEAT_SERVICE_NAME);
return resource.merge(Resource.builder().put(SERVICE_NAME,
HERTZBEAT_SERVICE_NAME).build());
});
}
- /**
- * Provides GrepTimeDB-specific OpenTelemetry configuration when enabled.
- */
@Bean
@ConditionalOnProperty(name = "warehouse.store.greptime.enabled",
havingValue = "true")
public AutoConfigurationCustomizerProvider
greptimeOtelCustomizer(GreptimeProperties greptimeProperties) {
- log.info("GreptimeDB is enabled. Applying additional OpenTelemetry SDK
customizations for GrepTimeDB.");
+ log.info("GreptimeDB is enabled. Applying OpenTelemetry customizations
for GrepTimeDB logs & traces.");
return providerCustomizer -> providerCustomizer
.addPropertiesCustomizer(sdkConfigProperties -> {
Map<String, String> newProperties = new HashMap<>();
@@ -134,18 +102,16 @@ public class OpenTelemetryConfig {
})
.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);
+ log.info("Configuring OtlpHttpSpanExporter for GreptimeDB.
Endpoint: {}", traceEndpoint);
OtlpHttpSpanExporterBuilder httpExporterBuilder =
OtlpHttpSpanExporter.builder()
.setEndpoint(traceEndpoint)
- .setHeaders(() -> traceHeaders)
+ .setHeaders(() ->
buildGreptimeOtlpTraceHeaders(greptimeProperties))
.setTimeout(10000, TimeUnit.MILLISECONDS);
return httpExporterBuilder.build();
})
.addLoggerProviderCustomizer((sdkLoggerProviderBuilder,
configProperties) -> {
- log.info("Customizing auto-configured
SdkLoggerProviderBuilder for GrepTimeDB logs.");
+ log.info("Customizing SdkLoggerProviderBuilder for
GrepTimeDB logs.");
OtlpHttpLogRecordExporter logExporter =
OtlpHttpLogRecordExporter.builder()
.setEndpoint(greptimeProperties.httpEndpoint() +
"/v1/otlp/v1/logs")
@@ -161,43 +127,4 @@ public class OpenTelemetryConfig {
return
sdkLoggerProviderBuilder.addLogRecordProcessor(batchLogProcessor);
});
}
-
- /**
- * Configures the SdkMeterProvider for Prometheus scraping.
- * This bean is conditionally created if the
otel.exporter.prometheus.enabled property is true.
- *
- * @return SdkMeterProvider
- */
- @Bean
- @ConditionalOnProperty(name = "otel.exporter.prometheus.enabled",
havingValue = "true")
- public SdkMeterProvider sdkMeterProvider() {
- return SdkMeterProvider.builder()
-
.setResource(Resource.create(io.opentelemetry.api.common.Attributes.of(SERVICE_NAME,
HERTZBEAT_SERVICE_NAME)))
- .build();
- }
-
- /**
- * Creates a PrometheusCollector bean that registers with the
SdkMeterProvider.
- * This collector is then used by the PrometheusMetricsController to
scrape metrics.
- *
- * @param sdkMeterProvider the SdkMeterProvider
- * @return a PrometheusCollector
- */
- @Bean
- @ConditionalOnProperty(name = "otel.exporter.prometheus.enabled",
havingValue = "true")
- public PrometheusCollector prometheusCollector(SdkMeterProvider
sdkMeterProvider) {
- return
PrometheusCollector.builder().setSdkMeterProvider(sdkMeterProvider).build();
- }
-
- /**
- * Provides a Meter bean for dependency injection across the application
(e.g., in MetricsService).
- *
- * @param sdkMeterProvider The SdkMeterProvider bean.
- * @return Meter
- */
- @Bean
- @ConditionalOnProperty(name = "otel.exporter.prometheus.enabled",
havingValue = "true")
- public Meter meter(SdkMeterProvider sdkMeterProvider) {
- return sdkMeterProvider.get(HERTZBEAT_SERVICE_NAME);
- }
}
\ No newline at end of file
diff --git
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/controller/PrometheusMetricsController.java
b/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/controller/PrometheusMetricsController.java
deleted file mode 100644
index 0c751b8601..0000000000
---
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/controller/PrometheusMetricsController.java
+++ /dev/null
@@ -1,58 +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.otel.controller;
-
-import io.opentelemetry.exporter.prometheus.PrometheusCollector;
-import io.prometheus.client.exporter.common.TextFormat;
-import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * A Spring MVC controller to expose Prometheus metrics.
- * This controller is enabled when `otel.exporter.prometheus.enabled` is set
to `true`.
- * It provides a `/metrics` endpoint that scrapes the OpenTelemetry metrics
- * and returns them in the Prometheus text format.
- */
-@RestController
-@ConditionalOnProperty(name = "otel.exporter.prometheus.enabled", havingValue
= "true")
-public class PrometheusMetricsController {
-
- private final PrometheusCollector prometheusCollector;
-
- public PrometheusMetricsController(PrometheusCollector
prometheusCollector) {
- this.prometheusCollector = prometheusCollector;
- }
-
- /**
- * Handles GET requests to the /metrics endpoint.
- *
- * @param response the HttpServletResponse to write the metrics to
- * @throws IOException if an I/O error occurs
- */
- @GetMapping(value = "/metrics", produces = TextFormat.CONTENT_TYPE_004)
- public void metrics(HttpServletResponse response) throws IOException {
- try (Writer writer = response.getWriter()) {
- TextFormat.write004(writer, prometheusCollector.collect());
- }
- }
-}
\ No newline at end of file
diff --git
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/service/MetricsService.java
b/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/service/MetricsService.java
index 7becbcc5b8..5c536de257 100644
---
a/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/service/MetricsService.java
+++
b/hertzbeat-otel/src/main/java/org/apache/hertzbeat/otel/service/MetricsService.java
@@ -1,90 +1,71 @@
-/*
- * 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.service;
-import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.api.metrics.DoubleHistogram;
-import io.opentelemetry.api.metrics.LongCounter;
-import io.opentelemetry.api.metrics.Meter;
-import org.apache.hertzbeat.common.entity.manager.Monitor;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.common.entity.job.Job;
import org.springframework.stereotype.Service;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
+import java.util.Map;
/**
- * Service for collecting and exporting HertzBeat's internal metrics using
OpenTelemetry.
+ * Service for managing and recording Micrometer metrics.
+ * This service uses Micrometer which integrates natively with Spring Boot
Actuator.
*/
@Service
+@Slf4j
public class MetricsService {
- private static final String STATUS_SUCCESS = "success";
- private static final String STATUS_FAIL = "fail";
- private static final String LABEL_STATUS = "status";
- private static final String LABEL_MONITOR_TYPE = "monitor_type";
- private static final String LABEL_MONITOR_ID = "monitor_id";
- private static final String LABEL_MONITOR_NAME = "monitor_name";
- private static final String LABEL_MONITOR_TARGET = "monitor_target";
-
- private final LongCounter collectTotalCounter;
- private final DoubleHistogram collectDurationHistogram;
+ private final MeterRegistry meterRegistry;
- public MetricsService(Meter meter) {
- this.collectTotalCounter =
meter.counterBuilder("hertzbeat_collect_total")
- .setDescription("The total number of collection tasks
executed.")
- .build();
-
- this.collectDurationHistogram =
meter.histogramBuilder("hertzbeat_collect_duration_seconds")
- .setDescription("The duration of collection task executions,
in seconds.")
- .setUnit("s")
- .build();
+ public MetricsService(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ log.info("MetricsService initialized with MeterRegistry: {}",
meterRegistry.getClass().getSimpleName());
}
/**
- * Records a successful collection task.
+ * Records the metrics for a completed collection sub-task.
*
- * @param monitor The monitor instance.
- * @param duration The duration of the collection in milliseconds.
+ * @param job The parent job containing monitor info.
+ * @param durationMillis The duration of the collection task in
milliseconds.
+ * @param status The final status of the collection ("success",
"fail", "timeout").
*/
- public void recordCollect(Monitor monitor, long duration) {
- Attributes attributes = buildAttributes(monitor, STATUS_SUCCESS);
- collectTotalCounter.add(1, attributes);
-
collectDurationHistogram.record(TimeUnit.MILLISECONDS.toSeconds(duration),
attributes);
- }
+ public void recordCollectMetrics(Job job, long durationMillis, String
status) {
+ if (job == null) {
+ return;
+ }
- /**
- * Records a failed collection task.
- *
- * @param monitor The monitor instance.
- * @param duration The duration of the collection in milliseconds.
- */
- public void recordCollect(Monitor monitor, long duration, Throwable
throwable) {
- Attributes attributes = buildAttributes(monitor, STATUS_FAIL);
- collectTotalCounter.add(1, attributes);
-
collectDurationHistogram.record(TimeUnit.MILLISECONDS.toSeconds(duration),
attributes);
- }
+ Map<String, String> metadata = job.getMetadata();
+ String monitorName = metadata != null ? metadata.get("instancename") :
"unknown";
+ String monitorTarget = metadata != null ? metadata.get("instancehost")
: "unknown";
+
+ // Record collection count
+ Counter.builder("hertzbeat.collect.total")
+ .description("The total number of collection tasks executed")
+ .tag("status", status)
+ .tag("monitor_type", job.getApp())
+ .tag("monitor_id", String.valueOf(job.getMonitorId()))
+ .tag("monitor_name", monitorName)
+ .tag("monitor_target", monitorTarget)
+ .register(meterRegistry)
+ .increment();
+
+ // Record collection duration
+ Timer.builder("hertzbeat.collect.duration")
+ .description("The duration of collection task executions")
+ .tag("status", status)
+ .tag("monitor_type", job.getApp())
+ .tag("monitor_id", String.valueOf(job.getMonitorId()))
+ .tag("monitor_name", monitorName)
+ .tag("monitor_target", monitorTarget)
+ .register(meterRegistry)
+ .record(Duration.ofMillis(durationMillis));
- private Attributes buildAttributes(Monitor monitor, String status) {
- return Attributes.builder()
- .put(LABEL_STATUS, status)
- .put(LABEL_MONITOR_ID, String.valueOf(monitor.getId()))
- .put(LABEL_MONITOR_NAME, monitor.getName())
- .put(LABEL_MONITOR_TYPE, monitor.getApp())
- .put(LABEL_MONITOR_TARGET, monitor.getHost())
- .build();
+ if (log.isDebugEnabled()) {
+ log.debug("Recorded metrics for monitor [{}] ({}): status={},
duration={}ms",
+ monitorName, job.getMonitorId(), status, durationMillis);
+ }
}
}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]