Skip to content
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

백엔드 로그 리팩토링 #859

Open
wants to merge 12 commits into
base: dev/be
Choose a base branch
from
Open

Conversation

HoeSeong123
Copy link
Contributor

@HoeSeong123 HoeSeong123 commented Oct 23, 2024

⚡️ 관련 이슈

close #768

📍주요 변경 사항

  • 400번대 응답은 WARN으로 변경하였습니다.
  • 500번대 응답은 ERROR로 변경하였습니다.
  • 위 두 변경에 대한 이유는 에러 로그가 찍힐 때 어떤 요청에서 발생한 에러인지 확인하기가 어려운 것 같았기 때문입니다.
  • 아래 화면과 같이 스택 트레이스는 한 로그에 포함되도록 수정하였습니다.
스크린샷 2024-10-23 14 56 11 - 위에 있는 두 로그로 어떤 요청에서 발생한 오류인지 확인할 수 있습니다. - 정규표현식 이슈로 로그에 색은 포함시키지 못했습니다.. - 이 부분은 5 레벨에 시간이 되면 개선해보도록 하겠습니다..ㅠ

promtail 설정파일 변경

- regex:
          expression: '^\[(?P<level>\w+)\]\[(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\[(?P<correlationId>[\w\s-]+)\]\[(?P<logger>.*?)\]\s(?P<message>.*)$'
  • 모든 로그의 기본 형태는 똑같습니다. (이 부분에서 색 처리에 대한 문제가 발생했는데, 색을 입힐 때 level 앞에 %cyan 이런 식으로 문자열이 들어갑니다. 근데 이게 레벨마다 문자열이 다 달라서 정규식 표현이 쉽지않음...)
  • 그 외에는 level에 따라 WARN, ERROR, INFO의 경우로 나누었습니다.
    • WARN과 ERROR는 스택 트레이스가 포함된 에러 로그, 요청 응답에 대한 로그로 나뉘어져 있습니다.
    • INFO는 요청 응답에 대한 로그만 포함하고 있습니다.

🎸기타

  • spring 로그와 nginx 로그를 분리하였습니다.
  • nginx 로그에서는 status 별로 필터링하여 확인이 가능합니다.
  • spring 로그에서는 level 별로 필터링하여 확인이 가능합니다.
  • 운영 서버에서 INFO 로그는 찍히지 않습니다.

🍗 PR 첫 리뷰 마감 기한

10:24 23:59

@HoeSeong123 HoeSeong123 added BE 백엔드 infrastructure 인프라 작업 labels Oct 23, 2024
@HoeSeong123 HoeSeong123 added this to the 6차 스프린트 🦴 milestone Oct 23, 2024
@HoeSeong123 HoeSeong123 self-assigned this Oct 23, 2024
@HoeSeong123 HoeSeong123 changed the base branch from main to dev/be October 23, 2024 06:22
Copy link
Contributor

@zangsu zangsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

초롱~ 고생 많으셨어요!
수정사항 남겨두었어요!!

Copy link
Contributor

@jminkkk jminkkk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

악 펜딩상태였네요ㅠ 리뷰 하나 봐주세요
초롱 수고했어요!!!!!!!!

모든 로그의 기본 형태는 똑같습니다. (이 부분에서 색 처리에 대한 문제가 발생했는데, 색을 입힐 때 level 앞에 %cyan 이런 식으로 문자열이 들어갑니다. 근데 이게 레벨마다 문자열이 다 달라서 정규식 표현이 쉽지않음...)

%highlight을 사용하면 알아서 레벨별로 색 칠해주지 않나용...??

Comment on lines 62 to 73
if (isError(status)) {
log.error(requestMessage);
log.error(requestMessage);
}
if (isWarn(status)) {
log.warn(requestMessage);
log.warn(responseMessage);
}
if (isInfo(status)) {
log.info(requestMessage);
log.info(responseMessage);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소하지만 메서드 분리해보면 어떨까요~~??

isError() 메서드에 log까지 아예 포함하도록 변경해봐도 좋을 것 같슴당

    private void logError(int status) {
        boolean isError = status >= ERROR_CODE;
        if (isError(status)) {
            log.error(requestMessage);
            log.error(requestMessage);
        }
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 좋아요👍

kyum-q
kyum-q previously approved these changes Nov 4, 2024
Copy link
Contributor

@kyum-q kyum-q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

초롱 수고하셨어요 ~!
미루고 미루다 이제 봤네요 ,,, ! 체곱니다 !

Copy link
Contributor Author

@HoeSeong123 HoeSeong123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공통

  • level, timestamp, correlationId 등 불필요한 정보는 화면에 보이지 않고 필드로만 가지고 있게 변경하였습니다.
  • correlationId의 경우 로그의 순서가 뒤섞일 수도 있어서 검색을 통해 원하는 요청에 대한 로그만 확인할 수 있습니다.
  • level 필터링을 통해 원하는 레벨의 로그만 확인할 수 있습니다.

WARN

  • WARN의 경우는 아래와 같이 스택 트레이스를 제거했습니다.
  • 이미 우리가 예상하고 있는 에러이기 때문에 에러 제목만으로도 해결할 수 있다고 판단했습니다.
    image

ERROR

  • ERROR의 경우는 아래와 같이 스택 트레이스가 같이 보이게 하였습니다.
  • 로그 한 줄에 전부 포함되게 하였으며 로그를 클릭하면 아래로 펼쳐지는 형태로 변경하였습니다.
  • 하지만 스택 트레이스가 너무 길어서 하나의 로그에 전부 담기지 못하고 있습니다.
  • 그래도 충분한 길이의 스택 트레이스가 담기기는 해서 이걸로 웬만하면 해결 가능하지 않을까 싶어서 일단 냅뒀습니다.
    • 만약 필요하다고 판단되면 해결책을 다시 생각해볼게요..
      image

++

%highlight을 사용하면 알아서 레벨별로 색 칠해주지 않나용...??

시도하려고 했는데 어차피 level, timestamp 이런거 전부 로그에 안보이게 수정해서 그냥 시도 안했습니다 ㅎㅎ
혹시 필요할 것 같다고 생각되면 얘기해주세요. 어렵진 않을듯?

jminkkk
jminkkk previously approved these changes Nov 9, 2024
Copy link
Contributor

@jminkkk jminkkk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correlationId를 더 쉽게 사용할 수 있군요 너무 편하겠어요~~
수고하셨씁니다~~

Copy link
Contributor

@zeus6768 zeus6768 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

드디어 로그가 개선되는군요 멋져요 👍

간단한 코멘트 남겼으니 확인해줘요 초롱~~

Comment on lines 97 to 119
private void logError(int status, String requestMessage, String responseMessage) {
boolean isError = status >= ERROR_CODE;
if (isError) {
log.error(requestMessage);
log.error(responseMessage);
}
}

private void logWarn(int status, String requestMessage, String responseMessage) {
boolean isWarn = status >= WARN_CODE && status < ERROR_CODE;
if (isWarn) {
log.warn(requestMessage);
log.warn(responseMessage);
}
}

private void logInfo(int status, String requestMessage, String responseMessage) {
boolean isInfo = status < WARN_CODE;
if (isInfo) {
log.info(requestMessage);
log.info(responseMessage);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 다음과 같이 작성하는 게 더 낫다고 생각해서 제안해요~

이렇게하면

  1. Early Return으로 호출되는 코드 수를 줄일 수 있어요.
  2. logResponse() 메서드에서 하나의 메서드만 호출하면 돼요.
  3. RequestResponseLogger 클래스의 전체 라인 수가 줄어들어요.
Suggested change
private void logError(int status, String requestMessage, String responseMessage) {
boolean isError = status >= ERROR_CODE;
if (isError) {
log.error(requestMessage);
log.error(responseMessage);
}
}
private void logWarn(int status, String requestMessage, String responseMessage) {
boolean isWarn = status >= WARN_CODE && status < ERROR_CODE;
if (isWarn) {
log.warn(requestMessage);
log.warn(responseMessage);
}
}
private void logInfo(int status, String requestMessage, String responseMessage) {
boolean isInfo = status < WARN_CODE;
if (isInfo) {
log.info(requestMessage);
log.info(responseMessage);
}
}
private void logByStatus(int status, String requestMessage, String responseMessage) {
if (status >= ERROR_CODE) {
log.error(requestMessage);
log.error(responseMessage);
return;
}
if (status >= WARN_CODE) {
log.warn(requestMessage);
log.warn(responseMessage);
return;
}
log.info(requestMessage);
log.info(responseMessage);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와우 진짜 너무 깔끔한데요👍

Comment on lines 68 to 86
private String getHeaderAsJson(ContentCachingRequestWrapper requestWrapper) {
Map<String, String> headersMap = new HashMap<>();
requestWrapper.getHeaderNames().asIterator().forEachRemaining(headerName -> {
String headerValue = requestWrapper.getHeader(headerName);
headerAndValue.append(headerName).append(" : ").append(headerValue).append("\n");
headersMap.put(headerName, headerValue);
});

return headerAndValue.toString();
return convertMapToJson(headersMap);
}

private String getHeaderAndValue(ContentCachingResponseWrapper requestWrapper) {
return requestWrapper.getHeaderNames().stream().map(headerName -> {
String headerValue = requestWrapper.getHeader(headerName);
return headerName + " : " + headerValue;
}).collect(Collectors.joining("\n"));
private String getHeaderAsJson(ContentCachingResponseWrapper responseWrapper) {
Map<String, String> headersMap = new HashMap<>();
responseWrapper.getHeaderNames().forEach(headerName -> {
String headerValue = responseWrapper.getHeader(headerName);
headersMap.put(headerName, headerValue);
});

return convertMapToJson(headersMap);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동일한 로직이지만, 디미터의 법칙 측면에서 개선할 수 있을 것 같아 제안해요~

    private String getHeaderAsJson(ContentCachingRequestWrapper requestWrapper) {
        Map<String, String> headersMap = new HashMap<>();
        Enumeration<String> headerNames = requestWrapper.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headersMap.put(headerName, requestWrapper.getHeader(headerName));
        }
        return convertMapToJson(headersMap);
    }

    private String getHeaderAsJson(ContentCachingResponseWrapper responseWrapper) {
        Map<String, String> headersMap = new HashMap<>();
        for (String headerName : responseWrapper.getHeaderNames()) {
            headersMap.put(headerName, responseWrapper.getHeader(headerName));
        }
        return convertMapToJson(headersMap);
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가독성도 더 올라가는 것 같네요!!

…refactor/768-log-refactor

# Conflicts:
#	backend/submodules/private
Copy link
Contributor Author

@HoeSeong123 HoeSeong123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 다음과 같이 prod, dev 별로 로그를 따로 확인할 수 있도록 변경하였습니다.
    • 이렇게 변경한 이유는 prod에서 인스턴스 별로 로그를 나눠서 확인하기 위함입니다.
    • 이를 위해 production의 secret 값이 변경되었으므로 확인 부탁드립니다.
스크린샷 2024-11-11 16 39 03

Comment on lines 97 to 119
private void logError(int status, String requestMessage, String responseMessage) {
boolean isError = status >= ERROR_CODE;
if (isError) {
log.error(requestMessage);
log.error(responseMessage);
}
}

private void logWarn(int status, String requestMessage, String responseMessage) {
boolean isWarn = status >= WARN_CODE && status < ERROR_CODE;
if (isWarn) {
log.warn(requestMessage);
log.warn(responseMessage);
}
}

private void logInfo(int status, String requestMessage, String responseMessage) {
boolean isInfo = status < WARN_CODE;
if (isInfo) {
log.info(requestMessage);
log.info(responseMessage);
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와우 진짜 너무 깔끔한데요👍

Comment on lines 68 to 86
private String getHeaderAsJson(ContentCachingRequestWrapper requestWrapper) {
Map<String, String> headersMap = new HashMap<>();
requestWrapper.getHeaderNames().asIterator().forEachRemaining(headerName -> {
String headerValue = requestWrapper.getHeader(headerName);
headerAndValue.append(headerName).append(" : ").append(headerValue).append("\n");
headersMap.put(headerName, headerValue);
});

return headerAndValue.toString();
return convertMapToJson(headersMap);
}

private String getHeaderAndValue(ContentCachingResponseWrapper requestWrapper) {
return requestWrapper.getHeaderNames().stream().map(headerName -> {
String headerValue = requestWrapper.getHeader(headerName);
return headerName + " : " + headerValue;
}).collect(Collectors.joining("\n"));
private String getHeaderAsJson(ContentCachingResponseWrapper responseWrapper) {
Map<String, String> headersMap = new HashMap<>();
responseWrapper.getHeaderNames().forEach(headerName -> {
String headerValue = responseWrapper.getHeader(headerName);
headersMap.put(headerName, headerValue);
});

return convertMapToJson(headersMap);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가독성도 더 올라가는 것 같네요!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BE 백엔드 infrastructure 인프라 작업
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

[REFACTOR] 그라파나 리팩토링
5 participants