Skip to content

Commit 982fe8d

Browse files
authored
Merge pull request #135 from redhat-developer-demos/observability
New Observability section with OpenTelemetry & Metrics
2 parents 784d453 + 6a1fbe5 commit 982fe8d

7 files changed

Lines changed: 260 additions & 21 deletions

File tree

104 KB
Loading
156 KB
Loading
103 KB
Loading

documentation/modules/ROOT/nav.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
** xref:rest-client.adoc[REST Client]
1414
** xref:fault-tolerance.adoc[Fault Tolerance]
1515
** xref:health.adoc[Health Check]
16-
** xref:metrics.adoc[Metrics]
16+
** xref:observability.adoc[Observability]
1717
** xref:security.adoc[Security with JWT RBAC]
1818
// ** xref:security-oidc.adoc[Security using OpenID Connect]
1919

documentation/modules/ROOT/pages/dev-services.adoc

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,25 @@ More on zero config setup of datasources can be found https://quarkus.io/guides/
1111

1212
== Replace Database Extensions
1313

14-
Instead of the built-in h2 database, we're now going to use an external database. Swapping out from one database provider to another is fairly trivial with Quarkus. In our case we just need to remove the h2 extension and add the postgresql extension instead:
14+
Instead of the built-in h2 database, we're now going to use an external database. Swapping out from one database provider to another is fairly trivial with Quarkus.
15+
Let's start with updating the configuration in the application.properties.
1516

17+
TIP: You technically don't need to add the db-kind property since there is only one jdbc driver in our application. We added it for clarity's sake.
18+
19+
[#quarkuspdb-update-props]
20+
[.console-input]
21+
[source,config,subs="+macros,+attributes"]
22+
----
23+
# Configuration file
24+
# key = value
25+
greeting=Hello y'all!
26+
quarkus.datasource.db-kind=postgresql
27+
quarkus.hibernate-orm.database.generation=drop-and-create
28+
----
29+
30+
WARNING: Make sure the h2 db-kind and jdbc.url properties have been removed.
31+
32+
Now let's swap the h2 extension out for the postgresql extension:
1633

1734
[tabs]
1835
====
@@ -40,24 +57,6 @@ quarkus extension add quarkus-jdbc-postgresql
4057
====
4158

4259

43-
== Update configuration
44-
45-
Update the configuration and change h2 to postgresql by updating the application.properties:
46-
47-
TIP: You technically don't need to add the db-kind property since there is only one jdbc driver in our application. We added it for clarity's sake.
48-
49-
[#quarkuspdb-update-props]
50-
[.console-input]
51-
[source,config,subs="+macros,+attributes"]
52-
----
53-
# Configuration file
54-
# key = value
55-
greeting=Hello y'all!
56-
quarkus.datasource.db-kind=postgresql
57-
quarkus.hibernate-orm.database.generation=drop-and-create
58-
----
59-
60-
6160
Notice in the logs how Quarkus has reloaded and started up a Postgresql database dev service:
6261

6362
[.console-output]

documentation/modules/ROOT/pages/metrics.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Quarkus provides JVM and other statistics out-of-box with the Metrics extension,
66

77
== Add the Metrics extension
88

9-
Just open a new terminal window, and make sure you’re at the root of your `{project-name}` project, then run:
9+
In a new terminal window at the root of your `{project-name}` project, run:
1010

1111
[tabs]
1212
====
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
= Observability
2+
3+
When running applications in production we need to send telemetry (metrics and tracing information) to some services like Prometheus and Jaeger.
4+
5+
Quarkus provides JVM and other statistics out-of-box with the Metrics extension, and it provides distributed tracing with the opentelemetry extensions.
6+
7+
== Tracing
8+
9+
Let's start with tracing. Tracing allows you to follow the flow of a request throughout your services. This becomes very important once you have more than one (micro) service running or are making external requests, eg. to a REST client or a database.
10+
11+
=== Add the OpenTelemetry extension
12+
13+
In a new terminal window at the root of your `{project-name}` project, run:
14+
15+
[tabs]
16+
====
17+
Maven::
18+
+
19+
--
20+
[.console-input]
21+
[source,bash,subs="+macros,+attributes"]
22+
----
23+
./mvnw quarkus:add-extension -D"extensions=opentelemetry"
24+
----
25+
26+
--
27+
Quarkus CLI::
28+
+
29+
--
30+
[.console-input]
31+
[source,bash,subs="+macros,+attributes"]
32+
----
33+
quarkus extension add opentelemetry
34+
----
35+
--
36+
====
37+
38+
The opentelementry extension works out of the box and will start sending data to a grpc receiver (by default localhost:4317) if we send new requests to the app.
39+
40+
=== Start an OpenTelemetry Collector
41+
42+
To start collecting and inspecting telemetry, let's run a container that instantiates Jaeger (a tool to inspect telemetry data) and also acts as an OpenTelemetry collector and query service:
43+
44+
[.console-input]
45+
[source,bash,subs="+macros,+attributes"]
46+
----
47+
docker run --name=jaeger -d -p 16686:16686 -p 4317:4317 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:latest
48+
----
49+
50+
Create a few requests so we have some telemetry data to work with by hitting our /fruit endpoint a few times again:
51+
52+
[.console-input]
53+
[source,bash,subs="+macros,+attributes"]
54+
----
55+
curl localhost:8080/fruit?season=Summer
56+
----
57+
58+
=== Trace down the requests in Jaeger
59+
60+
Go to http://localhost:16686 in your browser. You should see the Jaeger UI, if not make sure the docker run command was executed successfully. To see the traces we just produced, select 'tutorial-app' from the Service dropdown and click 'Find Traces'. Depending on how many times you sent requests to the fruit endpoint, you'll see a number of traces that were collected.
61+
62+
[.mt-4.center]
63+
image::Jaeger.png[Jaeger Tracing,800,500,align="center"]
64+
65+
Click on the first "tutorial-app: GET /fruit" trace to drill into the details of the latest request. Notice that without doing any further work, even data from the calls out to the FruityVice REST service was collected, so we can see how long each request took:
66+
67+
[.mt-4.center]
68+
image::Jaeger_Span.png[Jaeger Span,800,500,align="center"]
69+
70+
=== Tracing Database calls
71+
72+
The keen observer (no pun intended) might have noticed that there is no tracing data for the database calls. To be able to do so, we'll need to add the opentelemetry-jdbc dependency to our application. This time we'll need to add the dependency directly to the .pom.xml file because it's not a quarkus extension. Add the following snippet to your pom.xml just *before* the `</dependencies>` line:
73+
74+
[.console-input]
75+
[source,xml,subs="+macros,+attributes"]
76+
----
77+
<dependency>
78+
<groupId>io.opentelemetry.instrumentation</groupId>
79+
<artifactId>opentelemetry-jdbc</artifactId>
80+
</dependency>
81+
----
82+
83+
We'll also need to add a new property to our `application.properties` file to enable telemetry for our datasource:
84+
85+
[.console-input]
86+
[source,bash,subs="+macros,+attributes"]
87+
----
88+
# enable tracing
89+
quarkus.datasource.jdbc.telemetry=true
90+
----
91+
92+
Create a few more requests to the fruit resource:
93+
94+
[.console-input]
95+
[source,bash,subs="+macros,+attributes"]
96+
----
97+
curl localhost:8080/fruit?season=Summer
98+
----
99+
100+
...And go back to the Jaeger UI at http://localhost:16686. Click the 'Find Traces' again (make sure the tutorial-app Service is selected) and click on the first trace from the top. You will now see 2 more spans, one with 'DataSource.getConnection' that shows how long it took for the DB connection to get established, and one with 'SELECT quarkus.Fruit' that shows the details of the DB query and how long it took.
101+
102+
[.mt-4.center]
103+
image::Jaeger_DataSource.png[Jaeger DataSource,800,500,align="center"]
104+
105+
== Metrics
106+
107+
Observability also means the ability to expose, collect and observe detailed metrics about your application and the JVM running underneath (if applicable).
108+
Let's add the metrics extension that enables this capability in Quarkus:
109+
110+
=== Add the Metrics extension
111+
112+
In a terminal window at the root of your `{project-name}` project, run:
113+
114+
[tabs]
115+
====
116+
Maven::
117+
+
118+
--
119+
[.console-input]
120+
[source,bash,subs="+macros,+attributes"]
121+
----
122+
./mvnw quarkus:add-extension -D"extensions=quarkus-micrometer"
123+
----
124+
125+
--
126+
Quarkus CLI::
127+
+
128+
--
129+
[.console-input]
130+
[source,bash,subs="+macros,+attributes"]
131+
----
132+
quarkus extension add quarkus-micrometer
133+
----
134+
--
135+
====
136+
137+
You should also add the `quarkus-micrometer-registry-prometheus` extension which formats the metrics in format that Prometheus can easily ingest:
138+
139+
[tabs]
140+
====
141+
Maven::
142+
+
143+
--
144+
[.console-input]
145+
[source,bash,subs="+macros,+attributes"]
146+
----
147+
./mvnw quarkus:add-extension -D"extensions=quarkus-micrometer-registry-prometheus"
148+
----
149+
150+
--
151+
Quarkus CLI::
152+
+
153+
--
154+
[.console-input]
155+
[source,bash,subs="+macros,+attributes"]
156+
----
157+
quarkus extension add quarkus-micrometer-registry-prometheus
158+
----
159+
--
160+
====
161+
162+
By just adding these extensions, your application is now exposing metrics at the http://localhost:8080/q/metrics endpoint. You can also access the metrics by going to the http://localhost:8080/q/dev[Dev UI] where you will see a new card "Micrometer metrics" and a link in that card to a http://localhost:8080/q/dev-ui/io.quarkus.quarkus-micrometer/prometheus[Prometheus metrics page].
163+
164+
=== Create TimeResource
165+
166+
We can also generate custom metrics. Let's add a custom counter that counts how many times a particular method has been called.
167+
Create a new `TimeResource` Java class in `src/main/java` in the `com.redhat.developers` package with the following contents:
168+
169+
[.console-input]
170+
[source,java]
171+
----
172+
package com.redhat.developers;
173+
import java.time.Instant;
174+
import java.util.Calendar;
175+
import java.util.TimeZone;
176+
177+
import jakarta.ws.rs.GET;
178+
import jakarta.ws.rs.Path;
179+
import jakarta.ws.rs.Produces;
180+
import jakarta.ws.rs.core.MediaType;
181+
182+
import io.micrometer.core.annotation.Counted;
183+
import io.micrometer.core.instrument.MeterRegistry;
184+
185+
@Path("/time")
186+
public class TimeResource {
187+
188+
private final MeterRegistry registry; <1>
189+
190+
TimeResource(MeterRegistry registry) {
191+
this.registry = registry;
192+
registry.gauge("offsetFromUTC", this,
193+
TimeResource::offsetFromUTC);<2>
194+
}
195+
196+
@Counted(value = "time.now") <3>
197+
@GET
198+
@Produces(MediaType.TEXT_PLAIN)
199+
public Instant now() {
200+
return Instant.now();
201+
}
202+
203+
int offsetFromUTC() {
204+
return TimeZone.getDefault().getOffset(Calendar.ZONE_OFFSET)/(3600*1000);
205+
}
206+
}
207+
----
208+
<1> Meters in Micrometer are created from and contained in a MeterRegistry.
209+
<2> Add a gauge that returns a value computed by our application.
210+
<3> The `@Counted` annotation allows the Metrics extension to count the number of invocations to this method.
211+
212+
=== Invoke the endpoint multiple times
213+
214+
We need to send some requests to our endpoint to increment our `@Counted` metrics, so use the following command:
215+
216+
[.console-input]
217+
[source,bash]
218+
----
219+
for i in {1..5}; do curl -w '\n' localhost:8080/time; done
220+
----
221+
222+
[.console-output]
223+
[source,bash]
224+
----
225+
2020-05-12T22:38:10.546500Z
226+
2020-05-12T22:38:10.869378Z
227+
2020-05-12T22:38:11.188782Z
228+
2020-05-12T22:38:11.510367Z
229+
2020-05-12T22:38:11.832583Z
230+
----
231+
232+
=== Check the metrics
233+
234+
By default the metrics are exposed in Prometheus format. You can check the output by pointing your browser to http://localhost:8080/q/metrics[window=_blank]. See if you can find the TimeResource counter result.
235+
236+
[.mt-4.center]
237+
image::Timed_Resource.png[Micrometer Timed Resource,800,100,align="left"]
238+
239+
NOTE: In this tutorial we consulted the results in raw format, however these metrics are meant to be consumed by a monitoring system such as Prometheus so you can produce meaningful dashboards or alerts instead of accessing the metrics endpoint directly.
240+

0 commit comments

Comments
 (0)