Skip to content

Commit 8eaaef3

Browse files
committed
opentelemetry: add javadoc ref #3900
1 parent a1cede7 commit 8eaaef3

File tree

2 files changed

+363
-0
lines changed

2 files changed

+363
-0
lines changed

docs/asciidoc/modules/modules.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Modules are distributed as separate dependencies. Below is the catalog of offici
3838
* link:{uiVersion}/#tooling-and-operations-development[Jooby Run]: Run and hot reload your application.
3939
* link:{uiVersion}/modules/whoops[Whoops]: Pretty page stacktrace reporter.
4040
* link:{uiVersion}/modules/metrics[Metrics]: Application metrics from the excellent metrics library.
41+
* link:{uiVersion}/modules/opentelemetry[Open Telemetry]: Application metrics using Open Telemetry library.
4142

4243
==== Event Bus
4344
* link:{uiVersion}/modules/camel[Camel]: Camel module for Jooby.
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
== OpenTelemetry
2+
3+
The module provides the foundational engine for distributed tracing, metrics, and log correlation in your Jooby application. Its goal is to give you deep, vendor-neutral observability into your system. By integrating the https://opentelemetry.io/[OpenTelemetry] SDK, it automatically captures and exports telemetry data from HTTP requests, database connection pools, background jobs, and application logs.
4+
5+
Because https://opentelemetry.io/[OpenTelemetry] is an open standard, you are not locked into a specific vendor. You can seamlessly route your telemetry data to any compatible APM, backend, or collector (such as SigNoz, DataDog, Jaeger, or Grafana) simply by changing your configuration properties.
6+
7+
=== Usage
8+
9+
1) Add the dependency:
10+
11+
[dependency, artifactId="jooby-opentelemetry:OpenTelemetry Module"]
12+
.
13+
14+
2) Install and use OpenTelemetry:
15+
16+
.Java
17+
[source, java, role="primary"]
18+
----
19+
import io.jooby.opentelemetry.OtelModule;
20+
import io.jooby.opentelemetry.OtelHttpTracing;
21+
22+
{
23+
install(new OtelModule()); <1>
24+
25+
use(new OtelHttpTracing()); <2>
26+
27+
get("/", ctx -> {
28+
return "Hello OTel";
29+
});
30+
}
31+
----
32+
33+
.Kotlin
34+
[source, kt, role="secondary"]
35+
----
36+
import io.jooby.opentelemetry.OtelModule
37+
import io.jooby.opentelemetry.OtelHttpTracing
38+
39+
{
40+
install(OtelModule()) <1>
41+
42+
use(OtelHttpTracing()) <2>
43+
44+
get("/") { ctx ->
45+
"Hello OTel"
46+
}
47+
}
48+
----
49+
50+
<1> Installs the core OpenTelemetry SDK engine. It **must be installed at the very beginning** of your application setup.
51+
<2> Adds the `OtelHttpTracing` filter to automatically intercept, create, and propagate spans for incoming HTTP requests.
52+
53+
[NOTE]
54+
====
55+
**JVM Metrics:** Basic JVM operational metrics (such as memory usage, garbage collection times, and active thread counts) are automatically bound and exported by default the moment `OtelModule` is installed.
56+
====
57+
58+
=== Exporters Configuration
59+
60+
The OpenTelemetry SDK is completely driven by your application's configuration properties. Any property defined inside the `otel` block in your `application.conf` is automatically picked up by the SDK's auto-configuration engine.
61+
62+
Here is how you can configure the exporters to send your data to various popular backends:
63+
64+
==== SigNoz (or generic OTLP)
65+
SigNoz natively accepts the standard OTLP (OpenTelemetry Protocol) format over gRPC.
66+
67+
.application.conf
68+
[source, properties]
69+
----
70+
otel {
71+
service.name = "jooby-api"
72+
traces.exporter = otlp
73+
metrics.exporter = otlp
74+
logs.exporter = otlp
75+
exporter.otlp.protocol = grpc
76+
exporter.otlp.endpoint = "http://localhost:4317"
77+
}
78+
----
79+
80+
==== DataDog
81+
To send data to DataDog, you typically use the OTLP HTTP protocol pointing to the DataDog Agent running on your infrastructure, or directly to their intake API.
82+
83+
.application.conf
84+
[source, properties]
85+
----
86+
otel {
87+
service.name = "jooby-api"
88+
traces.exporter = otlp
89+
metrics.exporter = otlp
90+
logs.exporter = otlp
91+
exporter.otlp.protocol = http/protobuf
92+
exporter.otlp.endpoint = "http://localhost:4318" # Assuming local DataDog Agent
93+
# If sending directly to DataDog, you would include the API key in headers:
94+
# exporter.otlp.headers = "DD-API-KEY=your_api_key_here"
95+
}
96+
----
97+
98+
==== Jaeger
99+
Jaeger also natively supports accepting OTLP data.
100+
101+
.application.conf
102+
[source, properties]
103+
----
104+
otel {
105+
service.name = "jooby-api"
106+
traces.exporter = otlp
107+
metrics.exporter = none # Jaeger is for traces only
108+
logs.exporter = none # Jaeger is for traces only
109+
exporter.otlp.protocol = grpc
110+
exporter.otlp.endpoint = "http://localhost:4317"
111+
}
112+
----
113+
114+
=== Manual Tracing
115+
116+
For tracing specific business logic, database queries, or external API calls deep within your service layer, this module provides an injectable `Trace` utility.
117+
118+
You can retrieve it from the route context or inject it directly via DI to safely create and execute custom spans:
119+
120+
.Manual Tracing
121+
[source, java, role = "primary"]
122+
----
123+
import io.jooby.opentelemetry.Trace;
124+
125+
{
126+
get("/books/{isbn}", ctx -> {
127+
Trace trace = require(Trace.class);
128+
String isbn = ctx.path("isbn").value();
129+
130+
return trace.span("fetch_book")
131+
.attribute("isbn", isbn)
132+
.execute(span -> {
133+
span.addEvent("Executing database query");
134+
return repository.findByIsbn(isbn);
135+
});
136+
});
137+
}
138+
----
139+
140+
.Kotlin
141+
[source, kt, role="secondary"]
142+
----
143+
import io.jooby.opentelemetry.Trace
144+
145+
{
146+
get("/books/{isbn}") { ctx ->
147+
val trace = require(Trace::class)
148+
val isbn = ctx.path("isbn").value()
149+
150+
trace.span("fetch_book")
151+
.attribute("isbn", isbn)
152+
.execute { span ->
153+
span.addEvent("Executing database query")
154+
repository.findByIsbn(isbn)
155+
}
156+
}
157+
}
158+
----
159+
160+
The `execute` and `run` blocks automatically handle the span context lifecycle, error recording, and finalization, ensuring no spans are leaked even if exceptions are thrown.
161+
162+
=== Extensions
163+
164+
Additional integrations are provided via `OtelExtension` implementations. Many of these rely on official OpenTelemetry instrumentation libraries, which you must add to your project's classpath.
165+
166+
[NOTE]
167+
====
168+
**Lifecycle & Lazy Initialization:** Although `OtelModule` must be installed at the very beginning of your application, its extensions are **lazily initialized**. They defer their execution to the application's `onStarting` lifecycle hook. This ensures that all target components provided by other modules (like database connection pools or background schedulers) are fully configured and available in the service registry before the OpenTelemetry extensions attempt to instrument them.
169+
====
170+
171+
==== db-scheduler
172+
173+
Automatically instruments the `db-scheduler` library. It tracks background task executions, measuring execution durations and recording successes and failures.
174+
175+
.db-scheduler Integration
176+
[source, java, role = "primary"]
177+
----
178+
import io.jooby.opentelemetry.instrumentation.OtelDbScheduler;
179+
180+
{
181+
install(new DbSchedulerModule()
182+
.withExecutionInterceptor(new OtelDbScheduler(require(OpenTelemetry.class)))
183+
);
184+
}
185+
----
186+
187+
.Kotlin
188+
[source, kt, role="secondary"]
189+
----
190+
import io.jooby.opentelemetry.instrumentation.OtelDbScheduler
191+
192+
{
193+
install(DbSchedulerModule()
194+
.withExecutionInterceptor(OtelDbScheduler(require(OpenTelemetry::class)))
195+
)
196+
}
197+
----
198+
199+
==== HikariCP
200+
201+
Instruments all registered `HikariDataSource` instances to export critical pool metrics (active/idle connections, timeouts).
202+
203+
Required dependency:
204+
[dependency, groupId="io.opentelemetry.instrumentation", artifactId="opentelemetry-hikaricp-3.0", version="${otel-instrumentation.version}"]
205+
.
206+
207+
[NOTE]
208+
====
209+
Installation order is critical. `OtelModule` must be installed **before** `HikariModule`.
210+
====
211+
212+
.HikariCP Metrics
213+
[source, java, role = "primary"]
214+
----
215+
import io.jooby.hikari.HikariModule;
216+
import io.jooby.opentelemetry.instrumentation.OtelHikari;
217+
218+
{
219+
install(new OtelModule(new OtelHikari()));
220+
221+
install(new HikariModule());
222+
}
223+
----
224+
225+
.Kotlin
226+
[source, kt, role="secondary"]
227+
----
228+
import io.jooby.hikari.HikariModule
229+
import io.jooby.opentelemetry.instrumentation.OtelHikari
230+
231+
{
232+
install(OtelModule(OtelHikari()))
233+
234+
install(HikariModule())
235+
}
236+
----
237+
238+
==== Log4j2
239+
240+
Seamlessly exports all application logs to your OpenTelemetry backend, automatically correlated with active trace and span IDs using a dynamic appender.
241+
242+
Required dependency:
243+
[dependency, groupId="io.opentelemetry.instrumentation", artifactId="opentelemetry-log4j-appender-2.17", version="${otel-instrumentation.version}"]
244+
.
245+
246+
.Log4j2 Integration
247+
[source, java, role = "primary"]
248+
----
249+
import io.jooby.opentelemetry.instrumentation.OtelLog4j2;
250+
251+
{
252+
install(new OtelModule(
253+
new OtelLog4j2()
254+
));
255+
}
256+
----
257+
258+
.Kotlin
259+
[source, kt, role="secondary"]
260+
----
261+
import io.jooby.opentelemetry.instrumentation.OtelLog4j2
262+
263+
{
264+
install(OtelModule(
265+
OtelLog4j2()
266+
))
267+
}
268+
----
269+
270+
==== Logback
271+
272+
Seamlessly exports all application logs to your OpenTelemetry backend, automatically correlated with active trace and span IDs using a dynamic appender.
273+
274+
Required dependency:
275+
[dependency, groupId="io.opentelemetry.instrumentation", artifactId="opentelemetry-logback-appender-1.0", version="${otel-instrumentation.version}"]
276+
.
277+
278+
.Logback Integration
279+
[source, java, role = "primary"]
280+
----
281+
import io.jooby.opentelemetry.instrumentation.OtelLogback;
282+
283+
{
284+
install(new OtelModule(
285+
new OtelLogback()
286+
));
287+
}
288+
----
289+
290+
.Kotlin
291+
[source, kt, role="secondary"]
292+
----
293+
import io.jooby.opentelemetry.instrumentation.OtelLogback
294+
295+
{
296+
install(OtelModule(
297+
OtelLogback()
298+
))
299+
}
300+
----
301+
302+
==== Quartz
303+
304+
Tracks background task executions handled by the Quartz scheduler, creating individual spans for each execution to monitor scheduling delays and execution durations.
305+
306+
Required dependency:
307+
[dependency, groupId="io.opentelemetry.instrumentation", artifactId="opentelemetry-quartz-2.0", version="${otel-instrumentation.version}"]
308+
.
309+
310+
.Quartz Integration
311+
[source, java, role = "primary"]
312+
----
313+
import io.jooby.quartz.QuartzModule;
314+
import io.jooby.opentelemetry.instrumentation.OtelQuartz;
315+
316+
{
317+
install(new OtelModule(new OtelQuartz()));
318+
319+
install(new QuartzModule(MyJobs.class));
320+
}
321+
----
322+
323+
.Kotlin
324+
[source, kt, role="secondary"]
325+
----
326+
import io.jooby.quartz.QuartzModule
327+
import io.jooby.opentelemetry.instrumentation.OtelQuartz
328+
329+
{
330+
install(OtelModule(OtelQuartz()))
331+
332+
install(QuartzModule(MyJobs::class.java))
333+
}
334+
----
335+
336+
==== Server Metrics
337+
338+
Exports native, server-specific operational metrics. It automatically detects your underlying HTTP server (Jetty, Netty, or Undertow) and exports deep metrics like event loop pending tasks, thread pool sizes, and memory usage.
339+
340+
.Server Metrics
341+
[source, java, role = "primary"]
342+
----
343+
import io.jooby.opentelemetry.instrumentation.OtelServerMetrics;
344+
345+
{
346+
install(new OtelModule(
347+
new OtelServerMetrics()
348+
));
349+
}
350+
----
351+
352+
.Kotlin
353+
[source, kt, role="secondary"]
354+
----
355+
import io.jooby.opentelemetry.instrumentation.OtelServerMetrics
356+
357+
{
358+
install(OtelModule(
359+
OtelServerMetrics()
360+
))
361+
}
362+
----

0 commit comments

Comments
 (0)