Skip to content

Commit 994467a

Browse files
committed
FEAT:(BACKENDDEV-1930) loggingUtil 및 LogEnabled 추가
1 parent 61d745e commit 994467a

File tree

10 files changed

+309
-2
lines changed

10 files changed

+309
-2
lines changed

.idea/codeStyles/Project.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ dependencies {
3333

3434
//springframework
3535
implementation 'org.springframework.boot:spring-boot-starter-web:3.5.0'
36+
37+
//aop
38+
implementation 'org.springframework.boot:spring-boot-starter-aop:3.5.0'
3639
}
3740

3841
// Apply a specific Java toolchain to ease working on different environments.
@@ -49,7 +52,7 @@ tasks.named('test') {
4952
useJUnitPlatform()
5053
}
5154

52-
version = '1.0.7'
55+
version = '1.0.8'
5356

5457
publishing {
5558
repositories {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package kr.teamo2.utils;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import java.util.Enumeration;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Optional;
8+
import java.util.UUID;
9+
import lombok.AccessLevel;
10+
import lombok.NoArgsConstructor;
11+
import org.springframework.web.context.request.RequestContextHolder;
12+
import org.springframework.web.context.request.ServletRequestAttributes;
13+
14+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
15+
public class HttpServletUtil {
16+
17+
public static final String INTERNAL_REQUEST_ID_HEADER_KEY = "X-Internal-Request-ID";
18+
19+
public static String generateAndSetTrackingId() {
20+
String trackingId = UUID.randomUUID().toString();
21+
Optional
22+
.ofNullable(getHttpServletRequest())
23+
.ifPresent(request -> request.setAttribute(INTERNAL_REQUEST_ID_HEADER_KEY, trackingId));
24+
return trackingId;
25+
}
26+
27+
private static HttpServletRequest getHttpServletRequest() {
28+
try {
29+
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
30+
} catch (IllegalStateException ignored) {
31+
return null;
32+
}
33+
}
34+
35+
public static String getTrackingId() {
36+
return Optional
37+
.ofNullable(getHttpServletRequest())
38+
.flatMap(httpServletRequest -> Optional
39+
.ofNullable(httpServletRequest.getAttribute(INTERNAL_REQUEST_ID_HEADER_KEY))
40+
.map(Object::toString))
41+
.orElse(null);
42+
}
43+
44+
public static Map<String, String> requestToHeaderMap() {
45+
HttpServletRequest request = getHttpServletRequest();
46+
if (request == null) {
47+
return new HashMap<>();
48+
}
49+
Map<String, String> headerMap = new HashMap<>();
50+
Enumeration<String> headerNames = request.getHeaderNames();
51+
if (headerNames == null) {
52+
return new HashMap<>();
53+
}
54+
while (headerNames.hasMoreElements()) {
55+
String headerName = headerNames.nextElement();
56+
String headerValue = request.getHeader(headerName);
57+
headerMap.put(headerName, headerValue);
58+
}
59+
return headerMap;
60+
}
61+
62+
public static String getHttpMethod() {
63+
return Optional
64+
.ofNullable(getHttpServletRequest())
65+
.map(HttpServletRequest::getMethod)
66+
.orElse(null);
67+
}
68+
69+
public static String getUrlAndQueryString() {
70+
return Optional
71+
.ofNullable(getHttpServletRequest())
72+
.map(request -> request.getRequestURI() + (request.getQueryString() != null ? "?" + request.getQueryString() : ""))
73+
.orElse(null);
74+
}
75+
}
76+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import kr.teamo2.utils.HttpServletUtil;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.log4j.Log4j2;
8+
import org.aspectj.lang.JoinPoint;
9+
import org.springframework.http.HttpMethod;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.stereotype.Component;
12+
13+
@Log4j2
14+
@Component
15+
@RequiredArgsConstructor
16+
public class ApiLogger extends LoggingPointCut {
17+
18+
private final ObjectMapper objectMapper;
19+
20+
public void beforeLog(JoinPoint joinPoint) {
21+
String trackingId = HttpServletUtil.generateAndSetTrackingId();
22+
23+
Object[] args = joinPoint.getArgs();
24+
25+
String urlAndQueryString = HttpServletUtil.getUrlAndQueryString();
26+
RequestLog.RequestLogBuilder requestLogBuilder = RequestLog.builder()
27+
.requestID(trackingId)
28+
.url(urlAndQueryString)
29+
.method(HttpServletUtil.getHttpMethod())
30+
.header(HttpServletUtil.requestToHeaderMap());
31+
32+
for (Object o : args) {
33+
if (HttpServletUtil.getHttpMethod().equals(HttpMethod.POST.name())) {
34+
try {
35+
String bodyJson = objectMapper.writeValueAsString(o);
36+
requestLogBuilder.body(bodyJson);
37+
} catch (Exception e) {
38+
requestLogBuilder.body("Failed to serialize body: " + e.getMessage());
39+
}
40+
}
41+
}
42+
log.info("[ApiLogger] - {}", requestLogBuilder.build());
43+
}
44+
45+
public void afterLog(ResponseEntity response) {
46+
ResponseLog responseLog;
47+
boolean isSuccessful = response.getStatusCode().is2xxSuccessful();
48+
49+
Object body = response.getBody();
50+
String bodyJson = null;
51+
52+
try {
53+
bodyJson = this.objectMapper.writeValueAsString(body);
54+
} catch (JsonProcessingException e) {
55+
bodyJson = String.valueOf(body);
56+
}
57+
58+
responseLog = isSuccessful
59+
? ResponseLog.initSuccess()
60+
.requestId(HttpServletUtil.getTrackingId())
61+
.body(bodyJson)
62+
.build()
63+
: ResponseLog.initFail()
64+
.requestId(HttpServletUtil.getTrackingId())
65+
.body(bodyJson)
66+
.statusCode(response.getStatusCode().value())
67+
.build();
68+
69+
log.info("[ApiLogger] - {}", responseLog);
70+
}
71+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.aspectj.lang.JoinPoint;
5+
import org.aspectj.lang.annotation.AfterReturning;
6+
import org.aspectj.lang.annotation.Aspect;
7+
import org.aspectj.lang.annotation.Before;
8+
import org.aspectj.lang.reflect.MethodSignature;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.stereotype.Component;
11+
12+
@Aspect
13+
@Component
14+
@RequiredArgsConstructor
15+
public class ApiLoggingAspect extends LoggingPointCut {
16+
17+
private final ApiLogger apiLogger;
18+
19+
@Before("logEnabled()")
20+
public void apiBeforeLogging(JoinPoint joinPoint) {
21+
Class<?> returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
22+
if (ResponseEntity.class.isAssignableFrom(returnType)) {
23+
apiLogger.beforeLog(joinPoint);
24+
}
25+
}
26+
27+
@AfterReturning(value = "logEnabled()", returning = "response")
28+
public void apiAfterLogging(ResponseEntity response) {
29+
apiLogger.afterLog(response);
30+
}
31+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import org.springframework.core.annotation.AliasFor;
8+
9+
@Retention(RetentionPolicy.RUNTIME)
10+
@Target(ElementType.METHOD)
11+
public @interface LogEnabled {
12+
13+
@AliasFor("collection")
14+
String value() default "";
15+
16+
@AliasFor("value")
17+
String collection() default "";
18+
19+
boolean isEnabled() default true;
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
4+
import org.aspectj.lang.annotation.Pointcut;
5+
6+
public class LoggingPointCut {
7+
8+
@Pointcut("@annotation(kr.teamo2.utils.loggingUtil.LogEnabled)")
9+
public void logEnabled() {}
10+
11+
@Pointcut("args(..)")
12+
public void zeroArgs() {}
13+
14+
@Pointcut("args(*, ..)")
15+
public void oneArgs() {}
16+
17+
@Pointcut("args(*, *, ..)")
18+
public void twoArgs() {}
19+
20+
@Pointcut("args(*, *, *, ..)")
21+
public void threeArgs() {}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.ToString;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
13+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
14+
@ToString
15+
public class RequestId {
16+
17+
private String requestID;
18+
}
19+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.ToString;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
13+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
14+
@ToString
15+
public class RequestLog {
16+
17+
private String requestID;
18+
19+
private String url;
20+
21+
private String method;
22+
23+
private Object header;
24+
25+
private Object body;
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package kr.teamo2.utils.loggingUtil;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.ToString;
9+
10+
@Getter
11+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
12+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
13+
@ToString
14+
public class ResponseLog {
15+
16+
private String requestID;
17+
18+
private Integer statusCode;
19+
20+
private Object body;
21+
22+
private boolean isError;
23+
24+
@Builder(builderClassName = "InitSuccess", builderMethodName = "initSuccess")
25+
public ResponseLog(String requestId, Object body) {
26+
this.requestID = requestId;
27+
this.statusCode = 200;
28+
this.body = body;
29+
this.isError = false;
30+
}
31+
32+
@Builder(builderClassName = "InitFail", builderMethodName = "initFail")
33+
public ResponseLog(String requestId, Object body, Integer statusCode) {
34+
this.requestID = requestId;
35+
this.statusCode = statusCode;
36+
this.body = body;
37+
this.isError = true;
38+
}
39+
}
40+

0 commit comments

Comments
 (0)