Skip to content

Commit

Permalink
[merge] 서버 에러 로그 Discord 연결 - #226
Browse files Browse the repository at this point in the history
[FEAT] 서버 에러 로그 Discord 연결 - #226
  • Loading branch information
gardening-y authored Oct 3, 2024
2 parents d4c2204 + 32cb280 commit 5a78679
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 7 deletions.
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

0 comments on commit 5a78679

Please sign in to comment.