Remove disabled by default servlet instrumentation (#1684)
This commit is contained in:
parent
d183692699
commit
e092e87d6b
|
@ -283,7 +283,6 @@ Because the automatic instrumentation runs in a different classpath than the ins
|
||||||
Some instrumentations can produce too many spans and make traces very noisy.
|
Some instrumentations can produce too many spans and make traces very noisy.
|
||||||
For this reason, the following instrumentations are disabled by default:
|
For this reason, the following instrumentations are disabled by default:
|
||||||
- `jdbc-datasource` which creates spans whenever the `java.sql.DataSource#getConnection` method is called.
|
- `jdbc-datasource` which creates spans whenever the `java.sql.DataSource#getConnection` method is called.
|
||||||
- `servlet-filter` which creates spans around Servlet Filter methods.
|
|
||||||
- `servlet-service` which creates spans around Servlet methods.
|
- `servlet-service` which creates spans around Servlet methods.
|
||||||
|
|
||||||
To enable them, add the `otel.instrumentation.<name>.enabled` system property:
|
To enable them, add the `otel.instrumentation.<name>.enabled` system property:
|
||||||
|
|
|
@ -30,7 +30,7 @@ at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServ
|
||||||
<b>at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)</b>
|
<b>at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)</b>
|
||||||
<b>at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)</b>
|
<b>at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)</b>
|
||||||
...
|
...
|
||||||
<b>at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)</b>
|
<b>at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)</b>
|
||||||
...
|
...
|
||||||
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
|
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
|
||||||
|
@ -40,40 +40,23 @@ at java.base/java.lang.Thread.run(Thread.java:834)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Everything starts when HTTP request processing reaches the first class from Servlet specification.
|
Everything starts when HTTP request processing reaches the first class from Servlet specification.
|
||||||
In the example above this is `ApplicationFilterChain.doFilter(ServletRequest, ServletResponse)` method.
|
In the example above this is the
|
||||||
|
`OncePerRequestFilter.doFilter(ServletRequest, ServletResponse, FilterChain)` method.
|
||||||
Let us call this first servlet specific method an "entry point".
|
Let us call this first servlet specific method an "entry point".
|
||||||
This is the main target for `Servlet3Instrumentation` and `Servlet2Instrumentation` instruments:
|
This is the main target for `Servlet3Instrumentation` and `Servlet2Instrumentation`:
|
||||||
|
|
||||||
`public void javax.servlet.FilterChain#doFilter(ServletRequest, ServletResponse)`
|
`public void javax.servlet.Filter#doFilter(ServletRequest, ServletResponse, FilterChain)`
|
||||||
|
|
||||||
`public void javax.servlet.http.HttpServlet#service(ServletRequest, ServletResponse)`.
|
`public void javax.servlet.http.HttpServlet#service(ServletRequest, ServletResponse)`.
|
||||||
|
|
||||||
For example, Jetty Servlet container does not have default filter chain and in many cases will have
|
These instrumentations are located in two separate submodules `servlet-3.0` and `servlet-2.2`,
|
||||||
the second method as instrumentation entry point.
|
because they and corresponding tests depend on different versions of the servlet specification.
|
||||||
These instrumentations are located in two separate submodules `request-3.0` and `request-2.3`, respectively,
|
|
||||||
because they and corresponding tests depend on different versions of servlet specification.
|
|
||||||
|
|
||||||
Next, request passes several other methods from Servlet specification, such as
|
|
||||||
|
|
||||||
`protected void javax.servlet.http.HttpServlet#service(HttpServletRequest, HttpServletResponse)` or
|
|
||||||
|
|
||||||
`protected void org.springframework.web.servlet.FrameworkServlet#doGet(HttpServletRequest, HttpServletResponse)`.
|
|
||||||
|
|
||||||
They are the targets for `HttpServletInstrumentationModule`.
|
|
||||||
From the observability point of view nothing of interest usually happens inside these methods.
|
|
||||||
Thus it usually does not make sense to create spans from them, as they would only add useless noise.
|
|
||||||
For this reason `HttpServletInstrumentationModule` is disabled by default.
|
|
||||||
In rare cases when you need it, you can enable it using configuration property `otel.instrumentation.servlet-service.enabled`.
|
|
||||||
|
|
||||||
In exactly the same situation are all other Servlet filters beyond the initial entry point.
|
|
||||||
Usually unimportant, they may be sometimes of interest during troubleshooting.
|
|
||||||
They are instrumented by `FilterInstrumentationModule` which is also disabled by default.
|
|
||||||
You can enable it with the configuration property `otel.instrumentation.servlet-filter.enabled`.
|
|
||||||
At last, request processing may reach the specific framework that your application uses.
|
At last, request processing may reach the specific framework that your application uses.
|
||||||
In this case Spring MVC and `OwnerController.initCreationForm`.
|
In this case Spring MVC and `OwnerController.initCreationForm`.
|
||||||
|
|
||||||
If all instrumentations are enabled, then a new span will be created for every highlighted frame.
|
If all instrumentations are enabled, then a new span will be created for every highlighted frame.
|
||||||
All spans from Servlet API will have `kind=SERVER` and name based on corresponding class ana method names,
|
All spans from Servlet API will have `kind=SERVER` and name based on corresponding class and method names,
|
||||||
such as `ApplicationFilterChain.doFilter` or `FrameworkServlet.doGet`.
|
such as `ApplicationFilterChain.doFilter` or `FrameworkServlet.doGet`.
|
||||||
Span created by Spring MVC instrumentation will have `kind=INTERNAL` and named `OwnerController.initCreationForm`.
|
Span created by Spring MVC instrumentation will have `kind=INTERNAL` and named `OwnerController.initCreationForm`.
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,6 @@ final class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
||||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Here we are instrumenting the public method for HttpServlet. This should ensure that this
|
|
||||||
* advice is always called before HttpServletInstrumentation which is instrumenting the protected
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
return singletonMap(
|
return singletonMap(
|
||||||
|
|
|
@ -23,7 +23,7 @@ final class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
// Optimization for expensive typeMatcher.
|
// Optimization for expensive typeMatcher.
|
||||||
return hasClassesNamed("javax.servlet.http.HttpServlet");
|
return hasClassesNamed("javax.servlet.Filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,11 +31,6 @@ final class ServletAndFilterInstrumentation implements TypeInstrumentation {
|
||||||
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
return safeHasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.http.HttpServlet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Here we are instrumenting the public method for HttpServlet. This should ensure that this
|
|
||||||
* advice is always called before HttpServletInstrumentation which is instrumenting the protected
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
return singletonMap(
|
return singletonMap(
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.servlet.filter;
|
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.servlet.filter.FilterTracer.tracer;
|
|
||||||
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
|
|
||||||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
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 com.google.auto.service.AutoService;
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.opentelemetry.api.trace.Span.Kind;
|
|
||||||
import io.opentelemetry.context.Scope;
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
|
||||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
|
||||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.servlet.Filter;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instruments all filter invocations in filter chain.
|
|
||||||
*
|
|
||||||
* <p>See README.md for more information about different servlet instrumentations.
|
|
||||||
*/
|
|
||||||
@AutoService(InstrumentationModule.class)
|
|
||||||
public final class FilterInstrumentationModule extends InstrumentationModule {
|
|
||||||
public FilterInstrumentationModule() {
|
|
||||||
super("servlet-filter");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean defaultEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] helperClassNames() {
|
|
||||||
return new String[] {
|
|
||||||
packageName + ".FilterTracer",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
|
||||||
return singletonList(new FilterInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class FilterInstrumentation implements TypeInstrumentation {
|
|
||||||
@Override
|
|
||||||
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
|
||||||
// Optimization for expensive typeMatcher.
|
|
||||||
return hasClassesNamed("javax.servlet.Filter");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
|
||||||
return implementsInterface(named("javax.servlet.Filter"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
|
||||||
return singletonMap(
|
|
||||||
named("doFilter")
|
|
||||||
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
|
|
||||||
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
|
|
||||||
.and(isPublic()),
|
|
||||||
FilterInstrumentationModule.class.getName() + "$FilterAdvice");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FilterAdvice {
|
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
|
||||||
public static void start(
|
|
||||||
@Advice.This Filter filter,
|
|
||||||
@Advice.Local("otelSpan") Span span,
|
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
|
||||||
if (!Java8BytecodeBridge.currentSpan().getSpanContext().isValid()) {
|
|
||||||
// Don't want to generate a new top-level span
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we use "this" instead of "the method target" to distinguish abstract filter instances.
|
|
||||||
span = tracer().startSpan(filter.getClass().getSimpleName() + ".doFilter", Kind.INTERNAL);
|
|
||||||
scope = tracer().startScope(span);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
|
||||||
public static void stopSpan(
|
|
||||||
@Advice.Thrown Throwable throwable,
|
|
||||||
@Advice.Local("otelSpan") Span span,
|
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
|
||||||
if (scope == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (throwable != null) {
|
|
||||||
tracer().endExceptionally(span, throwable);
|
|
||||||
} else {
|
|
||||||
tracer().end(span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.servlet.filter;
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
|
||||||
|
|
||||||
public class FilterTracer extends BaseTracer {
|
|
||||||
private static final FilterTracer TRACER = new FilterTracer();
|
|
||||||
|
|
||||||
public static FilterTracer tracer() {
|
|
||||||
return TRACER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getInstrumentationName() {
|
|
||||||
return "io.opentelemetry.javaagent.servlet";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.servlet.http;
|
|
||||||
|
|
||||||
import static io.opentelemetry.javaagent.instrumentation.servlet.http.HttpServletTracer.tracer;
|
|
||||||
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
|
|
||||||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.extendsClass;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.opentelemetry.context.Scope;
|
|
||||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
|
||||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
|
||||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
|
||||||
|
|
||||||
// Please read README.md of this subproject to understand what this instrumentation is and why it
|
|
||||||
// requires a separate module.
|
|
||||||
@AutoService(InstrumentationModule.class)
|
|
||||||
public final class HttpServletInstrumentationModule extends InstrumentationModule {
|
|
||||||
public HttpServletInstrumentationModule() {
|
|
||||||
super("servlet-service");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean defaultEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] helperClassNames() {
|
|
||||||
return new String[] {
|
|
||||||
packageName + ".HttpServletTracer",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
|
||||||
return singletonList(new HttpServletInstrumentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class HttpServletInstrumentation implements TypeInstrumentation {
|
|
||||||
@Override
|
|
||||||
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
|
||||||
// Optimization for expensive typeMatcher.
|
|
||||||
return hasClassesNamed("javax.servlet.http.HttpServlet");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
|
||||||
return extendsClass(named("javax.servlet.http.HttpServlet"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Here we are instrumenting the protected method for HttpServlet. This should ensure that this
|
|
||||||
* advice is always called after Servlet3Instrumentation which is instrumenting the public
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
|
||||||
return singletonMap(
|
|
||||||
named("service")
|
|
||||||
.or(nameStartsWith("do")) // doGet, doPost, etc
|
|
||||||
.and(takesArgument(0, named("javax.servlet.http.HttpServletRequest")))
|
|
||||||
.and(takesArgument(1, named("javax.servlet.http.HttpServletResponse")))
|
|
||||||
.and(isProtected().or(isPublic())),
|
|
||||||
HttpServletInstrumentationModule.class.getName() + "$HttpServletAdvice");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HttpServletAdvice {
|
|
||||||
|
|
||||||
@Advice.OnMethodEnter(suppress = Throwable.class)
|
|
||||||
public static void start(
|
|
||||||
@Advice.Origin Method method,
|
|
||||||
@Advice.Local("otelSpan") Span span,
|
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
|
||||||
|
|
||||||
if (!Java8BytecodeBridge.currentSpan().getSpanContext().isValid()) {
|
|
||||||
// Don't want to generate a new top-level span
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
span = tracer().startSpan(method);
|
|
||||||
scope = tracer().startScope(span);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
|
||||||
public static void stopSpan(
|
|
||||||
@Advice.Thrown Throwable throwable,
|
|
||||||
@Advice.Local("otelSpan") Span span,
|
|
||||||
@Advice.Local("otelScope") Scope scope) {
|
|
||||||
if (scope == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
scope.close();
|
|
||||||
|
|
||||||
if (throwable != null) {
|
|
||||||
tracer().endExceptionally(span, throwable);
|
|
||||||
} else {
|
|
||||||
tracer().end(span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.servlet.http;
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
|
||||||
|
|
||||||
public class HttpServletTracer extends BaseTracer {
|
|
||||||
private static final HttpServletTracer TRACER = new HttpServletTracer();
|
|
||||||
|
|
||||||
public static HttpServletTracer tracer() {
|
|
||||||
return TRACER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getInstrumentationName() {
|
|
||||||
return "io.opentelemetry.javaagent.servlet";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
|
||||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
|
||||||
import io.opentelemetry.instrumentation.test.utils.ConfigUtils
|
|
||||||
import javax.servlet.Filter
|
|
||||||
import javax.servlet.FilterChain
|
|
||||||
import javax.servlet.FilterConfig
|
|
||||||
import javax.servlet.ServletException
|
|
||||||
import javax.servlet.ServletRequest
|
|
||||||
import javax.servlet.ServletResponse
|
|
||||||
|
|
||||||
class FilterTest extends AgentTestRunner {
|
|
||||||
static final PREVIOUS_CONFIG = ConfigUtils.updateConfigAndResetInstrumentation {
|
|
||||||
it.setProperty("otel.instrumentation.servlet-filter.enabled", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
ConfigUtils.setConfig(PREVIOUS_CONFIG)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test doFilter no-parent"() {
|
|
||||||
when:
|
|
||||||
filter.doFilter(null, null, null)
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(0) {}
|
|
||||||
|
|
||||||
where:
|
|
||||||
filter = new TestFilter()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test doFilter with parent"() {
|
|
||||||
when:
|
|
||||||
runUnderTrace("parent") {
|
|
||||||
filter.doFilter(null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
basicSpan(it, 0, "parent")
|
|
||||||
span(1) {
|
|
||||||
name "${filter.class.simpleName}.doFilter"
|
|
||||||
childOf span(0)
|
|
||||||
attributes {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
where:
|
|
||||||
filter << [new TestFilter(), new TestFilter() {}]
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test doFilter exception"() {
|
|
||||||
setup:
|
|
||||||
def ex = new Exception("some error")
|
|
||||||
def filter = new TestFilter() {
|
|
||||||
@Override
|
|
||||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when:
|
|
||||||
runUnderTrace("parent") {
|
|
||||||
filter.doFilter(null, null, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
def th = thrown(Exception)
|
|
||||||
th == ex
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
basicSpan(it, 0, "parent", null, ex)
|
|
||||||
span(1) {
|
|
||||||
name "${filter.class.simpleName}.doFilter"
|
|
||||||
childOf span(0)
|
|
||||||
errored true
|
|
||||||
errorEvent(ex.class, ex.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TestFilter implements Filter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void init(FilterConfig filterConfig) throws ServletException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void destroy() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
|
||||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
|
||||||
|
|
||||||
import groovy.servlet.AbstractHttpServlet
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestRunner
|
|
||||||
import io.opentelemetry.instrumentation.test.utils.ConfigUtils
|
|
||||||
import javax.servlet.http.HttpServletRequest
|
|
||||||
import javax.servlet.http.HttpServletResponse
|
|
||||||
|
|
||||||
class HttpServletTest extends AgentTestRunner {
|
|
||||||
static final PREVIOUS_CONFIG = ConfigUtils.updateConfigAndResetInstrumentation {
|
|
||||||
it.setProperty("otel.instrumentation.servlet-service.enabled", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
def specCleanup() {
|
|
||||||
ConfigUtils.setConfig(PREVIOUS_CONFIG)
|
|
||||||
}
|
|
||||||
|
|
||||||
def req = Mock(HttpServletRequest) {
|
|
||||||
getMethod() >> "GET"
|
|
||||||
getProtocol() >> "TEST"
|
|
||||||
}
|
|
||||||
def resp = Mock(HttpServletResponse)
|
|
||||||
|
|
||||||
def "test service no-parent"() {
|
|
||||||
when:
|
|
||||||
servlet.service(req, resp)
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(0) {}
|
|
||||||
|
|
||||||
where:
|
|
||||||
servlet = new TestServlet()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test service with parent"() {
|
|
||||||
when:
|
|
||||||
runUnderTrace("parent") {
|
|
||||||
servlet.service(req, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 3) {
|
|
||||||
basicSpan(it, 0, "parent")
|
|
||||||
span(1) {
|
|
||||||
name "HttpServlet.service"
|
|
||||||
childOf span(0)
|
|
||||||
attributes {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "${expectedSpanName}.doGet"
|
|
||||||
childOf span(1)
|
|
||||||
attributes {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
where:
|
|
||||||
servlet << [new TestServlet(), new TestServlet() {
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
|
|
||||||
expectedSpanName = servlet.class.anonymousClass ? servlet.class.name : servlet.class.simpleName
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test service exception"() {
|
|
||||||
setup:
|
|
||||||
def ex = new Exception("some error")
|
|
||||||
def servlet = new TestServlet() {
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when:
|
|
||||||
runUnderTrace("parent") {
|
|
||||||
servlet.service(req, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
def th = thrown(Exception)
|
|
||||||
th == ex
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 3) {
|
|
||||||
basicSpan(it, 0, "parent", null, ex)
|
|
||||||
span(1) {
|
|
||||||
name "HttpServlet.service"
|
|
||||||
childOf span(0)
|
|
||||||
errored true
|
|
||||||
errorEvent(ex.class, ex.message)
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "${servlet.class.name}.doGet"
|
|
||||||
childOf span(1)
|
|
||||||
errored true
|
|
||||||
errorEvent(ex.class, ex.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TestServlet extends AbstractHttpServlet {
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue