From c535ab1e83789abe381f80d07ee9d9d3823c6740 Mon Sep 17 00:00:00 2001 From: Matt Whelan Date: Tue, 28 Jul 2020 16:05:36 -0700 Subject: [PATCH 1/3] New test, which fails Describes the issue though: AWS can't deserialize the event if it can't tell what the input type is at runtime. Erasure is preventing that. --- .../opentracing/aws/ReflectionTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java diff --git a/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java new file mode 100644 index 0000000..98863f1 --- /dev/null +++ b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java @@ -0,0 +1,44 @@ +package com.newrelic.opentracing.aws; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; + +public class ReflectionTest { + private static final int SYNTHETIC_MODIFIER = 0x1000; + + private Object handler; + + @Before + public void setup() { + handler = new TestHandler(); + } + + @Test + public void testInputReflection() { + // Ignoring synthetics, we expect handleRequest to take the declared type as its first arg. + // This is necessary for correct payload deserialization. + final Method handleRequest = Arrays.stream(handler.getClass().getMethods()) + .filter(m -> ((m.getModifiers() & SYNTHETIC_MODIFIER) == 0) + && m.getName().equals("handleRequest")) + .findFirst() + .orElseThrow(AssertionError::new); + + assertEquals(APIGatewayProxyRequestEvent.class, handleRequest.getParameterTypes()[0]); + } + + public static class TestHandler implements TracingRequestHandler { + @Override + public APIGatewayProxyResponseEvent doHandleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + return new APIGatewayProxyResponseEvent(); + } + } +} From 0cde22189006902685a0a6d03dd1def33168764a Mon Sep 17 00:00:00 2001 From: Matt Whelan Date: Thu, 30 Jul 2020 10:04:56 -0700 Subject: [PATCH 2/3] Address type erasure/reflection issue This is a pretty broad rewrite, because the former approach cannot work. TracingRequestHandler's type parameters erase to `Object` at runtime, which defeats AWS's reflection-based automatic deserialization mechanism. The new approach uses composition over inheritance: the user provides their own event handler, using any mechanism AWS supports, and wraps their business logic with our instrumentation. This allows reflection to work as intended. In addition, the TracingRequestStreamHandler class's doHandler method didn't allow for IOException to be thrown, despite it being more or less inevitable. `StreamLambdaTracing` addresses this issue, while mirroring the design of `LambdaTracing`. --- README.md | 45 +++++------ .../opentracing/aws/LambdaTracing.java | 80 +++++++++++++++++++ .../opentracing/aws/StreamLambdaTracing.java | 70 ++++++++++++++++ .../aws/TracingRequestHandler.java | 27 ++----- .../aws/TracingRequestStreamHandler.java | 28 +++---- .../opentracing/aws/ReflectionTest.java | 69 ++++++++-------- .../aws/TracingRequestHandlerTest.java | 2 +- .../aws/TracingRequestStreamHandlerTest.java | 2 +- 8 files changed, 225 insertions(+), 98 deletions(-) create mode 100644 src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java create mode 100644 src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java diff --git a/README.md b/README.md index 524de2e..8fd9a6e 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,15 @@ Versioning will have the following format: {majorVersion}.{minorVersion}.{pointV ### How it Works -The SDK provides `TracingRequestHandler` and `TracingRequestStreamHandler` interfaces that extend AWS' Lambda request handlers. When a Lambda function that is using an implementation of either tracing request handler is invoked, the handler will obtain the globally registered OpenTracing [Tracer](https://opentracing.io/docs/overview/tracers/) and create/start an OpenTracing [Span](https://opentracing.io/docs/overview/spans/) to capture timing information and `key:value` pairs ([Tags/Logs](https://opentracing.io/docs/overview/tags-logs-baggage/)) detailing the trace data. +The SDK provides `LambdaTracing` and `StreamLambdaTracing` classes that instrument requests. When a Lambda function +that is using an implementation of either class is invoked, the handler will obtain the globally registered OpenTracing +[Tracer](https://opentracing.io/docs/overview/tracers/) and create/start an OpenTracing +[Span](https://opentracing.io/docs/overview/spans/) to capture timing information and `key:value` pairs +([Tags/Logs](https://opentracing.io/docs/overview/tags-logs-baggage/)) detailing the trace data. + +As part of the implementation the user's handler must call the `instrument` method, passing a callback that contains +the business logic for their handler. -As part of the implementation the user must override the tracing handler's `doHandleRequest` method which is called by the handler interface's `handleRequest` method. ### Collected Span Tags/Logs @@ -42,7 +48,7 @@ Below are a list of the collected exception attributes: You can add the dependency by adding the following to your `build.gradle` file: ``` dependencies { - compile "com.newrelic.opentracing:java-aws-lambda:2.0.0" + implementation "com.newrelic.opentracing:java-aws-lambda:2.1.0" } ``` @@ -59,36 +65,29 @@ dependencies { package com.handler.example; import com.amazonaws.services.lambda.runtime.Context; -import com.newrelic.opentracing.aws.TracingRequestHandler; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.newrelic.opentracing.LambdaTracer; +import com.newrelic.opentracing.aws.LambdaTracing; import io.opentracing.util.GlobalTracer; import java.util.Map; -/** - * Tracing request handler that creates a span on every invocation of a Lambda. - * - * @param Map The Lambda Function input - * @param String The Lambda Function output - */ -public class MyLambdaHandler implements TracingRequestHandler, String> { +public static class YourLambdaHandler implements RequestHandler { static { // Obtain an instance of the OpenTracing Tracer of your choice - Tracer tracer = new CustomTracer(...); + Tracer tracer = LambdaTracer.INSTANCE; // Register your tracer as the Global Tracer GlobalTracer.registerIfAbsent(tracer); } - - /** - * Method that handles the Lambda function request. - * - * @param input The Lambda Function input - * @param context The Lambda execution environment context object - * @return String The Lambda Function output - */ + @Override - public String doHandleRequest(Map input, Context context) { - // Your function logic here - return "Lambda Function output"; + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + return LambdaTracing.instrument(apiGatewayProxyRequestEvent, context, (event, ctx) -> { + // Your business logic here + return doSomethingWithTheEvent(event); + }); } } ``` diff --git a/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java new file mode 100644 index 0000000..3c65dc7 --- /dev/null +++ b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java @@ -0,0 +1,80 @@ +package com.newrelic.opentracing.aws; + +import com.amazonaws.services.lambda.runtime.Context; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; + +/** + * Trace calls to lambda functions, for arbitrary Input and Output types. + * + *

For flexibility, applications may extend this class to enhance the root span or handle novel + * invocation event types. + * + * @param The invocation payload type for your lambda function. + * @param The result type for your lambda function. + */ +public class LambdaTracing { + protected static final AtomicBoolean isColdStart = new AtomicBoolean(true); + + /** + * One-line instrumentation convenience method. + * + * @param input The invocation event + * @param context The invocation context + * @param realHandler The callback that implements the business logic for this event handler + * @param The type of the invocation event + * @param The type of the response + * @return The invocation response (the return value of the realHandler callback) + */ + public static Output instrument( + Input input, Context context, BiFunction realHandler) { + return new LambdaTracing().instrumentRequest(input, context, realHandler); + } + + /** + * Instrument a Lambda invocation + * + * @param input The invocation event + * @param context The invocation context + * @param realHandler The function that implements the business logic. Will be invoked with the + * input and context parameters, from within the instrumentation scope. + * @return the return value from realHandler + */ + public Output instrumentRequest( + Input input, Context context, BiFunction realHandler) { + final Tracer tracer = GlobalTracer.get(); + final SpanContext spanContext = extractContext(tracer, input); + + Span span = buildRootSpan(input, context, tracer, spanContext); + try (Scope scope = tracer.activateSpan(span)) { + try { + Output output = realHandler.apply(input, context); + parseResponse(span, output); + return output; + } catch (Throwable throwable) { + span.log(SpanUtil.createErrorAttributes(throwable)); + throw throwable; + } + } finally { + span.finish(); + } + } + + protected SpanContext extractContext(Tracer tracer, Object input) { + return HeadersParser.parseAndExtract(tracer, input); + } + + protected Span buildRootSpan( + Input input, Context context, Tracer tracer, SpanContext spanContext) { + return SpanUtil.buildSpan(input, context, tracer, spanContext, isColdStart); + } + + protected void parseResponse(Span span, Output output) { + ResponseParser.parseResponse(output, span); + } +} diff --git a/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java new file mode 100644 index 0000000..77cf5a6 --- /dev/null +++ b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java @@ -0,0 +1,70 @@ +package com.newrelic.opentracing.aws; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Trace calls to lambda functions, implementing manual JSON serialization. + * + *

For flexibility, applications may extend this class to enhance the root span. + */ +public class StreamLambdaTracing { + /** + * One-line instrumentation convenience method. + * + * @param input The invocation event's input stream + * @param output The invocation response output stream + * @param context The invocation context + * @param realHandler The callback that implements the business logic for this event handler + */ + public static void instrument( + InputStream input, OutputStream output, Context context, RequestStreamHandler realHandler) + throws IOException { + new StreamLambdaTracing().instrumentRequest(input, output, context, realHandler); + } + + /** + * Instrument a Lambda invocation + * + * @param input The invocation event's input stream + * @param output The invocation response output stream + * @param context The invocation context + * @param realHandler The function that implements the business logic. Will be invoked with the + * input and context parameters, from within the instrumentation scope. + */ + public void instrumentRequest( + InputStream input, OutputStream output, Context context, RequestStreamHandler realHandler) + throws IOException { + final Tracer tracer = GlobalTracer.get(); + final SpanContext spanContext = extractContext(tracer, input); + + Span span = buildRootSpan(input, context, tracer, spanContext); + try (Scope scope = tracer.activateSpan(span)) { + try { + realHandler.handleRequest(input, output, context); + } catch (Throwable throwable) { + span.log(SpanUtil.createErrorAttributes(throwable)); + throw throwable; + } + } finally { + span.finish(); + } + } + + protected Span buildRootSpan( + InputStream input, Context context, Tracer tracer, SpanContext spanContext) { + return SpanUtil.buildSpan(input, context, tracer, spanContext, LambdaTracing.isColdStart); + } + + protected SpanContext extractContext(Tracer tracer, InputStream input) { + return null; + } +} diff --git a/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java b/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java index 4adb592..0881f80 100644 --- a/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java +++ b/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java @@ -6,13 +6,9 @@ package com.newrelic.opentracing.aws; import com.amazonaws.services.lambda.runtime.Context; -import io.opentracing.Scope; -import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; -import io.opentracing.util.GlobalTracer; -import java.util.concurrent.atomic.AtomicBoolean; /** * Tracing request handler that creates a span on every invocation of a Lambda. @@ -20,14 +16,16 @@ *

Implement this interface and update your AWS Lambda Handler name to reference your class name, * e.g., com.mycompany.HandlerClass * + *

Due to an interaction between Java's type erasure and method inheritance, Input effectively + * must be a Map. For that reason, this interface is deprecated in favor of {@link LambdaTracing}. + * * @param The input parameter type * @param The output parameter type */ +@Deprecated public interface TracingRequestHandler extends com.amazonaws.services.lambda.runtime.RequestHandler { - AtomicBoolean isColdStart = new AtomicBoolean(true); - /** * Method that handles the Lambda function request. * @@ -40,22 +38,7 @@ public interface TracingRequestHandler Output doHandleRequest(Input input, Context context); default Output handleRequest(Input input, Context context) { - final Tracer tracer = GlobalTracer.get(); - final SpanContext spanContext = extractContext(tracer, input); - - Span span = SpanUtil.buildSpan(input, context, tracer, spanContext, isColdStart); - try (Scope scope = tracer.activateSpan(span)) { - try { - Output output = doHandleRequest(input, context); - ResponseParser.parseResponse(output, span); - return output; - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } - } finally { - span.finish(); - } + return LambdaTracing.instrument(input, context, this::doHandleRequest); } /** diff --git a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java index 86b3350..50db828 100644 --- a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java +++ b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java @@ -6,27 +6,26 @@ package com.newrelic.opentracing.aws; import com.amazonaws.services.lambda.runtime.Context; -import io.opentracing.Scope; -import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; -import io.opentracing.util.GlobalTracer; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.atomic.AtomicBoolean; /** * Tracing request stream handler that creates a span on every invocation of a Lambda. * *

Implement this interface and update your AWS Lambda Handler name to reference your class name, * e.g., com.mycompany.HandlerClass + * + *

While RequestStreamHandler's handleRequest method may throw an IOException, doHandleRequest + * may not. For that reason, this interface is deprecated in favor of {@link StreamLambdaTracing}. */ +@Deprecated public interface TracingRequestStreamHandler extends com.amazonaws.services.lambda.runtime.RequestStreamHandler { - AtomicBoolean isColdStart = new AtomicBoolean(true); - /** * Method that handles the Lambda function request. * @@ -39,19 +38,10 @@ public interface TracingRequestStreamHandler void doHandleRequest(InputStream input, OutputStream output, Context context); default void handleRequest(InputStream input, OutputStream output, Context context) { - final Tracer tracer = GlobalTracer.get(); - final SpanContext spanContext = extractContext(tracer, input); - - Span span = SpanUtil.buildSpan(input, context, tracer, spanContext, isColdStart); - try (Scope scope = tracer.activateSpan(span)) { - try { - doHandleRequest(input, output, context); - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } - } finally { - span.finish(); + try { + StreamLambdaTracing.instrument(input, output, context, this::doHandleRequest); + } catch (IOException e) { + throw new RuntimeException(e); } } diff --git a/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java index 98863f1..cd30972 100644 --- a/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java @@ -1,44 +1,49 @@ package com.newrelic.opentracing.aws; +import static org.junit.Assert.assertEquals; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import org.junit.Before; -import org.junit.Test; - import java.lang.reflect.Method; import java.util.Arrays; - -import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; public class ReflectionTest { - private static final int SYNTHETIC_MODIFIER = 0x1000; - - private Object handler; - - @Before - public void setup() { - handler = new TestHandler(); - } - - @Test - public void testInputReflection() { - // Ignoring synthetics, we expect handleRequest to take the declared type as its first arg. - // This is necessary for correct payload deserialization. - final Method handleRequest = Arrays.stream(handler.getClass().getMethods()) - .filter(m -> ((m.getModifiers() & SYNTHETIC_MODIFIER) == 0) - && m.getName().equals("handleRequest")) - .findFirst() - .orElseThrow(AssertionError::new); - - assertEquals(APIGatewayProxyRequestEvent.class, handleRequest.getParameterTypes()[0]); - } - - public static class TestHandler implements TracingRequestHandler { - @Override - public APIGatewayProxyResponseEvent doHandleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { - return new APIGatewayProxyResponseEvent(); - } + private static final int SYNTHETIC_MODIFIER = 0x1000; + + private Object handler; + + @Before + public void setup() { + handler = new TestHandler(); + } + + @Test + public void testInputReflection() { + // Ignoring synthetics, we expect handleRequest to take the declared type as its first arg. + // This is necessary for correct payload deserialization. + final Method handleRequest = + Arrays.stream(handler.getClass().getMethods()) + .filter( + m -> + ((m.getModifiers() & SYNTHETIC_MODIFIER) == 0) + && m.getName().equals("handleRequest")) + .findFirst() + .orElseThrow(AssertionError::new); + + assertEquals(APIGatewayProxyRequestEvent.class, handleRequest.getParameterTypes()[0]); + } + + public static class TestHandler + implements RequestHandler { + @Override + public APIGatewayProxyResponseEvent handleRequest( + APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + return LambdaTracing.instrument( + apiGatewayProxyRequestEvent, context, (event, c) -> new APIGatewayProxyResponseEvent()); } + } } diff --git a/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java b/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java index c3b3a06..0bffd4c 100644 --- a/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java @@ -48,7 +48,7 @@ public static void beforeClass() { public void before() { mockTracer.reset(); // reset isColdStart before each test - TracingRequestHandler.isColdStart.set(true); + LambdaTracing.isColdStart.set(true); } @Test diff --git a/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java b/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java index b15f0ec..d149a4d 100644 --- a/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java @@ -35,7 +35,7 @@ public static void beforeClass() { public void before() { mockTracer.reset(); // reset isColdStart before each test - TracingRequestStreamHandler.isColdStart.set(true); + LambdaTracing.isColdStart.set(true); } @Test From aacd04e913898943007947c46671860ee7b8a884 Mon Sep 17 00:00:00 2001 From: Matt Whelan Date: Thu, 27 Aug 2020 10:22:17 -0700 Subject: [PATCH 3/3] PR Feedback README code streamlining, unnesting try blocks, exception error message. --- README.md | 14 +------------- .../newrelic/opentracing/aws/LambdaTracing.java | 14 ++++++-------- .../opentracing/aws/StreamLambdaTracing.java | 10 ++++------ .../aws/TracingRequestStreamHandler.java | 2 +- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 8fd9a6e..9981f10 100644 --- a/README.md +++ b/README.md @@ -62,19 +62,7 @@ dependencies { #### Example Usage ```java -package com.handler.example; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.newrelic.opentracing.LambdaTracer; -import com.newrelic.opentracing.aws.LambdaTracing; -import io.opentracing.util.GlobalTracer; - -import java.util.Map; - -public static class YourLambdaHandler implements RequestHandler { +public class YourLambdaHandler implements RequestHandler { static { // Obtain an instance of the OpenTracing Tracer of your choice Tracer tracer = LambdaTracer.INSTANCE; diff --git a/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java index 3c65dc7..33e96c3 100644 --- a/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java +++ b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java @@ -52,14 +52,12 @@ public Output instrumentRequest( Span span = buildRootSpan(input, context, tracer, spanContext); try (Scope scope = tracer.activateSpan(span)) { - try { - Output output = realHandler.apply(input, context); - parseResponse(span, output); - return output; - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } + Output output = realHandler.apply(input, context); + parseResponse(span, output); + return output; + } catch (Throwable throwable) { + span.log(SpanUtil.createErrorAttributes(throwable)); + throw throwable; } finally { span.finish(); } diff --git a/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java index 77cf5a6..b5e5560 100644 --- a/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java +++ b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java @@ -48,12 +48,10 @@ public void instrumentRequest( Span span = buildRootSpan(input, context, tracer, spanContext); try (Scope scope = tracer.activateSpan(span)) { - try { - realHandler.handleRequest(input, output, context); - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } + realHandler.handleRequest(input, output, context); + } catch (Throwable throwable) { + span.log(SpanUtil.createErrorAttributes(throwable)); + throw throwable; } finally { span.finish(); } diff --git a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java index 50db828..6e1aed1 100644 --- a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java +++ b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java @@ -41,7 +41,7 @@ default void handleRequest(InputStream input, OutputStream output, Context conte try { StreamLambdaTracing.instrument(input, output, context, this::doHandleRequest); } catch (IOException e) { - throw new RuntimeException(e); + throw new RuntimeException("Exception while processing Lambda invocation", e); } }