Skip to content

Commit 1302d0b

Browse files
feat(tracing): ability to override object mapper used for serialization
1 parent 1daf96e commit 1302d0b

File tree

5 files changed

+140
-6
lines changed

5 files changed

+140
-6
lines changed

powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
*/
1414
package software.amazon.lambda.powertools.tracing;
1515

16+
import java.util.function.Consumer;
1617
import com.amazonaws.xray.AWSXRay;
1718
import com.amazonaws.xray.entities.Entity;
1819
import com.amazonaws.xray.entities.Subsegment;
19-
20-
import java.util.function.Consumer;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
2121

2222
import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName;
2323

@@ -27,6 +27,7 @@
2727
*
2828
*/
2929
public final class TracingUtils {
30+
private static ObjectMapper objectMapper;
3031

3132
/**
3233
* Put an annotation to the current subsegment with a String value.
@@ -155,4 +156,18 @@ public static void withSubsegment(String namespace, String name, Consumer<Subseg
155156
AWSXRay.endSubsegment();
156157
}
157158
}
159+
160+
/**
161+
* Sets the instance of ObjectMapper object which is used for serialising object response when
162+
* {@code @Tracing(captureMode=CaptureMode.RESPONSE)}.
163+
*
164+
* @param objectMapper Custom implementation of object mapper to be used for serializing response
165+
*/
166+
public static void defaultObjectMapper(ObjectMapper objectMapper) {
167+
TracingUtils.objectMapper = objectMapper;
168+
}
169+
170+
public static ObjectMapper objectMapper() {
171+
return objectMapper;
172+
}
158173
}

powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
package software.amazon.lambda.powertools.tracing.internal;
1515

1616
import java.util.function.Supplier;
17-
1817
import com.amazonaws.xray.AWSXRay;
1918
import com.amazonaws.xray.entities.Subsegment;
2019
import org.aspectj.lang.ProceedingJoinPoint;
@@ -30,6 +29,7 @@
3029
import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler;
3130
import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnStreamHandler;
3231
import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName;
32+
import static software.amazon.lambda.powertools.tracing.TracingUtils.objectMapper;
3333

3434
@Aspect
3535
public final class LambdaTracingAspect {
@@ -59,7 +59,7 @@ public Object around(ProceedingJoinPoint pjp,
5959
try {
6060
Object methodReturn = pjp.proceed(proceedArgs);
6161
if (captureResponse) {
62-
segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", methodReturn);
62+
segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", null != objectMapper() ? objectMapper().writeValueAsString(methodReturn): methodReturn);
6363
}
6464

6565
if (placedOnHandlerMethod(pjp)) {
@@ -69,7 +69,7 @@ public Object around(ProceedingJoinPoint pjp,
6969
return methodReturn;
7070
} catch (Exception e) {
7171
if (captureError) {
72-
segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", e);
72+
segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", null != objectMapper() ? objectMapper().writeValueAsString(e) : e);
7373
}
7474
throw e;
7575
} finally {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
package software.amazon.lambda.powertools.tracing.handlers;
15+
16+
import java.io.IOException;
17+
import com.amazonaws.services.lambda.runtime.Context;
18+
import com.amazonaws.services.lambda.runtime.RequestHandler;
19+
import com.fasterxml.jackson.core.JsonGenerator;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.fasterxml.jackson.databind.SerializerProvider;
22+
import com.fasterxml.jackson.databind.module.SimpleModule;
23+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
24+
import software.amazon.lambda.powertools.tracing.Tracing;
25+
import software.amazon.lambda.powertools.tracing.TracingUtils;
26+
27+
import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE;
28+
29+
public class PowerTracerToolEnabledForResponseWithCustomMapper implements RequestHandler<Object, Object> {
30+
static {
31+
ObjectMapper objectMapper = new ObjectMapper();
32+
SimpleModule simpleModule = new SimpleModule();
33+
simpleModule.addSerializer(ChildClass.class, new ChildSerializer());
34+
objectMapper.registerModule(simpleModule);
35+
36+
TracingUtils.defaultObjectMapper(objectMapper);
37+
}
38+
@Override
39+
@Tracing(namespace = "lambdaHandler", captureMode = RESPONSE)
40+
public Object handleRequest(Object input, Context context) {
41+
ParentClass parentClass = new ParentClass("parent");
42+
ChildClass childClass = new ChildClass("child", parentClass);
43+
parentClass.setC(childClass);
44+
return parentClass;
45+
}
46+
47+
public class ParentClass {
48+
public String name;
49+
public ChildClass c;
50+
51+
public ParentClass(String name) {
52+
this.name = name;
53+
}
54+
55+
public void setC(ChildClass c) {
56+
this.c = c;
57+
}
58+
}
59+
60+
public class ChildClass {
61+
public String name;
62+
public ParentClass p;
63+
64+
public ChildClass(String name, ParentClass p) {
65+
this.name = name;
66+
this.p = p;
67+
}
68+
}
69+
70+
public static class ChildSerializer extends StdSerializer<ChildClass> {
71+
72+
public ChildSerializer() {
73+
this(null);
74+
}
75+
76+
public ChildSerializer(Class<ChildClass> t) {
77+
super(t);
78+
}
79+
80+
@Override
81+
public void serialize(ChildClass value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
82+
jgen.writeStartObject();
83+
jgen.writeStringField("name", value.name);
84+
jgen.writeStringField("p", value.p.name);
85+
jgen.writeEndObject();
86+
}
87+
}
88+
}

powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.io.ByteArrayInputStream;
1717
import java.io.ByteArrayOutputStream;
1818
import java.io.IOException;
19-
2019
import com.amazonaws.services.lambda.runtime.Context;
2120
import com.amazonaws.services.lambda.runtime.RequestHandler;
2221
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
@@ -34,6 +33,7 @@
3433
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledExplicitlyForResponseAndError;
3534
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForError;
3635
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForResponse;
36+
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForResponseWithCustomMapper;
3737
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForStream;
3838
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledForStreamWithNoMetaData;
3939
import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithException;
@@ -43,6 +43,7 @@
4343

4444
import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField;
4545
import static org.assertj.core.api.Assertions.assertThat;
46+
import static org.assertj.core.api.Assertions.assertThatNoException;
4647
import static org.assertj.core.api.Assertions.catchThrowable;
4748
import static org.mockito.Mockito.mockStatic;
4849
import static org.mockito.Mockito.when;
@@ -277,6 +278,28 @@ void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() {
277278
}
278279
}
279280

281+
@Test
282+
void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() {
283+
requestHandler = new PowerTracerToolEnabledForResponseWithCustomMapper();
284+
285+
requestHandler.handleRequest(new Object(), context);
286+
287+
assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy())
288+
.hasSize(1)
289+
.allSatisfy(subsegment -> {
290+
assertThat(subsegment.getMetadata())
291+
.hasSize(1)
292+
.containsKey("lambdaHandler");
293+
294+
assertThat(subsegment.getMetadata().get("lambdaHandler"))
295+
.hasFieldOrPropertyWithValue("handleRequest response", "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}");
296+
});
297+
298+
assertThatNoException().isThrownBy(AWSXRay::endSegment);
299+
300+
AWSXRay.beginSegment(LambdaTracingAspectTest.class.getName());
301+
}
302+
280303
@Test
281304
void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled() {
282305
try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class)) {

spotbugs-exclude.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
<Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/>
7272
<Method name="defaultObjectMapper"/>
7373
</And>
74+
<And>
75+
<Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/>
76+
<Method name="defaultObjectMapper"/>
77+
</And>
7478
<And>
7579
<Class name="software.amazon.lambda.powertools.sqs.SqsUtils"/>
7680
<Method name="overrideSqsClient"/>
@@ -88,6 +92,10 @@
8892
<Class name="software.amazon.lambda.powertools.logging.LoggingUtils"/>
8993
<Method name="objectMapper"/>
9094
</And>
95+
<And>
96+
<Class name="software.amazon.lambda.powertools.tracing.TracingUtils"/>
97+
<Method name="objectMapper"/>
98+
</And>
9199
<And>
92100
<Class name="software.amazon.lambda.powertools.sqs.SqsUtils"/>
93101
<Method name="objectMapper"/>

0 commit comments

Comments
 (0)