Skip to content

Commit d63ac25

Browse files
authored
Merge pull request #43831 from brunobat/micrometer-to-otel-bridge
Micrometer to OpenTelemetry Bridge
2 parents e38b5d9 + a6c8a69 commit d63ac25

File tree

48 files changed

+3245
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3245
-3
lines changed

.github/native-tests.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
{
118118
"category": "Misc4",
119119
"timeout": 130,
120-
"test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, observability-lgtm, opentelemetry, opentelemetry-jdbc-instrumentation, opentelemetry-mongodb-client-instrumentation, opentelemetry-redis-instrumentation, web-dependency-locator",
120+
"test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, observability-lgtm, opentelemetry, opentelemetry-jdbc-instrumentation, opentelemetry-mongodb-client-instrumentation, opentelemetry-redis-instrumentation, web-dependency-locator, micrometer-opentelemetry",
121121
"os-name": "ubuntu-latest"
122122
},
123123
{

bom/application/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,6 +3199,16 @@
31993199
<artifactId>quarkus-micrometer</artifactId>
32003200
<version>${project.version}</version>
32013201
</dependency>
3202+
<dependency>
3203+
<groupId>io.quarkus</groupId>
3204+
<artifactId>quarkus-micrometer-opentelemetry-deployment</artifactId>
3205+
<version>${project.version}</version>
3206+
</dependency>
3207+
<dependency>
3208+
<groupId>io.quarkus</groupId>
3209+
<artifactId>quarkus-micrometer-opentelemetry</artifactId>
3210+
<version>${project.version}</version>
3211+
</dependency>
32023212
<dependency>
32033213
<groupId>io.quarkus</groupId>
32043214
<artifactId>quarkus-micrometer-registry-prometheus-deployment</artifactId>

devtools/bom-descriptor-json/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,19 @@
15911591
</exclusion>
15921592
</exclusions>
15931593
</dependency>
1594+
<dependency>
1595+
<groupId>io.quarkus</groupId>
1596+
<artifactId>quarkus-micrometer-opentelemetry</artifactId>
1597+
<version>${project.version}</version>
1598+
<type>pom</type>
1599+
<scope>test</scope>
1600+
<exclusions>
1601+
<exclusion>
1602+
<groupId>*</groupId>
1603+
<artifactId>*</artifactId>
1604+
</exclusion>
1605+
</exclusions>
1606+
</dependency>
15941607
<dependency>
15951608
<groupId>io.quarkus</groupId>
15961609
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>

docs/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,19 @@
16021602
</exclusion>
16031603
</exclusions>
16041604
</dependency>
1605+
<dependency>
1606+
<groupId>io.quarkus</groupId>
1607+
<artifactId>quarkus-micrometer-opentelemetry-deployment</artifactId>
1608+
<version>${project.version}</version>
1609+
<type>pom</type>
1610+
<scope>test</scope>
1611+
<exclusions>
1612+
<exclusion>
1613+
<groupId>*</groupId>
1614+
<artifactId>*</artifactId>
1615+
</exclusion>
1616+
</exclusions>
1617+
</dependency>
16051618
<dependency>
16061619
<groupId>io.quarkus</groupId>
16071620
<artifactId>quarkus-micrometer-registry-prometheus-deployment</artifactId>
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
////
2+
This guide is maintained in the main Quarkus repository
3+
and pull requests should be submitted there:
4+
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
5+
////
6+
[id=telemetry-micrometer-opentelemetry]
7+
= Micrometer and OpenTelemetry extension
8+
include::_attributes.adoc[]
9+
:extension-status: preview
10+
:diataxis-type: reference
11+
:categories: observability
12+
:summary: Guide to send Micrometer data to OpenTelemetry.
13+
:topics: observability,opentelemetry,metrics,micrometer,tracing,logs
14+
:extensions: io.quarkus:quarkus-micrometer-opentelemetry
15+
16+
This extension provides support for both Micrometer and OpenTelemetry in Quarkus applications. It streamlines integration by incorporating both extensions along with a bridge that enables sending Micrometer metrics via OpenTelemetry.
17+
18+
include::{includes}/extension-status.adoc[]
19+
20+
[NOTE]
21+
====
22+
- The xref:telemetry-micrometer.adoc[Micrometer Guide] is available for detailed information about the Micrometer extension.
23+
- The xref:opentelemetry.adoc[OpenTelemetry Guide] provides information about the OpenTelemetry extension.
24+
====
25+
26+
The extension allows the normal use of the Micrometer API, but have the metrics handled by the OpenTelemetry extension.
27+
28+
As an example, the `@Timed` annotation from Micrometer is used to measure the execution time of a method:
29+
[source,java]
30+
----
31+
import io.micrometer.core.annotation.Timed;
32+
//...
33+
@Timed(name = "timer_metric")
34+
public String timer() {
35+
return "OK";
36+
}
37+
----
38+
The output telemetry data is handled by the OpenTelemetry SDK and sent by the `quarkus-opentelemetry` extension exporter using the OTLP protocol.
39+
40+
This reduces the overhead of having an independent Micrometer registry plus the OpenTelemetry SDK in memory for the same application when both `quarkus-micrometer` and `quarkus-opentelemetry` extensions are used independently.
41+
42+
*The OpenTelemetry SDK will handle all metrics.* Either Micrometer metrics (manual or automatic) and OpenTelemetry Metrics can be used. All are available with this single extension.
43+
44+
All the configurations from the OpenTelemetry and Micrometer extensions are available with `quarkus-micrometer-opentelemetry`.
45+
46+
The bridge is more than the simple OTLP registry found in Quarkiverse. In this extension, the OpenTelemetry SDK provides a Micrometer registry implementation based on the https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/micrometer/micrometer-1.5/library[`micrometer/micrometer-1.5`] OpenTelemetry instrumentation library.
47+
48+
== Usage
49+
50+
If you already have your Quarkus project configured, you can add the `quarkus-micrometer-opentelemetry` extension to your project by running the following command in your project base directory:
51+
52+
:add-extension-extensions: micrometer-opentelemetry
53+
include::{includes}/devtools/extension-add.adoc[]
54+
55+
This will add the following to your build file:
56+
57+
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
58+
.pom.xml
59+
----
60+
<dependency>
61+
<groupId>io.quarkus</groupId>
62+
<artifactId>quarkus-micrometer-opentelemetry</artifactId>
63+
</dependency>
64+
----
65+
66+
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
67+
.build.gradle
68+
----
69+
implementation("io.quarkus:quarkus-micrometer-opentelemetry")
70+
----
71+
72+
== Configuration
73+
74+
When the extension is present, Micrometer is enabled by default as are OpenTelemetry tracing, metrics and logs.
75+
76+
OpenTelemetry metrics auto-instrumentation for HTTP server and JVM metrics are disabled by default because those metrics can be collected by Micrometer.
77+
78+
Specific automatic Micrometer metrics are all disabled by default and can be enabled by setting, for example in the case of JVM metrics:
79+
[source,properties]
80+
----
81+
quarkus.micrometer.binder.jvm=true
82+
----
83+
in the `application.properties` file.
84+
85+
For this and other properties you can use with the extension, Please refer to:
86+
87+
* xref:telemetry-micrometer.adoc#configuration-reference[Micrometer metrics configuration properties]
88+
* xref:opentelemetry.adoc#configuration-reference[OpenTelemetry configuration properties]
89+
90+
== Metric differences between Micrometer and OpenTelemetry
91+
92+
=== API differences
93+
The metrics produced with each framework follow different APIs and the mapping is not 1:1.
94+
95+
One fundamental API difference is that Micrometer uses a https://docs.micrometer.io/micrometer/reference/concepts/timers.html[Timer] and OpenTelemetry uses a https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram[Histogram] to record latency (execution time) metrics and the frequency of the events.
96+
97+
When using the `@Timed` annotation with Micrometer, 2 different metrics are https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/324fdbdd452ddffaf2da2c5bf004d8bb3fdfa1dd/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java#L31[created on the OpenTelemetry side], one `Gauge` for the `max` value and one `Histogram`.
98+
99+
The `DistributionSummary` from Micrometer is transformed into a `Histogram` and a `DoubleGauge` for the `max` value. If service level objectives (slo) are set to `true` when creating a `DistributionSummary`, an additional histogram is created for them.
100+
101+
This table shows the differences between the two frameworks:
102+
103+
|===
104+
|Micrometer |OpenTelemetry
105+
106+
|DistributionSummary
107+
|`<Metric name>` (Histogram), `<Metric name>.max` (DoubleGauge)
108+
109+
|DistributionSummary with SLOs
110+
|`<Metric name>` (Histogram), `<Metric name>.max` (DoubleGauge), `<Metric name>.histogram` (DoubleGauge)
111+
112+
|LongTaskTimer
113+
|`<Metric name>.active` (ObservableLongUpDownCounter), `<Metric name>.duration` (ObservableDoubleUpDownCounter)
114+
115+
|Timer
116+
|`<Metric name>` (Histogram), `<Metric name>.max` (ObservableDoubleGauge)
117+
|===
118+
119+
120+
=== Semantic convention differences
121+
122+
The 2 frameworks follow different semantic conventions. The OpenTelemetry Metrics are based on the https://opentelemetry.io/docs/concepts/semantic-conventions/[OpenTelemetry Semantic Conventions] and are still under active development (early 2025). Micrometer metrics convention format is around for a long time and has not changed much.
123+
124+
When these 2 configurations are set in the `application.properties` file:
125+
126+
[source,properties]
127+
----
128+
quarkus.micrometer.binder.jvm=true
129+
quarkus.micrometer.binder.http-server.enabled=true
130+
----
131+
132+
The JVM and HTTP server metrics are collected by Micrometer.
133+
134+
Next, are examples of the metrics collected by Micrometer and a comparison of what would be the `quarkus-micrometer-registry-prometheus` output vs the one on this bridge. A link to the equivalent OpenTelemetry Semantic Convention is also provided for reference and is not currently used in the bridge.
135+
136+
|===
137+
|Micrometer Meter |Quarkus Micrometer Prometheus output | This bridge OpenTelemetry output name | Related OpenTelemetry Semantic Convention (not applied)
138+
139+
|Using the @Timed interceptor.
140+
|
141+
|method.timed (Histogram), method.timed.max (DoubleGauge)
142+
|NA
143+
144+
|Using the @Counted interceptor.
145+
|
146+
|method.counted (DoubleSum)
147+
|NA
148+
149+
|`http.server.active.requests` (Gauge)
150+
|`http_server_active_requests` (Gauge)
151+
|`http.server.active.requests` (DoubleGauge)
152+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserveractive_requests[`http.server.active_requests`] (UpDownCounter)
153+
154+
|`http.server.requests` (Timer)
155+
|`http_server_requests_seconds_count`, `http_server_requests_seconds_sum`, `http_server_requests_seconds_max` (Gauge)
156+
|`http.server.requests` (Histogram), `http.server.requests.max` (DoubleGauge)
157+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestduration[`http.server.request.duration`] (Histogram)
158+
159+
|`http.server.bytes.read` (DistributionSummary)
160+
|`http_server_bytes_read_count`, `http_server_bytes_read_sum` , `http_server_bytes_read_max` (Gauge)
161+
|`http.server.bytes.read` (Histogram), `http.server.bytes.read.max` (DoubleGauge)
162+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestbodysize[`http.server.request.body.size`] (Histogram)
163+
164+
|`http.server.bytes.write` (DistributionSummary)
165+
|`http_server_bytes_write_count`, `http_server_bytes_write_sum` , `http_server_bytes_write_max` (Gauge)
166+
|`http.server.bytes.write` (Histogram), `http.server.bytes.write.max` (DoubleGauge)
167+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverresponsebodysize[`http.server.response.body.size`] (Histogram)
168+
169+
|`http.server.connections` (LongTaskTimer)
170+
|`http_server_connections_seconds_active_count`, `http_server_connections_seconds_duration_sum` `http_server_connections_seconds_max` (Gauge)
171+
|`http.server.connections.active` (LongSum), `http.server.connections.duration` (DoubleGauge)
172+
| N/A
173+
174+
|`jvm.threads.live` (Gauge)
175+
|`jvm_threads_live_threads` (Gauge)
176+
|`jvm.threads.live` (DoubleGauge)
177+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
178+
179+
|`jvm.threads.started` (FunctionCounter)
180+
|`jvm_threads_started_threads_total` (Counter)
181+
|`jvm.threads.started` (DoubleSum)
182+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
183+
184+
|`jvm.threads.daemon` (Gauge)
185+
|`jvm_threads_daemon_threads` (Gauge)
186+
|`jvm.threads.daemon` (DoubleGauge)
187+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
188+
189+
|`jvm.threads.peak` (Gauge)
190+
|`jvm_threads_peak_threads` (Gauge)
191+
|`jvm.threads.peak` (DoubleGauge)
192+
|N/A
193+
194+
|`jvm.threads.states` (Gauge per state)
195+
|`jvm_threads_states_threads` (Gauge)
196+
|`jvm.threads.states` (DoubleGauge)
197+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
198+
|===
199+
200+
201+
[NOTE]
202+
====
203+
Some metrics might be missing from the output if they contain no data.
204+
====
205+
206+
== See the output
207+
208+
=== Grafana-OTel-LGTM Dev Service
209+
You can use the xref:observability-devservices-lgtm.adoc[Grafana-OTel-LGTM] Dev Service.
210+
211+
This Dev Service includes Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics.
212+
It also provides an OTel collector to receive the data
213+
214+
=== Logging exporter
215+
216+
You can output all metrics to the console by setting the exporter to `logging` in the `application.properties` file:
217+
[source, properties]
218+
----
219+
quarkus.otel.metrics.exporter=logging <1>
220+
quarkus.otel.metric.export.interval=10000ms <2>
221+
----
222+
223+
<1> Set the exporter to `logging`.
224+
Normally you don't need to set this.
225+
The default is `cdi`.
226+
<2> Set the interval to export the metrics.
227+
The default is `1m`, which is too long for debugging.
228+
229+
Also add this dependency to your project:
230+
[source,xml]
231+
----
232+
<dependency>
233+
<groupId>io.opentelemetry</groupId>
234+
<artifactId>opentelemetry-exporter-logging</artifactId>
235+
</dependency>
236+
----

0 commit comments

Comments
 (0)