From 924889d3ffce3fdcddf9896e4412c61948ee034a Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Tue, 27 May 2025 10:20:32 -0400 Subject: [PATCH 1/4] AWS versions bump --- CONTRIBUTING.md | 13 +- build.gradle | 25 +- .../opentracing/aws/EnhancedSpanBuilder.java | 104 +- .../opentracing/aws/EventSourceParser.java | 367 +++--- .../opentracing/aws/HeadersParser.java | 79 +- .../opentracing/aws/LambdaTracing.java | 101 +- .../opentracing/aws/ResponseParser.java | 66 +- .../newrelic/opentracing/aws/SpanUtil.java | 52 +- .../opentracing/aws/StreamLambdaTracing.java | 87 +- .../aws/TracingRequestHandler.java | 58 +- .../aws/TracingRequestStreamHandler.java | 63 +- .../aws/EnhancedSpanBuilderTest.java | 273 ++--- .../aws/GlobalTracerTestUtils.java | 19 +- .../opentracing/aws/ReflectionTest.java | 75 +- .../aws/TracingRequestHandlerTest.java | 1001 +++++++++-------- .../aws/TracingRequestStreamHandlerTest.java | 271 ++--- 16 files changed, 1379 insertions(+), 1275 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96ba2fc..4c966e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,17 +37,8 @@ Before we can accept a pull request, you must sign our [Contributor Licensing Ag Minimally, the [test suite](#testing-guidelines) must pass for us to accept a PR. Ideally, we would love it if you also added appropriate tests if you're implementing a feature! ## Coding Style Guidelines -Our code base is formatted according to the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). - -The project is configured to use the [google-java-format-gradle-plugin](https://github.com/sherter/google-java-format-gradle-plugin) which can be utilized as follows: - -* Execute the task `googleJavaFormat` to format all `*.java` files in the project: - -`./gradlew goJF` - -* Execute the task `verifyGoogleJavaFormat` to verify that all `*.java` files are formatted properly: - -`./gradlew verGJF` +- Use the style provided in `dev-tools/code-style/java-agent-code-style.xml` in the project. +- We encourage you to reduce tech debt you might find in the area. Leave the code better than you found it. ## Testing Guidelines The AWS Lambda OpenTracing Java SDK comes with tests in `src/test` that can be run with `./gradlew test`. diff --git a/build.gradle b/build.gradle index b7edb71..78c2256 100644 --- a/build.gradle +++ b/build.gradle @@ -7,9 +7,6 @@ buildscript { } } - dependencies { - classpath "gradle.plugin.com.github.sherter.google-java-format:google-java-format-gradle-plugin:0.9" - } } plugins { @@ -25,8 +22,6 @@ java { withJavadocJar() } -apply plugin: 'com.github.sherter.google-java-format' - repositories { mavenCentral() maven { @@ -35,12 +30,22 @@ repositories { } dependencies { - implementation 'com.amazonaws:aws-lambda-java-core:1.1.0' + //implementation 'software.amazon.awssdk:bom:2.31.43' + + implementation 'software.amazon.awssdk:s3:2.31.43' + implementation 'software.amazon.awssdk:s3-event-notifications:2.31.43' + //implementation 'software.amazon.awssdk:kinesis:2.31.43' + //implementation 'software.amazon.awssdk:dynamodb:2.31.43' + + implementation 'com.amazonaws:aws-lambda-java-core:1.2.3' // 2.2.7 is earliest version that has all needed event sources - implementation 'com.amazonaws:aws-lambda-java-events:2.2.7' - implementation 'com.amazonaws:aws-java-sdk-s3:1.12.771' - implementation 'com.amazonaws:aws-java-sdk-kinesis:1.11.163' - implementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.163' + implementation 'com.amazonaws:aws-lambda-java-events:3.15.0' + + + //implementation 'com.amazonaws:aws-java-sdk-s3:1.12.771' + //implementation 'com.amazonaws:aws-java-sdk-kinesis:1.11.163' + //implementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.163' + implementation('io.opentracing:opentracing-api:0.33.0') implementation('io.opentracing:opentracing-util:0.33.0') implementation('io.opentracing:opentracing-noop:0.33.0') diff --git a/src/main/java/com/newrelic/opentracing/aws/EnhancedSpanBuilder.java b/src/main/java/com/newrelic/opentracing/aws/EnhancedSpanBuilder.java index eb2b30e..a68957a 100644 --- a/src/main/java/com/newrelic/opentracing/aws/EnhancedSpanBuilder.java +++ b/src/main/java/com/newrelic/opentracing/aws/EnhancedSpanBuilder.java @@ -10,66 +10,72 @@ import io.opentracing.Tracer; public class EnhancedSpanBuilder { - private final Tracer.SpanBuilder spanBuilder; + private final Tracer.SpanBuilder spanBuilder; - public static EnhancedSpanBuilder basedOn(Tracer tracer, String operationName) { - return new EnhancedSpanBuilder(tracer.buildSpan(operationName)); - } + public static EnhancedSpanBuilder basedOn(Tracer tracer, String operationName) { + return new EnhancedSpanBuilder(tracer.buildSpan(operationName)); + } - private EnhancedSpanBuilder(Tracer.SpanBuilder innerSpanBuilder) { - spanBuilder = innerSpanBuilder; - } + private EnhancedSpanBuilder(Tracer.SpanBuilder innerSpanBuilder) { + spanBuilder = innerSpanBuilder; + } - public EnhancedSpanBuilder asChildOf(SpanContext spanContext) { - this.spanBuilder.asChildOf(spanContext); - return this; - } + public EnhancedSpanBuilder asChildOf(SpanContext spanContext) { + this.spanBuilder.asChildOf(spanContext); + return this; + } - /** Same as {@link Span#setTag(String, String)}, but for the span to be built. */ - EnhancedSpanBuilder withTag(String key, String value) { - this.spanBuilder.withTag(key, value); - return this; - } + /** + * Same as {@link Span#setTag(String, String)}, but for the span to be built. + */ + EnhancedSpanBuilder withTag(String key, String value) { + this.spanBuilder.withTag(key, value); + return this; + } - /** Same as {@link Span#setTag(String, boolean)}, but for the span to be built. */ - EnhancedSpanBuilder withTag(String key, boolean value) { - this.spanBuilder.withTag(key, value); - return this; - } + /** + * Same as {@link Span#setTag(String, boolean)}, but for the span to be built. + */ + EnhancedSpanBuilder withTag(String key, boolean value) { + this.spanBuilder.withTag(key, value); + return this; + } - /** Same as {@link Span#setTag(String, Number)}, but for the span to be built. */ - EnhancedSpanBuilder withTag(String key, Number value) { - this.spanBuilder.withTag(key, value); - return this; - } + /** + * Same as {@link Span#setTag(String, Number)}, but for the span to be built. + */ + EnhancedSpanBuilder withTag(String key, Number value) { + this.spanBuilder.withTag(key, value); + return this; + } - /** - * A shorthand for withTag("key", "value"). - * - *

If parent==null, this is a noop. - */ - EnhancedSpanBuilder optionallyWithTag(String key, String value) { - if (value != null) { - this.spanBuilder.withTag(key, value); + /** + * A shorthand for withTag("key", "value"). + * + *

If parent==null, this is a noop. + */ + EnhancedSpanBuilder optionallyWithTag(String key, String value) { + if (value != null) { + this.spanBuilder.withTag(key, value); + } + return this; } - return this; - } - EnhancedSpanBuilder optionallyWithTag(String key, boolean value) { - if (value) { - this.spanBuilder.withTag(key, true); + EnhancedSpanBuilder optionallyWithTag(String key, boolean value) { + if (value) { + this.spanBuilder.withTag(key, true); + } + return this; } - return this; - } - EnhancedSpanBuilder optionallyWithTag(String key, Number value) { - if (value != null) { - this.spanBuilder.withTag(key, value); + EnhancedSpanBuilder optionallyWithTag(String key, Number value) { + if (value != null) { + this.spanBuilder.withTag(key, value); + } + return this; } - return this; - } - public Span start() { - return this.spanBuilder.start(); - } + public Span start() { + return this.spanBuilder.start(); + } } diff --git a/src/main/java/com/newrelic/opentracing/aws/EventSourceParser.java b/src/main/java/com/newrelic/opentracing/aws/EventSourceParser.java index 7c0faf4..3be24b1 100644 --- a/src/main/java/com/newrelic/opentracing/aws/EventSourceParser.java +++ b/src/main/java/com/newrelic/opentracing/aws/EventSourceParser.java @@ -11,9 +11,13 @@ import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; import com.amazonaws.services.lambda.runtime.events.KinesisEvent; import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent; +import com.amazonaws.services.lambda.runtime.events.S3Event; import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.s3.event.S3EventNotification; +import software.amazon.awssdk.eventnotifications.s3.model.S3; +import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification; +import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotificationRecord; + import java.util.List; import java.util.Map; @@ -29,217 +33,242 @@ */ final class EventSourceParser { - private EventSourceParser() {} - - static String parseEventSourceArn(Object object) { - if (object instanceof Map) { - return parseFromMap((Map) object); - } else if (object instanceof S3EventNotification) { - return parseS3BucketArn(object); - } else if (object instanceof SNSEvent) { - return parseSNSEventSubscriptionArn(object); - } else if (object instanceof SQSEvent) { - return parseSQSEventSourceArn(object); - } else if (object instanceof KinesisEvent) { - return parseKinesisStreamEventSourceArn(object); - } else if (object instanceof KinesisFirehoseEvent) { - return parseKinesisFirehoseDeliveryStreamArn(object); - } else if (object instanceof DynamodbEvent) { - return parseDynamodbEventSourceArn(object); - } else if (object instanceof CodeCommitEvent) { - return parseCodeCommitEventSourceArn(object); - } else if (object instanceof APIGatewayProxyRequestEvent) { - return parseAPIGatewayProxyRequestEventUserArn(object); - } else if (object instanceof APIGatewayV2ProxyRequestEvent) { - return parseAPIGatewayV2ProxyRequestEventUserArn(object); - } - return null; - } - - private static String parseFromMap(Map input) { - try { - if (input.get("streamArn") != null) { - return (String) input.get("streamArn"); - } else if (input.get("deliveryStreamArn") != null) { - return (String) input.get("deliveryStreamArn"); - } else if (input.get("requestContext") != null) { - Map context = (Map) input.get("requestContext"); - - // load balancer - final Map elb = (Map) context.get("elb"); - if (elb != null) { - return (String) elb.get("targetGroupArn"); - } - - // api gateway - final Map identity = (Map) context.get("identity"); - if (identity != null) { - return (String) identity.get("userArn"); - } - } else if (input.get("detail") != null) { // AWS Cloudwatch - Map detail = (Map) input.get("detail"); - return (String) detail.get("eventSource"); - } - - final List records = (List) input.get("Records"); - final Map record = (Map) records.get(0); - - if (record.get("eventSourceARN") != null) { - return (String) record.get("eventSourceARN"); - } else if (record.get("EventSubscriptionArn") != null) { - return (String) record.get("EventSubscriptionArn"); - } else if (record.containsKey("s3")) { // AWS S3 - final Map s3Event = (Map) record.get("s3"); - final Map bucket = (Map) s3Event.get("bucket"); - return (String) bucket.get("arn"); - } - - return null; - } catch (Throwable t) { + private EventSourceParser() { } - return null; - } - - private static String parseS3BucketArn(Object object) { - final S3EventNotification notification = (S3EventNotification) object; - if (notification.getRecords() == null || notification.getRecords().isEmpty()) { - return null; + static String parseEventSourceArn(Object object) { + if (object instanceof Map) { + return parseFromMap((Map) object); + } else if (object instanceof S3EventNotification) { + return parseS3BucketArn(object); + } else if (object instanceof S3Event) { + return parseS3EventBucketArn(object); + } else if (object instanceof SNSEvent) { + return parseSNSEventSubscriptionArn(object); + } else if (object instanceof SQSEvent) { + return parseSQSEventSourceArn(object); + } else if (object instanceof KinesisEvent) { + return parseKinesisStreamEventSourceArn(object); + } else if (object instanceof KinesisFirehoseEvent) { + return parseKinesisFirehoseDeliveryStreamArn(object); + } else if (object instanceof DynamodbEvent) { + return parseDynamodbEventSourceArn(object); + } else if (object instanceof CodeCommitEvent) { + return parseCodeCommitEventSourceArn(object); + } else if (object instanceof APIGatewayProxyRequestEvent) { + return parseAPIGatewayProxyRequestEventUserArn(object); + } else if (object instanceof APIGatewayV2ProxyRequestEvent) { + return parseAPIGatewayV2ProxyRequestEventUserArn(object); + } + return null; } - final S3EventNotification.S3EventNotificationRecord s3EventNotificationRecord = - notification.getRecords().get(0); - if (s3EventNotificationRecord == null || s3EventNotificationRecord.getS3() == null) { - return null; + private static String parseFromMap(Map input) { + try { + if (input.get("streamArn") != null) { + return (String) input.get("streamArn"); + } else if (input.get("deliveryStreamArn") != null) { + return (String) input.get("deliveryStreamArn"); + } else if (input.get("requestContext") != null) { + Map context = (Map) input.get("requestContext"); + + // load balancer + final Map elb = (Map) context.get("elb"); + if (elb != null) { + return (String) elb.get("targetGroupArn"); + } + + // api gateway + final Map identity = (Map) context.get("identity"); + if (identity != null) { + return (String) identity.get("userArn"); + } + } else if (input.get("detail") != null) { // AWS Cloudwatch + Map detail = (Map) input.get("detail"); + return (String) detail.get("eventSource"); + } + + final List records = (List) input.get("Records"); + final Map record = (Map) records.get(0); + + if (record.get("eventSourceARN") != null) { + return (String) record.get("eventSourceARN"); + } else if (record.get("EventSubscriptionArn") != null) { + return (String) record.get("EventSubscriptionArn"); + } else if (record.containsKey("s3")) { // AWS S3 + final Map s3Event = (Map) record.get("s3"); + final Map bucket = (Map) s3Event.get("bucket"); + return (String) bucket.get("arn"); + } + + return null; + } catch (Throwable t) { + } + return null; } - final S3EventNotification.S3Entity s3 = s3EventNotificationRecord.getS3(); - if (s3.getBucket() == null) { - return null; - } + private static String parseS3BucketArn(Object object) { + final S3EventNotification notification = (S3EventNotification) object; - return s3.getBucket().getArn(); - } + if (notification.getRecords() == null || notification.getRecords().isEmpty()) { + return null; + } - private static String parseSNSEventSubscriptionArn(Object object) { - final SNSEvent snsEvent = (SNSEvent) object; + final S3EventNotificationRecord s3EventNotificationRecord = + notification.getRecords().get(0); + if (s3EventNotificationRecord == null || s3EventNotificationRecord.getS3() == null) { + return null; + } - final List records = snsEvent.getRecords(); - if (records == null || records.isEmpty()) { - return null; - } + final S3 s3 = s3EventNotificationRecord.getS3(); + if (s3.getBucket() == null) { + return null; + } - final SNSEvent.SNSRecord snsRecord = records.get(0); - if (snsRecord == null) { - return null; + return s3.getBucket().getArn(); } - return snsRecord.getEventSubscriptionArn(); - } + private static String parseS3EventBucketArn(Object object) { + final S3Event notification = (S3Event) object; + + if (notification.getRecords() == null || notification.getRecords().isEmpty()) { + return null; + } - private static String parseSQSEventSourceArn(Object object) { - final SQSEvent sqsEvent = (SQSEvent) object; + com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord s3EventNotificationRecord = + notification.getRecords().get(0); - final List records = sqsEvent.getRecords(); - if (records == null || records.isEmpty()) { - return null; - } + if (s3EventNotificationRecord == null || s3EventNotificationRecord.getS3() == null) { + return null; + } - final SQSEvent.SQSMessage sqsMessage = records.get(0); - if (sqsMessage == null) { - return null; + final com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3Entity s3 = s3EventNotificationRecord.getS3(); + if (s3.getBucket() == null) { + return null; + } + + return s3.getBucket().getArn(); } - return sqsMessage.getEventSourceArn(); - } + private static String parseSNSEventSubscriptionArn(Object object) { + final SNSEvent snsEvent = (SNSEvent) object; - private static String parseKinesisStreamEventSourceArn(Object object) { - final KinesisEvent kinesisEvent = (KinesisEvent) object; + final List records = snsEvent.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } - final List records = kinesisEvent.getRecords(); - if (records == null || records.isEmpty()) { - return null; - } + final SNSEvent.SNSRecord snsRecord = records.get(0); + if (snsRecord == null) { + return null; + } - final KinesisEvent.KinesisEventRecord kinesisEventRecord = records.get(0); - if (kinesisEventRecord == null) { - return null; + return snsRecord.getEventSubscriptionArn(); } - return kinesisEventRecord.getEventSourceARN(); - } - - private static String parseKinesisFirehoseDeliveryStreamArn(Object object) { - final KinesisFirehoseEvent kinesisFirehoseEvent = (KinesisFirehoseEvent) object; + private static String parseSQSEventSourceArn(Object object) { + final SQSEvent sqsEvent = (SQSEvent) object; - return kinesisFirehoseEvent.getDeliveryStreamArn(); - } + final List records = sqsEvent.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } - private static String parseDynamodbEventSourceArn(Object object) { - final DynamodbEvent dynamodbEvent = (DynamodbEvent) object; + final SQSEvent.SQSMessage sqsMessage = records.get(0); + if (sqsMessage == null) { + return null; + } - final List records = dynamodbEvent.getRecords(); - if (records == null || records.isEmpty()) { - return null; + return sqsMessage.getEventSourceArn(); } - final DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord = records.get(0); - if (dynamodbStreamRecord == null) { - return null; - } + private static String parseKinesisStreamEventSourceArn(Object object) { + final KinesisEvent kinesisEvent = (KinesisEvent) object; - return dynamodbStreamRecord.getEventSourceARN(); - } + final List records = kinesisEvent.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } - private static String parseCodeCommitEventSourceArn(Object object) { - final CodeCommitEvent codeCommitEvent = (CodeCommitEvent) object; + final KinesisEvent.KinesisEventRecord kinesisEventRecord = records.get(0); + if (kinesisEventRecord == null) { + return null; + } - final List records = codeCommitEvent.getRecords(); - if (records == null || records.isEmpty()) { - return null; + return kinesisEventRecord.getEventSourceARN(); } - final CodeCommitEvent.Record record = records.get(0); - if (record == null) { - return null; + private static String parseKinesisFirehoseDeliveryStreamArn(Object object) { + final KinesisFirehoseEvent kinesisFirehoseEvent = (KinesisFirehoseEvent) object; + + return kinesisFirehoseEvent.getDeliveryStreamArn(); } - return record.getEventSourceArn(); - } + private static String parseDynamodbEventSourceArn(Object object) { + final DynamodbEvent dynamodbEvent = (DynamodbEvent) object; - private static String parseAPIGatewayProxyRequestEventUserArn(Object object) { - final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = - (APIGatewayProxyRequestEvent) object; + final List records = dynamodbEvent.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } - final APIGatewayProxyRequestEvent.ProxyRequestContext requestContext = - apiGatewayProxyRequestEvent.getRequestContext(); - if (requestContext == null) { - return null; - } + final DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord = records.get(0); + if (dynamodbStreamRecord == null) { + return null; + } - final APIGatewayProxyRequestEvent.RequestIdentity identity = requestContext.getIdentity(); - if (identity == null) { - return null; + return dynamodbStreamRecord.getEventSourceARN(); } - return identity.getUserArn(); - } + private static String parseCodeCommitEventSourceArn(Object object) { + final CodeCommitEvent codeCommitEvent = (CodeCommitEvent) object; - private static String parseAPIGatewayV2ProxyRequestEventUserArn(Object object) { - APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = - (APIGatewayV2ProxyRequestEvent) object; + final List records = codeCommitEvent.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } - APIGatewayV2ProxyRequestEvent.RequestContext requestContext = - apiGatewayV2ProxyRequestEvent.getRequestContext(); - if (requestContext == null) { - return null; + final CodeCommitEvent.Record record = records.get(0); + if (record == null) { + return null; + } + + return record.getEventSourceArn(); } - APIGatewayV2ProxyRequestEvent.RequestIdentity identity = requestContext.getIdentity(); - if (identity == null) { - return null; + private static String parseAPIGatewayProxyRequestEventUserArn(Object object) { + final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = + (APIGatewayProxyRequestEvent) object; + + final APIGatewayProxyRequestEvent.ProxyRequestContext requestContext = + apiGatewayProxyRequestEvent.getRequestContext(); + if (requestContext == null) { + return null; + } + + final APIGatewayProxyRequestEvent.RequestIdentity identity = requestContext.getIdentity(); + if (identity == null) { + return null; + } + + return identity.getUserArn(); } - return identity.getUserArn(); - } + private static String parseAPIGatewayV2ProxyRequestEventUserArn(Object object) { + APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = + (APIGatewayV2ProxyRequestEvent) object; + + APIGatewayV2ProxyRequestEvent.RequestContext requestContext = + apiGatewayV2ProxyRequestEvent.getRequestContext(); + if (requestContext == null) { + return null; + } + + APIGatewayV2ProxyRequestEvent.RequestIdentity identity = requestContext.getIdentity(); + if (identity == null) { + return null; + } + + return identity.getUserArn(); + } } diff --git a/src/main/java/com/newrelic/opentracing/aws/HeadersParser.java b/src/main/java/com/newrelic/opentracing/aws/HeadersParser.java index e0cb519..e6e5bfd 100644 --- a/src/main/java/com/newrelic/opentracing/aws/HeadersParser.java +++ b/src/main/java/com/newrelic/opentracing/aws/HeadersParser.java @@ -5,33 +5,76 @@ package com.newrelic.opentracing.aws; -import com.amazonaws.Request; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; +import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; import io.opentracing.propagation.TextMapAdapter; + +import java.util.HashMap; +import java.util.List; import java.util.Map; final class HeadersParser { - private HeadersParser() {} + private HeadersParser() { + } + + static SpanContext parseAndExtract(Tracer tracer, Input input) { + try { + if (input instanceof Map) { + Map map = (Map) input; + final Object headers = map.get("headers"); + if (headers instanceof Map) { + final Map headerStr = (Map) headers; + return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headerStr)); + } + } else if (input instanceof APIGatewayV2HTTPEvent) { + return tracer.extract(Format.Builtin.HTTP_HEADERS, + new TextMapAdapter(((APIGatewayV2HTTPEvent) input).getHeaders())); + } else if (input instanceof APIGatewayProxyRequestEvent) { + return tracer.extract(Format.Builtin.HTTP_HEADERS, + new TextMapAdapter(((APIGatewayProxyRequestEvent) input).getHeaders())); + } else if (input instanceof ApplicationLoadBalancerRequestEvent) { + return tracer.extract(Format.Builtin.HTTP_HEADERS, + new TextMapAdapter(((ApplicationLoadBalancerRequestEvent) input).getHeaders())); + } else if (input instanceof SNSEvent) { + SNSEvent snsEvent = (SNSEvent) input; + List records = snsEvent.getRecords(); + Map extractedHeaders = new HashMap<>(); + if (!records.isEmpty()) { + Map messageAttributes = records.get(0).getSNS().getMessageAttributes(); + + if (messageAttributes != null) { + for (Map.Entry entry : messageAttributes.entrySet()) { + extractedHeaders.put(entry.getKey(), entry.getValue().getValue()); + } + } + } + + return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(extractedHeaders)); + } else if (input instanceof SQSEvent) { + SQSEvent sqsEvent = (SQSEvent) input; + List records = sqsEvent.getRecords(); + Map extractedHeaders = new HashMap<>(); + if (!records.isEmpty()) { + Map messageAttributes = records.get(0).getMessageAttributes(); + + if (messageAttributes != null) { + for (Map.Entry entry : messageAttributes.entrySet()) { + extractedHeaders.put(entry.getKey(), entry.getValue().getStringValue()); + } + } + } - static SpanContext parseAndExtract(Tracer tracer, Input input) { - try { - if (input instanceof Map) { - Map map = (Map) input; - final Object headers = map.get("headers"); - if (headers instanceof Map) { - final Map headerStr = (Map) headers; - return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headerStr)); + return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(extractedHeaders)); + } + } catch (IllegalArgumentException exception) { } - } else if (input instanceof com.amazonaws.Request) { - final Request request = (Request) input; - final Map headers = request.getHeaders(); - return tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers)); - } - } catch (IllegalArgumentException exception) { + return null; } - return null; - } } diff --git a/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java index 33e96c3..41a2236 100644 --- a/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java +++ b/src/main/java/com/newrelic/opentracing/aws/LambdaTracing.java @@ -6,6 +6,7 @@ import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.util.GlobalTracer; + import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; @@ -15,64 +16,64 @@ *

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 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); + 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); - } + /** + * 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); + /** + * 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)) { - Output output = realHandler.apply(input, context); - parseResponse(span, output); - return output; - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } finally { - span.finish(); + Span span = buildRootSpan(input, context, tracer, spanContext); + try (Scope scope = tracer.activateSpan(span)) { + 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 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 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); - } + protected void parseResponse(Span span, Output output) { + ResponseParser.parseResponse(output, span); + } } diff --git a/src/main/java/com/newrelic/opentracing/aws/ResponseParser.java b/src/main/java/com/newrelic/opentracing/aws/ResponseParser.java index 87ca902..d83edfe 100644 --- a/src/main/java/com/newrelic/opentracing/aws/ResponseParser.java +++ b/src/main/java/com/newrelic/opentracing/aws/ResponseParser.java @@ -8,43 +8,45 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2ProxyResponseEvent; import io.opentracing.Span; + import java.util.Map; public class ResponseParser { - private ResponseParser() {} - - /** - * Attempt to parse a status code from the response object, which could be present if the event - * source type was created from an Application Load Balancer or API Gateway. - */ - public static void parseResponse(Output response, Span span) { - String statusCode = null; - - if (response instanceof Map) { - Map map = (Map) response; - Object statusCodeObject = map.get("statusCode"); - if (statusCodeObject instanceof String) { - statusCode = (String) statusCodeObject; - } else if (statusCodeObject instanceof Number) { - statusCode = ((Number) statusCodeObject) + ""; - } - } else if (response instanceof APIGatewayProxyResponseEvent) { - final APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent = - (APIGatewayProxyResponseEvent) response; - if (response != null) { - statusCode = apiGatewayProxyResponseEvent.getStatusCode() + ""; - } - } else if (response instanceof APIGatewayV2ProxyResponseEvent) { - final APIGatewayV2ProxyResponseEvent apiGatewayV2ProxyResponseEvent = - (APIGatewayV2ProxyResponseEvent) response; - if (response != null) { - statusCode = apiGatewayV2ProxyResponseEvent.getStatusCode() + ""; - } + private ResponseParser() { } - if (statusCode != null && !statusCode.isEmpty()) { - span.setTag("http.status_code", statusCode); + /** + * Attempt to parse a status code from the response object, which could be present if the event + * source type was created from an Application Load Balancer or API Gateway. + */ + public static void parseResponse(Output response, Span span) { + String statusCode = null; + + if (response instanceof Map) { + Map map = (Map) response; + Object statusCodeObject = map.get("statusCode"); + if (statusCodeObject instanceof String) { + statusCode = (String) statusCodeObject; + } else if (statusCodeObject instanceof Number) { + statusCode = ((Number) statusCodeObject) + ""; + } + } else if (response instanceof APIGatewayProxyResponseEvent) { + final APIGatewayProxyResponseEvent apiGatewayProxyResponseEvent = + (APIGatewayProxyResponseEvent) response; + if (response != null) { + statusCode = apiGatewayProxyResponseEvent.getStatusCode() + ""; + } + } else if (response instanceof APIGatewayV2ProxyResponseEvent) { + final APIGatewayV2ProxyResponseEvent apiGatewayV2ProxyResponseEvent = + (APIGatewayV2ProxyResponseEvent) response; + if (response != null) { + statusCode = apiGatewayV2ProxyResponseEvent.getStatusCode() + ""; + } + } + + if (statusCode != null && !statusCode.isEmpty()) { + span.setTag("http.status_code", statusCode); + } } - } } diff --git a/src/main/java/com/newrelic/opentracing/aws/SpanUtil.java b/src/main/java/com/newrelic/opentracing/aws/SpanUtil.java index e28615e..7f20e30 100644 --- a/src/main/java/com/newrelic/opentracing/aws/SpanUtil.java +++ b/src/main/java/com/newrelic/opentracing/aws/SpanUtil.java @@ -10,6 +10,7 @@ import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.tag.Tags; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -17,31 +18,32 @@ public class SpanUtil { - private SpanUtil() {} + private SpanUtil() { + } - static Span buildSpan( - Input input, - Context context, - Tracer tracer, - SpanContext spanContext, - AtomicBoolean isColdStart) { - return EnhancedSpanBuilder.basedOn(tracer, "handleRequest") - .asChildOf(spanContext) - .withTag("aws.requestId", context.getAwsRequestId()) - .withTag("aws.lambda.arn", context.getInvokedFunctionArn()) - .optionallyWithTag( - "aws.lambda.eventSource.arn", EventSourceParser.parseEventSourceArn(input)) - .optionallyWithTag("aws.lambda.coldStart", isColdStart.getAndSet(false)) - .start(); - } + static Span buildSpan( + Input input, + Context context, + Tracer tracer, + SpanContext spanContext, + AtomicBoolean isColdStart) { + return EnhancedSpanBuilder.basedOn(tracer, "handleRequest") + .asChildOf(spanContext) + .withTag("aws.requestId", context.getAwsRequestId()) + .withTag("aws.lambda.arn", context.getInvokedFunctionArn()) + .optionallyWithTag( + "aws.lambda.eventSource.arn", EventSourceParser.parseEventSourceArn(input)) + .optionallyWithTag("aws.lambda.coldStart", isColdStart.getAndSet(false)) + .start(); + } - public static Map createErrorAttributes(Throwable throwable) { - final Map errorAttributes = new HashMap<>(); - errorAttributes.put("event", Tags.ERROR.getKey()); - errorAttributes.put("error.object", throwable); - errorAttributes.put("message", throwable.getMessage()); - errorAttributes.put("stack", throwable.getStackTrace()); - errorAttributes.put("error.kind", "Exception"); - return Collections.unmodifiableMap(errorAttributes); - } + public static Map createErrorAttributes(Throwable throwable) { + final Map errorAttributes = new HashMap<>(); + errorAttributes.put("event", Tags.ERROR.getKey()); + errorAttributes.put("error.object", throwable); + errorAttributes.put("message", throwable.getMessage()); + errorAttributes.put("stack", throwable.getStackTrace()); + errorAttributes.put("error.kind", "Exception"); + return Collections.unmodifiableMap(errorAttributes); + } } diff --git a/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java index b5e5560..caa167b 100644 --- a/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java +++ b/src/main/java/com/newrelic/opentracing/aws/StreamLambdaTracing.java @@ -7,6 +7,7 @@ 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; @@ -17,52 +18,52 @@ *

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); - } + /** + * 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); + /** + * 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)) { - realHandler.handleRequest(input, output, context); - } catch (Throwable throwable) { - span.log(SpanUtil.createErrorAttributes(throwable)); - throw throwable; - } finally { - span.finish(); + Span span = buildRootSpan(input, context, tracer, spanContext); + try (Scope scope = tracer.activateSpan(span)) { + 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 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; - } + 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 0881f80..2bd9d13 100644 --- a/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java +++ b/src/main/java/com/newrelic/opentracing/aws/TracingRequestHandler.java @@ -19,40 +19,40 @@ *

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 input parameter type * @param The output parameter type */ @Deprecated public interface TracingRequestHandler - extends com.amazonaws.services.lambda.runtime.RequestHandler { + extends com.amazonaws.services.lambda.runtime.RequestHandler { - /** - * Method that handles the Lambda function request. - * - *

Override this method in your code. - * - * @param input The Lambda Function input - * @param context The Lambda execution environment context object - * @return The Lambda Function output - */ - Output doHandleRequest(Input input, Context context); + /** + * Method that handles the Lambda function request. + * + *

Override this method in your code. + * + * @param input The Lambda Function input + * @param context The Lambda execution environment context object + * @return The Lambda Function output + */ + Output doHandleRequest(Input input, Context context); - default Output handleRequest(Input input, Context context) { - return LambdaTracing.instrument(input, context, this::doHandleRequest); - } + default Output handleRequest(Input input, Context context) { + return LambdaTracing.instrument(input, context, this::doHandleRequest); + } - /** - * Override to extract context from Input. - * - *

Implementations should call {@link Tracer#extract(Format, Object)} and return the extracted - * SpanContext. - * - * @param tracer OpenTracing tracer - * @param input input to Lambda function - * @return SpanContext extracted from input, null if there was no context or there was an issue - * extracting this context - */ - default SpanContext extractContext(Tracer tracer, Input input) { - return HeadersParser.parseAndExtract(tracer, input); - } + /** + * Override to extract context from Input. + * + *

Implementations should call {@link Tracer#extract(Format, Object)} and return the extracted + * SpanContext. + * + * @param tracer OpenTracing tracer + * @param input input to Lambda function + * @return SpanContext extracted from input, null if there was no context or there was an issue + * extracting this context + */ + default SpanContext extractContext(Tracer tracer, Input input) { + return HeadersParser.parseAndExtract(tracer, input); + } } diff --git a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java index 6e1aed1..5a3c485 100644 --- a/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java +++ b/src/main/java/com/newrelic/opentracing/aws/TracingRequestStreamHandler.java @@ -9,6 +9,7 @@ import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,39 +25,39 @@ */ @Deprecated public interface TracingRequestStreamHandler - extends com.amazonaws.services.lambda.runtime.RequestStreamHandler { + extends com.amazonaws.services.lambda.runtime.RequestStreamHandler { - /** - * Method that handles the Lambda function request. - * - *

Override this method in your code. - * - * @param input The Lambda Function input stream - * @param output The Lambda Function output stream - * @param context The Lambda execution environment context object - */ - void doHandleRequest(InputStream input, OutputStream output, Context context); + /** + * Method that handles the Lambda function request. + * + *

Override this method in your code. + * + * @param input The Lambda Function input stream + * @param output The Lambda Function output stream + * @param context The Lambda execution environment context object + */ + void doHandleRequest(InputStream input, OutputStream output, Context context); - default void handleRequest(InputStream input, OutputStream output, Context context) { - try { - StreamLambdaTracing.instrument(input, output, context, this::doHandleRequest); - } catch (IOException e) { - throw new RuntimeException("Exception while processing Lambda invocation", e); + default void handleRequest(InputStream input, OutputStream output, Context context) { + try { + StreamLambdaTracing.instrument(input, output, context, this::doHandleRequest); + } catch (IOException e) { + throw new RuntimeException("Exception while processing Lambda invocation", e); + } } - } - /** - * Override to extract context from Input. - * - *

Implementations should call {@link Tracer#extract(Format, Object)} and return the extracted - * SpanContext. - * - * @param tracer OpenTracing tracer - * @param input Input to Lambda function - * @return SpanContext Extracted from input, null if there was no context or there was an issue - * extracting this context - */ - default SpanContext extractContext(Tracer tracer, InputStream input) { - return null; - } + /** + * Override to extract context from Input. + * + *

Implementations should call {@link Tracer#extract(Format, Object)} and return the extracted + * SpanContext. + * + * @param tracer OpenTracing tracer + * @param input Input to Lambda function + * @return SpanContext Extracted from input, null if there was no context or there was an issue + * extracting this context + */ + default SpanContext extractContext(Tracer tracer, InputStream input) { + return null; + } } diff --git a/src/test/java/com/newrelic/opentracing/aws/EnhancedSpanBuilderTest.java b/src/test/java/com/newrelic/opentracing/aws/EnhancedSpanBuilderTest.java index 7cfb8cf..0f1f121 100644 --- a/src/test/java/com/newrelic/opentracing/aws/EnhancedSpanBuilderTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/EnhancedSpanBuilderTest.java @@ -5,148 +5,153 @@ package com.newrelic.opentracing.aws; -import static io.opentracing.propagation.Format.Builtin.TEXT_MAP; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - import io.opentracing.References; import io.opentracing.mock.MockSpan; import io.opentracing.mock.MockTracer; import io.opentracing.propagation.TextMapAdapter; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import static io.opentracing.propagation.Format.Builtin.TEXT_MAP; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + public class EnhancedSpanBuilderTest { - private Random random = new Random(); - - private MockTracer tracer; - private MockSpan.MockContext context; - - @Before - public void beforeEach() { - Map parentIds = new HashMap<>(); - parentIds.put("traceid", Math.abs(this.random.nextLong()) + ""); - parentIds.put("spanid", Math.abs(this.random.nextLong()) + ""); - - this.tracer = new MockTracer(); - this.context = (MockSpan.MockContext) tracer.extract(TEXT_MAP, new TextMapAdapter(parentIds)); - } - - @After - public void afterEach() { - this.tracer.close(); - this.tracer.reset(); - } - - @Test - public void basedOn() { - MockSpan builtSpan = (MockSpan) EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation").start(); - assertThat(builtSpan.operationName(), is(equalTo("AnOperation"))); - assertThat(builtSpan.tags().entrySet(), hasSize(0)); - } - - @Test - public void asChildOf() { - MockSpan.Reference expectedReference = - new MockSpan.Reference(this.context, References.CHILD_OF); - - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation").asChildOf(this.context).start(); - - assertThat(builtSpan.operationName(), is(equalTo("AnOperation"))); - assertThat(builtSpan.references(), hasSize(1)); - assertThat(builtSpan.references(), contains(expectedReference)); - assertThat(builtSpan.tags().entrySet(), hasSize(0)); - } - - @Test - public void withTagStringKeyValue() { - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .withTag("ATag", "SomeValue") - .start(); - - assertThat(builtSpan.tags(), hasEntry("ATag", "SomeValue")); - } - - @Test - public void withTagBooleanValues() { - boolean expectedValue = this.random.nextBoolean(); - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .withTag("ABooleanKey", expectedValue) - .start(); - - assertThat(builtSpan.tags().entrySet(), hasSize(1)); - assertThat(builtSpan.tags(), hasEntry("ABooleanKey", expectedValue)); - } - - @Test - public void withTagNumberValues() { - Integer expectedValue = this.random.nextInt(); - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .withTag("ANumberKey", expectedValue) - .start(); - - assertThat(builtSpan.tags().entrySet(), hasSize(1)); - assertThat(builtSpan.tags(), hasEntry("ANumberKey", expectedValue)); - } - - @Test - public void optionallyWithTagDoesNotSetWithNullStrings() { - String expectedValue = null; - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .optionallyWithTag("ATagKey", expectedValue) - .start(); - - assertThat(builtSpan.tags().entrySet(), hasSize(0)); - } - - @Test - public void optionallyWithTagStringKeyValue() { - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .optionallyWithTag("ATag", "SomeValue") - .start(); - - assertThat(builtSpan.tags(), hasEntry("ATag", "SomeValue")); - } - - @Test - public void optionallyWithTagDoesNotSetWithNullNumbers() { - Number expectedValue = null; - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .optionallyWithTag("ATagKey", expectedValue) - .start(); - - assertThat(builtSpan.tags().entrySet(), hasSize(0)); - } - - @Test - public void optionallyWithTagNumberValues() { - Integer expectedValue = this.random.nextInt(); - MockSpan builtSpan = - (MockSpan) - EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") - .optionallyWithTag("ANumberKey", expectedValue) - .start(); - - assertThat(builtSpan.tags().entrySet(), hasSize(1)); - assertThat(builtSpan.tags(), hasEntry("ANumberKey", expectedValue)); - } + private Random random = new Random(); + + private MockTracer tracer; + private MockSpan.MockContext context; + + @Before + public void beforeEach() { + Map parentIds = new HashMap<>(); + parentIds.put("traceid", Math.abs(this.random.nextLong()) + ""); + parentIds.put("spanid", Math.abs(this.random.nextLong()) + ""); + + this.tracer = new MockTracer(); + this.context = (MockSpan.MockContext) tracer.extract(TEXT_MAP, new TextMapAdapter(parentIds)); + } + + @After + public void afterEach() { + this.tracer.close(); + this.tracer.reset(); + } + + @Test + public void basedOn() { + MockSpan builtSpan = (MockSpan) EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation").start(); + assertThat(builtSpan.operationName(), is(equalTo("AnOperation"))); + assertThat(builtSpan.tags().entrySet(), hasSize(0)); + } + + @Test + public void asChildOf() { + MockSpan.Reference expectedReference = + new MockSpan.Reference(this.context, References.CHILD_OF); + + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation").asChildOf(this.context).start(); + + assertThat(builtSpan.operationName(), is(equalTo("AnOperation"))); + assertThat(builtSpan.references(), hasSize(1)); + assertThat(builtSpan.references(), contains(expectedReference)); + assertThat(builtSpan.tags().entrySet(), hasSize(0)); + } + + @Test + public void withTagStringKeyValue() { + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .withTag("ATag", "SomeValue") + .start(); + + assertThat(builtSpan.tags(), hasEntry("ATag", "SomeValue")); + } + + @Test + public void withTagBooleanValues() { + boolean expectedValue = this.random.nextBoolean(); + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .withTag("ABooleanKey", expectedValue) + .start(); + + assertThat(builtSpan.tags().entrySet(), hasSize(1)); + assertThat(builtSpan.tags(), hasEntry("ABooleanKey", expectedValue)); + } + + @Test + public void withTagNumberValues() { + Integer expectedValue = this.random.nextInt(); + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .withTag("ANumberKey", expectedValue) + .start(); + + assertThat(builtSpan.tags().entrySet(), hasSize(1)); + assertThat(builtSpan.tags(), hasEntry("ANumberKey", expectedValue)); + } + + @Test + public void optionallyWithTagDoesNotSetWithNullStrings() { + String expectedValue = null; + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .optionallyWithTag("ATagKey", expectedValue) + .start(); + + assertThat(builtSpan.tags().entrySet(), hasSize(0)); + } + + @Test + public void optionallyWithTagStringKeyValue() { + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .optionallyWithTag("ATag", "SomeValue") + .start(); + + assertThat(builtSpan.tags(), hasEntry("ATag", "SomeValue")); + } + + @Test + public void optionallyWithTagDoesNotSetWithNullNumbers() { + Number expectedValue = null; + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .optionallyWithTag("ATagKey", expectedValue) + .start(); + + assertThat(builtSpan.tags().entrySet(), hasSize(0)); + } + + @Test + public void optionallyWithTagNumberValues() { + Integer expectedValue = this.random.nextInt(); + MockSpan builtSpan = + (MockSpan) + EnhancedSpanBuilder.basedOn(this.tracer, "AnOperation") + .optionallyWithTag("ANumberKey", expectedValue) + .start(); + + assertThat(builtSpan.tags().entrySet(), hasSize(1)); + assertThat(builtSpan.tags(), hasEntry("ANumberKey", expectedValue)); + } } diff --git a/src/test/java/com/newrelic/opentracing/aws/GlobalTracerTestUtils.java b/src/test/java/com/newrelic/opentracing/aws/GlobalTracerTestUtils.java index 342c783..d01e0b3 100644 --- a/src/test/java/com/newrelic/opentracing/aws/GlobalTracerTestUtils.java +++ b/src/test/java/com/newrelic/opentracing/aws/GlobalTracerTestUtils.java @@ -7,18 +7,19 @@ import io.opentracing.Tracer; import io.opentracing.util.GlobalTracer; + import java.lang.reflect.Field; public class GlobalTracerTestUtils { - public static void initTracer(Tracer tracer) { - try { - Field globalTracerField = GlobalTracer.class.getDeclaredField("tracer"); - globalTracerField.setAccessible(true); - globalTracerField.set(null, tracer); - globalTracerField.setAccessible(false); - } catch (Exception e) { - throw new RuntimeException("Unable to initialize tracer: " + e); + public static void initTracer(Tracer tracer) { + try { + Field globalTracerField = GlobalTracer.class.getDeclaredField("tracer"); + globalTracerField.setAccessible(true); + globalTracerField.set(null, tracer); + globalTracerField.setAccessible(false); + } catch (Exception e) { + throw new RuntimeException("Unable to initialize tracer: " + e); + } } - } } diff --git a/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java index cd30972..4c7958e 100644 --- a/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/ReflectionTest.java @@ -1,49 +1,50 @@ 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 java.lang.reflect.Method; -import java.util.Arrays; 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 RequestHandler { - @Override - public APIGatewayProxyResponseEvent handleRequest( - APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { - return LambdaTracing.instrument( - apiGatewayProxyRequestEvent, context, (event, c) -> 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 0bffd4c..5c852c4 100644 --- a/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/TracingRequestHandlerTest.java @@ -22,566 +22,575 @@ import com.amazonaws.services.lambda.runtime.events.S3Event; import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.s3.event.S3EventNotification; +import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; import io.opentracing.mock.MockSpan; import io.opentracing.mock.MockTracer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class TracingRequestHandlerTest { - private static final MockTracer mockTracer = new MockTracer(); - - @BeforeClass - public static void beforeClass() { - GlobalTracerTestUtils.initTracer(mockTracer); - } - - @Before - public void before() { - mockTracer.reset(); - // reset isColdStart before each test - LambdaTracing.isColdStart.set(true); - } - - @Test - public void testSpan() { - final MyRequestHandler handler = new MyRequestHandler(); - handler.handleRequest("world", createContext()); - - final List mockSpans = mockTracer.finishedSpans(); - final MockSpan mockSpan = mockSpans.get(0); - Assert.assertEquals("handleRequest", mockSpan.operationName()); - Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); - Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); - Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); - - // Clear out span - mockTracer.reset(); - - handler.handleRequest("world", createContext()); - final MockSpan secondSpan = mockTracer.finishedSpans().get(0); - Assert.assertNull(secondSpan.tags().get("aws.lambda.coldStart")); - } - - @Test - public void testError() { - Error error = null; - - final ErrorRequestHandler handler = new ErrorRequestHandler(); - try { - handler.handleRequest("abcdefg", createContext()); - } catch (Error e) { - error = e; - } - - // Make sure TraceRequestHandler rethrows error - Assert.assertNotNull(error); - - final List mockSpans = mockTracer.finishedSpans(); - final MockSpan mockSpan = mockSpans.get(0); - Assert.assertEquals("handleRequest", mockSpan.operationName()); - Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); - Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); - Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); - - Assert.assertEquals(1, mockSpan.logEntries().size()); - final MockSpan.LogEntry logEntry = mockSpan.logEntries().get(0); - final Map errorFields = logEntry.fields(); - Assert.assertNotNull(errorFields.get("stack")); - Assert.assertEquals("Exception", errorFields.get("error.kind").toString()); - Assert.assertEquals("java.lang.Error: abcdefg", errorFields.get("error.object").toString()); - Assert.assertEquals("error", errorFields.get("event")); - Assert.assertEquals("abcdefg", errorFields.get("message")); - } - - @Test - public void testS3Event() { - final MyS3RequestHandler myS3RequestHandler = new MyS3RequestHandler(); - - final List records = new ArrayList<>(); - final S3EventNotification.UserIdentityEntity userIdentity = - new S3EventNotification.UserIdentityEntity("principalId"); - final S3EventNotification.S3BucketEntity s3BucketEntity = - new S3EventNotification.S3BucketEntity("bucketName", userIdentity, "s3ARN"); - final S3EventNotification.S3Entity s3Entity = - new S3EventNotification.S3Entity("s3Entity", s3BucketEntity, null, null); - final S3EventNotification.S3EventNotificationRecord record = - new S3EventNotification.S3EventNotificationRecord( - "awsRegion", - "eventName", - "eventSource", - "2010-06-30T01:20+02:00", - "eventVersion", - null, - null, - s3Entity, - null); - records.add(record); - - myS3RequestHandler.handleRequest(new S3Event(records), createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals("s3ARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testSNSEvent() { - final MySNSRequestHandler mySNSRequestHandler = new MySNSRequestHandler(); - - final SNSEvent snsEvent = new SNSEvent(); - final SNSEvent.SNSRecord snsRecord = new SNSEvent.SNSRecord(); - snsRecord.setEventSubscriptionArn("SNSEventSubscriptionArn"); - final SNSEvent.SNS sns = new SNSEvent.SNS(); - final SNSEvent.MessageAttribute messageAttribute = new SNSEvent.MessageAttribute(); - final Map messageAttributes = new HashMap<>(); - messageAttributes.put("messageAttributes.key", messageAttribute); - sns.setMessageAttributes(messageAttributes); - snsRecord.setSns(sns); - final List records = new ArrayList<>(); - records.add(snsRecord); - snsEvent.setRecords(records); - - mySNSRequestHandler.handleRequest(snsEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals("SNSEventSubscriptionArn", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testSQSEvent() { - final MySQSRequestHandler mySQSRequestHandler = new MySQSRequestHandler(); - - final SQSEvent sqsEvent = new SQSEvent(); - final SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage(); - sqsMessage.setEventSourceArn("SQSEventSourceArn"); - final List records = new ArrayList<>(); - records.add(sqsMessage); - sqsEvent.setRecords(records); - - mySQSRequestHandler.handleRequest(sqsEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals("SQSEventSourceArn", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testKinesisStreamEvent() { - final MyKinesisStreamRequestHandler myKinesisStreamRequestHandler = - new MyKinesisStreamRequestHandler(); - - final KinesisEvent kinesisStreamEvent = new KinesisEvent(); - final KinesisEvent.KinesisEventRecord kinesisEventRecord = - new KinesisEvent.KinesisEventRecord(); - kinesisEventRecord.setEventSourceARN("KinesisStreamEventSourceARN"); - final List records = new ArrayList<>(); - records.add(kinesisEventRecord); - kinesisStreamEvent.setRecords(records); - - myKinesisStreamRequestHandler.handleRequest(kinesisStreamEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "KinesisStreamEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testKinesisFirehoseEvent() { - final MyKinesisFirehoseRequestHandler myKinesisFirehoseRequestHandler = - new MyKinesisFirehoseRequestHandler(); - - final KinesisFirehoseEvent kinesisFirehoseEvent = new KinesisFirehoseEvent(); - kinesisFirehoseEvent.setDeliveryStreamArn("KinesisFirehoseDeliveryStreamArn"); - - myKinesisFirehoseRequestHandler.handleRequest(kinesisFirehoseEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "KinesisFirehoseDeliveryStreamArn", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testDynamoDBEvent() { - final MyDynamoDBRequestHandler myDynamoDBRequestHandler = new MyDynamoDBRequestHandler(); - - final DynamodbEvent dynamodbEvent = new DynamodbEvent(); - final DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord = - new DynamodbEvent.DynamodbStreamRecord(); - dynamodbStreamRecord.setEventSourceARN("DynamodbEventSourceARN"); - List records = new ArrayList<>(); - records.add(dynamodbStreamRecord); - dynamodbEvent.setRecords(records); - - myDynamoDBRequestHandler.handleRequest(dynamodbEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals("DynamodbEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testCodeCommitEvent() { - final MyCodeCommitRequestHandler myCodeCommitRequestHandler = new MyCodeCommitRequestHandler(); - - final CodeCommitEvent codeCommitEvent = new CodeCommitEvent(); - final CodeCommitEvent.Record record = new CodeCommitEvent.Record(); - record.setEventSourceArn("CodeCommitEventSourceARN"); - List records = new ArrayList<>(); - records.add(record); - codeCommitEvent.setRecords(records); - - myCodeCommitRequestHandler.handleRequest(codeCommitEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals("CodeCommitEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testAPIGatewayProxyRequestEvent() { - final MyApiGatewayProxyRequestHandler myApiGatewayProxyRequestHandler = - new MyApiGatewayProxyRequestHandler(); - - final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = - new APIGatewayProxyRequestEvent(); - final APIGatewayProxyRequestEvent.RequestIdentity requestIdentity = - new APIGatewayProxyRequestEvent.RequestIdentity(); - requestIdentity.setUserArn("APIGatewayProxyRequestEventUserARN"); - final APIGatewayProxyRequestEvent.ProxyRequestContext proxyRequestContext = - new APIGatewayProxyRequestEvent.ProxyRequestContext(); - proxyRequestContext.setIdentity(requestIdentity); - apiGatewayProxyRequestEvent.setRequestContext(proxyRequestContext); - - myApiGatewayProxyRequestHandler.handleRequest(apiGatewayProxyRequestEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "APIGatewayProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - public void testAPIGatewayProxyRequestResponseEvent() { - int expectedStatusCode = 200; - final MyApiGatewayProxyRequestResponseHandler myApiGatewayProxyRequestHandler = - new MyApiGatewayProxyRequestResponseHandler(expectedStatusCode); - - final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = - new APIGatewayProxyRequestEvent(); - final APIGatewayProxyRequestEvent.RequestIdentity requestIdentity = - new APIGatewayProxyRequestEvent.RequestIdentity(); - requestIdentity.setUserArn("APIGatewayProxyRequestEventUserARN"); - final APIGatewayProxyRequestEvent.ProxyRequestContext proxyRequestContext = - new APIGatewayProxyRequestEvent.ProxyRequestContext(); - proxyRequestContext.setIdentity(requestIdentity); - apiGatewayProxyRequestEvent.setRequestContext(proxyRequestContext); - - myApiGatewayProxyRequestHandler.handleRequest(apiGatewayProxyRequestEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "APIGatewayProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); - Assert.assertEquals(Integer.toString(expectedStatusCode), span.tags().get("http.status_code")); - } - - @Test - public void testAPIGatewayV2ProxyRequestEvent() { - final MyApiGatewayV2ProxyRequestHandler myApiGatewayV2ProxyRequestHandler = - new MyApiGatewayV2ProxyRequestHandler(); - - final APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = - new APIGatewayV2ProxyRequestEvent(); - final APIGatewayV2ProxyRequestEvent.RequestIdentity requestIdentity = - new APIGatewayV2ProxyRequestEvent.RequestIdentity(); - requestIdentity.setUserArn("APIGatewayV2ProxyRequestEventUserARN"); - final APIGatewayV2ProxyRequestEvent.RequestContext proxyRequestContext = - new APIGatewayV2ProxyRequestEvent.RequestContext(); - proxyRequestContext.setIdentity(requestIdentity); - apiGatewayV2ProxyRequestEvent.setRequestContext(proxyRequestContext); - - myApiGatewayV2ProxyRequestHandler.handleRequest(apiGatewayV2ProxyRequestEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "APIGatewayV2ProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); - Assert.assertFalse(span.tags().containsKey("http.status_code")); - } - - @Test - public void testAPIGatewayV2ProxyRequestResponseEvent() { - int expectedStatusCode = 200; - final MyApiGatewayV2ProxyRequestResponseHandler myApiGatewayV2ProxyRequestHandler = - new MyApiGatewayV2ProxyRequestResponseHandler(expectedStatusCode); - - final APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = - new APIGatewayV2ProxyRequestEvent(); - final APIGatewayV2ProxyRequestEvent.RequestIdentity requestIdentity = - new APIGatewayV2ProxyRequestEvent.RequestIdentity(); - requestIdentity.setUserArn("APIGatewayV2ProxyRequestEventUserARN"); - final APIGatewayV2ProxyRequestEvent.RequestContext proxyRequestContext = - new APIGatewayV2ProxyRequestEvent.RequestContext(); - proxyRequestContext.setIdentity(requestIdentity); - apiGatewayV2ProxyRequestEvent.setRequestContext(proxyRequestContext); - - myApiGatewayV2ProxyRequestHandler.handleRequest(apiGatewayV2ProxyRequestEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "APIGatewayV2ProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); - Assert.assertEquals(Integer.toString(expectedStatusCode), span.tags().get("http.status_code")); - } - - @Test - @Ignore("We would like this but there doesn't seem to be an available arn currently") - public void testCloudWatchLogsEvent() { - final MyCloudWatchRequestHandler myCloudWatchRequestHandler = new MyCloudWatchRequestHandler(); - - final CloudWatchLogsEvent cloudWatchLogsEvent = new CloudWatchLogsEvent(); - - myCloudWatchRequestHandler.handleRequest(cloudWatchLogsEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "CloudWatchLogsEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - @Test - @Ignore("We would like this but there doesn't seem to be an available arn currently") - public void testCloudFrontEvent() { - final MyCloudFrontRequestHandler myCloudFrontRequestHandler = new MyCloudFrontRequestHandler(); - - final CloudFrontEvent cloudFrontEvent = new CloudFrontEvent(); - final CloudFrontEvent.Record record = new CloudFrontEvent.Record(); - - myCloudFrontRequestHandler.handleRequest(cloudFrontEvent, createContext()); - final MockSpan span = mockTracer.finishedSpans().get(0); - Assert.assertEquals( - "CloudFrontEventEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); - } - - static class MyRequestHandler implements TracingRequestHandler { + private static final MockTracer mockTracer = new MockTracer(); - @Override - public String doHandleRequest(String s, Context context) { - return "Request Handler says: hello " + s; - } - } - - static class ErrorRequestHandler implements TracingRequestHandler { + @BeforeClass + public static void beforeClass() { + GlobalTracerTestUtils.initTracer(mockTracer); + } - @Override - public String doHandleRequest(String s, Context context) { - throw new Error(s); + @Before + public void before() { + mockTracer.reset(); + // reset isColdStart before each test + LambdaTracing.isColdStart.set(true); } - } - // S3 - static class MyS3RequestHandler implements TracingRequestHandler { + @Test + public void testSpan() { + final MyRequestHandler handler = new MyRequestHandler(); + handler.handleRequest("world", createContext()); + + final List mockSpans = mockTracer.finishedSpans(); + final MockSpan mockSpan = mockSpans.get(0); + Assert.assertEquals("handleRequest", mockSpan.operationName()); + Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); + Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); + Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); - @Override - public Object doHandleRequest(S3Event s3Event, Context context) { - return "null"; + // Clear out span + mockTracer.reset(); + + handler.handleRequest("world", createContext()); + final MockSpan secondSpan = mockTracer.finishedSpans().get(0); + Assert.assertNull(secondSpan.tags().get("aws.lambda.coldStart")); } - } - // Simple Notification Service (SNS) - static class MySNSRequestHandler implements TracingRequestHandler { + @Test + public void testError() { + Error error = null; + + final ErrorRequestHandler handler = new ErrorRequestHandler(); + try { + handler.handleRequest("abcdefg", createContext()); + } catch (Error e) { + error = e; + } + + // Make sure TraceRequestHandler rethrows error + Assert.assertNotNull(error); + + final List mockSpans = mockTracer.finishedSpans(); + final MockSpan mockSpan = mockSpans.get(0); + Assert.assertEquals("handleRequest", mockSpan.operationName()); + Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); + Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); + Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); + + Assert.assertEquals(1, mockSpan.logEntries().size()); + final MockSpan.LogEntry logEntry = mockSpan.logEntries().get(0); + final Map errorFields = logEntry.fields(); + Assert.assertNotNull(errorFields.get("stack")); + Assert.assertEquals("Exception", errorFields.get("error.kind").toString()); + Assert.assertEquals("java.lang.Error: abcdefg", errorFields.get("error.object").toString()); + Assert.assertEquals("error", errorFields.get("event")); + Assert.assertEquals("abcdefg", errorFields.get("message")); + } - @Override - public Object doHandleRequest(SNSEvent snsEvent, Context context) { - return "null"; + @Test + public void testS3Event() { + final MyS3RequestHandler myS3RequestHandler = new MyS3RequestHandler(); + + final List records = new ArrayList<>(); + final S3EventNotification.UserIdentityEntity userIdentity = + new S3EventNotification.UserIdentityEntity("principalId"); + final S3EventNotification.S3BucketEntity s3BucketEntity = + new S3EventNotification.S3BucketEntity("bucketName", userIdentity, "s3ARN"); + final S3EventNotification.S3Entity s3Entity = + new S3EventNotification.S3Entity("s3Entity", s3BucketEntity, null, null); + final S3EventNotification.S3EventNotificationRecord record = + new S3EventNotification.S3EventNotificationRecord( + "awsRegion", + "eventName", + "eventSource", + "2010-06-30T01:20+02:00", + "eventVersion", + null, + null, + s3Entity, + null); + records.add(record); + + myS3RequestHandler.handleRequest(new S3Event(records), createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals("s3ARN", span.tags().get("aws.lambda.eventSource.arn")); } - } - // Kinesis Stream - static class MyKinesisStreamRequestHandler - implements TracingRequestHandler { + @Test + public void testSNSEvent() { + final MySNSRequestHandler mySNSRequestHandler = new MySNSRequestHandler(); + + final SNSEvent snsEvent = new SNSEvent(); + final SNSEvent.SNSRecord snsRecord = new SNSEvent.SNSRecord(); + snsRecord.setEventSubscriptionArn("SNSEventSubscriptionArn"); + final SNSEvent.SNS sns = new SNSEvent.SNS(); + final SNSEvent.MessageAttribute messageAttribute = new SNSEvent.MessageAttribute(); + final Map messageAttributes = new HashMap<>(); + messageAttributes.put("messageAttributes.key", messageAttribute); + sns.setMessageAttributes(messageAttributes); + snsRecord.setSns(sns); + final List records = new ArrayList<>(); + records.add(snsRecord); + snsEvent.setRecords(records); + + mySNSRequestHandler.handleRequest(snsEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals("SNSEventSubscriptionArn", span.tags().get("aws.lambda.eventSource.arn")); + } - @Override - public Object doHandleRequest(KinesisEvent kinesisEvent, Context context) { - return "null"; + @Test + public void testSQSEvent() { + final MySQSRequestHandler mySQSRequestHandler = new MySQSRequestHandler(); + + final SQSEvent sqsEvent = new SQSEvent(); + final SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage(); + sqsMessage.setEventSourceArn("SQSEventSourceArn"); + final List records = new ArrayList<>(); + records.add(sqsMessage); + sqsEvent.setRecords(records); + + mySQSRequestHandler.handleRequest(sqsEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals("SQSEventSourceArn", span.tags().get("aws.lambda.eventSource.arn")); + } + + @Test + public void testKinesisStreamEvent() { + final MyKinesisStreamRequestHandler myKinesisStreamRequestHandler = + new MyKinesisStreamRequestHandler(); + + final KinesisEvent kinesisStreamEvent = new KinesisEvent(); + final KinesisEvent.KinesisEventRecord kinesisEventRecord = + new KinesisEvent.KinesisEventRecord(); + kinesisEventRecord.setEventSourceARN("KinesisStreamEventSourceARN"); + final List records = new ArrayList<>(); + records.add(kinesisEventRecord); + kinesisStreamEvent.setRecords(records); + + myKinesisStreamRequestHandler.handleRequest(kinesisStreamEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "KinesisStreamEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); } - } - // Kinesis Firehose - static class MyKinesisFirehoseRequestHandler - implements TracingRequestHandler { + @Test + public void testKinesisFirehoseEvent() { + final MyKinesisFirehoseRequestHandler myKinesisFirehoseRequestHandler = + new MyKinesisFirehoseRequestHandler(); + + final KinesisFirehoseEvent kinesisFirehoseEvent = new KinesisFirehoseEvent(); + kinesisFirehoseEvent.setDeliveryStreamArn("KinesisFirehoseDeliveryStreamArn"); + + myKinesisFirehoseRequestHandler.handleRequest(kinesisFirehoseEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "KinesisFirehoseDeliveryStreamArn", span.tags().get("aws.lambda.eventSource.arn")); + } - @Override - public Object doHandleRequest(KinesisFirehoseEvent kinesisFirehoseEvent, Context context) { - return "null"; + @Test + public void testDynamoDBEvent() { + final MyDynamoDBRequestHandler myDynamoDBRequestHandler = new MyDynamoDBRequestHandler(); + + final DynamodbEvent dynamodbEvent = new DynamodbEvent(); + final DynamodbEvent.DynamodbStreamRecord dynamodbStreamRecord = + new DynamodbEvent.DynamodbStreamRecord(); + dynamodbStreamRecord.setEventSourceARN("DynamodbEventSourceARN"); + List records = new ArrayList<>(); + records.add(dynamodbStreamRecord); + dynamodbEvent.setRecords(records); + + myDynamoDBRequestHandler.handleRequest(dynamodbEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals("DynamodbEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); } - } - // Dynamo DB - static class MyDynamoDBRequestHandler implements TracingRequestHandler { + @Test + public void testCodeCommitEvent() { + final MyCodeCommitRequestHandler myCodeCommitRequestHandler = new MyCodeCommitRequestHandler(); - @Override - public Object doHandleRequest(DynamodbEvent dynamodbEvent, Context context) { - return "null"; + final CodeCommitEvent codeCommitEvent = new CodeCommitEvent(); + final CodeCommitEvent.Record record = new CodeCommitEvent.Record(); + record.setEventSourceArn("CodeCommitEventSourceARN"); + List records = new ArrayList<>(); + records.add(record); + codeCommitEvent.setRecords(records); + + myCodeCommitRequestHandler.handleRequest(codeCommitEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals("CodeCommitEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); } - } - // Simple Queue Service (SQS) - static class MySQSRequestHandler implements TracingRequestHandler { + @Test + public void testAPIGatewayProxyRequestEvent() { + final MyApiGatewayProxyRequestHandler myApiGatewayProxyRequestHandler = + new MyApiGatewayProxyRequestHandler(); + + final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = + new APIGatewayProxyRequestEvent(); + final APIGatewayProxyRequestEvent.RequestIdentity requestIdentity = + new APIGatewayProxyRequestEvent.RequestIdentity(); + requestIdentity.setUserArn("APIGatewayProxyRequestEventUserARN"); + final APIGatewayProxyRequestEvent.ProxyRequestContext proxyRequestContext = + new APIGatewayProxyRequestEvent.ProxyRequestContext(); + proxyRequestContext.setIdentity(requestIdentity); + apiGatewayProxyRequestEvent.setRequestContext(proxyRequestContext); + apiGatewayProxyRequestEvent.setHeaders(new HashMap<>()); + + myApiGatewayProxyRequestHandler.handleRequest(apiGatewayProxyRequestEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "APIGatewayProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); + } - @Override - public Object doHandleRequest(SQSEvent sqsEvent, Context context) { - return "null"; + @Test + public void testAPIGatewayProxyRequestResponseEvent() { + int expectedStatusCode = 200; + final MyApiGatewayProxyRequestResponseHandler myApiGatewayProxyRequestHandler = + new MyApiGatewayProxyRequestResponseHandler(expectedStatusCode); + + final APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent = + new APIGatewayProxyRequestEvent(); + final APIGatewayProxyRequestEvent.RequestIdentity requestIdentity = + new APIGatewayProxyRequestEvent.RequestIdentity(); + requestIdentity.setUserArn("APIGatewayProxyRequestEventUserARN"); + final APIGatewayProxyRequestEvent.ProxyRequestContext proxyRequestContext = + new APIGatewayProxyRequestEvent.ProxyRequestContext(); + + apiGatewayProxyRequestEvent.setHeaders(new HashMap<>()); + proxyRequestContext.setIdentity(requestIdentity); + apiGatewayProxyRequestEvent.setRequestContext(proxyRequestContext); + + myApiGatewayProxyRequestHandler.handleRequest(apiGatewayProxyRequestEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "APIGatewayProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); + Assert.assertEquals(Integer.toString(expectedStatusCode), span.tags().get("http.status_code")); } - } - // Code Commit - static class MyCodeCommitRequestHandler - implements TracingRequestHandler { + @Test + public void testAPIGatewayV2ProxyRequestEvent() { + final MyApiGatewayV2ProxyRequestHandler myApiGatewayV2ProxyRequestHandler = + new MyApiGatewayV2ProxyRequestHandler(); + + final APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = + new APIGatewayV2ProxyRequestEvent(); + final APIGatewayV2ProxyRequestEvent.RequestIdentity requestIdentity = + new APIGatewayV2ProxyRequestEvent.RequestIdentity(); + requestIdentity.setUserArn("APIGatewayV2ProxyRequestEventUserARN"); + final APIGatewayV2ProxyRequestEvent.RequestContext proxyRequestContext = + new APIGatewayV2ProxyRequestEvent.RequestContext(); + proxyRequestContext.setIdentity(requestIdentity); + apiGatewayV2ProxyRequestEvent.setRequestContext(proxyRequestContext); + + myApiGatewayV2ProxyRequestHandler.handleRequest(apiGatewayV2ProxyRequestEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "APIGatewayV2ProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); + Assert.assertFalse(span.tags().containsKey("http.status_code")); + } - @Override - public Object doHandleRequest(CodeCommitEvent codeCommitEvent, Context context) { - return "null"; + @Test + public void testAPIGatewayV2ProxyRequestResponseEvent() { + int expectedStatusCode = 200; + final MyApiGatewayV2ProxyRequestResponseHandler myApiGatewayV2ProxyRequestHandler = + new MyApiGatewayV2ProxyRequestResponseHandler(expectedStatusCode); + + final APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent = + new APIGatewayV2ProxyRequestEvent(); + final APIGatewayV2ProxyRequestEvent.RequestIdentity requestIdentity = + new APIGatewayV2ProxyRequestEvent.RequestIdentity(); + requestIdentity.setUserArn("APIGatewayV2ProxyRequestEventUserARN"); + final APIGatewayV2ProxyRequestEvent.RequestContext proxyRequestContext = + new APIGatewayV2ProxyRequestEvent.RequestContext(); + proxyRequestContext.setIdentity(requestIdentity); + apiGatewayV2ProxyRequestEvent.setRequestContext(proxyRequestContext); + + myApiGatewayV2ProxyRequestHandler.handleRequest(apiGatewayV2ProxyRequestEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "APIGatewayV2ProxyRequestEventUserARN", span.tags().get("aws.lambda.eventSource.arn")); + Assert.assertEquals(Integer.toString(expectedStatusCode), span.tags().get("http.status_code")); } - } - // API Gateway Proxy Request Event - static class MyApiGatewayProxyRequestHandler - implements TracingRequestHandler { + @Test + @Ignore("We would like this but there doesn't seem to be an available arn currently") + public void testCloudWatchLogsEvent() { + final MyCloudWatchRequestHandler myCloudWatchRequestHandler = new MyCloudWatchRequestHandler(); - @Override - public Object doHandleRequest( - APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { - return "null"; + final CloudWatchLogsEvent cloudWatchLogsEvent = new CloudWatchLogsEvent(); + + myCloudWatchRequestHandler.handleRequest(cloudWatchLogsEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "CloudWatchLogsEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); } - } - static class MyApiGatewayProxyRequestResponseHandler - implements TracingRequestHandler { + @Test + @Ignore("We would like this but there doesn't seem to be an available arn currently") + public void testCloudFrontEvent() { + final MyCloudFrontRequestHandler myCloudFrontRequestHandler = new MyCloudFrontRequestHandler(); - private int statusCode; + final CloudFrontEvent cloudFrontEvent = new CloudFrontEvent(); + final CloudFrontEvent.Record record = new CloudFrontEvent.Record(); - public MyApiGatewayProxyRequestResponseHandler(int statusCode) { - this.statusCode = statusCode; + myCloudFrontRequestHandler.handleRequest(cloudFrontEvent, createContext()); + final MockSpan span = mockTracer.finishedSpans().get(0); + Assert.assertEquals( + "CloudFrontEventEventSourceARN", span.tags().get("aws.lambda.eventSource.arn")); } - @Override - public APIGatewayProxyResponseEvent doHandleRequest( - APIGatewayProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { - APIGatewayProxyResponseEvent responseEvent = new APIGatewayProxyResponseEvent(); - responseEvent.setStatusCode(statusCode); - responseEvent.setBody("null"); - return responseEvent; + static class MyRequestHandler implements TracingRequestHandler { + + @Override + public String doHandleRequest(String s, Context context) { + return "Request Handler says: hello " + s; + } } - } - static class MyApiGatewayV2ProxyRequestHandler - implements TracingRequestHandler { + static class ErrorRequestHandler implements TracingRequestHandler { - @Override - public Object doHandleRequest( - APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { - return "null"; + @Override + public String doHandleRequest(String s, Context context) { + throw new Error(s); + } } - } - static class MyApiGatewayV2ProxyRequestResponseHandler - implements TracingRequestHandler< - APIGatewayV2ProxyRequestEvent, APIGatewayV2ProxyResponseEvent> { + // S3 + static class MyS3RequestHandler implements TracingRequestHandler { - private int statusCode; + @Override + public Object doHandleRequest(S3Event s3Event, Context context) { + return "null"; + } + } - public MyApiGatewayV2ProxyRequestResponseHandler(int statusCode) { - this.statusCode = statusCode; + // Simple Notification Service (SNS) + static class MySNSRequestHandler implements TracingRequestHandler { + + @Override + public Object doHandleRequest(SNSEvent snsEvent, Context context) { + return "null"; + } } - @Override - public APIGatewayV2ProxyResponseEvent doHandleRequest( - APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { - APIGatewayV2ProxyResponseEvent responseEvent = new APIGatewayV2ProxyResponseEvent(); - responseEvent.setStatusCode(statusCode); - responseEvent.setBody("null"); - return responseEvent; + // Kinesis Stream + static class MyKinesisStreamRequestHandler + implements TracingRequestHandler { + + @Override + public Object doHandleRequest(KinesisEvent kinesisEvent, Context context) { + return "null"; + } } - } - // Cloud Front - static class MyCloudFrontRequestHandler - implements TracingRequestHandler { + // Kinesis Firehose + static class MyKinesisFirehoseRequestHandler + implements TracingRequestHandler { - @Override - public Object doHandleRequest(CloudFrontEvent cloudFrontEvent, Context context) { - return "null"; + @Override + public Object doHandleRequest(KinesisFirehoseEvent kinesisFirehoseEvent, Context context) { + return "null"; + } } - } - // Cloud Watch - static class MyCloudWatchRequestHandler - implements TracingRequestHandler { + // Dynamo DB + static class MyDynamoDBRequestHandler implements TracingRequestHandler { - @Override - public Object doHandleRequest(CloudWatchLogsEvent cloudWatchLogsEvent, Context context) { - return "null"; + @Override + public Object doHandleRequest(DynamodbEvent dynamodbEvent, Context context) { + return "null"; + } } - } - private Context createContext() { - return new Context() { - @Override - public String getAwsRequestId() { - return "123"; - } + // Simple Queue Service (SQS) + static class MySQSRequestHandler implements TracingRequestHandler { - @Override - public String getLogGroupName() { - return "logGroupName"; - } + @Override + public Object doHandleRequest(SQSEvent sqsEvent, Context context) { + return "null"; + } + } - @Override - public String getLogStreamName() { - return "getLogStreamName"; - } + // Code Commit + static class MyCodeCommitRequestHandler + implements TracingRequestHandler { - @Override - public String getFunctionName() { - return null; - } + @Override + public Object doHandleRequest(CodeCommitEvent codeCommitEvent, Context context) { + return "null"; + } + } - @Override - public String getFunctionVersion() { - return "LATEST"; - } + // API Gateway Proxy Request Event + static class MyApiGatewayProxyRequestHandler + implements TracingRequestHandler { - @Override - public String getInvokedFunctionArn() { - return "arn"; - } + @Override + public Object doHandleRequest( + APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + return "null"; + } + } - @Override - public CognitoIdentity getIdentity() { - return new CognitoIdentity() { - @Override - public String getIdentityId() { - return "identity"; - } - - @Override - public String getIdentityPoolId() { - return "identityPoolId"; - } - }; - } - - @Override - public ClientContext getClientContext() { - return null; - } - - @Override - public int getRemainingTimeInMillis() { - return 100; - } - - @Override - public int getMemoryLimitInMB() { - return 510; - } - - @Override - public LambdaLogger getLogger() { - return new LambdaLogger() { - @Override - public void log(String string) {} + static class MyApiGatewayProxyRequestResponseHandler + implements TracingRequestHandler { + + private int statusCode; + + public MyApiGatewayProxyRequestResponseHandler(int statusCode) { + this.statusCode = statusCode; + } + + @Override + public APIGatewayProxyResponseEvent doHandleRequest( + APIGatewayProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { + APIGatewayProxyResponseEvent responseEvent = new APIGatewayProxyResponseEvent(); + responseEvent.setStatusCode(statusCode); + responseEvent.setBody("null"); + return responseEvent; + } + } + + static class MyApiGatewayV2ProxyRequestHandler + implements TracingRequestHandler { + + @Override + public Object doHandleRequest( + APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { + return "null"; + } + } + + static class MyApiGatewayV2ProxyRequestResponseHandler + implements TracingRequestHandler< + APIGatewayV2ProxyRequestEvent, APIGatewayV2ProxyResponseEvent> { + + private int statusCode; + + public MyApiGatewayV2ProxyRequestResponseHandler(int statusCode) { + this.statusCode = statusCode; + } + + @Override + public APIGatewayV2ProxyResponseEvent doHandleRequest( + APIGatewayV2ProxyRequestEvent apiGatewayV2ProxyRequestEvent, Context context) { + APIGatewayV2ProxyResponseEvent responseEvent = new APIGatewayV2ProxyResponseEvent(); + responseEvent.setStatusCode(statusCode); + responseEvent.setBody("null"); + return responseEvent; + } + } + + // Cloud Front + static class MyCloudFrontRequestHandler + implements TracingRequestHandler { + + @Override + public Object doHandleRequest(CloudFrontEvent cloudFrontEvent, Context context) { + return "null"; + } + } + + // Cloud Watch + static class MyCloudWatchRequestHandler + implements TracingRequestHandler { + + @Override + public Object doHandleRequest(CloudWatchLogsEvent cloudWatchLogsEvent, Context context) { + return "null"; + } + } + + private Context createContext() { + return new Context() { + @Override + public String getAwsRequestId() { + return "123"; + } + + @Override + public String getLogGroupName() { + return "logGroupName"; + } + + @Override + public String getLogStreamName() { + return "getLogStreamName"; + } + + @Override + public String getFunctionName() { + return null; + } + + @Override + public String getFunctionVersion() { + return "LATEST"; + } + + @Override + public String getInvokedFunctionArn() { + return "arn"; + } + + @Override + public CognitoIdentity getIdentity() { + return new CognitoIdentity() { + @Override + public String getIdentityId() { + return "identity"; + } + + @Override + public String getIdentityPoolId() { + return "identityPoolId"; + } + }; + } + + @Override + public ClientContext getClientContext() { + return null; + } + + @Override + public int getRemainingTimeInMillis() { + return 100; + } + + @Override + public int getMemoryLimitInMB() { + return 510; + } + + @Override + public LambdaLogger getLogger() { + return new LambdaLogger() { + @Override + public void log(String string) { + } + + @Override + public void log(byte[] message) { + } + }; + } }; - } - }; - } + } } diff --git a/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java b/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java index d149a4d..1c4f4c1 100644 --- a/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java +++ b/src/test/java/com/newrelic/opentracing/aws/TracingRequestStreamHandlerTest.java @@ -11,148 +11,155 @@ import com.amazonaws.services.lambda.runtime.LambdaLogger; import io.opentracing.mock.MockSpan; import io.opentracing.mock.MockTracer; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; public class TracingRequestStreamHandlerTest { - private static final MockTracer mockTracer = new MockTracer(); - - @BeforeClass - public static void beforeClass() { - GlobalTracerTestUtils.initTracer(mockTracer); - } - - @Before - public void before() { - mockTracer.reset(); - // reset isColdStart before each test - LambdaTracing.isColdStart.set(true); - } - - @Test - public void testSpan() { - final MyRequestHandler handler = new MyRequestHandler(); - - byte[] input1 = {'1', '2', '3'}; - ByteArrayInputStream inputStream1 = new ByteArrayInputStream(input1); - ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream(); - handler.handleRequest(inputStream1, outputStream1, createContext()); - - final List mockSpans = mockTracer.finishedSpans(); - final MockSpan mockSpan = mockSpans.get(0); - Assert.assertEquals("handleRequest", mockSpan.operationName()); - Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); - Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); - Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); - - // Clear out span - mockTracer.reset(); - - byte[] input2 = {'a', 'b', 'c'}; - ByteArrayInputStream inputStream2 = new ByteArrayInputStream(input2); - ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); - handler.handleRequest(inputStream2, outputStream2, createContext()); - - final MockSpan secondSpan = mockTracer.finishedSpans().get(0); - Assert.assertNull(secondSpan.tags().get("aws.lambda.coldStart")); - - Assert.assertArrayEquals(outputStream1.toByteArray(), new byte[] {'1', '2', '3', '?'}); - Assert.assertArrayEquals(outputStream2.toByteArray(), new byte[] {'a', 'b', 'c', '?'}); - } - - static class MyRequestHandler implements TracingRequestStreamHandler { - - @Override - public void doHandleRequest(InputStream input, OutputStream output, Context context) { - try { - int in; - while ((in = input.read()) != -1) { - output.write(in); + private static final MockTracer mockTracer = new MockTracer(); + + @BeforeClass + public static void beforeClass() { + GlobalTracerTestUtils.initTracer(mockTracer); + } + + @Before + public void before() { + mockTracer.reset(); + // reset isColdStart before each test + LambdaTracing.isColdStart.set(true); + } + + @Test + public void testSpan() { + final MyRequestHandler handler = new MyRequestHandler(); + + byte[] input1 = { '1', '2', '3' }; + ByteArrayInputStream inputStream1 = new ByteArrayInputStream(input1); + ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream(); + handler.handleRequest(inputStream1, outputStream1, createContext()); + + final List mockSpans = mockTracer.finishedSpans(); + final MockSpan mockSpan = mockSpans.get(0); + Assert.assertEquals("handleRequest", mockSpan.operationName()); + Assert.assertEquals("123", mockSpan.tags().get("aws.requestId")); + Assert.assertEquals("arn", mockSpan.tags().get("aws.lambda.arn")); + Assert.assertEquals(true, mockSpan.tags().get("aws.lambda.coldStart")); + + // Clear out span + mockTracer.reset(); + + byte[] input2 = { 'a', 'b', 'c' }; + ByteArrayInputStream inputStream2 = new ByteArrayInputStream(input2); + ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); + handler.handleRequest(inputStream2, outputStream2, createContext()); + + final MockSpan secondSpan = mockTracer.finishedSpans().get(0); + Assert.assertNull(secondSpan.tags().get("aws.lambda.coldStart")); + + Assert.assertArrayEquals(outputStream1.toByteArray(), new byte[] { '1', '2', '3', '?' }); + Assert.assertArrayEquals(outputStream2.toByteArray(), new byte[] { 'a', 'b', 'c', '?' }); + } + + static class MyRequestHandler implements TracingRequestStreamHandler { + + @Override + public void doHandleRequest(InputStream input, OutputStream output, Context context) { + try { + int in; + while ((in = input.read()) != -1) { + output.write(in); + } + output.write('?'); + } catch (IOException e) { + } } - output.write('?'); - } catch (IOException e) { - } } - } - - private Context createContext() { - return new Context() { - @Override - public String getAwsRequestId() { - return "123"; - } - - @Override - public String getLogGroupName() { - return "logGroupName"; - } - - @Override - public String getLogStreamName() { - return "getLogStreamName"; - } - - @Override - public String getFunctionName() { - return null; - } - - @Override - public String getFunctionVersion() { - return "LATEST"; - } - - @Override - public String getInvokedFunctionArn() { - return "arn"; - } - - @Override - public CognitoIdentity getIdentity() { - return new CognitoIdentity() { - @Override - public String getIdentityId() { - return "identity"; - } - - @Override - public String getIdentityPoolId() { - return "identityPoolId"; - } - }; - } - - @Override - public ClientContext getClientContext() { - return null; - } - - @Override - public int getRemainingTimeInMillis() { - return 100; - } - - @Override - public int getMemoryLimitInMB() { - return 510; - } - - @Override - public LambdaLogger getLogger() { - return new LambdaLogger() { - @Override - public void log(String string) {} + + private Context createContext() { + return new Context() { + @Override + public String getAwsRequestId() { + return "123"; + } + + @Override + public String getLogGroupName() { + return "logGroupName"; + } + + @Override + public String getLogStreamName() { + return "getLogStreamName"; + } + + @Override + public String getFunctionName() { + return null; + } + + @Override + public String getFunctionVersion() { + return "LATEST"; + } + + @Override + public String getInvokedFunctionArn() { + return "arn"; + } + + @Override + public CognitoIdentity getIdentity() { + return new CognitoIdentity() { + @Override + public String getIdentityId() { + return "identity"; + } + + @Override + public String getIdentityPoolId() { + return "identityPoolId"; + } + }; + } + + @Override + public ClientContext getClientContext() { + return null; + } + + @Override + public int getRemainingTimeInMillis() { + return 100; + } + + @Override + public int getMemoryLimitInMB() { + return 510; + } + + @Override + public LambdaLogger getLogger() { + return new LambdaLogger() { + @Override + public void log(String string) { + } + + @Override + public void log(byte[] message) { + + } + }; + } }; - } - }; - } + } } From 900190972800b854907371642b18642c9d6433d1 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Tue, 27 May 2025 10:21:10 -0400 Subject: [PATCH 2/4] Addition of code-style XML --- .../code-style/java-agent-code-style.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 dev-tools/code-style/java-agent-code-style.xml diff --git a/dev-tools/code-style/java-agent-code-style.xml b/dev-tools/code-style/java-agent-code-style.xml new file mode 100644 index 0000000..9f56849 --- /dev/null +++ b/dev-tools/code-style/java-agent-code-style.xml @@ -0,0 +1,32 @@ + + \ No newline at end of file From 3da3938466062a001813c3710314fc96c1bc3e70 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Wed, 28 May 2025 13:53:06 -0400 Subject: [PATCH 3/4] README updates; bump version --- README.md | 11 +++++++++++ build.gradle | 10 ---------- gradle.properties | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2e1c671..465c1c2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,17 @@ This SDK provides Open Tracing instrumentation for AWS Lambda. Releases for this Versioning will have the following format: {majorVersion}.{minorVersion}.{pointVersion} +### Version 3.0.0 + +Version 3.0.0 of this library made changes to the versions of AWS dependencies. The following dependencies are now used: +- `com.amazonaws:aws-lambda-java-core:1.2.3` (previous version: 1.1.0) +- `com.amazonaws:aws-lambda-java-events:3.15.0` (previous version: 2.2.7) +- `software.amazon.awssdk:s3:2.31.43` (replaces com.amazonaws:aws-java-sdk-s3:1.12.771) +- `software.amazon.awssdk:s3-event-notifications:2.31.43` (replaces com.amazonaws:aws-java-sdk-s3:1.12.771) + +It is recommended that functions that utilize the New Relic AWS Lamdba OpenTracing Java SDK utilize the same (or higher) versions +of the underlying AWS libraries as noted above. Functions that are unable to upgrade should remain on version 2.2.0 of this library. + ### Supported OpenTracing Versions * OpenTracing 0.31.0: [com.newrelic.opentracing:java-aws-lambda:1.0.0](https://mvnrepository.com/artifact/com.newrelic.opentracing/java-aws-lambda/1.0.0) diff --git a/build.gradle b/build.gradle index 78c2256..ea5fab0 100644 --- a/build.gradle +++ b/build.gradle @@ -30,22 +30,12 @@ repositories { } dependencies { - //implementation 'software.amazon.awssdk:bom:2.31.43' - implementation 'software.amazon.awssdk:s3:2.31.43' implementation 'software.amazon.awssdk:s3-event-notifications:2.31.43' - //implementation 'software.amazon.awssdk:kinesis:2.31.43' - //implementation 'software.amazon.awssdk:dynamodb:2.31.43' implementation 'com.amazonaws:aws-lambda-java-core:1.2.3' - // 2.2.7 is earliest version that has all needed event sources implementation 'com.amazonaws:aws-lambda-java-events:3.15.0' - - //implementation 'com.amazonaws:aws-java-sdk-s3:1.12.771' - //implementation 'com.amazonaws:aws-java-sdk-kinesis:1.11.163' - //implementation 'com.amazonaws:aws-java-sdk-dynamodb:1.11.163' - implementation('io.opentracing:opentracing-api:0.33.0') implementation('io.opentracing:opentracing-util:0.33.0') implementation('io.opentracing:opentracing-noop:0.33.0') diff --git a/gradle.properties b/gradle.properties index d7b0471..9a9e852 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group = com.newrelic.opentracing -version = 2.3.0 +version = 3.0.0 From 44401d7d6eb57032d4a30bfde09b1f0b320bf42f Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Thu, 29 May 2025 08:12:19 -0400 Subject: [PATCH 4/4] update wrapper validation action --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ea26c5f..f4ac674 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # pin@v4 - - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # pin@v1 + - uses: gradle/actions/wrapper-validation@v3 - name: Set up JDK 11 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # pin@v4 with: