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

[FEAT] 서버 에러 로그 Discord 연결 - #226 #293

Merged
merged 13 commits into from
Oct 3, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.dateroad.point.repository.PointRepository;
import org.dateroad.refreshtoken.domain.RefreshToken;
import org.dateroad.refreshtoken.repository.RefreshTokenRepository;
import org.dateroad.s3.S3Service;
import org.dateroad.tag.domain.DateTagType;
import org.dateroad.tag.domain.UserTag;
import org.dateroad.tag.repository.UserTagRepository;
Expand Down
7 changes: 7 additions & 0 deletions dateroad-api/src/main/resources/console-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</included>
18 changes: 18 additions & 0 deletions dateroad-api/src/main/resources/discord-appender.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<included>
<appender name="DISCORD" class="org.dateroad.feign.discord.DiscordLogAppender">
<param name="discordLogUrl" value="${discordLogUrl}" />
</appender>
<appender name="ASYNC_DISCORD" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="DISCORD" />
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- 비동기 Discord 로그 설정 -->
<appender name="ASYNC_DISCORD" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="DISCORD" />
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
</included>
12 changes: 12 additions & 0 deletions dateroad-api/src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<springProperty name="discordLogUrl" source="feign.discord.webhook.log-url"/>
<include resource="console-appender.xml"/>
<include resource="discord-appender.xml"/>

<!-- 로그 레벨 지정 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_DISCORD" />
</root>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Optional;
import org.assertj.core.api.Assertions;
import org.dateroad.auth.jwt.JwtProvider;
import org.dateroad.auth.jwt.Token;
import org.dateroad.code.FailureCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public enum FailureCode {
* 403 Forbidden
*/
FORBIDDEN(HttpStatus.FORBIDDEN, "e4030", "리소스 접근 권한이 없습니다."),
DATE_DELETE_ACCESS_DENIED(HttpStatus.FORBIDDEN, "e4032", "해당 일정에 권한이 없습니다."),
DATE_DELETE_ACCESS_DENIED(HttpStatus.FORBIDDEN, "e4031", "해당 일정에 권한이 없습니다."),

/**
* 404 Not Found
Expand Down Expand Up @@ -99,8 +99,9 @@ public enum FailureCode {
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5000", "서버 내부 오류입니다."),
COURSE_CREATE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"e5001" , "코스 생성에 실패했습니다."),
POINT_CREATE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5002", "포인트 생성에 실패했습니다"),
REDIS_CONNECTION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5003", "Redis 연결에 실패했습니다");
POINT_CREATE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5002", "포인트 생성에 실패했습니다."),
REDIS_CONNECTION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5003", "Redis 연결에 실패했습니다."),
DISCORD_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "e5004", "디스코드 로그 전송 내용이 존재하지 않습니다.");


private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package org.dateroad.event;
import lombok.AccessLevel;
import lombok.Builder;
import org.dateroad.code.EventCode;

@Builder
@Builder(access = AccessLevel.PRIVATE)
public record SignUpEventInfo (
EventCode eventCode,
String nickName,
Expand Down
29 changes: 29 additions & 0 deletions dateroad-common/src/main/java/org/dateroad/mdc/MDCFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.dateroad.mdc;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;

import java.io.IOException;

@Slf4j
public class MDCFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpReq = WebUtils.getNativeRequest(request, HttpServletRequest.class);

MDCManager.setJsonValue(MDCManager.MDC_REQUEST_URI, MDCManager.getRequestUri(httpReq));
MDCManager.setJsonValue(MDCManager.MDC_USER_IP, MDCManager.getUserIP(httpReq));
MDCManager.setJsonValue(MDCManager.MDC_REQUEST_COOKIES, MDCManager.getCookies(httpReq));
MDCManager.setJsonValue(MDCManager.MDC_REQUEST_ORIGIN, MDCManager.getRequestOrigin(httpReq));
MDCManager.setJsonValue(MDCManager.MDC_HEADER, MDCManager.getHeader(httpReq));
MDCManager.setJsonValue(MDCManager.MDC_PARAMETER, MDCManager.getParameter(httpReq));
MDCManager.set(MDCManager.MDC_BODY, MDCManager.getBody(httpReq));

filterChain.doFilter(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.dateroad.mdc;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Profile("!local")
@Configuration
public class MDCFilterConfig {
@Bean
public FilterRegistrationBean<RequestWrappingFilter> secondFilter() {
FilterRegistrationBean<RequestWrappingFilter> filterRegistrationBean = new FilterRegistrationBean<>(new RequestWrappingFilter());
filterRegistrationBean.setOrder(0);
return filterRegistrationBean;
}

@Bean
public FilterRegistrationBean<MDCFilter> thirdFilter() {
FilterRegistrationBean<MDCFilter> filterRegistrationBean = new FilterRegistrationBean<>(new MDCFilter());
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
}
97 changes: 97 additions & 0 deletions dateroad-common/src/main/java/org/dateroad/mdc/MDCManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.dateroad.mdc;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.slf4j.spi.MDCAdapter;
import org.springframework.stereotype.Component;
import org.springframework.web.util.WebUtils;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class MDCManager {
public static final String MDC_REQUEST_URI = "Request URI";
public static final String MDC_USER_IP = "사용자 IP";
public static final String MDC_REQUEST_COOKIES = "Request Cookie";
public static final String MDC_REQUEST_ORIGIN = "Request Origin";
public static final String MDC_HEADER = "HTTP Header";
public static final String MDC_PARAMETER = "Parameter";
public static final String MDC_BODY = "HTTP Body";
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final MDCAdapter mdcAdapter = MDC.getMDCAdapter();
public static void set(String key, String value) {
mdcAdapter.put(key, value);
}

public static Object get(String key) {
return mdcAdapter.get(key);
}

public static void setJsonValue(String key, Object value) throws JsonProcessingException {
try {
if (value != null) {
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
mdcAdapter.put(key, json);
} else {
mdcAdapter.put(key, "내용이 없습니다.");
}
} catch (JsonProcessingException ex) {
throw ex;
}
}

public static String getRequestUri(HttpServletRequest request) {
return request.getRequestURI();
}

public static String getUserIP(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null)
ip = request.getRemoteAddr();

return ip;
}

public static Cookie[] getCookies(HttpServletRequest request) {
return request.getCookies();
}

public static String getRequestOrigin(HttpServletRequest request) {
return request.getHeader("Origin");
}

public static Map<String, String> getHeader(HttpServletRequest request) {
Map<String, String> headerMap = new HashMap<>();
request.getHeaderNames().asIterator()
.forEachRemaining(name -> {
if (!name.equals("user-agent")) {
headerMap.put(name, request.getHeader(name));
}
});
return headerMap;
}

public static Map<String, String> getParameter(HttpServletRequest request) {
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(name -> paramMap.put(name, request.getParameter(name)));

return paramMap;
}

public static String getBody(HttpServletRequest request) {
RequestBodyWrapper requestBodyWrapper = WebUtils.getNativeRequest(request, RequestBodyWrapper.class);

if (requestBodyWrapper != null) {
return requestBodyWrapper.getRequestBody();
}

return "requestBody 정보 없음";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.dateroad.mdc;

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;
import org.dateroad.code.FailureCode;
import org.dateroad.exception.BadRequestException;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

@Getter
public class RequestBodyWrapper extends HttpServletRequestWrapper {
private final String requestBody;

public RequestBodyWrapper(HttpServletRequest request) {
super(request);

StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader bufferedReader = request.getReader()) {
char[] charBuffer = new char[128];
int bytesRead;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} catch (IOException e) {
throw new BadRequestException(FailureCode.BAD_REQUEST);
}

requestBody = stringBuilder.toString();
}

@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}

public int read() {
return byteArrayInputStream.read();
}
};
}

@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.dateroad.mdc;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Slf4j
public class RequestWrappingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
RequestBodyWrapper requestBodyWrapper = new RequestBodyWrapper(request);
filterChain.doFilter(requestBodyWrapper, response);
}
}
Loading
Loading