Skip to content

Commit 120031f

Browse files
committed
exception handling and logging improved
1 parent b829eab commit 120031f

File tree

24 files changed

+240
-18
lines changed

24 files changed

+240
-18
lines changed

common/pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,21 @@
3939
<optional>true</optional>
4040
<scope>provided</scope>
4141
</dependency>
42+
<dependency>
43+
<groupId>io.micrometer</groupId>
44+
<artifactId>micrometer-tracing</artifactId>
45+
<optional>true</optional>
46+
<scope>provided</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.apache.commons</groupId>
50+
<artifactId>commons-lang3</artifactId>
51+
<optional>true</optional>
52+
<scope>provided</scope>
53+
</dependency>
4254
<dependency>
4355
<groupId>org.springframework</groupId>
44-
<artifactId>spring-web</artifactId>
56+
<artifactId>spring-webmvc</artifactId>
4557
<optional>true</optional>
4658
<scope>provided</scope>
4759
</dependency>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package gt.common.config;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
import java.io.Serial;
7+
8+
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Something Bad Happened")
9+
public class BaseException extends RuntimeException {
10+
11+
@Serial
12+
private static final long serialVersionUID = -420530763778423325L;
13+
14+
public BaseException(String msg) {
15+
super(msg);
16+
}
17+
18+
public BaseException(String msg, Exception e) {
19+
super(msg, e);
20+
}
21+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package gt.common.config;
2+
3+
import io.micrometer.tracing.Span;
4+
import io.micrometer.tracing.Tracer;
5+
import jakarta.servlet.http.HttpServletRequest;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.apache.commons.lang3.exception.ExceptionUtils;
8+
import org.springframework.http.HttpStatus;
9+
import org.springframework.http.ProblemDetail;
10+
import org.springframework.util.StringUtils;
11+
import org.springframework.web.bind.annotation.ExceptionHandler;
12+
import org.springframework.web.bind.annotation.ResponseStatus;
13+
import org.springframework.web.filter.ServerHttpObservationFilter;
14+
15+
@Slf4j
16+
public class CommonExceptionHandler {
17+
private final Tracer tracer;
18+
19+
public CommonExceptionHandler(Tracer tracer) {
20+
this.tracer = tracer;
21+
}
22+
23+
@ExceptionHandler(BaseException.class)
24+
ProblemDetail onResourceNotFound(HttpServletRequest request, BaseException baseException) {
25+
ResponseStatus resp = getResponseStatus(baseException);
26+
return handleError(request, baseException, resp.code(), resp.reason());
27+
}
28+
29+
static ResponseStatus getResponseStatus(BaseException ex) {
30+
return ex.getClass().getAnnotationsByType(ResponseStatus.class)[0];
31+
}
32+
33+
ProblemDetail handleError(HttpServletRequest request, Throwable error, HttpStatus status, String reason) {
34+
35+
String requestURI = request.getRequestURI();
36+
StringBuffer requestURL = request.getRequestURL();
37+
if (requestURL != null) {
38+
requestURI = requestURL.toString();
39+
}
40+
String method = request.getMethod();
41+
String queryString = StringUtils.hasText(request.getQueryString()) ? "?" + request.getQueryString() : "";
42+
43+
log.error("Failed to process {}: {}{}", method, requestURI, queryString, error);
44+
ServerHttpObservationFilter.findObservationContext(request).ifPresent(context -> context.setError(error));
45+
return createProblemDetail(status, reason, error);
46+
}
47+
48+
ProblemDetail createProblemDetail(HttpStatus status, String reason, Throwable error) {
49+
var pd = ProblemDetail.forStatus(status);
50+
pd.setTitle(status.getReasonPhrase());
51+
pd.setDetail(error.toString());
52+
pd.setProperty("reason", reason);
53+
pd.setProperty("series", status.series());
54+
pd.setProperty("rootCause", ExceptionUtils.getRootCause(error).toString());
55+
pd.setProperty("trace", getTraceParent());
56+
57+
return pd;
58+
}
59+
60+
String getTraceParent() {
61+
Span span = tracer.currentSpan();
62+
if (span == null) {
63+
return "";
64+
}
65+
66+
return "%s-%s".formatted(span.context().traceId(), span.context().spanId());
67+
}
68+
}

common/src/main/java/gt/common/config/ReqLogFilter.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import java.util.HashMap;
99
import java.util.Map;
1010

11+
/**
12+
* we want this filter to run after ServerHttpObservationFilter if span/trace id should be printed on log
13+
*/
1114
@Slf4j
12-
public
13-
class ReqLogFilter implements Filter {
15+
public class ReqLogFilter implements Filter {
1416

1517
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
1618

@@ -30,10 +32,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
3032
req.put("req.req.queryString", httpServletRequest.getQueryString());
3133
req.put("req.userAgent", httpServletRequest.getHeader("User-Agent"));
3234
req.put("req.xForwardedFor", httpServletRequest.getHeader("X-Forwarded-For"));
33-
// req.put("req.x-b3-sampled", httpServletRequest.getHeader("x-b3-sampled"));
34-
// req.put("req.x-b3-parentspanid", httpServletRequest.getHeader("x-b3-parentspanid"));
35-
req.put("req.x-b3-spanid", httpServletRequest.getHeader("x-b3-spanid"));
36-
req.put("req.x-b3-traceid", httpServletRequest.getHeader("x-b3-traceid"));
3735
}
3836

3937
log.trace("Received request {} ", req);

content-checker/content-checker-service/pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
</properties>
1818

1919
<dependencies>
20+
<dependency>
21+
<artifactId>common</artifactId>
22+
<groupId>gt.app</groupId>
23+
</dependency>
2024
<dependency>
2125
<artifactId>content-checker-service-model</artifactId>
2226
<groupId>gt.app</groupId>
@@ -25,6 +29,11 @@
2529
<groupId>org.springframework.boot</groupId>
2630
<artifactId>spring-boot-starter-web</artifactId>
2731
</dependency>
32+
<dependency>
33+
<groupId>org.springdoc</groupId>
34+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
35+
</dependency>
36+
2837
<!-- tracing -->
2938
<dependency>
3039
<groupId>org.springframework.boot</groupId>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package gt.contentchecker;
2+
3+
import gt.common.config.CommonExceptionHandler;
4+
import io.micrometer.tracing.Tracer;
5+
import org.springframework.web.bind.annotation.RestControllerAdvice;
6+
7+
@RestControllerAdvice
8+
class ExceptionHandler extends CommonExceptionHandler {
9+
public ExceptionHandler(Tracer tracer) {
10+
super(tracer);
11+
}
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="refresh" content="0; url='/swagger-ui.html'"/>
5+
</head>
6+
<body>
7+
<p>Open <a href="/swagger-ui.html">Swagger UI</a>.</p>
8+
<p>Open <a href="/swagger-ui.html">Swagger UI</a>.</p>
9+
</body>
10+
</html>

email/email-service/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
<artifactId>spring-boot-starter-mail</artifactId>
4040
</dependency>
4141

42+
<dependency>
43+
<groupId>org.springdoc</groupId>
44+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
45+
</dependency>
4246
<!-- tracing -->
4347
<dependency>
4448
<groupId>org.springframework.boot</groupId>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package gt.mail.config;
2+
3+
import gt.common.config.CommonExceptionHandler;
4+
import io.micrometer.tracing.Tracer;
5+
import org.springframework.web.bind.annotation.RestControllerAdvice;
6+
7+
@RestControllerAdvice
8+
class ExceptionHandler extends CommonExceptionHandler {
9+
public ExceptionHandler(Tracer tracer) {
10+
super(tracer);
11+
}
12+
}

email/email-service/src/main/java/gt/mail/config/WebConfigurer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
@Slf4j
1414
public class WebConfigurer {
1515

16+
1617
@Bean
1718
public FilterRegistrationBean<ReqLogFilter> loggingFilter() {
1819
FilterRegistrationBean<ReqLogFilter> registrationBean = new FilterRegistrationBean<>();
1920

2021
registrationBean.setFilter(new ReqLogFilter());
21-
registrationBean.setOrder((Ordered.HIGHEST_PRECEDENCE));
22+
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
2223

2324
return registrationBean;
2425
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package gt.mail.web.rest;
22

3+
import gt.common.config.BaseException;
34
import lombok.RequiredArgsConstructor;
45
import lombok.extern.slf4j.Slf4j;
56
import org.springframework.web.bind.annotation.GetMapping;
67
import org.springframework.web.bind.annotation.RequestMapping;
78
import org.springframework.web.bind.annotation.RestController;
89

910
import java.util.Map;
11+
import java.util.Random;
1012

1113
@RestController
1214
@RequiredArgsConstructor
@@ -15,7 +17,11 @@
1517
public class HelloResource {
1618

1719
@GetMapping("/hello")
18-
public Map<String, String> sayHello() {
20+
public Map<String, String> sayHello() throws Exception {
21+
log.info("Received hello request");
22+
if (new Random().nextBoolean()) {
23+
throw new BaseException("Something");
24+
}
1925
return Map.of("hello", "world");
2026
}
2127
}

email/email-service/src/main/resources/application.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ spring:
1414

1515
server:
1616
port: 8085
17-
17+
error:
18+
include-stacktrace: never
19+
include-exception: true
20+
include-binding-errors: always
21+
include-message: always
1822
logging:
1923
level:
2024
org.springframework.web: INFO
2125
ROOT: WARN
22-
gt: DEBUG
26+
gt: TRACE
2327

2428
management:
2529
tracing:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="refresh" content="0; url='/swagger-ui.html'"/>
5+
</head>
6+
<body>
7+
<p>Open <a href="/swagger-ui.html">Swagger UI</a>.</p>
8+
<p>Open <a href="/swagger-ui.html">Swagger UI</a>.</p>
9+
</body>
10+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package gt.app.config;
2+
3+
import gt.common.config.CommonExceptionHandler;
4+
import io.micrometer.tracing.Tracer;
5+
import org.springframework.web.bind.annotation.RestControllerAdvice;
6+
7+
@RestControllerAdvice
8+
class ExceptionHandler extends CommonExceptionHandler {
9+
public ExceptionHandler(Tracer tracer) {
10+
super(tracer);
11+
}
12+
}

main-app/main-webapp/src/main/java/gt/app/config/WebMvcConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public FilterRegistrationBean<ReqLogFilter> loggingFilter() {
5959
var registrationBean = new FilterRegistrationBean<ReqLogFilter>();
6060

6161
registrationBean.setFilter(new ReqLogFilter());
62-
registrationBean.setOrder((Ordered.HIGHEST_PRECEDENCE));
62+
registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE);
6363

6464
return registrationBean;
6565
}

main-app/main-webapp/src/main/java/gt/app/exception/DuplicateRecordException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package gt.app.exception;
22

3+
import gt.common.config.BaseException;
34
import org.springframework.http.HttpStatus;
45
import org.springframework.web.bind.annotation.ResponseStatus;
56

67
@ResponseStatus(value = HttpStatus.NOT_FOUND)
7-
public class DuplicateRecordException extends RuntimeException {
8+
public class DuplicateRecordException extends BaseException {
89

910
public DuplicateRecordException(String description) {
1011
super(description);

main-app/main-webapp/src/main/java/gt/app/exception/OperationNotAllowedException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package gt.app.exception;
22

3+
import gt.common.config.BaseException;
34
import org.springframework.http.HttpStatus;
45
import org.springframework.web.bind.annotation.ResponseStatus;
56

67
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
7-
public class OperationNotAllowedException extends RuntimeException {
8+
public class OperationNotAllowedException extends BaseException {
89

910
public OperationNotAllowedException(String what) {
1011
super(what);

main-app/main-webapp/src/main/java/gt/app/exception/RecordNotFoundException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package gt.app.exception;
22

3+
import gt.common.config.BaseException;
34
import org.springframework.http.HttpStatus;
45
import org.springframework.web.bind.annotation.ResponseStatus;
56

67
@ResponseStatus(value = HttpStatus.NOT_FOUND)
7-
public class RecordNotFoundException extends RuntimeException {
8+
public class RecordNotFoundException extends BaseException {
89

910
public RecordNotFoundException(String description) {
1011
super(description);

main-app/main-webapp/src/main/java/gt/app/web/mvc/ErrorControllerAdvice.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
@Slf4j
1414
class ErrorControllerAdvice {
1515

16-
1716
@ExceptionHandler(Throwable.class)
1817
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
1918
public String exception(final Throwable throwable, final Model model) {
@@ -32,7 +31,6 @@ public String storageException(final StorageException throwable, final Model mod
3231
return "error";
3332
}
3433

35-
3634
@ExceptionHandler(RetrievalException.class)
3735
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
3836
public String retrievalException(final RetrievalException throwable, final Model model) {

main-app/main-webapp/src/main/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ logging:
3131
cloud: INFO
3232
sql: INFO
3333
ROOT: WARN
34-
gt: DEBUG
34+
gt: TRACE
3535
# 'org.springframework.web.socket': TRACE
3636

3737

main-app/report-service/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
</properties>
1818

1919
<dependencies>
20+
<dependency>
21+
<artifactId>common</artifactId>
22+
<groupId>gt.app</groupId>
23+
</dependency>
2024
<dependency>
2125
<artifactId>main-orm-jooq</artifactId>
2226
<groupId>gt.app</groupId>
@@ -25,6 +29,10 @@
2529
<groupId>org.springframework.boot</groupId>
2630
<artifactId>spring-boot-starter-jooq</artifactId>
2731
</dependency>
32+
<dependency>
33+
<groupId>org.springdoc</groupId>
34+
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
35+
</dependency>
2836
<!-- tracing -->
2937
<dependency>
3038
<groupId>org.springframework.boot</groupId>

0 commit comments

Comments
 (0)