feat: batch logging 반영
parent
411ccb2d16
commit
5ee5c5eec3
@ -1,279 +0,0 @@
|
||||
package kr.xit.core.aop;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import kr.xit.core.biz.model.LoggingDTO;
|
||||
import kr.xit.core.biz.service.ILoggingService;
|
||||
import kr.xit.core.exception.BizRuntimeException;
|
||||
import kr.xit.core.model.ApiResponseDTO;
|
||||
import kr.xit.core.spring.util.error.ErrorParse;
|
||||
import kr.xit.core.support.slack.SlackWebhookPush;
|
||||
import kr.xit.core.support.utils.JsonUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* description : logging trace aspect
|
||||
* 공통 core 모듈의 LoggerAspect 상속 -> traceLogging / traceLoggingError / traceLoggingResult 구현
|
||||
*
|
||||
* Logging trace 구현시
|
||||
* - MDC(Mapped Diagnostic Context : logback, log4j에 포함) 사용 로깅
|
||||
* - ThreadLocal 사용
|
||||
* - nginx : proxy_set_header X-RequestID $request_id;
|
||||
* - logback log pattern : [traceId=%X{request_id}]
|
||||
*
|
||||
* app.slack-webhook.enabled: true인 경우 slack push
|
||||
* Slack webhook : SlackWebhookPush
|
||||
*
|
||||
* packageName : kr.xit.core.aop
|
||||
* fileName : TraceLoggerAspect
|
||||
* author : julim
|
||||
* date : 2023-04-28
|
||||
* ======================================================================
|
||||
* 변경일 변경자 변경 내용
|
||||
* ----------------------------------------------------------------------
|
||||
* 2023-04-28 julim 최초 생성
|
||||
* 2023-06-12 julim 배치처리시 RequestContextHolder.HttpServletRequest 객체 미사용에 따른 처리 추가
|
||||
* </pre>
|
||||
* @see kr.xit.core.support.slack.SlackWebhookPush#sendSlackAlertLog(String, String, String)
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
//@Aspect
|
||||
//@Component
|
||||
public class TraceLoggerAspect {
|
||||
|
||||
@Value("${app.slack-webhook.enabled:false}")
|
||||
private boolean isSlackEnabled;
|
||||
|
||||
@Value("#{'${app.log.mdc.exclude-patterns}'.split(',')}")
|
||||
private String[] excludes;
|
||||
|
||||
private final ILoggingService loggingService;
|
||||
private final SlackWebhookPush slackWebhookPush;
|
||||
private static final String REQUEST_TRACE_ID = "request_trace_id";
|
||||
|
||||
public TraceLoggerAspect(@Lazy ILoggingService loggingService, SlackWebhookPush slackWebhookPush) {
|
||||
this.loggingService = loggingService;
|
||||
this.slackWebhookPush = slackWebhookPush;
|
||||
}
|
||||
|
||||
@Pointcut("execution(public * egovframework..*.*(..)) || execution(public * kr.xit..*.*(..))")
|
||||
public void errorPointCut() {
|
||||
}
|
||||
|
||||
@Around(value = "@annotation(kr.xit.core.spring.annotation.TraceLogging)")
|
||||
public Object serviceTraceLogging(final ProceedingJoinPoint pjp) throws Throwable {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = attributes != null? attributes.getRequest(): null;
|
||||
|
||||
traceLogging(JsonUtils.toJson(pjp.getArgs()), request);
|
||||
Object result = pjp.proceed();
|
||||
|
||||
//noinspection rawtypes
|
||||
if(result instanceof CompletableFuture future){
|
||||
while(true) {
|
||||
if (future.isDone()) break;
|
||||
}
|
||||
traceLoggingResult(future.get());
|
||||
}else{
|
||||
traceLoggingResult(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//@AfterThrowing(value = "@annotation(kr.xit.core.spring.annotation.TraceLogging)", throwing = "error")
|
||||
@AfterThrowing(value = "errorPointCut()", throwing="error")
|
||||
public void afterThrowingProceed(final JoinPoint jp, final Throwable error) {
|
||||
log.error("+++++++++++++++>>>>>>> Batch Error log trace::{}", error.getMessage());
|
||||
traceLoggingError(jp, error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 배치 실행시 여기에서 set한 MDC 값이 batch 모듈에서 reading이 불가하여, 배치 tasklet에서 set한 trace_id로 set
|
||||
* @param params
|
||||
* @param request
|
||||
*/
|
||||
protected void traceLogging(final String params, final HttpServletRequest request) {
|
||||
if(request != null) {
|
||||
String uri = request.getRequestURI().toString();
|
||||
if(Arrays.asList(excludes).stream().anyMatch(regx -> uri.matches(regx))) return;
|
||||
|
||||
MDC.put(REQUEST_TRACE_ID,
|
||||
StringUtils.defaultString(MDC.get("request_trace_batch_id"), UUID.randomUUID().toString().replaceAll("/-/g", "")));
|
||||
MDC.put("method", request.getMethod());
|
||||
MDC.put("uri", uri);
|
||||
MDC.put("ip", request.getRemoteAddr());
|
||||
MDC.put("sessionId", request.getSession().getId());
|
||||
|
||||
// batch로 실행되는 경우 task에서 설정 : uri / method
|
||||
}else{
|
||||
// request_id는 중복 처리 되면 않되므로 여기에서 생성
|
||||
MDC.put(REQUEST_TRACE_ID, StringUtils.defaultString(MDC.get("request_trace_batch_id"), UUID.randomUUID().toString().replaceAll("-", "")));
|
||||
}
|
||||
//TODO::systemId, reqSystemId 설정 필요
|
||||
log.info("@@@@@@@@@@@@@@@@@로깅 start : [\n{}\n]",MDC.getCopyOfContextMap());
|
||||
MDC.put("systemId", "ENS");
|
||||
MDC.put("reqSystemId", "KAKAO");
|
||||
MDC.put("param", params);
|
||||
MDC.put("accessToken", "");
|
||||
|
||||
LoggingDTO loggingDTO = LoggingDTO.builder()
|
||||
.requestId(MDC.getCopyOfContextMap().get(REQUEST_TRACE_ID))
|
||||
.systemId("ENS")
|
||||
.reqSystemId("KAKAO")
|
||||
.method(MDC.getCopyOfContextMap().get("method"))
|
||||
.uri(MDC.getCopyOfContextMap().get("uri"))
|
||||
.param(params)
|
||||
.ip(MDC.getCopyOfContextMap().get("ip"))
|
||||
.accessToken("")
|
||||
.sessionId(MDC.getCopyOfContextMap().get("sessionId"))
|
||||
.build();
|
||||
loggingService.saveLogging(loggingDTO);
|
||||
|
||||
}
|
||||
|
||||
protected void traceLoggingResult(final Object result) {
|
||||
String success = "true";
|
||||
//FIXME: slack webhook log push
|
||||
if(result instanceof ApiResponseDTO<?> apiResponseDTO){
|
||||
if(!apiResponseDTO.isSuccess()){
|
||||
success = "false";
|
||||
|
||||
if(isSlackEnabled) {
|
||||
|
||||
if(RequestContextHolder.getRequestAttributes() != null) {
|
||||
slackWebhookPush.sendSlackAlertLog(
|
||||
String.format("[%s]%s", MDC.get(REQUEST_TRACE_ID), apiResponseDTO.getMessage()),
|
||||
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest());
|
||||
}else{
|
||||
slackWebhookPush.sendSlackAlertLog(
|
||||
String.format("[%s]%s", MDC.get(REQUEST_TRACE_ID), apiResponseDTO.getMessage()),
|
||||
MDC.get("uri"),
|
||||
"batch call");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(apiResponseDTO.getData() != null){
|
||||
log.info("~~~~");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoggingDTO reqDTO = LoggingDTO
|
||||
.builder()
|
||||
.requestId(MDC.get(REQUEST_TRACE_ID))
|
||||
.success(success)
|
||||
.response(getResult(result))
|
||||
.message(HttpStatus.OK.name())
|
||||
.build();
|
||||
//}
|
||||
loggingService.modifyLogging(reqDTO);
|
||||
//loggingService.saveLogging(reqDTO);
|
||||
|
||||
log.info("@@@@@@@@@@@@@@로깅 end[\n{}\n]", MDC.getCopyOfContextMap());
|
||||
|
||||
//if(RequestContextHolder.getRequestAttributes() != null)
|
||||
MDC.clear();
|
||||
//}
|
||||
}
|
||||
|
||||
private String getResult(final Object o){
|
||||
//noinspection rawtypes
|
||||
if(o instanceof Future future) {
|
||||
try {
|
||||
return JsonUtils.toJson(future.get());
|
||||
} catch (InterruptedException ie){
|
||||
// thread pool에 에러 상태 전송
|
||||
Thread.currentThread().interrupt();
|
||||
throw BizRuntimeException.create(ie);
|
||||
|
||||
} catch (ExecutionException ee) {
|
||||
throw BizRuntimeException.create(ee);
|
||||
}
|
||||
}else{
|
||||
return JsonUtils.toJson(o);
|
||||
}
|
||||
}
|
||||
|
||||
protected void traceLoggingError(final JoinPoint jp, final Throwable e) {
|
||||
log.info("~~~~~~~~~~~~~~~~~~~~~~~>>>>{}", MDC.get(REQUEST_TRACE_ID));
|
||||
// if(Checks.isEmpty(MDC.get(REQUEST_TRACE_ID))) return;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ApiResponseDTO dto = ErrorParse.extractError(e);
|
||||
|
||||
loggingService.modifyLogging(
|
||||
LoggingDTO
|
||||
.builder()
|
||||
.requestId(MDC.get(REQUEST_TRACE_ID))
|
||||
.success("false")
|
||||
.response(JsonUtils.toJson(dto))
|
||||
.message(dto.getMessage())
|
||||
.build());
|
||||
|
||||
//FIXME :: slack webhook log push
|
||||
if(isSlackEnabled){
|
||||
if(RequestContextHolder.getRequestAttributes() != null) {
|
||||
slackWebhookPush.sendSlackAlertLog(
|
||||
String.format("[%s]%s(%s)", MDC.get(REQUEST_TRACE_ID), dto.getCode(), dto.getMessage()),
|
||||
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest());
|
||||
}else{
|
||||
slackWebhookPush.sendSlackAlertLog(
|
||||
String.format("[%s]%s(%s)", MDC.get(REQUEST_TRACE_ID), dto.getCode(), dto.getMessage()),
|
||||
MDC.get("uri"),
|
||||
"batch call");
|
||||
}
|
||||
}
|
||||
log.info("@@@@@@@@@@@@ 로깅 end[\n{}\n]", MDC.getCopyOfContextMap());
|
||||
//if(RequestContextHolder.getRequestAttributes() != null)
|
||||
MDC.clear();
|
||||
}
|
||||
|
||||
@Getter
|
||||
enum ApiSystemId {
|
||||
KAKAO("KAKAO", "카카오", "/kakao/"),
|
||||
KT_BC("KT-BC", "공공알림문자", "/kt/"),
|
||||
PPLUS("POST-PLUS", "Post Plus", "/pplus/"),
|
||||
NICE("NICE", "NICE CI", "/nice/"),
|
||||
EPOST("EPOST", "E-POST", "/ag/"),
|
||||
NONE("NONE", "미정의", "/미정의/"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String desc;
|
||||
private final String uri;
|
||||
|
||||
ApiSystemId(final String code, final String desc, final String uri) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public static ApiSystemId compareByUri(final String uri){
|
||||
return Arrays.stream(ApiSystemId.values())
|
||||
.filter(ssc -> uri.contains(ssc.getUri()))
|
||||
.findFirst()
|
||||
.orElseGet(() -> ApiSystemId.valueOf("NONE"));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="kr.xit.core.biz.mapper.ILoggingMapper">
|
||||
|
||||
<insert id="saveLogging" parameterType="kr.xit.core.biz.model.LoggingDTO">
|
||||
/** logging-mysql-mapper|saveLogging|julim */
|
||||
INSERT INTO tb_cmm_api_log (
|
||||
request_id
|
||||
, system_id
|
||||
, req_system_id
|
||||
, method
|
||||
, uri
|
||||
, param
|
||||
, ip
|
||||
, access_token
|
||||
, session_id
|
||||
, success
|
||||
, response
|
||||
, message
|
||||
, regist_dt
|
||||
, regist_id
|
||||
) VALUES (
|
||||
#{requestId}
|
||||
, #{systemId}
|
||||
, #{reqSystemId}
|
||||
, #{method}
|
||||
, #{uri}
|
||||
, #{param}
|
||||
, #{ip}
|
||||
, #{accessToken}
|
||||
, #{sessionId}
|
||||
, #{success}
|
||||
, #{response}
|
||||
, #{message}
|
||||
, now(3)
|
||||
, #{registId}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateLogging" parameterType="kr.xit.core.biz.model.LoggingDTO">
|
||||
/** logging-mysql-mapper|updateLogging|julim */
|
||||
UPDATE tb_cmm_api_log
|
||||
SET success = #{success}
|
||||
, response = #{response}
|
||||
, message = #{message}
|
||||
, updt_dt = now(3)
|
||||
, updt_id = #{updtId}
|
||||
WHERE request_id = #{requestId}
|
||||
</update>
|
||||
|
||||
<select id="selectLogging" resultType="kr.xit.core.biz.model.LoggingDTO">
|
||||
/** logging-mysql-mapper|selectLogging|julim */
|
||||
SELECT request_id
|
||||
, system_id
|
||||
, req_system_id
|
||||
, method
|
||||
, uri
|
||||
, param
|
||||
, ip
|
||||
, access_token
|
||||
, session_id
|
||||
, regist_dt
|
||||
, regist_id
|
||||
FROM tb_cmm_api_log
|
||||
<where>
|
||||
<if test='requestId != null and requestId != ""'>
|
||||
AND request_id = #{requestId}
|
||||
</if>
|
||||
<if test='systemId != null and systemId != ""'>
|
||||
AND system_id = #{systemId}
|
||||
</if>
|
||||
<if test='uri != null and uri != ""'>
|
||||
AND uri LIKE CONCAT('%', #{url}, '%')
|
||||
</if>
|
||||
</where>
|
||||
|
||||
</select>
|
||||
</mapper>
|
Loading…
Reference in New Issue