-
Notifications
You must be signed in to change notification settings - Fork 716
Add jersey server jakarta module #1458
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
codefromthecrypt
merged 4 commits into
openzipkin:master
from
dennyac:add-jersey-server-jakarta-module
Apr 24, 2025
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # brave-instrumentation-jersey-server-jakarta | ||
| This module contains application event listeners for [Jersey Server 3.x](https://jersey.github.io/documentation/latest/monitoring_tracing.html#d0e16007). | ||
|
|
||
| These instrumentation is an alternative to the [jaxrs2](../jaxrs2) container | ||
| instrumentation. Do *not* use both. | ||
|
|
||
| `TracingApplicationEventListener` extracts trace state from incoming | ||
| requests. Then, it reports Zipkin how long each request takes, along | ||
| with relevant tags like the http url and the resource. | ||
|
|
||
| `SpanCustomizingApplicationEventListener` layers over [servlet](../servlet), | ||
| adding resource tags and route information to servlet-originated spans. | ||
|
|
||
| When in a servlet environment, use `SpanCustomizingApplicationEventListener`. | ||
| When not, use `TracingApplicationEventListener`. Don't use both! | ||
|
|
||
| `TracingApplicationEventListener` extracts trace state from incoming | ||
| requests. Then, it reports Zipkin how long each request takes, along | ||
| with relevant tags like the http url. | ||
|
|
||
| To enable tracing, you need to register the `TracingApplicationEventListener`. | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Normal configuration (`TracingApplicationEventListener`) | ||
|
|
||
| The `TracingApplicationEventListener` requires an instance of | ||
| `HttpTracing` to operate. With that in mind, use [standard means](https://jersey.github.io/apidocs/2.26/jersey/org/glassfish/jersey/server/monitoring/ApplicationEventListener.html) | ||
|
||
| to register the listener. | ||
|
|
||
| For example, you could wire up like this: | ||
| ```java | ||
| public class MyApplication extends Application { | ||
|
|
||
| public Set<Object> getSingletons() { | ||
| HttpTracing httpTracing = // configure me! | ||
| return new LinkedHashSet<>(Arrays.asList( | ||
| TracingApplicationEventListener.create(httpTracing), | ||
| new MyResource() | ||
| )); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Servlet-based configuration (`SpanCustomizingApplicationEventListener`) | ||
|
|
||
| When using `jersey-container-servlet`, setup [servlet tracing](../servlet), | ||
| an register `SpanCustomizingContainerFilter`. | ||
|
|
||
| ```java | ||
| public class MyApplication extends Application { | ||
| public Set<Object> getSingletons() { | ||
| HttpTracing httpTracing = // configure me! | ||
| return new LinkedHashSet<>(Arrays.asList( | ||
| SpanCustomizingApplicationEventListener.create(), | ||
| new MyResource() | ||
| )); | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
| ## Customizing Span data based on resources | ||
| `EventParser` decides which resource-specific (data beyond normal | ||
| http tags) end up on the span. You can override this to change what's | ||
| parsed, or use `NOOP` to disable controller-specific data. | ||
|
|
||
| Ex. If you want less tags, you can disable the JAX-RS resource ones. | ||
| ```java | ||
| SpanCustomizingApplicationEventListener.create(EventParser.NOOP); | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # We use brave.internal.Nullable, but it is not used at runtime. | ||
| Import-Package: \ | ||
| !brave.internal*,\ | ||
| * | ||
| Export-Package: \ | ||
| brave.jakarta.jersey.server |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <?xml version="1.0"?> | ||
| <!-- | ||
|
|
||
| Copyright The OpenZipkin Authors | ||
| SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| --> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <parent> | ||
| <groupId>io.zipkin.brave</groupId> | ||
| <artifactId>brave-instrumentation-parent</artifactId> | ||
| <version>6.1.1-SNAPSHOT</version> | ||
|
||
| </parent> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <artifactId>brave-instrumentation-jersey-server-jakarta</artifactId> | ||
| <name>Brave Instrumentation: Jersey Server 3.x (Jakarta)</name> | ||
|
|
||
| <properties> | ||
| <!-- Matches Export-Package in bnd.bnd --> | ||
| <module.name>brave.jakarta.jersey.server</module.name> | ||
|
|
||
| <main.basedir>${project.basedir}/../..</main.basedir> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.glassfish.jersey.core</groupId> | ||
| <artifactId>jersey-server</artifactId> | ||
| <version>${jersey3.version}</version> | ||
| <scope>provided</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>${project.groupId}</groupId> | ||
| <artifactId>brave-instrumentation-http</artifactId> | ||
| <version>${project.version}</version> | ||
| </dependency> | ||
|
|
||
| <dependency> | ||
| <groupId>${project.groupId}</groupId> | ||
| <artifactId>brave-instrumentation-http-tests-jakarta</artifactId> | ||
| <version>${project.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>${project.groupId}</groupId> | ||
| <artifactId>brave-instrumentation-servlet-jakarta</artifactId> | ||
| <version>${project.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.eclipse.jetty</groupId> | ||
| <artifactId>jetty-servlet</artifactId> | ||
| <version>${jetty11.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.glassfish.jersey.containers</groupId> | ||
| <artifactId>jersey-container-servlet</artifactId> | ||
| <version>${jersey3.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <!-- Avoid InjectionManagerFactory not found --> | ||
| <dependency> | ||
| <groupId>org.glassfish.jersey.inject</groupId> | ||
| <artifactId>jersey-hk2</artifactId> | ||
| <version>${jersey3.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
|
|
||
| <!-- to test @Inject annotations --> | ||
| <dependency> | ||
| <groupId>com.google.inject</groupId> | ||
| <artifactId>guice</artifactId> | ||
| <version>${guice6.version}</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> | ||
50 changes: 50 additions & 0 deletions
50
...entation/jersey-server-jakarta/src/main/java/brave/jakarta/jersey/server/EventParser.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /* | ||
| * Copyright The OpenZipkin Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| package brave.jakarta.jersey.server; | ||
|
|
||
| import brave.SpanCustomizer; | ||
| import brave.http.HttpTracing; | ||
| import org.glassfish.jersey.server.model.Invocable; | ||
| import org.glassfish.jersey.server.model.ResourceMethod; | ||
| import org.glassfish.jersey.server.monitoring.RequestEvent; | ||
| import org.glassfish.jersey.server.monitoring.RequestEventListener; | ||
|
|
||
| /** | ||
| * Jersey specific type used to customize traced requests based on the JAX-RS resource. | ||
| * | ||
| * <p>Note: This should not duplicate data added by {@link HttpTracing}. For example, this should | ||
| * not add the tag "http.url". | ||
| */ | ||
| // named event parser, not request event parser, in case we want to later support application event. | ||
| public class EventParser { | ||
| /** Adds no data to the request */ | ||
| public static final EventParser NOOP = new EventParser() { | ||
| @Override protected void requestMatched(RequestEvent event, SpanCustomizer customizer) { | ||
| } | ||
| }; | ||
|
|
||
| /** Simple class name that processed the request. ex BookResource */ | ||
| public static final String RESOURCE_CLASS = "jaxrs.resource.class"; | ||
| /** Method name that processed the request. ex listOfBooks */ | ||
| public static final String RESOURCE_METHOD = "jaxrs.resource.method"; | ||
|
|
||
| /** | ||
| * Invoked prior to request invocation during {@link RequestEventListener#onEvent(RequestEvent)} | ||
| * where the event type is {@link RequestEvent.Type#REQUEST_MATCHED} | ||
| * | ||
| * <p>Adds the tags {@link #RESOURCE_CLASS} and {@link #RESOURCE_METHOD}. Override or use {@link | ||
| * #NOOP} to change this behavior. | ||
| */ | ||
| protected void requestMatched(RequestEvent event, SpanCustomizer customizer) { | ||
| ResourceMethod method = event.getContainerRequest().getUriInfo().getMatchedResourceMethod(); | ||
| if (method == null) return; // This case is extremely odd as this is called on REQUEST_MATCHED! | ||
| Invocable i = method.getInvocable(); | ||
| customizer.tag(RESOURCE_CLASS, i.getHandler().getHandlerClass().getSimpleName()); | ||
| customizer.tag(RESOURCE_METHOD, i.getHandlingMethod().getName()); | ||
| } | ||
|
|
||
| public EventParser() { // intentionally public for @Inject to work without explicit binding | ||
| } | ||
| } |
120 changes: 120 additions & 0 deletions
120
...ta/src/main/java/brave/jakarta/jersey/server/SpanCustomizingApplicationEventListener.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| /* | ||
| * Copyright The OpenZipkin Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| package brave.jakarta.jersey.server; | ||
|
|
||
| import brave.SpanCustomizer; | ||
| import brave.internal.Nullable; | ||
| import jakarta.inject.Inject; | ||
| import jakarta.ws.rs.WebApplicationException; | ||
| import jakarta.ws.rs.ext.Provider; | ||
| import java.util.List; | ||
| import org.glassfish.jersey.server.ContainerRequest; | ||
| import org.glassfish.jersey.server.ExtendedUriInfo; | ||
| import org.glassfish.jersey.server.internal.process.MappableException; | ||
| import org.glassfish.jersey.server.monitoring.ApplicationEvent; | ||
| import org.glassfish.jersey.server.monitoring.ApplicationEventListener; | ||
| import org.glassfish.jersey.server.monitoring.RequestEvent; | ||
| import org.glassfish.jersey.server.monitoring.RequestEventListener; | ||
| import org.glassfish.jersey.uri.UriTemplate; | ||
|
|
||
| import static org.glassfish.jersey.server.monitoring.RequestEvent.Type.FINISHED; | ||
|
|
||
| /** | ||
| * Adds application-tier data to an existing http span via {@link EventParser}. This also sets the | ||
| * request property "http.route" so that it can be used in naming the http span. | ||
| * | ||
| * <p>Use this instead of {@link TracingApplicationEventListener} when you start traces at the | ||
| * servlet level via {@code brave.servlet.TracingFilter}. | ||
| */ | ||
| @Provider | ||
| public class SpanCustomizingApplicationEventListener | ||
| implements ApplicationEventListener, RequestEventListener { | ||
| public static SpanCustomizingApplicationEventListener create() { | ||
| return new SpanCustomizingApplicationEventListener(new EventParser()); | ||
| } | ||
|
|
||
| public static SpanCustomizingApplicationEventListener create(EventParser parser) { | ||
| return new SpanCustomizingApplicationEventListener(parser); | ||
| } | ||
|
|
||
| final EventParser parser; | ||
|
|
||
| @Inject | ||
| SpanCustomizingApplicationEventListener(EventParser parser) { | ||
| if (parser == null) throw new NullPointerException("parser == null"); | ||
| this.parser = parser; | ||
| } | ||
|
|
||
| @Override public void onEvent(ApplicationEvent event) { | ||
| // only onRequest is used | ||
| } | ||
|
|
||
| @Override public RequestEventListener onRequest(RequestEvent requestEvent) { | ||
| if (requestEvent.getType() == RequestEvent.Type.START) return this; | ||
| return null; | ||
| } | ||
|
|
||
| @Override public void onEvent(RequestEvent event) { | ||
| // Note: until REQUEST_MATCHED, we don't know metadata such as if the request is async or not | ||
| if (event.getType() != FINISHED) return; | ||
| ContainerRequest request = event.getContainerRequest(); | ||
| Object maybeSpan = request.getProperty(SpanCustomizer.class.getName()); | ||
| if (!(maybeSpan instanceof SpanCustomizer)) return; | ||
|
|
||
| // Set the HTTP route attribute so that TracingFilter can see it | ||
| request.setProperty("http.route", route(request)); | ||
|
|
||
| Throwable error = unwrapError(event); | ||
| // Set the error attribute so that TracingFilter can see it | ||
| if (error != null && request.getProperty("error") == null) request.setProperty("error", error); | ||
|
|
||
| parser.requestMatched(event, (SpanCustomizer) maybeSpan); | ||
| } | ||
|
|
||
| @Nullable static Throwable unwrapError(RequestEvent event) { | ||
| Throwable error = event.getException(); | ||
| // For example, if thrown in an async controller | ||
| if (error instanceof MappableException && error.getCause() != null) { | ||
| error = error.getCause(); | ||
| } | ||
| // Don't create error messages for normal HTTP status codes. | ||
| if (error instanceof WebApplicationException) return error.getCause(); | ||
| return error; | ||
| } | ||
|
|
||
| /** | ||
| * This returns the matched template as defined by a base URL and path expressions. | ||
| * | ||
| * <p>Matched templates are pairs of (resource path, method path) added with | ||
| * {@link org.glassfish.jersey.server.internal.routing.RoutingContext#pushTemplates(UriTemplate, | ||
| * UriTemplate)}. This code skips redundant slashes from either source caused by Path("/") or | ||
| * Path(""). | ||
| */ | ||
| @Nullable static String route(ContainerRequest request) { | ||
| ExtendedUriInfo uriInfo = request.getUriInfo(); | ||
| List<UriTemplate> templates = uriInfo.getMatchedTemplates(); | ||
| int templateCount = templates.size(); | ||
| if (templateCount == 0) return ""; | ||
| StringBuilder builder = null; // don't allocate unless you need it! | ||
| String basePath = uriInfo.getBaseUri().getPath(); | ||
| String result = null; | ||
| if (!"/" .equals(basePath)) { // skip empty base paths | ||
| result = basePath; | ||
| } | ||
| for (int i = templateCount - 1; i >= 0; i--) { | ||
| String template = templates.get(i).getTemplate(); | ||
| if ("/" .equals(template)) continue; // skip allocation | ||
| if (builder != null) { | ||
| builder.append(template); | ||
| } else if (result != null) { | ||
| builder = new StringBuilder(result).append(template); | ||
| result = null; | ||
| } else { | ||
| result = template; | ||
| } | ||
| } | ||
| return result != null ? result : builder != null ? builder.toString() : ""; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.