Skip to content

Commit 00a5554

Browse files
authored
Include unhandled exceptions in request log (#11)
1 parent a335042 commit 00a5554

File tree

7 files changed

+277
-233
lines changed

7 files changed

+277
-233
lines changed

src/main/java/io/apitally/common/RequestLogger.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.fasterxml.jackson.databind.ObjectMapper;
2525
import com.fasterxml.jackson.databind.node.ObjectNode;
2626

27+
import io.apitally.common.dto.ExceptionDto;
2728
import io.apitally.common.dto.Header;
2829
import io.apitally.common.dto.Request;
2930
import io.apitally.common.dto.Response;
@@ -123,7 +124,7 @@ public void setSuspendUntil(long timestamp) {
123124
this.suspendUntil = timestamp;
124125
}
125126

126-
public void logRequest(Request request, Response response) {
127+
public void logRequest(Request request, Response response, Exception exception) {
127128
if (!enabled || suspendUntil != null && suspendUntil > System.currentTimeMillis()) {
128129
return;
129130
}
@@ -197,6 +198,10 @@ public void logRequest(Request request, Response response) {
197198
item.put("uuid", UUID.randomUUID().toString());
198199
item.set("request", skipEmptyValues(objectMapper.valueToTree(request)));
199200
item.set("response", skipEmptyValues(objectMapper.valueToTree(response)));
201+
if (exception != null && config.isExceptionIncluded()) {
202+
ExceptionDto exceptionDto = new ExceptionDto(exception);
203+
item.set("exception", objectMapper.valueToTree(exceptionDto));
204+
}
200205

201206
String serializedItem = objectMapper.writeValueAsString(item);
202207
pendingWrites.add(serializedItem);

src/main/java/io/apitally/common/RequestLoggingConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class RequestLoggingConfig {
1010
private boolean requestBodyIncluded = false;
1111
private boolean responseHeadersIncluded = true;
1212
private boolean responseBodyIncluded = false;
13+
private boolean exceptionIncluded = true;
1314
private List<String> queryParamMaskPatterns = new ArrayList<>();
1415
private List<String> headerMaskPatterns = new ArrayList<>();
1516
private List<String> pathExcludePatterns = new ArrayList<>();
@@ -63,6 +64,14 @@ public void setResponseBodyIncluded(boolean responseBodyIncluded) {
6364
this.responseBodyIncluded = responseBodyIncluded;
6465
}
6566

67+
public boolean isExceptionIncluded() {
68+
return exceptionIncluded;
69+
}
70+
71+
public void setExceptionIncluded(boolean exceptionIncluded) {
72+
this.exceptionIncluded = exceptionIncluded;
73+
}
74+
6675
public List<String> getQueryParamMaskPatterns() {
6776
return queryParamMaskPatterns;
6877
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.apitally.common.dto;
2+
3+
public class ExceptionDto {
4+
private String type;
5+
private String message;
6+
private String stackTrace;
7+
8+
public ExceptionDto(Exception exception) {
9+
this.type = exception.getClass().getSimpleName();
10+
this.message = ServerError.truncateMessage(exception.getMessage());
11+
this.stackTrace = ServerError.truncateStackTrace(exception.getStackTrace());
12+
}
13+
14+
public String getType() {
15+
return type;
16+
}
17+
18+
public String getMessage() {
19+
return message;
20+
}
21+
22+
public String getStackTrace() {
23+
return stackTrace;
24+
}
25+
}

src/main/java/io/apitally/common/dto/ServerError.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public String getStackTraceString() {
6464
return stackTraceString;
6565
}
6666

67-
private String truncateMessage(String msg) {
67+
public static String truncateMessage(String msg) {
6868
msg = msg.trim();
6969
if (msg.length() <= MAX_MSG_LENGTH) {
7070
return msg;
@@ -74,7 +74,7 @@ private String truncateMessage(String msg) {
7474
return msg.substring(0, cutoff) + suffix;
7575
}
7676

77-
private String truncateStackTrace(StackTraceElement[] stackTrace) {
77+
public static String truncateStackTrace(StackTraceElement[] stackTrace) {
7878
String suffix = "... (truncated) ...";
7979
int cutoff = MAX_STACKTRACE_LENGTH - suffix.length();
8080
List<String> truncatedLines = new ArrayList<>();

src/main/java/io/apitally/spring/ApitallyFilter.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht
9292
client.consumerRegistry.addOrUpdateConsumer(consumer);
9393
final String consumerIdentifier = consumer != null ? consumer.getIdentifier() : "";
9494

95+
// Get captured exception
96+
Object capturedException = request.getAttribute("apitallyCapturedException");
97+
if (exception == null && capturedException != null && capturedException instanceof Exception) {
98+
exception = (Exception) capturedException;
99+
}
100+
95101
// Add request to counter
96102
final long requestContentLength = request.getContentLengthLong();
97103
final long requestSize = requestContentLength >= 0 ? requestContentLength
@@ -119,12 +125,12 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht
119125
new Request(startTime / 1000.0, consumerIdentifier, request.getMethod(), path,
120126
request.getRequestURL().toString(), requestHeaders, requestSize, requestBody),
121127
new Response(response.getStatus(), responseTimeInMillis / 1000.0, responseHeaders,
122-
responseSize, responseBody));
128+
responseSize, responseBody),
129+
exception);
123130
}
124131

125132
// Add validation error to counter
126133
if (response.getStatus() >= 400 && response.getStatus() < 500) {
127-
Object capturedException = request.getAttribute("apitallyCapturedException");
128134
if (capturedException instanceof ConstraintViolationException e) {
129135
for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
130136
client.validationErrorCounter.addValidationError(
@@ -144,15 +150,8 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht
144150
}
145151

146152
// Add server error to counter
147-
if (response.getStatus() == 500) {
148-
Object capturedException = request.getAttribute("apitallyCapturedException");
149-
if (exception == null && capturedException != null && capturedException instanceof Exception) {
150-
exception = (Exception) capturedException;
151-
}
152-
if (exception != null) {
153-
client.serverErrorCounter.addServerError(
154-
consumerIdentifier, request.getMethod(), path, exception);
155-
}
153+
if (response.getStatus() == 500 && exception != null) {
154+
client.serverErrorCounter.addServerError(consumerIdentifier, request.getMethod(), path, exception);
156155
}
157156
} catch (Exception e) {
158157
logger.error("Error in Apitally filter", e);

src/test/java/io/apitally/common/ApitallyClientTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void testSync() {
6868
responseHeaders,
6969
13L,
7070
"{\"items\": []}".getBytes());
71-
client.requestLogger.logRequest(request, response);
71+
client.requestLogger.logRequest(request, response, null);
7272
client.requestLogger.maintain();
7373

7474
List<Path> paths = Arrays.asList(new Path("GET", "/items"));

0 commit comments

Comments
 (0)