Grizzly: add exception to span
MODIFY
instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyInstrumentationModule.java
instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/GrizzlyInstrumentationModule.java
@@ -24,7 +24,8 @@ public class GrizzlyInstrumentationModule extends InstrumentationModule {
new DefaultFilterChainInstrumentation(),
new FilterInstrumentation(),
new HttpCodecFilterInstrumentation(),
- new HttpServerFilterInstrumentation());
+ new HttpServerFilterInstrumentation(),
+ new HttpHandlerInstrumentation());
}
@Override

ADD
instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/HttpHandlerAdvice.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.grizzly;
+
+import static io.opentelemetry.javaagent.instrumentation.grizzly.GrizzlyHttpServerTracer.tracer;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
+import net.bytebuddy.asm.Advice;
+
+public class HttpHandlerAdvice {
+
+ @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+ public static void onExit(@Advice.Thrown Throwable throwable) {
+ Span span = Java8BytecodeBridge.currentSpan();
+ if (span.getSpanContext().isValid() && throwable != null) {
+ tracer().addThrowable(span, throwable);
+ }
+ }
+}

ADD
instrumentation/grizzly-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grizzly/HttpHandlerInstrumentation.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.grizzly;
+
+import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
+import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
+import static net.bytebuddy.matcher.ElementMatchers.isMethod;
+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.Collections;
+import java.util.Map;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+public class HttpHandlerInstrumentation implements TypeInstrumentation {
+
+ @Override
+ public ElementMatcher<ClassLoader> classLoaderOptimization() {
+ return hasClassesNamed("org.glassfish.grizzly.http.server.HttpHandler");
+ }
+
+ @Override
+ public ElementMatcher<? super TypeDescription> typeMatcher() {
+ return safeHasSuperType(named("org.glassfish.grizzly.http.server.HttpHandler"));
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return Collections.singletonMap(
+ isMethod()
+ .and(isPublic())
+ .and(named("service"))
+ .and(takesArgument(0, named("org.glassfish.grizzly.http.server.Request")))
+ .and(takesArgument(1, named("org.glassfish.grizzly.http.server.Response"))),
+ HttpHandlerAdvice.class.getName());
+ }
+}

MODIFY
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyAsyncTest.groovy
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyAsyncTest.groovy
@@ -31,6 +31,12 @@ class GrizzlyAsyncTest extends GrizzlyTest {
GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
}
+ @Override
+ boolean testException() {
+ // justification: exception is handled by jersey
+ false
+ }
+
@Path("/")
static class AsyncServiceResource {
private ExecutorService executor = Executors.newSingleThreadExecutor()

MODIFY
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyIOStrategyTest.groovy
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyIOStrategyTest.groovy
@@ -17,9 +17,15 @@ abstract class GrizzlyIOStrategyTest extends GrizzlyTest {
ResourceConfig rc = new ResourceConfig()
rc.register(SimpleExceptionMapper)
rc.register(ServiceResource)
- def server = GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
+
+ def server = GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc, false)
+ // Default in NIOTransportBuilder is WorkerThreadIOStrategy, so don't need to retest that.
server.getListener("grizzly").getTransport().setIOStrategy(strategy())
- // Default in NIOTransportBuilder is WorkerThreadIOStrategy, so don't need to retest that.s
+ // jersey doesn't propagate exceptions up to the grizzly handler
+ // so we use a standalone HttpHandler to test exception capture
+ server.getServerConfiguration().addHttpHandler(new ExceptionHttpHandler(), "/exception")
+ server.start()
+
return server
}

MODIFY
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyTest.groovy
instrumentation/grizzly-2.0/javaagent/src/test/groovy/GrizzlyTest.groovy
@@ -16,7 +16,9 @@ import javax.ws.rs.Path
import javax.ws.rs.QueryParam
import javax.ws.rs.core.Response
import javax.ws.rs.ext.ExceptionMapper
+import org.glassfish.grizzly.http.server.HttpHandler
import org.glassfish.grizzly.http.server.HttpServer
+import org.glassfish.grizzly.http.server.Request
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
import org.glassfish.jersey.server.ResourceConfig
@@ -27,7 +29,14 @@ class GrizzlyTest extends HttpServerTest<HttpServer> {
ResourceConfig rc = new ResourceConfig()
rc.register(SimpleExceptionMapper)
rc.register(ServiceResource)
- GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc)
+
+ def server = GrizzlyHttpServerFactory.createHttpServer(new URI("http://localhost:$port"), rc, false)
+ // jersey doesn't propagate exceptions up to the grizzly handler
+ // so we use a standalone HttpHandler to test exception capture
+ server.getServerConfiguration().addHttpHandler(new ExceptionHttpHandler(), "/exception")
+ server.start()
+
+ return server
}
@Override
@@ -80,15 +89,6 @@ class GrizzlyTest extends HttpServerTest<HttpServer> {
Response.status(ERROR.status).entity(ERROR.body).build()
}
}
-
- @GET
- @Path("exception")
- Response exception() {
- controller(EXCEPTION) {
- throw new Exception(EXCEPTION.body)
- }
- return null
- }
}
@Override
@@ -96,9 +96,18 @@ class GrizzlyTest extends HttpServerTest<HttpServer> {
return "HttpCodecFilter.handleRead"
}
+ static class ExceptionHttpHandler extends HttpHandler {
+
+ @Override
+ void service(Request request, org.glassfish.grizzly.http.server.Response response) throws Exception {
+ controller(EXCEPTION) {
+ throw new Exception(EXCEPTION.body)
+ }
+ }
+ }
+
@Override
- boolean testException() {
- // TODO(anuraaga): https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/807
- return false
+ boolean testExceptionBody() {
+ false
}
}

Grizzly: add exception to span (#1987)