sentry-java
sentry-java copied to clipboard
Any help setting up Transaction tracking with Jersey JAX-RS
Platform:
- [X] Java -> 8
- [X] Logback ->
- [X] Spring -> 5.13
The version of the SDK: 5.5.2
I have the following issue:
The provided spring SentryTracingFilter does not record any transaction as it is unable to get a transaction name.
We are not using spring-webmvc, but rather Jersey (2.35), so the request attribute "BEST_MATCHING_PATTERN" is not set.
The whole stuff seems to be working fine, except for the transaction name. there does not seems to be any way to get the 'MATCHING_PATTERN' for the request. I wouldn't mind having the class / method name instead, but i didn't found it in the request either. As if the jersey dispatcher would forget the method name / pattern immediately after calling it.
Is it the right place to ask for such help ?
Did anyone succeeded in recording traces in this context ?
Thanks.
There seems to be no easy way to get the best matching patch in Jersey. If there would be, you could follow same steps as in the answer for similar problem referring Grails integration: https://forum.sentry.io/t/sentry-java-spring-boot-and-grails-performance-tracing-not-working/15765/3
One hint I can think of is looking up how its done in OpenTelemetry:
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/spring/spring-webmvc-3.1/javaagent/src/main/java/org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.java
But, it does seem to be hacky, as the filter class needs to be in the org.springframework.web.servlet package to have access to package protected classes.
Thanks for the hint. That class might be a place to start looking at: https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/JerseyRequestContextInstrumentation.java
Got it. Using a JAX-RS ContainerRequestFilter, one can request the best_matching_pattern. You'll need to inject the ServletRequest to set the attribute. Then you can use the SentryTracingFilter out of the box. It's a bit sad that we'll need spring-webmvc` just to be able to pass the attribute name, but relaxing the transaction name provider will probably help there.
Note: the filter needs to be registered in the init-params of your servlet.
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.springframework.web.servlet.HandlerMapping;
public class BestMatchRequestExtractor implements ContainerRequestFilter {
@Context private HttpServletRequest servletRequest;
@Override
public void filter(ContainerRequestContext aContainerRequestContext) {
ExtendedUriInfo uriInfo = (ExtendedUriInfo) aContainerRequestContext.getUriInfo();
String path = uriInfo.getMatchedTemplates().stream()
.map(t -> normalizePath(t.getTemplate()))
.reduce((a, b) -> b + a) // concatenate in reverse order
.orElse("");
servletRequest.setAttribute(
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE,
path);
}
// See {@link io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxrsPathUtil.normalizePath}
private static String normalizePath(String path) {
// ensure that non-empty path starts with /
if (path == null || "/".equals(path)) {
path = "";
} else if (!path.startsWith("/")) {
path = "/" + path;
}
// remove trailing /
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
return path;
}
}
@maciejwalkowiak What should we do with that class?
Thanks for figuring it out! You don't need a dependency to spring-mvc to set attribute name. After all, HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE is a constant that won't change so you can just use a string equivalent.
There is an open PR for relaxing TransactionNameProvider https://github.com/getsentry/sentry-java/pull/1861 - so you will be able to use another attribute name anyways.
I think we will keep this issue open, see if it gets upvoted - I don't think we have ever had before a request to support Jersey.
I'd like to take advantage of the recent changes in 6.0.0 that made ContainerRequestContext an interface.
The trouble I'm having is I'm not sure what exactly the string returned should be.
@benallard's solution relies on ContainerRequestContext which isn't available inside an implementation of ContainerRequestContext (only HttpServletRequest is).
I presume there is some way to build the required string with that class, but what should it be? The java docs don't state anywhere what the format of the transaction name should take.
@Ramblurr Jersey JAX-RS sounds like something that could be a community package. We'd be happy to point customers to your package if you had one.
The transaction name should make sense to the user and should not be something auto generated because transactions are grouped by their name in Sentry. It's often a function name or URL template (e.g. /posts/{postId}).