-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [chore] #62 add discord appender dependency * [chore] #62 add discord logback dependency * [chore] #62 create logback yml * [feat] #62 create Wrapper to cache RequestBody * [feat] #62 create ServletWrappingFilter to cache requestBody * [feat] #62 create MDCFilter to save info in MDC * [feat] #62 apply filters with order * [feat] #62 create HttpRequestUtil to parse info from HttpRequest * [feat] #62 create MDCUtil to set MDC * [feat] #62 create model to send discord message prettier * [feat] #62 create discordWebHook & Appender to send message in jsonformat * [feat] #62 create ApiCallUtil to send discord message send API * [feat] #62 add Discord ErrorCode in BusinessErrorCode * [feat] #62 pass exception over log
- Loading branch information
Showing
25 changed files
with
980 additions
and
3 deletions.
There are no files selected for viewing
Submodule SERVER_YML
updated
from 049619 to e77592
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
src/main/java/org/kkumulkkum/server/config/FilterConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.kkumulkkum.server.config; | ||
|
||
import org.kkumulkkum.server.log.filter.ServletWrappingFilter; | ||
import org.kkumulkkum.server.log.filter.MDCFilter; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class FilterConfig { | ||
@Bean | ||
public FilterRegistrationBean<ServletWrappingFilter> secondFilter() { | ||
FilterRegistrationBean<ServletWrappingFilter> filterRegistrationBean = new FilterRegistrationBean<>( | ||
new ServletWrappingFilter()); | ||
filterRegistrationBean.setOrder(0); | ||
return filterRegistrationBean; | ||
} | ||
|
||
@Bean | ||
public FilterRegistrationBean<MDCFilter> thirdFilter() { | ||
FilterRegistrationBean<MDCFilter> filterRegistrationBean = new FilterRegistrationBean<>( | ||
new MDCFilter()); | ||
filterRegistrationBean.setOrder(1); | ||
return filterRegistrationBean; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
src/main/java/org/kkumulkkum/server/log/discord/DiscordAppender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package org.kkumulkkum.server.log.discord; | ||
|
||
import ch.qos.logback.classic.spi.ILoggingEvent; | ||
import ch.qos.logback.classic.spi.IThrowableProxy; | ||
import ch.qos.logback.classic.spi.ThrowableProxyUtil; | ||
import ch.qos.logback.core.UnsynchronizedAppenderBase; | ||
import io.micrometer.core.instrument.util.StringEscapeUtils; | ||
import lombok.Setter; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.kkumulkkum.server.exception.BusinessException; | ||
import org.kkumulkkum.server.exception.code.BusinessErrorCode; | ||
import org.kkumulkkum.server.log.model.EmbedObject; | ||
import org.kkumulkkum.server.log.util.MDCUtil; | ||
import org.kkumulkkum.server.log.util.StringUtil; | ||
|
||
import java.awt.*; | ||
import java.io.IOException; | ||
import java.time.LocalDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.Map; | ||
|
||
@Slf4j | ||
@Setter | ||
public class DiscordAppender extends UnsynchronizedAppenderBase<ILoggingEvent> { | ||
|
||
private String discordWebhookUrl; | ||
private String username; | ||
private String avatarUrl; | ||
|
||
private static Color getLevelColor(ILoggingEvent eventObject) { | ||
String level = eventObject.getLevel().levelStr; | ||
if (level.equals("WARN")) { | ||
return Color.yellow; | ||
} else if (level.equals("ERROR")) { | ||
return Color.red; | ||
} | ||
|
||
return Color.blue; | ||
} | ||
|
||
@Override | ||
protected void append(ILoggingEvent eventObject) { | ||
DiscordWebHook discordWebhook = new DiscordWebHook(discordWebhookUrl, username, avatarUrl, false); | ||
Map<String, String> mdcPropertyMap = eventObject.getMDCPropertyMap(); | ||
Color messageColor = getLevelColor(eventObject); | ||
|
||
String level = eventObject.getLevel().levelStr; | ||
String exceptionBrief = ""; | ||
String exceptionDetail = ""; | ||
IThrowableProxy throwable = eventObject.getThrowableProxy(); | ||
|
||
if (throwable != null) { | ||
exceptionBrief = throwable.getClassName() + ": " + throwable.getMessage(); | ||
} | ||
|
||
if (exceptionBrief.equals("")) { | ||
exceptionBrief = "EXCEPTION 정보가 남지 않았습니다."; | ||
} | ||
|
||
discordWebhook.addEmbed(new EmbedObject() | ||
.setTitle("[" + level + " - 문제 간략 내용]") | ||
.setColor(messageColor) | ||
.setDescription(exceptionBrief) | ||
.addField("[" + "Exception Level" + "]", | ||
StringEscapeUtils.escapeJson(level), | ||
true) | ||
.addField("[문제 발생 시각]", | ||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), | ||
false) | ||
.addField( | ||
"[" + MDCUtil.REQUEST_URI_MDC + "]", | ||
StringEscapeUtils.escapeJson(mdcPropertyMap.get(MDCUtil.REQUEST_URI_MDC)), | ||
false) | ||
.addField( | ||
"[" + MDCUtil.USER_IP_MDC + "]", | ||
StringEscapeUtils.escapeJson(mdcPropertyMap.get(MDCUtil.USER_IP_MDC)), | ||
false) | ||
.addField( | ||
"[" + MDCUtil.USER_INFO + "]", | ||
StringEscapeUtils.escapeJson(mdcPropertyMap.get(MDCUtil.USER_INFO)), | ||
false) | ||
.addField( | ||
"[" + MDCUtil.HEADER_MAP_MDC + "]", | ||
StringEscapeUtils.escapeJson(mdcPropertyMap.get(MDCUtil.HEADER_MAP_MDC).replaceAll("[\\{\\{\\}]", "")), | ||
true) | ||
// .addField( | ||
// "[" + MDCUtil.USER_REQUEST_COOKIES + "]", | ||
// StringEscapeUtils.escapeJson( | ||
// mdcPropertyMap.get(MDCUtil.USER_REQUEST_COOKIES).replaceAll("[\\{\\{\\}]", "")), | ||
// false) | ||
.addField( | ||
"[" + MDCUtil.PARAMETER_MAP_MDC + "]", | ||
StringEscapeUtils.escapeJson( | ||
mdcPropertyMap.get(MDCUtil.PARAMETER_MAP_MDC).replaceAll("[\\{\\{\\}]", "")), | ||
false) | ||
.addField("[" + MDCUtil.BODY_MDC + "]", | ||
StringEscapeUtils.escapeJson(StringUtil.translateEscapes(mdcPropertyMap.get(MDCUtil.BODY_MDC))), | ||
false) | ||
); | ||
|
||
if (throwable != null) { | ||
exceptionDetail = ThrowableProxyUtil.asString(throwable); | ||
String exception = exceptionDetail.substring(0, 4000); | ||
discordWebhook.addEmbed( | ||
new EmbedObject() | ||
.setTitle("[Exception 상세 내용]") | ||
.setColor(messageColor) | ||
.setDescription(StringEscapeUtils.escapeJson(exception)) | ||
); | ||
} | ||
|
||
try { | ||
discordWebhook.execute(); | ||
} catch (IOException ioException) { | ||
throw new BusinessException(BusinessErrorCode.DISCORD_WEBHOOK_ERROR); | ||
} | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
src/main/java/org/kkumulkkum/server/log/discord/DiscordWebHook.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package org.kkumulkkum.server.log.discord; | ||
|
||
import org.kkumulkkum.server.exception.BusinessException; | ||
import org.kkumulkkum.server.exception.code.BusinessErrorCode; | ||
import org.kkumulkkum.server.log.model.*; | ||
import org.kkumulkkum.server.log.model.Image; | ||
import org.kkumulkkum.server.log.util.ApiCallUtil; | ||
|
||
import java.awt.*; | ||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class DiscordWebHook { | ||
|
||
private final String urlString; | ||
private final List<EmbedObject> embeds = new ArrayList<>(); | ||
private String username; | ||
private String avatarUrl; | ||
private boolean tts; | ||
|
||
public DiscordWebHook(String urlString, String username, String avatarUrl, boolean tts) { | ||
this.urlString = urlString; | ||
this.username = username; | ||
this.avatarUrl = avatarUrl; | ||
this.tts = tts; | ||
} | ||
|
||
public void addEmbed(EmbedObject embed) { | ||
this.embeds.add(embed); | ||
} | ||
|
||
public void execute() throws IOException { | ||
if (this.embeds.isEmpty()) { | ||
throw new RuntimeException("컨텐츠를 설정하거나 하나 이상의 Embed Object를 추가해야 합니다."); | ||
} | ||
|
||
try { | ||
ApiCallUtil.callDiscordAppenderPostAPI( | ||
this.urlString, createDiscordEmbedObject( | ||
this.embeds, initializerDiscordSendForJsonObject(new JsonObject()) | ||
)); | ||
|
||
} catch (IOException ioException) { | ||
throw ioException; | ||
} | ||
} | ||
|
||
private JsonObject initializerDiscordSendForJsonObject(JsonObject json) { | ||
json.put("username", this.username); | ||
json.put("avatar_url", this.avatarUrl); | ||
json.put("tts", this.tts); | ||
return json; | ||
} | ||
|
||
private JsonObject createDiscordEmbedObject(List<EmbedObject> embeds, JsonObject json) { | ||
if (embeds.isEmpty()) { | ||
throw new BusinessException(BusinessErrorCode.DISCORD_WEBHOOK_ERROR); | ||
} | ||
|
||
List<JsonObject> embedObjects = new ArrayList<>(); | ||
|
||
for (EmbedObject embed : embeds) { | ||
JsonObject jsonEmbed = new JsonObject(); | ||
|
||
jsonEmbed.put("title", embed.getTitle()); | ||
jsonEmbed.put("description", embed.getDescription()); | ||
jsonEmbed.put("url", embed.getUrl()); | ||
|
||
processDiscordEmbedColor(embed, jsonEmbed); | ||
processDiscordEmbedFooter(embed.getFooter(), jsonEmbed); | ||
processDiscordEmbedImage(embed.getImage(), jsonEmbed); | ||
processDiscordEmbedThumbnail(embed.getThumbnail(), jsonEmbed); | ||
processDiscordEmbedAuthor(embed.getAuthor(), jsonEmbed); | ||
processDiscordEmbedMessageFields(embed.getFields(), jsonEmbed); | ||
|
||
embedObjects.add(jsonEmbed); | ||
} | ||
json.put("embeds", embedObjects.toArray()); | ||
|
||
return json; | ||
} | ||
|
||
private void processDiscordEmbedColor(EmbedObject embed, JsonObject jsonEmbed) { | ||
if (embed.getColor() != null) { | ||
Color color = embed.getColor(); | ||
int rgb = color.getRed(); | ||
rgb = (rgb << 8) + color.getGreen(); | ||
rgb = (rgb << 8) + color.getBlue(); | ||
|
||
jsonEmbed.put("color", rgb); | ||
} | ||
} | ||
|
||
private void processDiscordEmbedFooter(Footer footer, JsonObject jsonEmbed) { | ||
if (footer != null) { | ||
JsonObject jsonFooter = new JsonObject(); | ||
jsonFooter.put("text", footer.getText()); | ||
jsonFooter.put("icon_url", footer.getIconUrl()); | ||
jsonEmbed.put("footer", jsonFooter); | ||
} | ||
} | ||
|
||
private void processDiscordEmbedImage(Image image, JsonObject jsonEmbed) { | ||
if (image != null) { | ||
JsonObject jsonImage = new JsonObject(); | ||
jsonImage.put("url", image.getUrl()); | ||
jsonEmbed.put("image", jsonImage); | ||
} | ||
} | ||
|
||
private void processDiscordEmbedThumbnail(Thumbnail thumbnail, JsonObject jsonEmbed) { | ||
if (thumbnail != null) { | ||
JsonObject jsonThumbnail = new JsonObject(); | ||
jsonThumbnail.put("url", thumbnail.getUrl()); | ||
jsonEmbed.put("thumbnail", jsonThumbnail); | ||
} | ||
} | ||
|
||
private void processDiscordEmbedAuthor(Author author, JsonObject jsonEmbed) { | ||
if (author != null) { | ||
JsonObject jsonAuthor = new JsonObject(); | ||
jsonAuthor.put("name", author.getName()); | ||
jsonAuthor.put("url", author.getUrl()); | ||
jsonAuthor.put("icon_url", author.getIconUrl()); | ||
jsonEmbed.put("author", jsonAuthor); | ||
} | ||
} | ||
|
||
private void processDiscordEmbedMessageFields(List<Field> fields, JsonObject jsonEmbed) { | ||
List<JsonObject> jsonFields = new ArrayList<>(); | ||
|
||
for (Field field : fields) { | ||
JsonObject jsonField = new JsonObject(); | ||
|
||
jsonField.put("name", field.getName()); | ||
jsonField.put("value", field.getValue()); | ||
jsonField.put("inline", field.isInline()); | ||
|
||
jsonFields.add(jsonField); | ||
} | ||
|
||
jsonEmbed.put("fields", jsonFields.toArray()); | ||
} | ||
} | ||
|
42 changes: 42 additions & 0 deletions
42
src/main/java/org/kkumulkkum/server/log/filter/MDCFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.kkumulkkum.server.log.filter; | ||
|
||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.kkumulkkum.server.log.util.HttpRequestUtil; | ||
import org.kkumulkkum.server.log.util.MDCUtil; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
import org.springframework.web.util.WebUtils; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
@RequiredArgsConstructor | ||
@Slf4j | ||
@Component | ||
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); | ||
|
||
MDCUtil.setJsonValue(MDCUtil.REQUEST_URI_MDC, HttpRequestUtil.getRequestUri(Objects.requireNonNull(httpReq))); | ||
MDCUtil.setJsonValue(MDCUtil.USER_IP_MDC, HttpRequestUtil.getUserIP(Objects.requireNonNull(httpReq))); | ||
MDCUtil.setJsonValue(MDCUtil.HEADER_MAP_MDC, HttpRequestUtil.getHeaderMap(httpReq)); | ||
MDCUtil.setJsonValue(MDCUtil.USER_INFO, SecurityContextHolder.getContext().getAuthentication().getPrincipal()); | ||
// MDCUtil.setJsonValue(MDCUtil.USER_REQUEST_COOKIES, HttpRequestUtil.getUserCookies(httpReq)); | ||
MDCUtil.setJsonValue(MDCUtil.PARAMETER_MAP_MDC, HttpRequestUtil.getParamMap(httpReq)); | ||
MDCUtil.setJsonValue(MDCUtil.BODY_MDC, HttpRequestUtil.getBody(httpReq)); | ||
|
||
filterChain.doFilter(request, response); | ||
|
||
} | ||
} |
Oops, something went wrong.