Skip to content

Commit 768dfe3

Browse files
committed
Refactor JFR extension structure: introduce SPI modules
1 parent db51904 commit 768dfe3

File tree

50 files changed

+385
-190
lines changed

Some content is hidden

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

50 files changed

+385
-190
lines changed

bom/application/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6500,11 +6500,21 @@
65006500
</dependency>
65016501

65026502
<!-- JDK Flight Recorder -->
6503+
<dependency>
6504+
<groupId>io.quarkus</groupId>
6505+
<artifactId>quarkus-jfr-api</artifactId>
6506+
<version>${project.version}</version>
6507+
</dependency>
65036508
<dependency>
65046509
<groupId>io.quarkus</groupId>
65056510
<artifactId>quarkus-jfr</artifactId>
65066511
<version>${project.version}</version>
65076512
</dependency>
6513+
<dependency>
6514+
<groupId>io.quarkus</groupId>
6515+
<artifactId>quarkus-jfr-deployment-spi</artifactId>
6516+
<version>${project.version}</version>
6517+
</dependency>
65086518
<dependency>
65096519
<groupId>io.quarkus</groupId>
65106520
<artifactId>quarkus-jfr-deployment</artifactId>

core/deployment/src/main/java/io/quarkus/deployment/Capability.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public interface Capability {
4141

4242
String JAXB = QUARKUS_PREFIX + ".jaxb";
4343
String JAXP = QUARKUS_PREFIX + ".jaxp";
44+
String JFR = QUARKUS_PREFIX + ".jfr";
4445

4546
String KOTLIN = QUARKUS_PREFIX + ".kotlin";
4647

docs/src/main/asciidoc/jfr.adoc

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ and pull requests should be submitted there:
44
https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
55
////
66
[id="jfr"]
7-
= Using Java Flight Recorder
7+
= Using JDK Flight Recorder
88
include::_attributes.adoc[]
99
:categories: observability
10-
:summary: This guide explains how Java Flight Recorder (JFR) can be extended to provide additional insight into your Quarkus application.
10+
:summary: This guide explains how JDK Flight Recorder (JFR) can be extended to provide additional insight into your Quarkus application.
1111
:topics: observability,jfr
1212
:extensions: io.quarkus:quarkus-jfr
1313

14-
This guide explains how https://openjdk.org/jeps/328[Java Flight Recorder] (JFR) can be extended to provide additional insight into your Quarkus application.
14+
This guide explains how https://openjdk.org/jeps/328[Flight Recorder] can be extended to provide additional insight into your Quarkus application.
1515
JFR records various information from the Java standard API and JVM as events.
1616
By adding the Quarkus JFR extension, you can add custom Quarkus events to JFR. This will help you solve potential problems in your application.
1717

@@ -99,7 +99,7 @@ We can launch the application with JFR configured to be enabled from the startup
9999
:dev-additional-parameters: -Djvm.args="-XX:StartFlightRecording=name=quarkus,dumponexit=true,filename=myrecording.jfr"
100100
include::{includes}/devtools/dev.adoc[]
101101

102-
With the Java Flight Recorder and the application running, we can make a request to the provided endpoint:
102+
With the JDK Flight Recorder and the application running, we can make a request to the provided endpoint:
103103

104104
[source,shell]
105105
----
@@ -269,7 +269,7 @@ Extension::
269269

270270
=== Native Image
271271

272-
Native executables supports Java Flight Recorder.
272+
Native executables supports JDK Flight Recorder.
273273
This extension also supports native executables.
274274

275275
To enable JFR on native executables, it is usually built with `--enable-monitoring`.
@@ -329,7 +329,40 @@ include::{includes}/devtools/dev.adoc[]
329329

330330
== Adding new events
331331

332-
This section is for those who would like to add new events to this extension.
332+
This section is for extension authors who want to add new JFR events.
333+
334+
=== Depending on the API module
335+
336+
The Quarkus JFR extension separates the public API from the runtime implementation.
337+
As a result, other extensions can implement JFR-based instrumentation by depending only on the JFR API module, without adding a dependency on the Quarkus JFR extension implementation itself.
338+
339+
At deployment time, an extension can detect whether the application has enabled the Quarkus JFR extension by using Quarkus capabilities:
340+
341+
[source,java]
342+
----
343+
import io.quarkus.deployment.Capabilities;
344+
import io.quarkus.deployment.Capability;
345+
import io.quarkus.deployment.annotations.BuildStep;
346+
import io.quarkus.deployment.annotations.BuildSteps;
347+
348+
@BuildSteps
349+
class MyProcessor {
350+
351+
@BuildStep
352+
void step(Capabilities capabilities) {
353+
if (capabilities.isPresent(Capability.JFR)) {
354+
// The Quarkus JFR extension is enabled
355+
// Register JFR-related build items, bytecode transformations, etc.
356+
}
357+
}
358+
}
359+
----
360+
361+
This pattern allows your extension to:
362+
* compile against stable API types only, and
363+
* activate JFR instrumentation only when the Quarkus JFR extension is present in the application.
364+
365+
=== Associating events by request
333366

334367
We recommend that new events be associated with existing events.
335368
Associations are useful when looking at the details of a process that is taking a long time.
@@ -345,14 +378,17 @@ Datastore events are not implemented yet.
345378
The Quarkus JFR extension provides a Request ID for event association.
346379
See <<identifying-requests>> for more information on Request IDs.
347380

348-
In specific code, the following two steps are required.
349-
First, implement `traceId` and `spanId` on the new event as follows
350-
The `@TraceIdRelational` and `@SpanIdRelational` attached to these events will provide the association.
381+
In code, you typically need the following two steps.
382+
383+
==== 1) Add trace/span identifiers to your event
384+
385+
Implement `traceId` and `spanId` on the new event as follows.
386+
The `@TraceIdRelational` and `@SpanIdRelational` annotations mark these fields as relational identifiers used for association.
351387

352388
[source,java]
353389
----
354-
import io.quarkus.jfr.runtime.SpanIdRelational;
355-
import io.quarkus.jfr.runtime.TraceIdRelational;
390+
import io.quarkus.jfr.api.SpanIdRelational;
391+
import io.quarkus.jfr.api.TraceIdRelational;
356392
import jdk.jfr.Description;
357393
import jdk.jfr.Event;
358394
import jdk.jfr.Label;
@@ -374,11 +410,13 @@ public class NewEvent extends Event {
374410
}
375411
----
376412

377-
Then you get the information to store in the event from the `IdProducer` object's `getTraceId()` and `getSpanId()`.
413+
==== 2) Populate the identifiers from the IdProducer
414+
415+
Obtain the values to store in the event from an `IdProducer` and set them just before committing the event.
378416

379417
[source,java]
380418
----
381-
import io.quarkus.jfr.runtime.IdProducer;
419+
import io.quarkus.jfr.api.IdProducer;
382420
383421
public class NewInterceptor {
384422
@@ -392,11 +430,11 @@ public class NewInterceptor {
392430
// The process you want to measure
393431
394432
event.end();
395-
if (endEvent.shouldCommit()) {
433+
if (event.shouldCommit()) {
396434
event.setTraceId(idProducer.getTraceId());
397435
event.setSpanId(idProducer.getSpanId());
398436
// call other setters which you want to add
399-
endEvent.commit();
437+
event.commit();
400438
}
401439
}
402440
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>quarkus-jfr-parent</artifactId>
7+
<groupId>io.quarkus</groupId>
8+
<version>999-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>quarkus-jfr-deployment-spi</artifactId>
13+
<name>Quarkus - JFR - SPI</name>
14+
<description>Artifact that provides BuildItems specific to JFR extension</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>io.quarkus</groupId>
19+
<artifactId>quarkus-core-deployment</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>io.quarkus</groupId>
23+
<artifactId>quarkus-jfr-api</artifactId>
24+
</dependency>
25+
</dependencies>
26+
</project>

extensions/jfr/deployment/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
<artifactId>quarkus-jfr</artifactId>
1717
<version>${project.version}</version>
1818
</dependency>
19+
<dependency>
20+
<groupId>io.quarkus</groupId>
21+
<artifactId>quarkus-jfr-deployment-spi</artifactId>
22+
</dependency>
1923
<dependency>
2024
<groupId>io.quarkus</groupId>
2125
<artifactId>quarkus-arc-deployment</artifactId>

extensions/jfr/deployment/src/main/java/io/quarkus/jfr/deployment/JfrProcessor.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@
1515
import io.quarkus.deployment.annotations.BuildSteps;
1616
import io.quarkus.deployment.annotations.ExecutionTime;
1717
import io.quarkus.deployment.annotations.Record;
18+
import io.quarkus.deployment.builditem.CapabilityBuildItem;
1819
import io.quarkus.deployment.builditem.FeatureBuildItem;
1920
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
20-
import io.quarkus.jfr.runtime.JfrRecorder;
21-
import io.quarkus.jfr.runtime.OTelIdProducer;
22-
import io.quarkus.jfr.runtime.QuarkusIdProducer;
23-
import io.quarkus.jfr.runtime.http.rest.classic.ClassicServerFilter;
24-
import io.quarkus.jfr.runtime.http.rest.classic.ClassicServerRecorderProducer;
25-
import io.quarkus.jfr.runtime.http.rest.reactive.ReactiveServerFilters;
26-
import io.quarkus.jfr.runtime.http.rest.reactive.ReactiveServerRecorderProducer;
27-
import io.quarkus.jfr.runtime.http.rest.reactive.ServerStartRecordingHandler;
28-
import io.quarkus.jfr.runtime.runtime.JfrRuntimeBean;
29-
import io.quarkus.jfr.runtime.runtime.QuarkusRuntimeInfo;
21+
import io.quarkus.jfr.runtime.internal.JfrRecorder;
22+
import io.quarkus.jfr.runtime.internal.OTelIdProducer;
23+
import io.quarkus.jfr.runtime.internal.QuarkusIdProducer;
24+
import io.quarkus.jfr.runtime.internal.http.rest.classic.ClassicServerEventRecorderProducer;
25+
import io.quarkus.jfr.runtime.internal.http.rest.classic.ClassicServerFilter;
26+
import io.quarkus.jfr.runtime.internal.http.rest.reactive.ReactiveServerEventRecorderProducer;
27+
import io.quarkus.jfr.runtime.internal.http.rest.reactive.ReactiveServerFilters;
28+
import io.quarkus.jfr.runtime.internal.http.rest.reactive.ServerStartRecordingHandler;
29+
import io.quarkus.jfr.runtime.internal.runtime.JfrRuntimeBean;
30+
import io.quarkus.jfr.runtime.internal.runtime.QuarkusRuntimeInfo;
3031
import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem;
3132
import io.quarkus.resteasy.reactive.server.spi.GlobalHandlerCustomizerBuildItem;
3233
import io.quarkus.resteasy.reactive.spi.CustomContainerRequestFilterBuildItem;
@@ -39,6 +40,11 @@ FeatureBuildItem feature() {
3940
return new FeatureBuildItem(Feature.JFR);
4041
}
4142

43+
@BuildStep
44+
CapabilityBuildItem capability() {
45+
return new CapabilityBuildItem(Capability.JFR);
46+
}
47+
4248
@BuildStep
4349
@Record(ExecutionTime.RUNTIME_INIT)
4450
void registerVersion(List<FeatureBuildItem> features, BuildProducer<AdditionalBeanBuildItem> additionalBeans,
@@ -88,7 +94,7 @@ void registerRestIntegration(Capabilities capabilities,
8894
if (capabilities.isPresent(Capability.RESTEASY_REACTIVE)) {
8995

9096
additionalBeans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
91-
.addBeanClasses(ReactiveServerRecorderProducer.class)
97+
.addBeanClasses(ReactiveServerEventRecorderProducer.class)
9298
.build());
9399

94100
filterBeans
@@ -106,7 +112,7 @@ void registerResteasyClassicIntegration(Capabilities capabilities,
106112
if (capabilities.isPresent(Capability.RESTEASY)) {
107113

108114
additionalBeans.produce(AdditionalBeanBuildItem.builder().setUnremovable()
109-
.addBeanClasses(ClassicServerRecorderProducer.class)
115+
.addBeanClasses(ClassicServerEventRecorderProducer.class)
110116
.build());
111117

112118
resteasyJaxrsProviderBuildItemBuildProducer

extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrConfigurationTest.java renamed to extensions/jfr/deployment/src/test/java/io/quarkus/jfr/deployment/JfrConfigurationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.quarkus.jfr.test;
1+
package io.quarkus.jfr.deployment;
22

33
import static org.junit.jupiter.api.Assertions.assertFalse;
44

@@ -7,7 +7,7 @@
77
import org.junit.jupiter.api.Test;
88
import org.junit.jupiter.api.extension.RegisterExtension;
99

10-
import io.quarkus.jfr.runtime.config.JfrRuntimeConfig;
10+
import io.quarkus.jfr.runtime.internal.config.JfrRuntimeConfig;
1111
import io.quarkus.test.QuarkusUnitTest;
1212

1313
public class JfrConfigurationTest {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.quarkus.jfr.deployment;
2+
3+
import jakarta.enterprise.context.control.ActivateRequestContext;
4+
import jakarta.inject.Inject;
5+
6+
import org.junit.jupiter.api.Assertions;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.extension.RegisterExtension;
9+
10+
import io.quarkus.jfr.api.IdProducer;
11+
import io.quarkus.test.QuarkusUnitTest;
12+
13+
@ActivateRequestContext
14+
public class JfrIdTest {
15+
16+
@RegisterExtension
17+
static final QuarkusUnitTest TEST = new QuarkusUnitTest();
18+
19+
@Inject
20+
IdProducer idProducer;
21+
22+
@Test
23+
public void test() {
24+
String traceId = idProducer.getTraceId();
25+
String spanId = idProducer.getSpanId();
26+
27+
Assertions.assertNotNull(traceId);
28+
Assertions.assertNull(spanId);
29+
}
30+
}

extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/runtime/JfrRuntimeBeanTest.java renamed to extensions/jfr/deployment/src/test/java/io/quarkus/jfr/deployment/runtime/JfrRuntimeBeanTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.quarkus.jfr.test.runtime;
1+
package io.quarkus.jfr.deployment.runtime;
22

33
import jakarta.inject.Inject;
44

@@ -9,7 +9,7 @@
99
import org.junit.jupiter.api.extension.RegisterExtension;
1010

1111
import io.quarkus.builder.Version;
12-
import io.quarkus.jfr.runtime.runtime.QuarkusRuntimeInfo;
12+
import io.quarkus.jfr.runtime.internal.runtime.QuarkusRuntimeInfo;
1313
import io.quarkus.runtime.ImageMode;
1414
import io.quarkus.test.QuarkusUnitTest;
1515

extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/runtime/JfrRuntimeDisableTest.java renamed to extensions/jfr/deployment/src/test/java/io/quarkus/jfr/deployment/runtime/JfrRuntimeDisableTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.quarkus.jfr.test.runtime;
1+
package io.quarkus.jfr.deployment.runtime;
22

33
import java.io.IOException;
44
import java.nio.file.Files;

0 commit comments

Comments
 (0)