-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat/logger] 로깅 모듈 (@ServiceLog) #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3d49ea2
c0aa504
e70c573
1f5b919
29078be
4057f4d
743b29f
a8419c8
af2d90a
e9fd8c6
0efe234
cd6b3af
c41eb6e
35b55d0
d645292
bda4f50
2f01a02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package org.runimo.runimo.common.log; | ||
|
|
||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.aspectj.lang.ProceedingJoinPoint; | ||
| import org.aspectj.lang.annotation.Around; | ||
| import org.aspectj.lang.annotation.Aspect; | ||
| import org.aspectj.lang.annotation.Before; | ||
| import org.aspectj.lang.annotation.Pointcut; | ||
| import org.runimo.runimo.common.log.model.HttpRequestLogInfo; | ||
| import org.runimo.runimo.common.log.model.MethodEndLogInfo; | ||
| import org.runimo.runimo.common.log.model.MethodStartLogInfo; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
|
||
| @Aspect | ||
| @Slf4j | ||
| @RequiredArgsConstructor | ||
| @Component | ||
| public class HttpRequestLogAspect { | ||
|
|
||
| private final LogMessageFormatter logMessageFormatter; | ||
|
|
||
| @Pointcut("execution(* org.runimo.runimo..controller.*Controller.*(..))") | ||
| private void controller() { | ||
| } | ||
|
|
||
| @Before("controller()") | ||
| public void apiRequestLogger() { | ||
| ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()); | ||
| if (attributes == null) { | ||
| log.info("ServletRequestAttributes is null"); | ||
| return; | ||
| } | ||
|
|
||
| HttpServletRequest request = attributes.getRequest(); | ||
| HttpRequestLogInfo logInfo = HttpRequestLogInfo.of(request); | ||
|
|
||
| log.info(logMessageFormatter.toHttpRequestLogMessage(logInfo)); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,108 @@ | ||||||||||||||||||||||||||||||||||
| package org.runimo.runimo.common.log; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import java.time.ZonedDateTime; | ||||||||||||||||||||||||||||||||||
| import java.time.format.DateTimeFormatter; | ||||||||||||||||||||||||||||||||||
| import java.util.Iterator; | ||||||||||||||||||||||||||||||||||
| import java.util.LinkedHashMap; | ||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||
| import java.util.Map.Entry; | ||||||||||||||||||||||||||||||||||
| import org.runimo.runimo.common.log.model.HttpRequestLogInfo; | ||||||||||||||||||||||||||||||||||
| import org.runimo.runimo.common.log.model.MethodEndLogInfo; | ||||||||||||||||||||||||||||||||||
| import org.runimo.runimo.common.log.model.MethodStartLogInfo; | ||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Component | ||||||||||||||||||||||||||||||||||
| public class LogMessageFormatter { // TODO : 중복코드 리팩토링 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public String toHttpRequestLogMessage(HttpRequestLogInfo logInfo) { | ||||||||||||||||||||||||||||||||||
| String queryParamString = convertMapToLogFormatString(logInfo.queryParams(), | ||||||||||||||||||||||||||||||||||
| new StringBuilder()).toString(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Map<String, String> logs = new LinkedHashMap<>(); | ||||||||||||||||||||||||||||||||||
| logs.put("method", logInfo.requestMethod()); | ||||||||||||||||||||||||||||||||||
| logs.put("uri", logInfo.uri()); | ||||||||||||||||||||||||||||||||||
| logs.put("query_params", queryParamString); | ||||||||||||||||||||||||||||||||||
| logs.put("time", getCurrentTime()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StringBuilder sb = new StringBuilder(); | ||||||||||||||||||||||||||||||||||
| sb.append("HTTP_REQUEST "); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| convertMapToLogFormatString(logs, sb); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return sb.toString(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public String toMethodStartLogMessage(MethodStartLogInfo logInfo) { | ||||||||||||||||||||||||||||||||||
| String paramString = convertMapToLogFormatString(logInfo.params(), | ||||||||||||||||||||||||||||||||||
| new StringBuilder()).toString(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Map<String, String> logs = new LinkedHashMap<>(); | ||||||||||||||||||||||||||||||||||
| logs.put("name", logInfo.className() + "." + logInfo.methodName()); | ||||||||||||||||||||||||||||||||||
| logs.put("authenticated", String.valueOf(logInfo.authenticated())); | ||||||||||||||||||||||||||||||||||
| logs.put("user_id", logInfo.userId()); | ||||||||||||||||||||||||||||||||||
| logs.put("params", paramString); | ||||||||||||||||||||||||||||||||||
| logs.put("time", getCurrentTime()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StringBuilder sb = new StringBuilder(); | ||||||||||||||||||||||||||||||||||
| sb.append("METHOD_CALL "); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| convertMapToLogFormatString(logs, sb); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return sb.toString(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public String toMethodEndLogMessage(MethodEndLogInfo logInfo) { | ||||||||||||||||||||||||||||||||||
| Map<String, String> logs = new LinkedHashMap<>(); | ||||||||||||||||||||||||||||||||||
| logs.put("name", logInfo.className() + "." + logInfo.methodName()); | ||||||||||||||||||||||||||||||||||
| logs.put("elapsed_time", logInfo.elapsedTimeMillis() + "ms"); | ||||||||||||||||||||||||||||||||||
| logs.put("return", logInfo.returnData()); | ||||||||||||||||||||||||||||||||||
| logs.put("time", getCurrentTime()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StringBuilder sb = new StringBuilder(); | ||||||||||||||||||||||||||||||||||
| sb.append("METHOD_END "); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| convertMapToLogFormatString(logs, sb); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return sb.toString(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public String toMethodErrorLogMessage(Throwable ex) { | ||||||||||||||||||||||||||||||||||
| StringBuilder sb = new StringBuilder(); | ||||||||||||||||||||||||||||||||||
| sb.append("METHOD_EXCEPTION "); | ||||||||||||||||||||||||||||||||||
| sb.append(ex.getMessage()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return sb.toString(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance error log format to include more debug information The current method error log only includes the exception message. For effective debugging, include the exception type and method information as well. Improve the exception logging by adding more context: public String toMethodErrorLogMessage(Throwable ex) {
StringBuilder sb = new StringBuilder();
sb.append("METHOD_EXCEPTION ");
- sb.append(ex.getMessage());
+ sb.append("[exception_type=").append(ex.getClass().getName());
+ sb.append(", message=").append(ex.getMessage());
+ sb.append("]");
return sb.toString();
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private StringBuilder convertMapToLogFormatString(Map<String, String> infoMap, | ||||||||||||||||||||||||||||||||||
| StringBuilder sb) { | ||||||||||||||||||||||||||||||||||
| sb.append("["); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Iterator<Entry<String, String>> it = infoMap.entrySet().iterator(); | ||||||||||||||||||||||||||||||||||
| while (it.hasNext()) { | ||||||||||||||||||||||||||||||||||
| Entry<String, String> entry = it.next(); | ||||||||||||||||||||||||||||||||||
| sb.append(convertCamelCaseToSnakeCase(entry.getKey())); | ||||||||||||||||||||||||||||||||||
| sb.append("="); | ||||||||||||||||||||||||||||||||||
| sb.append(entry.getValue()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (it.hasNext()) { | ||||||||||||||||||||||||||||||||||
| sb.append(", "); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| sb.append("]"); | ||||||||||||||||||||||||||||||||||
| return sb; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private String getCurrentTime() { | ||||||||||||||||||||||||||||||||||
| return ZonedDateTime.now().toString(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private String convertCamelCaseToSnakeCase(String camelCase) { | ||||||||||||||||||||||||||||||||||
ekgns33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| return camelCase | ||||||||||||||||||||||||||||||||||
| .replaceAll("([A-Z])(?=[A-Z])", "$1_") | ||||||||||||||||||||||||||||||||||
| .replaceAll("([a-z])([A-Z])", "$1_$2") | ||||||||||||||||||||||||||||||||||
| .toLowerCase(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package org.runimo.runimo.common.log; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.aspectj.lang.ProceedingJoinPoint; | ||
| import org.aspectj.lang.annotation.Around; | ||
| import org.aspectj.lang.annotation.Aspect; | ||
| import org.aspectj.lang.annotation.Pointcut; | ||
| import org.runimo.runimo.common.log.model.MethodEndLogInfo; | ||
| import org.runimo.runimo.common.log.model.MethodStartLogInfo; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Aspect | ||
| @Slf4j | ||
| @RequiredArgsConstructor | ||
| @Component | ||
| public class MethodLogAspect { | ||
|
|
||
| private final LogMessageFormatter logMessageFormatter; | ||
|
|
||
| @Pointcut("@within(org.runimo.runimo.common.log.ServiceLog) || @annotation(org.runimo.runimo.common.log.ServiceLog)") | ||
| private void annotatedClassAndMethod() { | ||
| } | ||
|
|
||
| @Around("annotatedClassAndMethod()") | ||
| public Object calledMethodLogger(ProceedingJoinPoint pjp) throws Throwable { | ||
| MethodStartLogInfo methodStartLogInfo = getMethodStartLogInfo(pjp); | ||
| log.info(logMessageFormatter.toMethodStartLogMessage(methodStartLogInfo)); | ||
|
|
||
| long startTime = getCurrentTimeMillis(); | ||
| long endTime; | ||
| Object proceedReturn = null; | ||
| try { | ||
| proceedReturn = pjp.proceed(); | ||
| } catch (Throwable ex) { | ||
| log.error(logMessageFormatter.toMethodErrorLogMessage(ex)); | ||
| } | ||
| endTime = getCurrentTimeMillis(); | ||
|
|
||
| MethodEndLogInfo methodEndLogInfo = MethodEndLogInfo.of(pjp, endTime - startTime, | ||
| proceedReturn); | ||
| log.info(logMessageFormatter.toMethodEndLogMessage(methodEndLogInfo)); | ||
|
|
||
| return proceedReturn; | ||
| } | ||
|
|
||
| private static MethodStartLogInfo getMethodStartLogInfo(ProceedingJoinPoint pjp) { | ||
| MethodStartLogInfo methodStartLogInfo; | ||
|
|
||
| Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
| if (authentication != null) { | ||
| methodStartLogInfo = MethodStartLogInfo.of(pjp, true, authentication.getName()); | ||
| } else { | ||
| methodStartLogInfo = MethodStartLogInfo.of(pjp, false, null); | ||
| } | ||
|
|
||
| return methodStartLogInfo; | ||
| } | ||
|
|
||
| private long getCurrentTimeMillis() { | ||
| return System.currentTimeMillis(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package org.runimo.runimo.common.log; | ||
|
|
||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
|
|
||
| /** | ||
| * 비즈니스 로그 기록 어노테이션 - 메서드 실행 시 메서드명, 인자 목록, 반환값, 소요시간 등의 정보를 로깅 | ||
| */ | ||
| @Target({ElementType.TYPE, ElementType.METHOD}) | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface ServiceLog { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package org.runimo.runimo.common.log.model; | ||
|
|
||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import java.util.Collections; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.StringTokenizer; | ||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
|
|
||
| @Builder(access = AccessLevel.PRIVATE) | ||
| public record HttpRequestLogInfo( | ||
| String requestMethod, | ||
| String uri, | ||
| Map<String, String> queryParams | ||
ekgns33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) { | ||
|
|
||
| public static HttpRequestLogInfo of(HttpServletRequest request) { | ||
| String queryString = request.getQueryString(); | ||
|
|
||
| Map<String, String> queryParams = getQueryParamMap(queryString); | ||
|
|
||
| return HttpRequestLogInfo.builder() | ||
| .requestMethod(request.getMethod()) | ||
| .uri(request.getRequestURI()) | ||
| .queryParams(queryParams) | ||
| .build(); | ||
| } | ||
|
|
||
| private static Map<String, String> getQueryParamMap(String queryString) { | ||
| if (queryString == null || queryString.isBlank()) { | ||
| return Collections.emptyMap(); | ||
| } | ||
|
|
||
| Map<String, String> queryParams = new HashMap<>(); | ||
|
|
||
| String[] paramPairs = queryString.split("&"); | ||
| for (String paramPair : paramPairs) { | ||
| String[] keyVal = paramPair.split("=", 2); | ||
|
|
||
| String val = keyVal.length < 2 ? "" : keyVal[1]; | ||
| queryParams.put(keyVal[0], val); | ||
| } | ||
|
|
||
| return queryParams; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package org.runimo.runimo.common.log.model; | ||
|
|
||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
| import org.aspectj.lang.JoinPoint; | ||
|
|
||
| @Builder(access = AccessLevel.PRIVATE) | ||
| public record MethodEndLogInfo( | ||
| String className, | ||
| String methodName, | ||
| long elapsedTimeMillis, | ||
| String returnData | ||
| ) { | ||
|
|
||
| public static MethodEndLogInfo of(JoinPoint joinPoint, long elapsedTimeMillis, | ||
| Object returnData) { | ||
| return MethodEndLogInfo.builder() | ||
| .className(getClassName(joinPoint)) | ||
| .methodName(joinPoint.getSignature().getName()) | ||
| .elapsedTimeMillis(elapsedTimeMillis) | ||
| .returnData(String.valueOf(returnData)) | ||
| .build(); | ||
| } | ||
|
|
||
| private static String getClassName(JoinPoint joinPoint) { | ||
| String classPath = joinPoint.getSignature().getDeclaringTypeName(); | ||
| return classPath.substring(classPath.lastIndexOf(".") + 1); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package org.runimo.runimo.common.log.model; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
| import org.aspectj.lang.JoinPoint; | ||
| import org.aspectj.lang.reflect.MethodSignature; | ||
|
|
||
| @Builder(access = AccessLevel.PRIVATE) | ||
| public record MethodStartLogInfo( | ||
| String className, | ||
| String methodName, | ||
| boolean authenticated, | ||
| String userId, | ||
| Map<String, String> params | ||
| ) { | ||
|
|
||
| public static MethodStartLogInfo of(JoinPoint joinPoint, boolean authenticated, String userId) { | ||
| String className = getClassName(joinPoint); | ||
|
|
||
| Map<String, String> params = getParamMap(joinPoint); | ||
|
|
||
| return MethodStartLogInfo.builder() | ||
| .className(className) | ||
| .methodName(joinPoint.getSignature().getName()) | ||
| .authenticated(authenticated) | ||
| .userId(userId) | ||
| .params(params) | ||
| .build(); | ||
| } | ||
|
|
||
| private static Map<String, String> getParamMap(JoinPoint joinPoint) { | ||
| MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); | ||
| String[] parameterNames = methodSignature.getParameterNames(); | ||
| Object[] parameterValues = joinPoint.getArgs(); | ||
|
|
||
| Map<String, String> params = new HashMap<>(); | ||
| for (int i = 0; i < parameterNames.length; i++) { | ||
| params.put(parameterNames[i], String.valueOf(parameterValues[i])); | ||
| } | ||
| return params; | ||
| } | ||
|
|
||
| private static String getClassName(JoinPoint joinPoint) { | ||
| String classPath = joinPoint.getSignature().getDeclaringTypeName(); | ||
| return classPath.substring(classPath.lastIndexOf(".") + 1); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.