Skip to content

Commit 47d5c7b

Browse files
committed
Micrometer to OTel Bridge
1 parent 3d7e154 commit 47d5c7b

File tree

47 files changed

+3254
-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.

47 files changed

+3254
-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: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 to 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+
```java
30+
import io.micrometer.core.annotation.Timed;
31+
//...
32+
@Timed(name = "timer_metric")
33+
public String timer() {
34+
return "OK";
35+
}
36+
```
37+
The output telemetry data is handled by the OpenTelemetry SDK and sent by the `quarkus-opentelemetry` extension exporter using the OTLP protocol.
38+
39+
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.
40+
41+
*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.
42+
43+
All the configurations from the OpenTelemetry and Micrometer extensions are available with `quarkus-micrometer-opentelemetry`.
44+
45+
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.
46+
47+
== Usage
48+
49+
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:
50+
51+
:add-extension-extensions: micrometer-opentelemetry
52+
include::{includes}/devtools/extension-add.adoc[]
53+
54+
This will add the following to your build file:
55+
56+
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
57+
.pom.xml
58+
----
59+
<dependency>
60+
<groupId>io.quarkus</groupId>
61+
<artifactId>quarkus-micrometer-opentelemetry</artifactId>
62+
</dependency>
63+
----
64+
65+
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
66+
.build.gradle
67+
----
68+
implementation("io.quarkus:quarkus-micrometer-opentelemetry")
69+
----
70+
71+
== Configuration
72+
73+
When the extension is present, Micrometer is enabled by default as are OpenTelemetry tracing, metrics and logs.
74+
75+
OpenTelemetry metrics auto-instrumentation for HTTP server and JVM metrics are disabled by default because those metrics can be collected by Micrometer.
76+
77+
Specific automatic Micrometer metrics are all disabled by default and can be enabled by setting, for example in the case of JVM metrics:
78+
```
79+
quarkus.micrometer.binder.jvm=true
80+
```
81+
on the `application.properties` file.
82+
83+
For this and other properties you can use with the extension, Please refer to:
84+
85+
* xref:telemetry-micrometer.adoc#configuration-reference[Micrometer metrics configurations]
86+
* xref:opentelemetry.adoc#configuration-reference[OpenTelemetry configurations]
87+
88+
== Metric differences between Micrometer and OpenTelemetry
89+
90+
=== API differences
91+
The metrics produced with each framework follow different APIs and the mapping is not 1:1.
92+
93+
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.
94+
95+
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`.
96+
97+
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.
98+
99+
This table shows the differences between the two frameworks:
100+
101+
|===
102+
|Micrometer |OpenTelemetry
103+
104+
|DistributionSummary
105+
|`<Metric name>` (Histogram), `<Metric name>.max` (DoubleGauge)
106+
107+
|DistributionSummary with SLOs
108+
|`<Metric name>` (Histogram), `<Metric name>.max` (DoubleGauge), `<Metric name>.histogram` (DoubleGauge)
109+
110+
|LongTaskTimer
111+
|`<Metric name>.active` (ObservableLongUpDownCounter), `<Metric name>.duration` (ObservableDoubleUpDownCounter)
112+
113+
|Timer
114+
|`<Metric name>` (Histogram), `<Metric name>.max` (ObservableDoubleGauge)
115+
|===
116+
117+
118+
=== Semantic convention differences
119+
120+
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.
121+
122+
When these 2 configurations are set in the `application.properties` file:
123+
124+
```
125+
quarkus.micrometer.binder.jvm=true
126+
quarkus.micrometer.binder.http-server.enabled=true
127+
128+
```
129+
The JVM and HTTP server metrics are collected by Micrometer.
130+
131+
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.
132+
133+
|===
134+
|Micrometer Meter |Quarkus Micrometer Prometheus output | This bridge OpenTelemetry output name | Related OpenTelemetry Semantic Convention (not applied)
135+
136+
|Using the @Timed interceptor.
137+
|
138+
|method.timed (Histogram), method.timed.max (DoubleGauge)
139+
|NA
140+
141+
|Using the @Counted interceptor.
142+
|
143+
|method.counted (DoubleSum)
144+
|NA
145+
146+
|`http.server.active.requests` (Gauge)
147+
|`http_server_active_requests` (Gauge)
148+
|`http.server.active.requests` (DoubleGauge)
149+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserveractive_requests[`http.server.active_requests`] (UpDownCounter)
150+
151+
|`http.server.requests` (Timer)
152+
|`http_server_requests_seconds_count`, `http_server_requests_seconds_sum`, `http_server_requests_seconds_max` (Gauge)
153+
|`http.server.requests` (Histogram), `http.server.requests.max` (DoubleGauge)
154+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestduration[`http.server.request.duration`] (Histogram)
155+
156+
|`http.server.bytes.read` (DistributionSummary)
157+
|`http_server_bytes_read_count`, `http_server_bytes_read_sum` , `http_server_bytes_read_max` (Gauge)
158+
|`http.server.bytes.read` (Histogram), `http.server.bytes.read.max` (DoubleGauge)
159+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverrequestbodysize[`http.server.request.body.size`] (Histogram)
160+
161+
|`http.server.bytes.write` (DistributionSummary)
162+
|`http_server_bytes_write_count`, `http_server_bytes_write_sum` , `http_server_bytes_write_max` (Gauge)
163+
|`http.server.bytes.write` (Histogram), `http.server.bytes.write.max` (DoubleGauge)
164+
|https://opentelemetry.io/docs/specs/semconv/http/http-metrics/#metric-httpserverresponsebodysize[`http.server.response.body.size`] (Histogram)
165+
166+
|`http.server.connections` (LongTaskTimer)
167+
|`http_server_connections_seconds_active_count`, `http_server_connections_seconds_duration_sum` `http_server_connections_seconds_max` (Gauge)
168+
|`http.server.connections.active` (LongSum), `http.server.connections.duration` (DoubleGauge)
169+
| N/A
170+
171+
|`jvm.threads.live` (Gauge)
172+
|`jvm_threads_live_threads` (Gauge)
173+
|`jvm.threads.live` (DoubleGauge)
174+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
175+
176+
|`jvm.threads.started` (FunctionCounter)
177+
|`jvm_threads_started_threads_total` (Counter)
178+
|`jvm.threads.started` (DoubleSum)
179+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
180+
181+
|`jvm.threads.daemon` (Gauge)
182+
|`jvm_threads_daemon_threads` (Gauge)
183+
|`jvm.threads.daemon` (DoubleGauge)
184+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
185+
186+
|`jvm.threads.peak` (Gauge)
187+
|`jvm_threads_peak_threads` (Gauge)
188+
|`jvm.threads.peak` (DoubleGauge)
189+
|N/A
190+
191+
|`jvm.threads.states` (Gauge per state)
192+
|`jvm_threads_states_threads` (Gauge)
193+
|`jvm.threads.states` (DoubleGauge)
194+
|https://opentelemetry.io/docs/specs/semconv/runtime/jvm-metrics/#metric-jvmthreadcount[`jvm.threads.live`] (UpDownCounter)
195+
|===
196+
197+
198+
[NOTE]
199+
====
200+
- Some metrics might be missing from the output if they contain no data.
201+
====
202+
203+
== See the output
204+
205+
=== Grafana-OTel-LGTM Dev Service
206+
You can use the xref:observability-devservices-lgtm.adoc[Grafana-OTel-LGTM] devservice.
207+
208+
This Dev service includes a Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics.
209+
Also provides and OTel collector to receive the data.
210+
211+
=== Logging exporter
212+
213+
You can output all metrics to the console by setting the exporter to `logging` in the `application.properties` file:
214+
[source, properties]
215+
----
216+
quarkus.otel.metrics.exporter=logging <1>
217+
quarkus.otel.metric.export.interval=10000ms <2>
218+
----
219+
220+
<1> Set the exporter to `logging`.
221+
Normally you don't need to set this.
222+
The default is `cdi`.
223+
<2> Set the interval to export the metrics.
224+
The default is `1m`, which is too long for debugging.
225+
226+
Also add this dependency to your project:
227+
[source,xml]
228+
----
229+
<dependency>
230+
<groupId>io.opentelemetry</groupId>
231+
<artifactId>opentelemetry-exporter-logging</artifactId>
232+
</dependency>
233+
----

0 commit comments

Comments
 (0)