Server span naming for servlet filters (#2887)
* Server span naming for servlet filters * wildfly default servlet has empty mappings * jetty11 requires java11 * try a differnt way to disable jetty11 tests on java8 * Update instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5FilterMappingTest.groovy Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> * review fix * rework to use InstrumentationContext * remove debugging code * move MappingResolver to avoid ClassCastException on wildfly * Update instrumentation/servlet/servlet-3.0/javaagent/src/test/groovy/TomcatServlet3FilterMappingTest.groovy Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com> * review fixes Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
This commit is contained in:
parent
c9a3793bf8
commit
357140c081
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.servlet;
|
||||
package io.opentelemetry.instrumentation.api.servlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -12,7 +12,10 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MappingResolver {
|
||||
/**
|
||||
* Helper class for finding a mapping that matches current request from a collection of mappings.
|
||||
*/
|
||||
public final class MappingResolver {
|
||||
private final Set<String> exactMatches;
|
||||
private final List<WildcardMatcher> wildcardMatchers;
|
||||
private final boolean hasDefault;
|
|
@ -28,6 +28,9 @@ public final class ServerSpanNaming {
|
|||
}
|
||||
|
||||
private volatile Source updatedBySource;
|
||||
// Length of the currently set name. This is used when setting name from a servlet filter
|
||||
// to pick the most descriptive (longest) name.
|
||||
private volatile int nameLength;
|
||||
|
||||
private ServerSpanNaming(Source initialSource) {
|
||||
this.updatedBySource = initialSource;
|
||||
|
@ -58,15 +61,24 @@ public final class ServerSpanNaming {
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (source.order > serverSpanNaming.updatedBySource.order) {
|
||||
// special case for servlet filters, even when we have a name from previous filter see whether
|
||||
// the new name is better and if so use it instead
|
||||
boolean onlyIfBetterName =
|
||||
!source.useFirst && source.order == serverSpanNaming.updatedBySource.order;
|
||||
if (source.order > serverSpanNaming.updatedBySource.order || onlyIfBetterName) {
|
||||
String name = serverSpanName.get();
|
||||
if (name != null) {
|
||||
if (name != null && (!onlyIfBetterName || serverSpanNaming.isBetterName(name))) {
|
||||
serverSpan.updateName(name);
|
||||
serverSpanNaming.updatedBySource = source;
|
||||
serverSpanNaming.nameLength = name.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBetterName(String name) {
|
||||
return name.length() > nameLength;
|
||||
}
|
||||
|
||||
// TODO (trask) migrate the one usage (ServletHttpServerTracer) to ServerSpanNaming.init() once we
|
||||
// migrate to new Instrumenters (see
|
||||
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/2814#discussion_r617351334
|
||||
|
@ -82,13 +94,22 @@ public final class ServerSpanNaming {
|
|||
|
||||
public enum Source {
|
||||
CONTAINER(1),
|
||||
SERVLET(2),
|
||||
CONTROLLER(3);
|
||||
// for servlet filters we try to find the best name which isn't necessarily from the first
|
||||
// filter that is called
|
||||
FILTER(2, false),
|
||||
SERVLET(3),
|
||||
CONTROLLER(4);
|
||||
|
||||
private final int order;
|
||||
private final boolean useFirst;
|
||||
|
||||
Source(int order) {
|
||||
this(order, true);
|
||||
}
|
||||
|
||||
Source(int order, boolean useFirst) {
|
||||
this.order = order;
|
||||
this.useFirst = useFirst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,13 @@ import static io.opentelemetry.instrumentation.servlet.v3_0.Servlet3HttpServerTr
|
|||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -36,12 +40,24 @@ public class Servlet3Advice {
|
|||
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
|
||||
boolean servlet = servletOrFilter instanceof Servlet;
|
||||
MappingResolver mappingResolver;
|
||||
if (servlet) {
|
||||
mappingResolver =
|
||||
InstrumentationContext.get(Servlet.class, MappingResolver.class)
|
||||
.get((Servlet) servletOrFilter);
|
||||
} else {
|
||||
mappingResolver =
|
||||
InstrumentationContext.get(Filter.class, MappingResolver.class)
|
||||
.get((Filter) servletOrFilter);
|
||||
}
|
||||
|
||||
Context attachedContext = tracer().getServerContext(httpServletRequest);
|
||||
if (attachedContext != null) {
|
||||
// We are inside nested servlet/filter/app-server span, don't create new span
|
||||
if (tracer().needsRescoping(attachedContext)) {
|
||||
attachedContext =
|
||||
tracer().updateContext(attachedContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
|
||||
scope = attachedContext.makeCurrent();
|
||||
return;
|
||||
}
|
||||
|
@ -50,7 +66,7 @@ public class Servlet3Advice {
|
|||
// instrumentation, if needed update span with info from current request.
|
||||
Context currentContext = Java8BytecodeBridge.currentContext();
|
||||
Context updatedContext =
|
||||
tracer().updateContext(currentContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
|
||||
if (updatedContext != currentContext) {
|
||||
// runOnceUnderAppServer updated context, need to re-scope
|
||||
scope = updatedContext.makeCurrent();
|
||||
|
@ -65,7 +81,7 @@ public class Servlet3Advice {
|
|||
// In case it was created by app server integration we need to update it with info from
|
||||
// current request.
|
||||
Context updatedContext =
|
||||
tracer().updateContext(currentContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
|
||||
if (currentContext != updatedContext) {
|
||||
// updateContext updated context, need to re-scope
|
||||
scope = updatedContext.makeCurrent();
|
||||
|
@ -73,7 +89,7 @@ public class Servlet3Advice {
|
|||
return;
|
||||
}
|
||||
|
||||
context = tracer().startSpan(servletOrFilter, httpServletRequest);
|
||||
context = tracer().startSpan(httpServletRequest, mappingResolver, servlet);
|
||||
scope = context.makeCurrent();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class Servlet3FilterInitAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void filterInit(
|
||||
@Advice.This Filter filter, @Advice.Argument(0) FilterConfig filterConfig) {
|
||||
if (filterConfig == null) {
|
||||
return;
|
||||
}
|
||||
InstrumentationContext.get(Filter.class, MappingResolver.class)
|
||||
.putIfAbsent(filter, new Servlet3FilterMappingResolverFactory(filterConfig));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletFilterMappingResolverFactory;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.FilterRegistration;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRegistration;
|
||||
|
||||
public class Servlet3FilterMappingResolverFactory
|
||||
extends ServletFilterMappingResolverFactory<FilterRegistration>
|
||||
implements ContextStore.Factory<MappingResolver> {
|
||||
private final FilterConfig filterConfig;
|
||||
|
||||
public Servlet3FilterMappingResolverFactory(FilterConfig filterConfig) {
|
||||
this.filterConfig = filterConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterRegistration getFilterRegistration() {
|
||||
String filterName = filterConfig.getFilterName();
|
||||
ServletContext servletContext = filterConfig.getServletContext();
|
||||
if (filterName == null || servletContext == null) {
|
||||
return null;
|
||||
}
|
||||
return servletContext.getFilterRegistration(filterName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getUrlPatternMappings(FilterRegistration filterRegistration) {
|
||||
return filterRegistration.getUrlPatternMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getServletNameMappings(FilterRegistration filterRegistration) {
|
||||
return filterRegistration.getServletNameMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getServletMappings(String servletName) {
|
||||
ServletRegistration servletRegistration =
|
||||
filterConfig.getServletContext().getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
return servletRegistration.getMappings();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class Servlet3InitAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void servletInit(
|
||||
@Advice.This Servlet servlet, @Advice.Argument(0) ServletConfig servletConfig) {
|
||||
if (servletConfig == null) {
|
||||
return;
|
||||
}
|
||||
InstrumentationContext.get(Servlet.class, MappingResolver.class)
|
||||
.putIfAbsent(servlet, new Servlet3MappingResolverFactory(servletConfig));
|
||||
}
|
||||
}
|
|
@ -26,7 +26,11 @@ public class Servlet3InstrumentationModule extends InstrumentationModule {
|
|||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return asList(
|
||||
new AsyncContextInstrumentation(BASE_PACKAGE, adviceClassName(".AsyncDispatchAdvice")),
|
||||
new ServletAndFilterInstrumentation(BASE_PACKAGE, adviceClassName(".Servlet3Advice")));
|
||||
new ServletAndFilterInstrumentation(
|
||||
BASE_PACKAGE,
|
||||
adviceClassName(".Servlet3Advice"),
|
||||
adviceClassName(".Servlet3InitAdvice"),
|
||||
adviceClassName(".Servlet3FilterInitAdvice")));
|
||||
}
|
||||
|
||||
private static String adviceClassName(String suffix) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletMappingResolverFactory;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRegistration;
|
||||
|
||||
public class Servlet3MappingResolverFactory extends ServletMappingResolverFactory
|
||||
implements ContextStore.Factory<MappingResolver> {
|
||||
private final ServletConfig servletConfig;
|
||||
|
||||
public Servlet3MappingResolverFactory(ServletConfig servletConfig) {
|
||||
this.servletConfig = servletConfig;
|
||||
}
|
||||
|
||||
public Collection<String> getMappings() {
|
||||
String servletName = servletConfig.getServletName();
|
||||
ServletContext servletContext = servletConfig.getServletContext();
|
||||
if (servletName == null || servletContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServletRegistration servletRegistration = servletContext.getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
return servletRegistration.getMappings();
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import okhttp3.RequestBody
|
|||
import okhttp3.Response
|
||||
import spock.lang.Unroll
|
||||
|
||||
abstract class AbstractServletMappingTest<SERVER, CONTEXT> extends AgentInstrumentationSpecification implements HttpServerTestTrait<SERVER> {
|
||||
abstract class AbstractServlet3MappingTest<SERVER, CONTEXT> extends AgentInstrumentationSpecification implements HttpServerTestTrait<SERVER> {
|
||||
|
||||
abstract void addServlet(CONTEXT context, String path, Class<Servlet> servlet)
|
||||
|
|
@ -11,7 +11,7 @@ import javax.servlet.http.HttpServletResponse
|
|||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||
|
||||
class JettyServletMappingTest extends AbstractServletMappingTest<Server, ServletContextHandler> {
|
||||
class JettyServlet3MappingTest extends AbstractServlet3MappingTest<Server, ServletContextHandler> {
|
||||
|
||||
@Override
|
||||
Server startServer(int port) {
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.FilterConfig
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.ServletRequest
|
||||
import javax.servlet.ServletResponse
|
||||
import javax.servlet.http.HttpServlet
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
import org.apache.catalina.Context
|
||||
import org.apache.catalina.startup.Tomcat
|
||||
import org.apache.tomcat.util.descriptor.web.FilterDef
|
||||
import org.apache.tomcat.util.descriptor.web.FilterMap
|
||||
|
||||
abstract class TomcatServlet3FilterMappingTest extends TomcatServlet3MappingTest {
|
||||
|
||||
void addFilter(Context servletContext, String path, Class<Filter> filter) {
|
||||
String name = UUID.randomUUID()
|
||||
FilterDef filterDef = new FilterDef()
|
||||
filterDef.setFilter(filter.newInstance())
|
||||
filterDef.setFilterName(name)
|
||||
servletContext.addFilterDef(filterDef)
|
||||
FilterMap filterMap = new FilterMap()
|
||||
filterMap.setFilterName(name)
|
||||
filterMap.addURLPattern(path)
|
||||
servletContext.addFilterMap(filterMap)
|
||||
}
|
||||
|
||||
void addFilterWithServletName(Context servletContext, String servletName, Class<Filter> filter) {
|
||||
String name = UUID.randomUUID()
|
||||
FilterDef filterDef = new FilterDef()
|
||||
filterDef.setFilter(filter.newInstance())
|
||||
filterDef.setFilterName(name)
|
||||
servletContext.addFilterDef(filterDef)
|
||||
FilterMap filterMap = new FilterMap()
|
||||
filterMap.setFilterName(name)
|
||||
filterMap.addServletName(servletName)
|
||||
servletContext.addFilterMap(filterMap)
|
||||
}
|
||||
|
||||
static class TestFilter implements Filter {
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (servletRequest.getAttribute("firstFilterCalled") != null) {
|
||||
servletRequest.setAttribute("testFilterCalled", Boolean.TRUE)
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
} else {
|
||||
throw new IllegalStateException("First filter should have been called.")
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class FirstFilter implements Filter {
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
servletRequest.setAttribute("firstFilterCalled", Boolean.TRUE)
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class LastFilter implements Filter {
|
||||
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (servletRequest.getAttribute("testFilterCalled") != null) {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse
|
||||
response.getWriter().write("Ok")
|
||||
response.setStatus(HttpServletResponse.SC_OK)
|
||||
} else {
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultServlet extends HttpServlet {
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) {
|
||||
throw new IllegalStateException("Servlet should not have been called, filter should have handled the request.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TomcatServlet3FilterUrlPatternMappingTest extends TomcatServlet3FilterMappingTest {
|
||||
@Override
|
||||
protected void setupServlets(Context context) {
|
||||
addFilter(context, "/*", FirstFilter)
|
||||
addFilter(context, "/prefix/*", TestFilter)
|
||||
addFilter(context, "*.suffix", TestFilter)
|
||||
addFilter(context, "/*", LastFilter)
|
||||
}
|
||||
}
|
||||
|
||||
class TomcatServlet3FilterServletNameMappingTest extends TomcatServlet3FilterMappingTest {
|
||||
@Override
|
||||
protected void setupServlets(Context context) {
|
||||
Tomcat.addServlet(context, "prefix-servlet", new DefaultServlet())
|
||||
context.addServletMappingDecoded("/prefix/*", "prefix-servlet")
|
||||
Tomcat.addServlet(context, "suffix-servlet", new DefaultServlet())
|
||||
context.addServletMappingDecoded("*.suffix", "suffix-servlet")
|
||||
|
||||
addFilter(context, "/*", FirstFilter)
|
||||
addFilterWithServletName(context, "prefix-servlet", TestFilter)
|
||||
addFilterWithServletName(context, "suffix-servlet", TestFilter)
|
||||
addFilterWithServletName(context, "prefix-servlet", LastFilter)
|
||||
addFilterWithServletName(context, "suffix-servlet", LastFilter)
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import org.apache.catalina.startup.Tomcat
|
|||
import org.apache.tomcat.JarScanFilter
|
||||
import org.apache.tomcat.JarScanType
|
||||
|
||||
class TomcatServletMappingTest extends AbstractServletMappingTest<Tomcat, Context> {
|
||||
class TomcatServlet3MappingTest extends AbstractServlet3MappingTest<Tomcat, Context> {
|
||||
|
||||
@Override
|
||||
Tomcat startServer(int port) {
|
|
@ -5,22 +5,21 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.servlet.v3_0;
|
||||
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
||||
import io.opentelemetry.instrumentation.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.javax.JavaxServletHttpServerTracer;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRegistration;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletSpanNameProvider;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class Servlet3HttpServerTracer extends JavaxServletHttpServerTracer<HttpServletResponse> {
|
||||
private static final Servlet3HttpServerTracer TRACER = new Servlet3HttpServerTracer();
|
||||
private static final ServletSpanNameProvider<HttpServletRequest> SPAN_NAME_PROVIDER =
|
||||
new ServletSpanNameProvider<>(Servlet3Accessor.INSTANCE);
|
||||
|
||||
protected Servlet3HttpServerTracer() {
|
||||
super(Servlet3Accessor.INSTANCE);
|
||||
|
@ -30,89 +29,20 @@ public class Servlet3HttpServerTracer extends JavaxServletHttpServerTracer<HttpS
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
public Context startSpan(Object servletOrFilter, HttpServletRequest request) {
|
||||
return startSpan(
|
||||
request, getSpanName(servletOrFilter, request), servletOrFilter instanceof Servlet);
|
||||
}
|
||||
|
||||
private String getSpanName(Object servletOrFilter, HttpServletRequest request) {
|
||||
String spanName = getSpanNameFromPath(servletOrFilter, request);
|
||||
if (spanName == null) {
|
||||
String contextPath = request.getContextPath();
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return "HTTP " + request.getMethod();
|
||||
}
|
||||
return contextPath;
|
||||
}
|
||||
return spanName;
|
||||
}
|
||||
|
||||
private static String getSpanNameFromPath(Object servletOrFilter, HttpServletRequest request) {
|
||||
// we are only interested in Servlets
|
||||
if (!(servletOrFilter instanceof Servlet)) {
|
||||
return null;
|
||||
}
|
||||
Servlet servlet = (Servlet) servletOrFilter;
|
||||
|
||||
String mapping = getMapping(servlet, request.getServletPath(), request.getPathInfo());
|
||||
// mapping was not found
|
||||
if (mapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prepend context path
|
||||
String contextPath = request.getContextPath();
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return mapping;
|
||||
}
|
||||
return contextPath + mapping;
|
||||
}
|
||||
|
||||
private static String getMapping(Servlet servlet, String servletPath, String pathInfo) {
|
||||
ServletConfig servletConfig = servlet.getServletConfig();
|
||||
if (servletConfig == null) {
|
||||
return null;
|
||||
}
|
||||
String servletName = servletConfig.getServletName();
|
||||
ServletContext servletContext = servletConfig.getServletContext();
|
||||
MappingResolver mappingResolver = getMappingResolver(servletContext, servletName);
|
||||
if (mappingResolver == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mappingResolver.resolve(servletPath, pathInfo);
|
||||
}
|
||||
|
||||
private static MappingResolver getMappingResolver(
|
||||
ServletContext servletContext, String servletName) {
|
||||
if (servletContext == null || servletName == null) {
|
||||
return null;
|
||||
}
|
||||
String key = MappingResolver.class.getName() + "." + servletName;
|
||||
MappingResolver mappingResolver = (MappingResolver) servletContext.getAttribute(key);
|
||||
if (mappingResolver != null) {
|
||||
return mappingResolver;
|
||||
}
|
||||
|
||||
ServletRegistration servletRegistration = servletContext.getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
Collection<String> mappings = servletRegistration.getMappings();
|
||||
if (mappings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
mappingResolver = MappingResolver.build(mappings);
|
||||
servletContext.setAttribute(key, mappingResolver);
|
||||
|
||||
return mappingResolver;
|
||||
public Context startSpan(
|
||||
HttpServletRequest request, MappingResolver mappingResolver, boolean servlet) {
|
||||
return startSpan(request, SPAN_NAME_PROVIDER.getSpanName(mappingResolver, request), servlet);
|
||||
}
|
||||
|
||||
public Context updateContext(
|
||||
Context context, Object servletOrFilter, HttpServletRequest request) {
|
||||
Context context,
|
||||
HttpServletRequest request,
|
||||
MappingResolver mappingResolver,
|
||||
boolean servlet) {
|
||||
ServerSpanNaming.updateServerSpanName(
|
||||
context, SERVLET, () -> getSpanNameFromPath(servletOrFilter, request));
|
||||
context,
|
||||
servlet ? SERVLET : FILTER,
|
||||
() -> SPAN_NAME_PROVIDER.getSpanNameOrNull(mappingResolver, request));
|
||||
return updateContext(context, request);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,4 +13,9 @@ dependencies {
|
|||
api(project(':instrumentation:servlet:servlet-5.0:library'))
|
||||
implementation(project(':instrumentation:servlet:servlet-common:javaagent'))
|
||||
compileOnly group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '5.0.0'
|
||||
|
||||
testLibrary group: 'org.eclipse.jetty', name: 'jetty-server', version: '11.0.0'
|
||||
testLibrary group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '11.0.0'
|
||||
testLibrary group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.0.0'
|
||||
testLibrary group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '10.0.0'
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ public class JakartaServletInstrumentationModule extends InstrumentationModule {
|
|||
new AsyncContextInstrumentation(
|
||||
BASE_PACKAGE, adviceClassName(".async.AsyncDispatchAdvice")),
|
||||
new ServletAndFilterInstrumentation(
|
||||
BASE_PACKAGE, adviceClassName(".service.JakartaServletServiceAdvice")),
|
||||
BASE_PACKAGE,
|
||||
adviceClassName(".service.JakartaServletServiceAdvice"),
|
||||
adviceClassName(".service.JakartaServletInitAdvice"),
|
||||
adviceClassName(".service.JakartaServletFilterInitAdvice")),
|
||||
new HttpServletResponseInstrumentation(
|
||||
BASE_PACKAGE, adviceClassName(".response.ResponseSendAdvice")),
|
||||
new RequestDispatcherInstrumentation(
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.service;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class JakartaServletFilterInitAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void filterInit(
|
||||
@Advice.This Filter filter, @Advice.Argument(0) FilterConfig filterConfig) {
|
||||
if (filterConfig == null) {
|
||||
return;
|
||||
}
|
||||
InstrumentationContext.get(Filter.class, MappingResolver.class)
|
||||
.putIfAbsent(filter, new JakartaServletFilterMappingResolverFactory(filterConfig));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.service;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletFilterMappingResolverFactory;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.FilterRegistration;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
import java.util.Collection;
|
||||
|
||||
public class JakartaServletFilterMappingResolverFactory
|
||||
extends ServletFilterMappingResolverFactory<FilterRegistration>
|
||||
implements ContextStore.Factory<MappingResolver> {
|
||||
private final FilterConfig filterConfig;
|
||||
|
||||
public JakartaServletFilterMappingResolverFactory(FilterConfig filterConfig) {
|
||||
this.filterConfig = filterConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterRegistration getFilterRegistration() {
|
||||
String filterName = filterConfig.getFilterName();
|
||||
ServletContext servletContext = filterConfig.getServletContext();
|
||||
if (filterName == null || servletContext == null) {
|
||||
return null;
|
||||
}
|
||||
return servletContext.getFilterRegistration(filterName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getUrlPatternMappings(FilterRegistration filterRegistration) {
|
||||
return filterRegistration.getUrlPatternMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getServletNameMappings(FilterRegistration filterRegistration) {
|
||||
return filterRegistration.getServletNameMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getServletMappings(String servletName) {
|
||||
ServletRegistration servletRegistration =
|
||||
filterConfig.getServletContext().getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
return servletRegistration.getMappings();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.service;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import jakarta.servlet.Servlet;
|
||||
import jakarta.servlet.ServletConfig;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
public class JakartaServletInitAdvice {
|
||||
|
||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||
public static void servletInit(
|
||||
@Advice.This Servlet servlet, @Advice.Argument(0) ServletConfig servletConfig) {
|
||||
if (servletConfig == null) {
|
||||
return;
|
||||
}
|
||||
InstrumentationContext.get(Servlet.class, MappingResolver.class)
|
||||
.putIfAbsent(servlet, new JakartaServletMappingResolverFactory(servletConfig));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.service;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletMappingResolverFactory;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.ContextStore;
|
||||
import jakarta.servlet.ServletConfig;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
import java.util.Collection;
|
||||
|
||||
public class JakartaServletMappingResolverFactory extends ServletMappingResolverFactory
|
||||
implements ContextStore.Factory<MappingResolver> {
|
||||
private final ServletConfig servletConfig;
|
||||
|
||||
public JakartaServletMappingResolverFactory(ServletConfig servletConfig) {
|
||||
this.servletConfig = servletConfig;
|
||||
}
|
||||
|
||||
public Collection<String> getMappings() {
|
||||
String servletName = servletConfig.getServletName();
|
||||
ServletContext servletContext = servletConfig.getServletContext();
|
||||
if (servletName == null || servletContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServletRegistration servletRegistration = servletContext.getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
return servletRegistration.getMappings();
|
||||
}
|
||||
}
|
|
@ -10,9 +10,13 @@ import static io.opentelemetry.instrumentation.servlet.jakarta.v5_0.JakartaServl
|
|||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.instrumentation.api.servlet.AppServerBridge;
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.CallDepthThreadLocalMap;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.instrumentation.servlet.common.service.ServletAndFilterAdviceHelper;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.Servlet;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -37,12 +41,24 @@ public class JakartaServletServiceAdvice {
|
|||
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
|
||||
boolean servlet = servletOrFilter instanceof Servlet;
|
||||
MappingResolver mappingResolver;
|
||||
if (servlet) {
|
||||
mappingResolver =
|
||||
InstrumentationContext.get(Servlet.class, MappingResolver.class)
|
||||
.get((Servlet) servletOrFilter);
|
||||
} else {
|
||||
mappingResolver =
|
||||
InstrumentationContext.get(Filter.class, MappingResolver.class)
|
||||
.get((Filter) servletOrFilter);
|
||||
}
|
||||
|
||||
Context attachedContext = tracer().getServerContext(httpServletRequest);
|
||||
if (attachedContext != null) {
|
||||
// We are inside nested servlet/filter/app-server span, don't create new span
|
||||
if (tracer().needsRescoping(attachedContext)) {
|
||||
attachedContext =
|
||||
tracer().updateContext(attachedContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(attachedContext, httpServletRequest, mappingResolver, servlet);
|
||||
scope = attachedContext.makeCurrent();
|
||||
return;
|
||||
}
|
||||
|
@ -51,7 +67,7 @@ public class JakartaServletServiceAdvice {
|
|||
// instrumentation, if needed update span with info from current request.
|
||||
Context currentContext = Java8BytecodeBridge.currentContext();
|
||||
Context updatedContext =
|
||||
tracer().updateContext(currentContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
|
||||
if (updatedContext != currentContext) {
|
||||
// runOnceUnderAppServer updated context, need to re-scope
|
||||
scope = updatedContext.makeCurrent();
|
||||
|
@ -66,7 +82,7 @@ public class JakartaServletServiceAdvice {
|
|||
// In case it was created by app server integration we need to update it with info from
|
||||
// current request.
|
||||
Context updatedContext =
|
||||
tracer().updateContext(currentContext, servletOrFilter, httpServletRequest);
|
||||
tracer().updateContext(currentContext, httpServletRequest, mappingResolver, servlet);
|
||||
if (currentContext != updatedContext) {
|
||||
// updateContext updated context, need to re-scope
|
||||
scope = updatedContext.makeCurrent();
|
||||
|
@ -74,7 +90,7 @@ public class JakartaServletServiceAdvice {
|
|||
return;
|
||||
}
|
||||
|
||||
context = tracer().startSpan(servletOrFilter, httpServletRequest);
|
||||
context = tracer().startSpan(httpServletRequest, mappingResolver, servlet);
|
||||
scope = context.makeCurrent();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||
|
||||
import io.opentelemetry.api.trace.SpanKind
|
||||
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait
|
||||
import jakarta.servlet.Servlet
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.http.HttpServlet
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import spock.lang.Unroll
|
||||
|
||||
abstract class AbstractServlet5MappingTest<SERVER, CONTEXT> extends AgentInstrumentationSpecification implements HttpServerTestTrait<SERVER> {
|
||||
|
||||
abstract void addServlet(CONTEXT context, String path, Class<Servlet> servlet)
|
||||
|
||||
protected void setupServlets(CONTEXT context) {
|
||||
addServlet(context, "/prefix/*", TestServlet)
|
||||
addServlet(context, "*.suffix", TestServlet)
|
||||
}
|
||||
|
||||
static class TestServlet extends HttpServlet {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.getWriter().write("Ok")
|
||||
}
|
||||
}
|
||||
|
||||
Request.Builder request(HttpUrl url, String method, RequestBody body) {
|
||||
return new Request.Builder()
|
||||
.url(url)
|
||||
.method(method, body)
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "test path #path"() {
|
||||
setup:
|
||||
def url = HttpUrl.get(address.resolve(path)).newBuilder().build()
|
||||
def request = request(url, "GET", null).build()
|
||||
Response response = client.newCall(request).execute()
|
||||
|
||||
expect:
|
||||
response.code() == success ? 200 : 404
|
||||
|
||||
and:
|
||||
def spanCount = success ? 1 : 2
|
||||
assertTraces(1) {
|
||||
trace(0, spanCount) {
|
||||
span(0) {
|
||||
name getContextPath() + spanName
|
||||
kind SpanKind.SERVER
|
||||
if (!success) {
|
||||
status ERROR
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
span(1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
path | spanName | success
|
||||
'prefix' | '/prefix/*' | true
|
||||
'prefix/' | '/prefix/*' | true
|
||||
'prefix/a' | '/prefix/*' | true
|
||||
'prefixa' | '/*' | false
|
||||
'a.suffix' | '/*.suffix' | true
|
||||
'.suffix' | '/*.suffix' | true
|
||||
'suffix' | '/*' | false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import jakarta.servlet.Servlet
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.http.HttpServlet
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||
import spock.lang.IgnoreIf
|
||||
|
||||
@IgnoreIf({ !jvm.java11Compatible })
|
||||
class JettyServlet5MappingTest extends AbstractServlet5MappingTest<Object, Object> {
|
||||
|
||||
@Override
|
||||
Object startServer(int port) {
|
||||
Server server = new Server(port)
|
||||
ServletContextHandler handler = new ServletContextHandler(null, contextPath)
|
||||
setupServlets(handler)
|
||||
server.setHandler(handler)
|
||||
server.start()
|
||||
return server
|
||||
}
|
||||
|
||||
@Override
|
||||
void stopServer(Object serverObject) {
|
||||
Server server = (Server) serverObject
|
||||
server.stop()
|
||||
server.destroy()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupServlets(Object handlerObject) {
|
||||
ServletContextHandler handler = (ServletContextHandler) handlerObject
|
||||
super.setupServlets(handler)
|
||||
|
||||
addServlet(handler, "/", DefaultServlet)
|
||||
}
|
||||
|
||||
@Override
|
||||
void addServlet(Object handlerObject, String path, Class<Servlet> servlet) {
|
||||
ServletContextHandler handler = (ServletContextHandler) handlerObject
|
||||
handler.addServlet(servlet, path)
|
||||
}
|
||||
|
||||
static class DefaultServlet extends HttpServlet {
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.sendError(404)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String getContextPath() {
|
||||
"/jetty-context"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.FilterConfig
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import jakarta.servlet.http.HttpServlet
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.apache.catalina.Context
|
||||
import org.apache.catalina.startup.Tomcat
|
||||
import org.apache.tomcat.util.descriptor.web.FilterDef
|
||||
import org.apache.tomcat.util.descriptor.web.FilterMap
|
||||
|
||||
abstract class TomcatServlet5FilterMappingTest extends TomcatServlet5MappingTest {
|
||||
|
||||
void addFilter(Context servletContext, String path, Class<Filter> filter) {
|
||||
String name = UUID.randomUUID()
|
||||
FilterDef filterDef = new FilterDef()
|
||||
filterDef.setFilter(filter.newInstance())
|
||||
filterDef.setFilterName(name)
|
||||
servletContext.addFilterDef(filterDef)
|
||||
FilterMap filterMap = new FilterMap()
|
||||
filterMap.setFilterName(name)
|
||||
filterMap.addURLPattern(path)
|
||||
servletContext.addFilterMap(filterMap)
|
||||
}
|
||||
|
||||
void addFilterWithServletName(Context servletContext, String servletName, Class<Filter> filter) {
|
||||
String name = UUID.randomUUID()
|
||||
FilterDef filterDef = new FilterDef()
|
||||
filterDef.setFilter(filter.newInstance())
|
||||
filterDef.setFilterName(name)
|
||||
servletContext.addFilterDef(filterDef)
|
||||
FilterMap filterMap = new FilterMap()
|
||||
filterMap.setFilterName(name)
|
||||
filterMap.addServletName(servletName)
|
||||
servletContext.addFilterMap(filterMap)
|
||||
}
|
||||
|
||||
static class TestFilter implements Filter {
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (servletRequest.getAttribute("firstFilterCalled") != null) {
|
||||
servletRequest.setAttribute("testFilterCalled", Boolean.TRUE)
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
} else {
|
||||
throw new IllegalStateException("First filter should have been called.")
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class FirstFilter implements Filter {
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
servletRequest.setAttribute("firstFilterCalled", Boolean.TRUE)
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class LastFilter implements Filter {
|
||||
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (servletRequest.getAttribute("testFilterCalled") != null) {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse
|
||||
response.getWriter().write("Ok")
|
||||
response.setStatus(HttpServletResponse.SC_OK)
|
||||
} else {
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultServlet extends HttpServlet {
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) {
|
||||
throw new IllegalStateException("Servlet should not have been called, filter should have handled the request.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TomcatServlet5FilterUrlPatternMappingTest extends TomcatServlet5FilterMappingTest {
|
||||
@Override
|
||||
protected void setupServlets(Context context) {
|
||||
addFilter(context, "/*", FirstFilter)
|
||||
addFilter(context, "/prefix/*", TestFilter)
|
||||
addFilter(context, "*.suffix", TestFilter)
|
||||
addFilter(context, "/*", LastFilter)
|
||||
}
|
||||
}
|
||||
|
||||
class TomcatServlet5FilterServletNameMappingTest extends TomcatServlet5FilterMappingTest {
|
||||
@Override
|
||||
protected void setupServlets(Context context) {
|
||||
Tomcat.addServlet(context, "prefix-servlet", DefaultServlet.newInstance())
|
||||
context.addServletMappingDecoded("/prefix/*", "prefix-servlet")
|
||||
Tomcat.addServlet(context, "suffix-servlet", DefaultServlet.newInstance())
|
||||
context.addServletMappingDecoded("*.suffix", "suffix-servlet")
|
||||
|
||||
addFilter(context, "/*", FirstFilter)
|
||||
addFilterWithServletName(context, "prefix-servlet", TestFilter)
|
||||
addFilterWithServletName(context, "suffix-servlet", TestFilter)
|
||||
addFilterWithServletName(context, "prefix-servlet", LastFilter)
|
||||
addFilterWithServletName(context, "suffix-servlet", LastFilter)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import jakarta.servlet.Servlet
|
||||
import java.nio.file.Files
|
||||
import org.apache.catalina.Context
|
||||
import org.apache.catalina.startup.Tomcat
|
||||
import org.apache.tomcat.JarScanFilter
|
||||
import org.apache.tomcat.JarScanType
|
||||
|
||||
class TomcatServlet5MappingTest extends AbstractServlet5MappingTest<Tomcat, Context> {
|
||||
|
||||
@Override
|
||||
Tomcat startServer(int port) {
|
||||
def tomcatServer = new Tomcat()
|
||||
|
||||
def baseDir = Files.createTempDirectory("tomcat").toFile()
|
||||
baseDir.deleteOnExit()
|
||||
tomcatServer.setBaseDir(baseDir.getAbsolutePath())
|
||||
|
||||
tomcatServer.setPort(port)
|
||||
tomcatServer.getConnector().enableLookups = true // get localhost instead of 127.0.0.1
|
||||
|
||||
File applicationDir = new File(baseDir, "/webapps/ROOT")
|
||||
if (!applicationDir.exists()) {
|
||||
applicationDir.mkdirs()
|
||||
applicationDir.deleteOnExit()
|
||||
}
|
||||
Context servletContext = tomcatServer.addWebapp(contextPath, applicationDir.getAbsolutePath())
|
||||
// Speed up startup by disabling jar scanning:
|
||||
servletContext.getJarScanner().setJarScanFilter(new JarScanFilter() {
|
||||
@Override
|
||||
boolean check(JarScanType jarScanType, String jarName) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
setupServlets(servletContext)
|
||||
|
||||
tomcatServer.start()
|
||||
|
||||
return tomcatServer
|
||||
}
|
||||
|
||||
@Override
|
||||
void stopServer(Tomcat server) {
|
||||
server.stop()
|
||||
server.destroy()
|
||||
}
|
||||
|
||||
@Override
|
||||
void addServlet(Context servletContext, String path, Class<Servlet> servlet) {
|
||||
String name = UUID.randomUUID()
|
||||
Tomcat.addServlet(servletContext, name, servlet.newInstance())
|
||||
servletContext.addServletMappingDecoded(path, name)
|
||||
}
|
||||
|
||||
@Override
|
||||
String getContextPath() {
|
||||
return "/tomcat-context"
|
||||
}
|
||||
}
|
|
@ -85,6 +85,11 @@ public class JakartaServletAccessor
|
|||
return request.getServletPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestPathInfo(HttpServletRequest request) {
|
||||
return request.getPathInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getRequestUserPrincipal(HttpServletRequest request) {
|
||||
return request.getUserPrincipal();
|
||||
|
|
|
@ -5,25 +5,24 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.servlet.jakarta.v5_0;
|
||||
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
|
||||
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.TextMapGetter;
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
|
||||
import io.opentelemetry.instrumentation.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.ServletHttpServerTracer;
|
||||
import io.opentelemetry.instrumentation.servlet.naming.ServletSpanNameProvider;
|
||||
import jakarta.servlet.RequestDispatcher;
|
||||
import jakarta.servlet.Servlet;
|
||||
import jakarta.servlet.ServletConfig;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Collection;
|
||||
|
||||
public class JakartaServletHttpServerTracer
|
||||
extends ServletHttpServerTracer<HttpServletRequest, HttpServletResponse> {
|
||||
private static final JakartaServletHttpServerTracer TRACER = new JakartaServletHttpServerTracer();
|
||||
private static final ServletSpanNameProvider<HttpServletRequest> SPAN_NAME_PROVIDER =
|
||||
new ServletSpanNameProvider<>(JakartaServletAccessor.INSTANCE);
|
||||
|
||||
public JakartaServletHttpServerTracer() {
|
||||
super(JakartaServletAccessor.INSTANCE);
|
||||
|
@ -33,89 +32,20 @@ public class JakartaServletHttpServerTracer
|
|||
return TRACER;
|
||||
}
|
||||
|
||||
public Context startSpan(Object servletOrFilter, HttpServletRequest request) {
|
||||
return startSpan(
|
||||
request, getSpanName(servletOrFilter, request), servletOrFilter instanceof Servlet);
|
||||
}
|
||||
|
||||
private String getSpanName(Object servletOrFilter, HttpServletRequest request) {
|
||||
String spanName = getSpanNameFromPath(servletOrFilter, request);
|
||||
if (spanName == null) {
|
||||
String contextPath = request.getContextPath();
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return "HTTP " + request.getMethod();
|
||||
}
|
||||
return contextPath;
|
||||
}
|
||||
return spanName;
|
||||
}
|
||||
|
||||
private static String getSpanNameFromPath(Object servletOrFilter, HttpServletRequest request) {
|
||||
// we are only interested in Servlets
|
||||
if (!(servletOrFilter instanceof Servlet)) {
|
||||
return null;
|
||||
}
|
||||
Servlet servlet = (Servlet) servletOrFilter;
|
||||
|
||||
String mapping = getMapping(servlet, request.getServletPath(), request.getPathInfo());
|
||||
// mapping was not found
|
||||
if (mapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prepend context path
|
||||
String contextPath = request.getContextPath();
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return mapping;
|
||||
}
|
||||
return contextPath + mapping;
|
||||
}
|
||||
|
||||
private static String getMapping(Servlet servlet, String servletPath, String pathInfo) {
|
||||
ServletConfig servletConfig = servlet.getServletConfig();
|
||||
if (servletConfig == null) {
|
||||
return null;
|
||||
}
|
||||
String servletName = servletConfig.getServletName();
|
||||
ServletContext servletContext = servletConfig.getServletContext();
|
||||
MappingResolver mappingResolver = getMappingResolver(servletContext, servletName);
|
||||
if (mappingResolver == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mappingResolver.resolve(servletPath, pathInfo);
|
||||
}
|
||||
|
||||
private static MappingResolver getMappingResolver(
|
||||
ServletContext servletContext, String servletName) {
|
||||
if (servletContext == null || servletName == null) {
|
||||
return null;
|
||||
}
|
||||
String key = MappingResolver.class.getName() + "." + servletName;
|
||||
MappingResolver mappingResolver = (MappingResolver) servletContext.getAttribute(key);
|
||||
if (mappingResolver != null) {
|
||||
return mappingResolver;
|
||||
}
|
||||
|
||||
ServletRegistration servletRegistration = servletContext.getServletRegistration(servletName);
|
||||
if (servletRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
Collection<String> mappings = servletRegistration.getMappings();
|
||||
if (mappings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
mappingResolver = MappingResolver.build(mappings);
|
||||
servletContext.setAttribute(key, mappingResolver);
|
||||
|
||||
return mappingResolver;
|
||||
public Context startSpan(
|
||||
HttpServletRequest request, MappingResolver mappingResolver, boolean servlet) {
|
||||
return startSpan(request, SPAN_NAME_PROVIDER.getSpanName(mappingResolver, request), servlet);
|
||||
}
|
||||
|
||||
public Context updateContext(
|
||||
Context context, Object servletOrFilter, HttpServletRequest request) {
|
||||
Context context,
|
||||
HttpServletRequest request,
|
||||
MappingResolver mappingResolver,
|
||||
boolean servlet) {
|
||||
ServerSpanNaming.updateServerSpanName(
|
||||
context, SERVLET, () -> getSpanNameFromPath(servletOrFilter, request));
|
||||
context,
|
||||
servlet ? SERVLET : FILTER,
|
||||
() -> SPAN_NAME_PROVIDER.getSpanNameOrNull(mappingResolver, request));
|
||||
return updateContext(context, request);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@ package io.opentelemetry.javaagent.instrumentation.servlet.common.service;
|
|||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
|
||||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
|
@ -22,10 +22,22 @@ import net.bytebuddy.matcher.ElementMatcher;
|
|||
public class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
||||
private final String basePackageName;
|
||||
private final String adviceClassName;
|
||||
private final String servletInitAdviceClassName;
|
||||
private final String filterInitAdviceClassName;
|
||||
|
||||
public ServletAndFilterInstrumentation(String basePackageName, String adviceClassName) {
|
||||
public ServletAndFilterInstrumentation(
|
||||
String basePackageName,
|
||||
String adviceClassName,
|
||||
String servletInitAdviceClassName,
|
||||
String filterInitAdviceClassName) {
|
||||
this.basePackageName = basePackageName;
|
||||
this.adviceClassName = adviceClassName;
|
||||
this.servletInitAdviceClassName = servletInitAdviceClassName;
|
||||
this.filterInitAdviceClassName = filterInitAdviceClassName;
|
||||
}
|
||||
|
||||
public ServletAndFilterInstrumentation(String basePackageName, String adviceClassName) {
|
||||
this(basePackageName, adviceClassName, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,11 +52,23 @@ public class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
|||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
Map<ElementMatcher<MethodDescription>, String> transformers = new HashMap<>();
|
||||
transformers.put(
|
||||
namedOneOf("doFilter", "service")
|
||||
.and(takesArgument(0, named(basePackageName + ".ServletRequest")))
|
||||
.and(takesArgument(1, named(basePackageName + ".ServletResponse")))
|
||||
.and(isPublic()),
|
||||
adviceClassName);
|
||||
if (servletInitAdviceClassName != null) {
|
||||
transformers.put(
|
||||
named("init").and(takesArgument(0, named(basePackageName + ".ServletConfig"))),
|
||||
servletInitAdviceClassName);
|
||||
}
|
||||
if (filterInitAdviceClassName != null) {
|
||||
transformers.put(
|
||||
named("init").and(takesArgument(0, named(basePackageName + ".FilterConfig"))),
|
||||
filterInitAdviceClassName);
|
||||
}
|
||||
return transformers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ package io.opentelemetry.instrumentation.servlet;
|
|||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* This interface is used to access methods of HttpServletRequest and HttpServletResponse classes in
|
||||
* shared code that is used for both jakarta.servlet and javax.servlet versions of those classes. A
|
||||
* wrapper class with extra information attached may be used as well in cases where the class itself
|
||||
* does not provide some field (such as response status for Servlet API 2.2).
|
||||
* This interface is used to access methods of ServletContext, HttpServletRequest and
|
||||
* HttpServletResponse classes in shared code that is used for both jakarta.servlet and
|
||||
* javax.servlet versions of those classes. A wrapper class with extra information attached may be
|
||||
* used as well in cases where the class itself does not provide some field (such as response status
|
||||
* for Servlet API 2.2).
|
||||
*
|
||||
* @param <REQUEST> HttpServletRequest class (or a wrapper)
|
||||
* @param <RESPONSE> HttpServletResponse class (or a wrapper)
|
||||
|
@ -43,6 +44,8 @@ public interface ServletAccessor<REQUEST, RESPONSE> {
|
|||
|
||||
String getRequestServletPath(REQUEST request);
|
||||
|
||||
String getRequestPathInfo(REQUEST request);
|
||||
|
||||
Principal getRequestUserPrincipal(REQUEST request);
|
||||
|
||||
Integer getRequestRemotePort(REQUEST request);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package io.opentelemetry.instrumentation.servlet;
|
||||
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTAINER;
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.FILTER;
|
||||
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.SERVLET;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
|
@ -48,11 +49,10 @@ public abstract class ServletHttpServerTracer<REQUEST, RESPONSE>
|
|||
accessor.setRequestAttribute(request, "trace_id", spanContext.getTraceId());
|
||||
accessor.setRequestAttribute(request, "span_id", spanContext.getSpanId());
|
||||
|
||||
if (servlet) {
|
||||
// server span name shouldn't be updated when server span was created from a call to Servlet
|
||||
// (if created from a call to Filter then name may be updated from updateContext)
|
||||
ServerSpanNaming.updateSource(context, SERVLET);
|
||||
}
|
||||
// server span name shouldn't be updated when server span was created from a call to Servlet
|
||||
// (if created from a call to Filter then name may be updated from updateContext)
|
||||
ServerSpanNaming.updateSource(context, servlet ? SERVLET : FILTER);
|
||||
|
||||
return addServletContextPath(context, request);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.servlet.naming;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class ServletFilterMappingResolverFactory<FILTERREGISTRATION> {
|
||||
|
||||
protected abstract FILTERREGISTRATION getFilterRegistration();
|
||||
|
||||
protected abstract Collection<String> getUrlPatternMappings(
|
||||
FILTERREGISTRATION filterRegistration);
|
||||
|
||||
protected abstract Collection<String> getServletNameMappings(
|
||||
FILTERREGISTRATION filterRegistration);
|
||||
|
||||
protected abstract Collection<String> getServletMappings(String servletName);
|
||||
|
||||
private Collection<String> getMappings() {
|
||||
FILTERREGISTRATION filterRegistration = getFilterRegistration();
|
||||
if (filterRegistration == null) {
|
||||
return null;
|
||||
}
|
||||
Set<String> mappings = new HashSet<>();
|
||||
Collection<String> urlPatternMappings = getUrlPatternMappings(filterRegistration);
|
||||
if (urlPatternMappings != null) {
|
||||
mappings.addAll(urlPatternMappings);
|
||||
}
|
||||
Collection<String> servletNameMappings = getServletNameMappings(filterRegistration);
|
||||
if (servletNameMappings != null) {
|
||||
for (String servletName : servletNameMappings) {
|
||||
Collection<String> servletMappings = getServletMappings(servletName);
|
||||
if (servletMappings != null) {
|
||||
mappings.addAll(servletMappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mappings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> mappingsList = new ArrayList<>(mappings);
|
||||
// sort longest mapping first
|
||||
Collections.sort(mappingsList, (s1, s2) -> s2.length() - s1.length());
|
||||
|
||||
return mappingsList;
|
||||
}
|
||||
|
||||
public final MappingResolver create() {
|
||||
Collection<String> mappings = getMappings();
|
||||
if (mappings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MappingResolver.build(mappings);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.servlet.naming;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import java.util.Collection;
|
||||
|
||||
public abstract class ServletMappingResolverFactory {
|
||||
|
||||
protected abstract Collection<String> getMappings();
|
||||
|
||||
public final MappingResolver create() {
|
||||
Collection<String> mappings = getMappings();
|
||||
if (mappings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MappingResolver.build(mappings);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.servlet.naming;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.servlet.MappingResolver;
|
||||
import io.opentelemetry.instrumentation.servlet.ServletAccessor;
|
||||
|
||||
/** Helper class for constructing span name for given servlet/filter mapping and request. */
|
||||
public class ServletSpanNameProvider<REQUEST> {
|
||||
private final ServletAccessor<REQUEST, ?> servletAccessor;
|
||||
|
||||
public ServletSpanNameProvider(ServletAccessor<REQUEST, ?> servletAccessor) {
|
||||
this.servletAccessor = servletAccessor;
|
||||
}
|
||||
|
||||
public String getSpanName(MappingResolver mappingResolver, REQUEST request) {
|
||||
String spanName = getSpanNameOrNull(mappingResolver, request);
|
||||
if (spanName == null) {
|
||||
String contextPath = servletAccessor.getRequestContextPath(request);
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return "HTTP " + servletAccessor.getRequestMethod(request);
|
||||
}
|
||||
return contextPath + "/*";
|
||||
}
|
||||
return spanName;
|
||||
}
|
||||
|
||||
public String getSpanNameOrNull(MappingResolver mappingResolver, REQUEST request) {
|
||||
if (mappingResolver == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String servletPath = servletAccessor.getRequestServletPath(request);
|
||||
String pathInfo = servletAccessor.getRequestPathInfo(request);
|
||||
String mapping = mappingResolver.resolve(servletPath, pathInfo);
|
||||
// mapping was not found
|
||||
if (mapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prepend context path
|
||||
String contextPath = servletAccessor.getRequestContextPath(request);
|
||||
if (contextPath == null || contextPath.isEmpty() || contextPath.equals("/")) {
|
||||
return mapping;
|
||||
}
|
||||
return contextPath + mapping;
|
||||
}
|
||||
}
|
|
@ -76,6 +76,11 @@ public abstract class JavaxServletAccessor<R> implements ServletAccessor<HttpSer
|
|||
return request.getServletPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestPathInfo(HttpServletRequest request) {
|
||||
return request.getPathInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getRequestUserPrincipal(HttpServletRequest request) {
|
||||
return request.getUserPrincipal();
|
||||
|
|
|
@ -87,7 +87,7 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
|
|||
case NOT_FOUND:
|
||||
return getContextPath() + "/**"
|
||||
case LOGIN:
|
||||
return "HTTP POST"
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return super.expectedServerSpanName(endpoint)
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class Struts2ActionSpanTest extends HttpServerTest<Server> implements AgentTestT
|
|||
case PATH_PARAM:
|
||||
return getContextPath() + "/path/{id}/param"
|
||||
case NOT_FOUND:
|
||||
return "HTTP GET"
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return endpoint.resolvePath(address).path
|
||||
}
|
||||
|
|
|
@ -16,4 +16,13 @@ class LibertyServletOnlySmokeTest extends LibertySmokeTest {
|
|||
return ["liberty-servlet.xml": "/config/server.xml"]
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSpanName(String path) {
|
||||
switch (path) {
|
||||
case "/app/hello.txt":
|
||||
case "/app/file-that-does-not-exist":
|
||||
return "HTTP GET"
|
||||
}
|
||||
return super.getSpanName(path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,4 @@ class LibertySmokeTest extends AppServerTest {
|
|||
protected TargetWaitStrategy getWaitStrategy() {
|
||||
return new TargetWaitStrategy.Log(Duration.ofMinutes(3), ".*server is ready to run a smarter planet.*")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSpanName(String path) {
|
||||
switch (path) {
|
||||
case "/app/hello.txt":
|
||||
case "/app/file-that-does-not-exist":
|
||||
return "HTTP GET"
|
||||
}
|
||||
return super.getSpanName(path)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue