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]

Reply via email to