Wicket instrumentation (#2139)
* Wicket instrumentation * Change supported version to 8.0, turns out earlier versions didn't work
This commit is contained in:
parent
71ceb7486c
commit
02ca471577
|
@ -318,6 +318,7 @@ These are the supported libraries and frameworks:
|
||||||
| [Akka HTTP](https://doc.akka.io/docs/akka-http/current/index.html) | 10.0+ |
|
| [Akka HTTP](https://doc.akka.io/docs/akka-http/current/index.html) | 10.0+ |
|
||||||
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ |
|
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ |
|
||||||
| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ |
|
| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ |
|
||||||
|
| [Apache Wicket](https://wicket.apache.org/) | 8.0+ |
|
||||||
| [Armeria](https://armeria.dev) | 1.3+ |
|
| [Armeria](https://armeria.dev) | 1.3+ |
|
||||||
| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ (not including 2.x yet) |
|
| [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ (not including 2.x yet) |
|
||||||
| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ |
|
| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ |
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.wicket;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Collections;
|
||||||
|
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;
|
||||||
|
import org.apache.wicket.WicketRuntimeException;
|
||||||
|
|
||||||
|
public class DefaultExceptionMapperInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("org.apache.wicket.DefaultExceptionMapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return Collections.singletonMap(
|
||||||
|
named("mapUnexpectedExceptions").and(takesArgument(0, named(Exception.class.getName()))),
|
||||||
|
DefaultExceptionMapperInstrumentation.class.getName() + "$ExceptionAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExceptionAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.Argument(0) Exception exception) {
|
||||||
|
Span serverSpan = BaseTracer.getCurrentServerSpan();
|
||||||
|
if (serverSpan != null) {
|
||||||
|
// unwrap exception
|
||||||
|
Throwable throwable = exception;
|
||||||
|
while (throwable.getCause() != null
|
||||||
|
&& (throwable instanceof WicketRuntimeException
|
||||||
|
|| throwable instanceof InvocationTargetException)) {
|
||||||
|
throwable = throwable.getCause();
|
||||||
|
}
|
||||||
|
// as we don't create a span for wicket we record exception on server span
|
||||||
|
serverSpan.recordException(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.wicket;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.instrumentation.api.servlet.ServletContextPath;
|
||||||
|
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.util.Collections;
|
||||||
|
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;
|
||||||
|
import org.apache.wicket.core.request.handler.IPageClassRequestHandler;
|
||||||
|
import org.apache.wicket.request.IRequestHandler;
|
||||||
|
import org.apache.wicket.request.cycle.RequestCycle;
|
||||||
|
|
||||||
|
public class RequestHandlerExecutorInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
|
return named("org.apache.wicket.request.RequestHandlerExecutor");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return Collections.singletonMap(
|
||||||
|
named("execute").and(takesArgument(0, named("org.apache.wicket.request.IRequestHandler"))),
|
||||||
|
RequestHandlerExecutorInstrumentation.class.getName() + "$ExecuteAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExecuteAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.Argument(0) IRequestHandler handler) {
|
||||||
|
Span serverSpan = BaseTracer.getCurrentServerSpan();
|
||||||
|
if (serverSpan == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (handler instanceof IPageClassRequestHandler) {
|
||||||
|
// using class name as page name
|
||||||
|
String pageName = ((IPageClassRequestHandler) handler).getPageClass().getName();
|
||||||
|
// wicket filter mapping without wildcard, if wicket filter is mapped to /*
|
||||||
|
// this will be an empty string
|
||||||
|
String filterPath = RequestCycle.get().getRequest().getFilterPath();
|
||||||
|
serverSpan.updateName(
|
||||||
|
ServletContextPath.prepend(
|
||||||
|
Java8BytecodeBridge.currentContext(), filterPath + "/" + pageName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.wicket;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||||
|
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(InstrumentationModule.class)
|
||||||
|
public class WicketInstrumentationModule extends InstrumentationModule {
|
||||||
|
|
||||||
|
public WicketInstrumentationModule() {
|
||||||
|
super("wicket", "wicket-8.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||||
|
// missing before 8.0
|
||||||
|
return hasClassesNamed("org.apache.wicket.request.RequestHandlerExecutor");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new RequestHandlerExecutorInstrumentation(), new DefaultExceptionMapperInstrumentation());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
||||||
|
|
||||||
|
import hello.HelloApplication
|
||||||
|
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||||
|
import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait
|
||||||
|
import javax.servlet.DispatcherType
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.apache.wicket.protocol.http.WicketFilter
|
||||||
|
import org.eclipse.jetty.server.Server
|
||||||
|
import org.eclipse.jetty.servlet.DefaultServlet
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||||
|
import org.eclipse.jetty.util.resource.FileResource
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
|
class WicketTest extends AgentInstrumentationSpecification implements HttpServerTestTrait<Server> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Server startServer(int port) {
|
||||||
|
def server = new Server(port)
|
||||||
|
ServletContextHandler context = new ServletContextHandler(0)
|
||||||
|
context.setContextPath(getContextPath())
|
||||||
|
def resource = new FileResource(getClass().getResource("/"))
|
||||||
|
context.setBaseResource(resource)
|
||||||
|
server.setHandler(context)
|
||||||
|
|
||||||
|
context.addServlet(DefaultServlet, "/")
|
||||||
|
def registration = context.getServletContext().addFilter("WicketApplication", WicketFilter)
|
||||||
|
registration.setInitParameter("applicationClassName", HelloApplication.getName())
|
||||||
|
registration.setInitParameter("filterMappingUrlPattern", "/wicket-test/*")
|
||||||
|
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/wicket-test/*")
|
||||||
|
|
||||||
|
server.start()
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stopServer(Server server) {
|
||||||
|
server.stop()
|
||||||
|
server.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getContextPath() {
|
||||||
|
return "/jetty-context"
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hello"() {
|
||||||
|
setup:
|
||||||
|
def url = HttpUrl.get(address.resolve("wicket-test/")).newBuilder().build()
|
||||||
|
def request = request(url, "GET", null).build()
|
||||||
|
Response response = client.newCall(request).execute()
|
||||||
|
def doc = Jsoup.parse(response.body().string())
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.code() == 200
|
||||||
|
doc.selectFirst("#message").text() == "Hello World!"
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
basicSpan(it, 0, getContextPath() + "/wicket-test/hello.HelloPage")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test exception"() {
|
||||||
|
setup:
|
||||||
|
def url = HttpUrl.get(address.resolve("wicket-test/exception")).newBuilder().build()
|
||||||
|
def request = request(url, "GET", null).build()
|
||||||
|
Response response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
response.code() == 500
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
basicSpan(it, 0, getContextPath() + "/wicket-test/org.apache.wicket.markup.html.pages.InternalErrorPage", null, new Exception("test exception"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request.Builder request(HttpUrl url, String method, RequestBody body) {
|
||||||
|
return new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.method(method, body)
|
||||||
|
.header("User-Agent", TEST_USER_AGENT)
|
||||||
|
.header("X-Forwarded-For", TEST_CLIENT_IP)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hello
|
||||||
|
|
||||||
|
import org.apache.wicket.markup.html.WebPage
|
||||||
|
|
||||||
|
class ExceptionPage extends WebPage {
|
||||||
|
ExceptionPage() {
|
||||||
|
throw new Exception("test exception")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hello
|
||||||
|
|
||||||
|
import org.apache.wicket.Page
|
||||||
|
import org.apache.wicket.RuntimeConfigurationType
|
||||||
|
import org.apache.wicket.protocol.http.WebApplication
|
||||||
|
|
||||||
|
class HelloApplication extends WebApplication {
|
||||||
|
@Override
|
||||||
|
Class<? extends Page> getHomePage() {
|
||||||
|
HelloPage
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
mountPage("/exception", ExceptionPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
RuntimeConfigurationType getConfigurationType() {
|
||||||
|
return RuntimeConfigurationType.DEPLOYMENT
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hello
|
||||||
|
|
||||||
|
import org.apache.wicket.markup.html.WebPage
|
||||||
|
import org.apache.wicket.markup.html.basic.Label
|
||||||
|
|
||||||
|
class HelloPage extends WebPage {
|
||||||
|
HelloPage() {
|
||||||
|
add(new Label("message", "Hello World!"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
|
||||||
|
<body>
|
||||||
|
<span wicket:id="message" id="message">Message goes here</span>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,21 @@
|
||||||
|
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = 'org.apache.wicket'
|
||||||
|
module = 'wicket'
|
||||||
|
versions = "[8.0.0,]"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library group: 'org.apache.wicket', name: 'wicket', version: '8.0.0'
|
||||||
|
|
||||||
|
testImplementation(project(':testing-common'))
|
||||||
|
testImplementation group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
|
||||||
|
testImplementation group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
|
||||||
|
testImplementation group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '8.0.0.v20110901'
|
||||||
|
|
||||||
|
testInstrumentation project(":instrumentation:servlet:servlet-3.0:javaagent")
|
||||||
|
}
|
|
@ -213,6 +213,7 @@ include ':instrumentation:twilio-6.6:javaagent'
|
||||||
include ':instrumentation:undertow:javaagent'
|
include ':instrumentation:undertow:javaagent'
|
||||||
include ':instrumentation:vertx-web-3.0'
|
include ':instrumentation:vertx-web-3.0'
|
||||||
include ':instrumentation:vertx-reactive-3.5:javaagent'
|
include ':instrumentation:vertx-reactive-3.5:javaagent'
|
||||||
|
include ':instrumentation:wicket-8.0:javaagent'
|
||||||
|
|
||||||
include ':instrumentation-core:reactor-3.1'
|
include ':instrumentation-core:reactor-3.1'
|
||||||
include ':instrumentation-core:servlet-2.2'
|
include ':instrumentation-core:servlet-2.2'
|
||||||
|
|
Loading…
Reference in New Issue