This is an automated email from the ASF dual-hosted git repository.
zrlw pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.3 by this push:
new 82e77e4603 Fix issue 12517, implemented new module for otlp config
along with test checking jvm metric export using mock server (#16023)
82e77e4603 is described below
commit 82e77e4603e12ef686129555870fa4eea04add30
Author: Manas Agarwal <[email protected]>
AuthorDate: Wed Jan 28 10:45:33 2026 +0530
Fix issue 12517, implemented new module for otlp config along with test
checking jvm metric export using mock server (#16023)
---
.artifacts | 1 +
.../dubbo/common/constants/MetricsConstants.java | 2 +
.../org/apache/dubbo/config/MetricsConfig.java | 15 +++
.../dubbo/config/nested/OtlpMetricConfig.java | 90 +++++++++++++
dubbo-distribution/dubbo-all-shaded/pom.xml | 10 +-
dubbo-distribution/dubbo-all/pom.xml | 9 +-
dubbo-distribution/dubbo-bom/pom.xml | 6 +-
dubbo-metrics/dubbo-metrics-otlp/pom.xml | 74 +++++++++++
.../dubbo/metrics/otlp/OtlpMetricsReporter.java | 143 ++++++++++++++++++++
.../metrics/otlp/OtlpMetricsReporterFactory.java | 34 +++++
...che.dubbo.metrics.report.MetricsReporterFactory | 1 +
.../otlp/OtlpMetricsReporterFactoryTest.java | 146 +++++++++++++++++++++
dubbo-metrics/pom.xml | 1 +
dubbo-test/dubbo-dependencies-all/pom.xml | 6 +-
14 files changed, 534 insertions(+), 4 deletions(-)
diff --git a/.artifacts b/.artifacts
index 09d2d03525..28b0249adb 100644
--- a/.artifacts
+++ b/.artifacts
@@ -53,6 +53,7 @@ dubbo-metrics-api
dubbo-metrics-default
dubbo-metrics-metadata
dubbo-metrics-prometheus
+dubbo-metrics-otlp
dubbo-metrics-registry
dubbo-metrics-config-center
dubbo-metrics-netty
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
index b9b503d0e9..2fa7e2d5b5 100644
---
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
+++
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/MetricsConstants.java
@@ -98,4 +98,6 @@ public interface MetricsConstants {
String METRIC_FILTER_START_TIME = "metric_filter_start_time";
String TAG_THREAD_NAME = "thread.pool.name";
+
+ String PROTOCOL_OTLP = "otlp";
}
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
index 5a56ed40ea..7c948b4b02 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.config.nested.AggregationConfig;
import org.apache.dubbo.config.nested.HistogramConfig;
+import org.apache.dubbo.config.nested.OtlpMetricConfig;
import org.apache.dubbo.config.nested.PrometheusConfig;
import org.apache.dubbo.config.support.Nested;
import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -127,6 +128,12 @@ public class MetricsConfig extends AbstractConfig {
*/
private String rpcLevel;
+ /**
+ * Configuration for the metrics exporter.
+ */
+ @Nested
+ private OtlpMetricConfig otlp;
+
public MetricsConfig() {}
public MetricsConfig(ApplicationModel applicationModel) {
@@ -287,4 +294,12 @@ public class MetricsConfig extends AbstractConfig {
public void setEnableNetty(Boolean enableNetty) {
this.enableNetty = enableNetty;
}
+
+ public OtlpMetricConfig getOtlp() {
+ return otlp;
+ }
+
+ public void setOtlp(OtlpMetricConfig otlp) {
+ this.otlp = otlp;
+ }
}
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/config/nested/OtlpMetricConfig.java
b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/OtlpMetricConfig.java
new file mode 100644
index 0000000000..97ddc40c86
--- /dev/null
+++
b/dubbo-common/src/main/java/org/apache/dubbo/config/nested/OtlpMetricConfig.java
@@ -0,0 +1,90 @@
+/*
+ * 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.dubbo.config.nested;
+
+import java.io.Serializable;
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class OtlpMetricConfig implements Serializable {
+
+ /**
+ * URI of the OLTP server.
+ */
+ private String endpoint;
+
+ /**
+ * Monitored resource's attributes.
+ */
+ private Map<String, String> resourceAttributes;
+
+ /**
+ * Headers for the exported metrics.
+ */
+ private Map<String, String> headers;
+
+ /**
+ * Time unit for exported metrics.
+ */
+ private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS;
+
+ /**
+ * Intervals for pushing metrics
+ */
+ private Duration step;
+
+ public String getEndpoint() {
+ return this.endpoint;
+ }
+
+ public void setUrl(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public Map<String, String> getResourceAttributes() {
+ return this.resourceAttributes;
+ }
+
+ public void setResourceAttributes(Map<String, String> resourceAttributes) {
+ this.resourceAttributes = resourceAttributes;
+ }
+
+ public Map<String, String> getHeaders() {
+ return this.headers;
+ }
+
+ public void setHeaders(Map<String, String> headers) {
+ this.headers = headers;
+ }
+
+ public TimeUnit getBaseTimeUnit() {
+ return this.baseTimeUnit;
+ }
+
+ public void setBaseTimeUnit(TimeUnit baseTimeUnit) {
+ this.baseTimeUnit = baseTimeUnit;
+ }
+
+ public Duration getStep() {
+ return this.step;
+ }
+
+ public void setStep(Duration step) {
+ this.step = step;
+ }
+}
diff --git a/dubbo-distribution/dubbo-all-shaded/pom.xml
b/dubbo-distribution/dubbo-all-shaded/pom.xml
index b5c1d40dd8..84884121d5 100644
--- a/dubbo-distribution/dubbo-all-shaded/pom.xml
+++ b/dubbo-distribution/dubbo-all-shaded/pom.xml
@@ -207,7 +207,13 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-otlp</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
<!-- tracing -->
<dependency>
<groupId>org.apache.dubbo</groupId>
@@ -508,6 +514,7 @@
<include>org.apache.dubbo:dubbo-metrics-config-center</include>
<include>org.apache.dubbo:dubbo-metrics-netty</include>
<include>org.apache.dubbo:dubbo-metrics-prometheus</include>
+ <include>org.apache.dubbo:dubbo-metrics-otlp</include>
<include>org.apache.dubbo:dubbo-tracing</include>
<include>org.apache.dubbo:dubbo-qos</include>
<include>org.apache.dubbo:dubbo-qos-api</include>
@@ -541,6 +548,7 @@
<include>org.apache.dubbo:dubbo-serialization-hessian2</include>
<include>org.apache.dubbo:dubbo-serialization-fastjson2</include>
<include>org.apache.dubbo:dubbo-plugin-loom</include>
+
<include>io.netty:*</include>
</includes>
</artifactSet>
diff --git a/dubbo-distribution/dubbo-all/pom.xml
b/dubbo-distribution/dubbo-all/pom.xml
index a766ee0558..3ac1a3939f 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -207,7 +207,13 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-otlp</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
<!-- tracing -->
<dependency>
<groupId>org.apache.dubbo</groupId>
@@ -507,6 +513,7 @@
<include>org.apache.dubbo:dubbo-metrics-config-center</include>
<include>org.apache.dubbo:dubbo-metrics-netty</include>
<include>org.apache.dubbo:dubbo-metrics-prometheus</include>
+ <include>org.apache.dubbo:dubbo-metrics-otlp</include>
<include>org.apache.dubbo:dubbo-tracing</include>
<include>org.apache.dubbo:dubbo-qos</include>
<include>org.apache.dubbo:dubbo-qos-api</include>
diff --git a/dubbo-distribution/dubbo-bom/pom.xml
b/dubbo-distribution/dubbo-bom/pom.xml
index 558f95a7c6..d699478357 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -237,7 +237,11 @@
<artifactId>dubbo-metrics-netty</artifactId>
<version>${project.version}</version>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-otlp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- tracing -->
<dependency>
<groupId>org.apache.dubbo</groupId>
diff --git a/dubbo-metrics/dubbo-metrics-otlp/pom.xml
b/dubbo-metrics/dubbo-metrics-otlp/pom.xml
new file mode 100644
index 0000000000..16f7b54e62
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-otlp/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-metrics-otlp</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The otlp metrics module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-qos-api</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-registry-otlp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-default</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporter.java
b/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporter.java
new file mode 100644
index 0000000000..5d8b938e98
--- /dev/null
+++
b/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporter.java
@@ -0,0 +1,143 @@
+/*
+ * 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.dubbo.metrics.otlp;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.MetricsConstants;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.nested.OtlpMetricConfig;
+import org.apache.dubbo.metrics.report.AbstractMetricsReporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import io.micrometer.core.instrument.Clock;
+import io.micrometer.registry.otlp.AggregationTemporality;
+import io.micrometer.registry.otlp.OtlpConfig;
+import io.micrometer.registry.otlp.OtlpMeterRegistry;
+
+/**
+ * Metrics reporter for Otlp.
+ */
+public class OtlpMetricsReporter extends AbstractMetricsReporter {
+
+ private final OtlpMeterRegistry otlpMeterRegistry;
+
+ public OtlpMetricsReporter(final URL url, ApplicationModel
applicationModel) {
+ super(url, applicationModel);
+ Optional<MetricsConfig> configOptional =
+ applicationModel.getApplicationConfigManager().getMetrics();
+ // If no specific metrics type is configured and there is no
Prometheus dependency in the dependencies.
+ MetricsConfig metricsConfig = configOptional.orElse(new
MetricsConfig(applicationModel));
+ OtlpMetricConfig otlpMetricConfig = metricsConfig.getOtlp();
+ // check protocol whether is otlp
+ if (otlpMetricConfig == null ||
!MetricsConstants.PROTOCOL_OTLP.equals(metricsConfig.getProtocol())) {
+ throw new IllegalStateException(
+ "Otlp metrics reporter config is required oltp protocol
but real does not match.");
+ }
+
+ OtlpConfig config = new OtlpWrapperConfig(otlpMetricConfig,
applicationModel);
+ this.otlpMeterRegistry = new OtlpMeterRegistry(config, Clock.SYSTEM);
+ }
+
+ @Override
+ public void doInit() {
+ addMeterRegistry(this.otlpMeterRegistry);
+ }
+
+ @Override
+ public String getResponse() {
+ return null;
+ }
+
+ @Override
+ public void doDestroy() {
+ if (this.otlpMeterRegistry != null) {
+ this.otlpMeterRegistry.close();
+ }
+ }
+
+ public static class OtlpWrapperConfig implements OtlpConfig {
+
+ private final OtlpMetricConfig otlpMetricConfig;
+ private final ApplicationModel applicationModel;
+
+ public OtlpWrapperConfig(OtlpMetricConfig otlpMetricConfig,
ApplicationModel applicationModel) {
+ this.otlpMetricConfig = otlpMetricConfig;
+ this.applicationModel = applicationModel;
+ }
+
+ @Override
+ public String get(String key) {
+ // just use default value
+ return OtlpConfig.DEFAULT.get(key);
+ }
+
+ @Override
+ public String url() {
+ if (this.otlpMetricConfig.getEndpoint() == null) {
+ return OtlpConfig.super.url();
+ }
+ return this.otlpMetricConfig.getEndpoint();
+ }
+
+ @Override
+ public Duration step() {
+ if (this.otlpMetricConfig.getStep() == null) {
+ return OtlpConfig.super.step();
+ }
+ return this.otlpMetricConfig.getStep();
+ }
+
+ @Override
+ public Map<String, String> resourceAttributes() {
+ Map<String, String> resourceAttributes =
this.otlpMetricConfig.getResourceAttributes();
+ if (resourceAttributes == null) {
+ resourceAttributes = OtlpConfig.super.resourceAttributes();
+ }
+ // set service.name
+ resourceAttributes.computeIfAbsent("service.name", (key) ->
getApplicationName());
+ return resourceAttributes;
+ }
+
+ @Override
+ public AggregationTemporality aggregationTemporality() {
+ return OtlpConfig.super.aggregationTemporality();
+ }
+
+ @Override
+ public Map<String, String> headers() {
+ Map<String, String> headers = this.otlpMetricConfig.getHeaders();
+ if (headers == null) {
+ headers = OtlpConfig.super.headers();
+ }
+ return headers;
+ }
+
+ @Override
+ public TimeUnit baseTimeUnit() {
+ return this.otlpMetricConfig.getBaseTimeUnit();
+ }
+
+ private String getApplicationName() {
+ return this.applicationModel.getApplicationName();
+ }
+ }
+}
diff --git
a/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactory.java
b/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactory.java
new file mode 100644
index 0000000000..67dbc34e7f
--- /dev/null
+++
b/dubbo-metrics/dubbo-metrics-otlp/src/main/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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.dubbo.metrics.otlp;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metrics.report.AbstractMetricsReporterFactory;
+import org.apache.dubbo.metrics.report.MetricsReporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+public class OtlpMetricsReporterFactory extends AbstractMetricsReporterFactory
{
+
+ public OtlpMetricsReporterFactory(ApplicationModel applicationModel) {
+ super(applicationModel);
+ }
+
+ @Override
+ public MetricsReporter createMetricsReporter(URL url) {
+ return new OtlpMetricsReporter(url, getApplicationModel());
+ }
+}
diff --git
a/dubbo-metrics/dubbo-metrics-otlp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory
b/dubbo-metrics/dubbo-metrics-otlp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory
new file mode 100644
index 0000000000..4036456bc9
--- /dev/null
+++
b/dubbo-metrics/dubbo-metrics-otlp/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.report.MetricsReporterFactory
@@ -0,0 +1 @@
+otlp=org.apache.dubbo.metrics.otlp.OtlpMetricsReporterFactory
diff --git
a/dubbo-metrics/dubbo-metrics-otlp/src/test/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactoryTest.java
b/dubbo-metrics/dubbo-metrics-otlp/src/test/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactoryTest.java
new file mode 100644
index 0000000000..f04092c2a5
--- /dev/null
+++
b/dubbo-metrics/dubbo-metrics-otlp/src/test/java/org/apache/dubbo/metrics/otlp/OtlpMetricsReporterFactoryTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.dubbo.metrics.otlp;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.MetricsConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.nested.AggregationConfig;
+import org.apache.dubbo.config.nested.OtlpMetricConfig;
+import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.metrics.report.MetricsReporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.MetricsConstants.PROTOCOL_OTLP;
+
+public class OtlpMetricsReporterFactoryTest {
+
+ private MockWebServer otlpMockServer;
+ private CopyOnWriteArrayList<byte[]> capturedRequests;
+ private ApplicationModel applicationModel;
+ private MetricsConfig metricsConfig;
+ private FrameworkModel frameworkModel;
+
+ @BeforeEach
+ public void setup() {
+ otlpMockServer = new MockWebServer();
+ for (int i = 0; i < 100; i++) {
+ otlpMockServer.enqueue(new MockResponse()
+ .setResponseCode(200)
+ .setBody("{}")
+ .addHeader("Content-Type", "application/json"));
+ }
+ applicationModel = ApplicationModel.defaultModel();
+ ConfigManager applicationConfigManager =
applicationModel.getApplicationConfigManager();
+ metricsConfig = new MetricsConfig();
+ metricsConfig.setEnableJvm(true);
+ metricsConfig.setProtocol(PROTOCOL_OTLP);
+ AggregationConfig aggregationConfig = new AggregationConfig();
+ aggregationConfig.setEnabled(false);
+ metricsConfig.setAggregation(aggregationConfig);
+ OtlpMetricConfig otlpMetricsConfig = new OtlpMetricConfig();
+ otlpMetricsConfig.setStep(Duration.ofSeconds(1));
+ metricsConfig.setOtlp(otlpMetricsConfig);
+ otlpMetricsConfig.setUrl(otlpMockServer.url("/v1/metrics").toString());
+ applicationConfigManager.setMetrics(metricsConfig);
+ ApplicationConfig applicationConfig = new ApplicationConfig();
+ applicationConfig.setName("test_app_name");
+ applicationConfigManager.setApplication(applicationConfig);
+ frameworkModel = FrameworkModel.defaultModel();
+
frameworkModel.getBeanFactory().getOrRegisterBean(DefaultMetricsCollector.class);
+ }
+
+ @AfterEach
+ public void teardown() throws IOException {
+ applicationModel.destroy();
+ if (otlpMockServer != null) {
+ otlpMockServer.shutdown();
+ }
+ }
+
+ @Test
+ public void test_MetricsReporter() {
+
+ OtlpMetricsReporterFactory factory = new
OtlpMetricsReporterFactory(applicationModel);
+ MetricsReporter reporter =
factory.createMetricsReporter(metricsConfig.toUrl());
+ Assertions.assertTrue(reporter instanceof OtlpMetricsReporter);
+ applicationModel.destroy();
+ }
+
+ @Test
+ public void test_MetricsReporter_with_not_match_protocol() {
+ OtlpMetricsReporterFactory factory = new
OtlpMetricsReporterFactory(applicationModel);
+ try {
+ factory.createMetricsReporter(metricsConfig.toUrl());
+ } catch (Exception ex) {
+ Assertions.assertInstanceOf(IllegalStateException.class, ex);
+ }
+ }
+
+ @Test
+ public void export_Test() throws IOException, InterruptedException {
+
+ OtlpMetricsReporter reporter = new
OtlpMetricsReporter(metricsConfig.toUrl(), applicationModel);
+ reporter.init();
+ try {
+
+ List<String> requestBodies = new ArrayList<>();
+
+ long endTime = System.currentTimeMillis() + 3000;
+ while (System.currentTimeMillis() < endTime) {
+ RecordedRequest request = otlpMockServer.takeRequest(500,
TimeUnit.MILLISECONDS);
+ if (request != null) {
+ String body =
request.getBody().readString(StandardCharsets.UTF_8);
+ requestBodies.add(body);
+ }
+ }
+
+ Assertions.assertFalse(requestBodies.isEmpty(), "Expected OTLP
metrics to be pushed");
+
+ boolean hasJvmMetrics = requestBodies.stream()
+ .anyMatch(body -> body.contains("jvm")
+ || body.contains("JVM")
+ || body.contains("memory")
+ || body.contains("gc")
+ || body.contains("threads"));
+
+ Assertions.assertTrue(
+ hasJvmMetrics,
+ "Expected JVM metrics to be present in OTLP export.
Captured bodies: " + requestBodies.size());
+
+ } finally {
+ reporter.destroy();
+ }
+ }
+}
diff --git a/dubbo-metrics/pom.xml b/dubbo-metrics/pom.xml
index 7176e2e182..af0ff2c692 100644
--- a/dubbo-metrics/pom.xml
+++ b/dubbo-metrics/pom.xml
@@ -37,6 +37,7 @@
<module>dubbo-metrics-config-center</module>
<module>dubbo-tracing</module>
<module>dubbo-metrics-netty</module>
+ <module>dubbo-metrics-otlp</module>
</modules>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
diff --git a/dubbo-test/dubbo-dependencies-all/pom.xml
b/dubbo-test/dubbo-dependencies-all/pom.xml
index fcdc9b593b..9d5b67d66e 100644
--- a/dubbo-test/dubbo-dependencies-all/pom.xml
+++ b/dubbo-test/dubbo-dependencies-all/pom.xml
@@ -190,7 +190,11 @@
<artifactId>dubbo-tracing</artifactId>
<version>${project.version}</version>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-metrics-otlp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- native -->
<dependency>
<groupId>org.apache.dubbo</groupId>