Skip to content

Commit

Permalink
Capture exception on finatra controller span (open-telemetry#4669)
Browse files Browse the repository at this point in the history
* Capture exception on finatra controller span

* add return type check

* store VirtualField in a static field
  • Loading branch information
laurit authored and RashmiRam committed May 23, 2022
1 parent ba8c751 commit 34793da
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
* SPDX-License-Identifier: Apache-2.0
*/

import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION

import com.twitter.app.lifecycle.Event
import com.twitter.app.lifecycle.Observer
import com.twitter.finatra.http.HttpServer
import com.twitter.util.Await
import com.twitter.util.Duration
import com.twitter.util.Promise
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
Expand Down Expand Up @@ -81,8 +84,10 @@ class FinatraServerLatestTest extends HttpServerTest<HttpServer> implements Agen
name "FinatraController"
kind INTERNAL
childOf(parent as SpanData)
// Finatra doesn't propagate the stack trace or exception to the instrumentation
// so the normal errorAttributes() method can't be used
if (endpoint == EXCEPTION) {
status StatusCode.ERROR
errorEvent(Exception, EXCEPTION.body)
}
attributes {
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.finatra;

import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.twitter.finagle.http.Response;
import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class FinatraExceptionManagerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.twitter.finatra.http.exceptions.ExceptionManager");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("toResponse"))
.and(takesArgument(1, Throwable.class))
.and(returns(named("com.twitter.finagle.http.Response"))),
this.getClass().getName() + "$HandleExceptionAdvice");
}

@SuppressWarnings("unused")
public static class HandleExceptionAdvice {

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleException(
@Advice.Return Response response, @Advice.Argument(1) Throwable throwable) {

if (throwable == null) {
return;
}

VirtualField<Response, Throwable> virtualField =
VirtualField.find(Response.class, Throwable.class);
virtualField.set(response, throwable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.finatra;

import static java.util.Collections.singletonList;
import static java.util.Arrays.asList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
Expand All @@ -20,6 +20,6 @@ public FinatraInstrumentationModule() {

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new FinatraRouteInstrumentation());
return asList(new FinatraRouteInstrumentation(), new FinatraExceptionManagerInstrumentation());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
import com.twitter.finagle.http.Response;
import com.twitter.util.FutureEventListener;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.field.VirtualField;

public final class FinatraResponseListener implements FutureEventListener<Response> {

private static final VirtualField<Response, Throwable> responseThrowableField =
VirtualField.find(Response.class, Throwable.class);

private final Context context;
private final Class<?> request;

Expand All @@ -23,7 +27,8 @@ public FinatraResponseListener(Context context, Class<?> request) {

@Override
public void onSuccess(Response response) {
instrumenter().end(context, request, null, null);
Throwable throwable = responseThrowableField.get(response);
instrumenter().end(context, request, null, throwable);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/

import static io.opentelemetry.instrumentation.test.base.HttpServerTest.ServerEndpoint.EXCEPTION

import com.twitter.finatra.http.HttpServer
import com.twitter.util.Await
import com.twitter.util.Duration
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
import io.opentelemetry.instrumentation.test.base.HttpServerTest
Expand Down Expand Up @@ -65,8 +68,10 @@ class FinatraServerTest extends HttpServerTest<HttpServer> implements AgentTestT
name "FinatraController"
kind INTERNAL
childOf(parent as SpanData)
// Finatra doesn't propagate the stack trace or exception to the instrumentation
// so the normal errorAttributes() method can't be used
if (endpoint == EXCEPTION) {
status StatusCode.ERROR
errorEvent(Exception, EXCEPTION.body)
}
attributes {
}
}
Expand Down

0 comments on commit 34793da

Please sign in to comment.